mirror of
https://github.com/tendermint/tendermint.git
synced 2026-01-12 07:42:48 +00:00
Compare commits
60 Commits
v0.26.4
...
show_valid
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ee32d3089 | ||
|
|
8a993c6486 | ||
|
|
3547ce2516 | ||
|
|
f7e463f6d3 | ||
|
|
bc2a9b20c0 | ||
|
|
9e075d8dd5 | ||
|
|
8003786c9a | ||
|
|
2594cec116 | ||
|
|
df32ea4be5 | ||
|
|
f69e2c6d6c | ||
|
|
d5d0d2bd77 | ||
|
|
3d5e06ff7c | ||
|
|
41eaf0e31d | ||
|
|
e33f256359 | ||
|
|
68b467886a | ||
|
|
5c09275691 | ||
|
|
094699f9bc | ||
|
|
c8ab062ce9 | ||
|
|
1765eb2cd2 | ||
|
|
2f64717bb5 | ||
|
|
c4a1cfc5c2 | ||
|
|
0f96bea41d | ||
|
|
9c236ffd6c | ||
|
|
9f8761d105 | ||
|
|
5413c11150 | ||
|
|
a14fd8eba0 | ||
|
|
1bb7e31d63 | ||
|
|
222b8978c8 | ||
|
|
d9a1aad5c5 | ||
|
|
8ef0c2681d | ||
|
|
df20323cd2 | ||
|
|
f17c04c892 | ||
|
|
50ac191f9d | ||
|
|
977a138b1f | ||
|
|
1c6ed0e7d0 | ||
|
|
80c464c9b4 | ||
|
|
5233ac154f | ||
|
|
db04ff4896 | ||
|
|
394edc9230 | ||
|
|
1fa463ecab | ||
|
|
d79d2093ff | ||
|
|
78b7a41319 | ||
|
|
c4d93fd27b | ||
|
|
dc2a338d96 | ||
|
|
725ed7969a | ||
|
|
417c30b9c1 | ||
|
|
44b769b1ac | ||
|
|
380afaa678 | ||
|
|
b30c34e713 | ||
|
|
4039276085 | ||
|
|
3f987adc92 | ||
|
|
b11788d36d | ||
|
|
9adcfe2804 | ||
|
|
3d15579e0c | ||
|
|
4571f0fbe8 | ||
|
|
8a73feae14 | ||
|
|
e291fbbebe | ||
|
|
416d143bf7 | ||
|
|
7213869fc6 | ||
|
|
ef9902e602 |
@@ -7,6 +7,13 @@ defaults: &defaults
|
||||
environment:
|
||||
GOBIN: /tmp/workspace/bin
|
||||
|
||||
docs_update_config: &docs_update_config
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: tendermint/docs_deployment
|
||||
environment:
|
||||
AWS_REGION: us-east-1
|
||||
|
||||
jobs:
|
||||
setup_dependencies:
|
||||
<<: *defaults
|
||||
@@ -339,10 +346,25 @@ jobs:
|
||||
name: upload
|
||||
command: bash .circleci/codecov.sh -f coverage.txt
|
||||
|
||||
deploy_docs:
|
||||
<<: *docs_update_config
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Trigger website build
|
||||
command: |
|
||||
chamber exec tendermint -- start_website_build
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
test-suite:
|
||||
jobs:
|
||||
- deploy_docs:
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- develop
|
||||
- setup_dependencies
|
||||
- lint:
|
||||
requires:
|
||||
|
||||
72
CHANGELOG.md
72
CHANGELOG.md
@@ -1,13 +1,79 @@
|
||||
# Changelog
|
||||
|
||||
## v0.27.0
|
||||
|
||||
*December 5th, 2018*
|
||||
|
||||
Special thanks to external contributors on this release:
|
||||
@danil-lashin, @srmo
|
||||
|
||||
Special thanks to @dlguddus for discovering a [major
|
||||
issue](https://github.com/tendermint/tendermint/issues/2718#issuecomment-440888677)
|
||||
in the proposer selection algorithm.
|
||||
|
||||
Friendly reminder, we have a [bug bounty
|
||||
program](https://hackerone.com/tendermint).
|
||||
|
||||
This release is primarily about fixes to the proposer selection algorithm
|
||||
in preparation for the [Cosmos Game of
|
||||
Stakes](https://blog.cosmos.network/the-game-of-stakes-is-open-for-registration-83a404746ee6).
|
||||
It also makes use of the `ConsensusParams.Validator.PubKeyTypes` to restrict the
|
||||
key types that can be used by validators, and removes the `Heartbeat` consensus
|
||||
message.
|
||||
|
||||
### BREAKING CHANGES:
|
||||
|
||||
* CLI/RPC/Config
|
||||
- [rpc] [\#2932](https://github.com/tendermint/tendermint/issues/2932) Rename `accum` to `proposer_priority`
|
||||
|
||||
* Go API
|
||||
- [db] [\#2913](https://github.com/tendermint/tendermint/pull/2913)
|
||||
ReverseIterator API change: start < end, and end is exclusive.
|
||||
- [types] [\#2932](https://github.com/tendermint/tendermint/issues/2932) Rename `Validator.Accum` to `Validator.ProposerPriority`
|
||||
|
||||
* Blockchain Protocol
|
||||
- [state] [\#2714](https://github.com/tendermint/tendermint/issues/2714) Validators can now only use pubkeys allowed within
|
||||
ConsensusParams.Validator.PubKeyTypes
|
||||
|
||||
* P2P Protocol
|
||||
- [consensus] [\#2871](https://github.com/tendermint/tendermint/issues/2871)
|
||||
Remove *ProposalHeartbeat* message as it serves no real purpose (@srmo)
|
||||
- [state] Fixes for proposer selection:
|
||||
- [\#2785](https://github.com/tendermint/tendermint/issues/2785) Accum for new validators is `-1.125*totalVotingPower` instead of 0
|
||||
- [\#2941](https://github.com/tendermint/tendermint/issues/2941) val.Accum is preserved during ValidatorSet.Update to avoid being
|
||||
reset to 0
|
||||
|
||||
### IMPROVEMENTS:
|
||||
|
||||
- [state] [\#2929](https://github.com/tendermint/tendermint/issues/2929) Minor refactor of updateState logic (@danil-lashin)
|
||||
- [node] \#2959 Allow node to start even if software's BlockProtocol is
|
||||
different from state's BlockProtocol
|
||||
- [pex] \#2959 Pex reactor logger uses `module=pex`
|
||||
|
||||
### BUG FIXES:
|
||||
|
||||
- [p2p] \#2968 Panic on transport error rather than continuing to run but not
|
||||
accept new connections
|
||||
- [p2p] \#2969 Fix mismatch in peer count between `/net_info` and the prometheus
|
||||
metrics
|
||||
- [rpc] \#2408 `/broadcast_tx_commit`: Fix "interface conversion: interface {} in nil, not EventDataTx" panic (could happen if somebody sent a tx using `/broadcast_tx_commit` while Tendermint was being stopped)
|
||||
- [state] [\#2785](https://github.com/tendermint/tendermint/issues/2785) Fix accum for new validators to be `-1.125*totalVotingPower`
|
||||
instead of 0, forcing them to wait before becoming the proposer. Also:
|
||||
- do not batch clip
|
||||
- keep accums averaged near 0
|
||||
- [txindex/kv] [\#2925](https://github.com/tendermint/tendermint/issues/2925) Don't return false positives when range searching for a prefix of a tag value
|
||||
- [types] [\#2938](https://github.com/tendermint/tendermint/issues/2938) Fix regression in v0.26.4 where we panic on empty
|
||||
genDoc.Validators
|
||||
- [types] [\#2941](https://github.com/tendermint/tendermint/issues/2941) Preserve val.Accum during ValidatorSet.Update to avoid it being
|
||||
reset to 0 every time a validator is updated
|
||||
|
||||
## v0.26.4
|
||||
|
||||
*November 27th, 2018*
|
||||
|
||||
Special thanks to external contributors on this release:
|
||||
ackratos, goolAdapter, james-ray, joe-bowman, kostko,
|
||||
nagarajmanjunath, tomtau
|
||||
|
||||
@ackratos, @goolAdapter, @james-ray, @joe-bowman, @kostko,
|
||||
@nagarajmanjunath, @tomtau
|
||||
|
||||
Friendly reminder, we have a [bug bounty
|
||||
program](https://hackerone.com/tendermint).
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
# Pending
|
||||
|
||||
## v0.27.0
|
||||
## v0.27.1
|
||||
|
||||
*TBD*
|
||||
|
||||
Special thanks to external contributors on this release:
|
||||
|
||||
Friendly reminder, we have a [bug bounty
|
||||
program](https://hackerone.com/tendermint).
|
||||
|
||||
### BREAKING CHANGES:
|
||||
|
||||
* CLI/RPC/Config
|
||||
|
||||
- [privval] \#2926 split up `PubKeyMsg` into `PubKeyRequest` and `PubKeyResponse` to be consistent with other message types
|
||||
|
||||
* Apps
|
||||
|
||||
* Go API
|
||||
* Go API
|
||||
- [types] \#2926 memoize consensus public key on initialization of remote signer and return the memoized key on
|
||||
`PrivValidator.GetPubKey()` instead of requesting it again
|
||||
- [types] \#2981 Remove `PrivValidator.GetAddress()`
|
||||
|
||||
* Blockchain Protocol
|
||||
|
||||
@@ -24,5 +24,11 @@ program](https://hackerone.com/tendermint).
|
||||
### FEATURES:
|
||||
|
||||
### IMPROVEMENTS:
|
||||
- [rpc] Add `UnconfirmedTxs(limit)` and `NumUnconfirmedTxs()` methods to HTTP/Local clients (@danil-lashin)
|
||||
- [ci/cd] Updated CircleCI job to trigger website build when docs are updated
|
||||
|
||||
### BUG FIXES:
|
||||
- [kv indexer] \#2912 don't ignore key when executing CONTAINS
|
||||
- [types] \#2926 do not panic if retrieving the private validator's public key fails
|
||||
- [mempool] \#2994 Don't allow txs with negative gas wanted
|
||||
- [p2p] \#2715 fix a bug where seeds don't disconnect from a peer after 3h
|
||||
|
||||
5
Makefile
5
Makefile
@@ -32,6 +32,9 @@ build_race:
|
||||
install:
|
||||
CGO_ENABLED=0 go install $(BUILD_FLAGS) -tags $(BUILD_TAGS) ./cmd/tendermint
|
||||
|
||||
install_c:
|
||||
CGO_ENABLED=1 go install $(BUILD_FLAGS) -tags "$(BUILD_TAGS) gcc" ./cmd/tendermint
|
||||
|
||||
########################################
|
||||
### Protobuf
|
||||
|
||||
@@ -328,4 +331,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 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 build_c install_c
|
||||
|
||||
45
UPGRADING.md
45
UPGRADING.md
@@ -3,9 +3,50 @@
|
||||
This guide provides steps to be followed when you upgrade your applications to
|
||||
a newer version of Tendermint Core.
|
||||
|
||||
## v0.27.0
|
||||
|
||||
This release contains some breaking changes to the block and p2p protocols,
|
||||
but does not change any core data structures, so it should be compatible with
|
||||
existing blockchains from the v0.26 series that only used Ed25519 validator keys.
|
||||
Blockchains using Secp256k1 for validators will not be compatible. This is due
|
||||
to the fact that we now enforce which key types validators can use as a
|
||||
consensus param. The default is Ed25519, and Secp256k1 must be activated
|
||||
explicitly.
|
||||
|
||||
It is recommended to upgrade all nodes at once to avoid incompatibilities at the
|
||||
peer layer - namely, the heartbeat consensus message has been removed (only
|
||||
relevant if `create_empty_blocks=false` or `create_empty_blocks_interval > 0`),
|
||||
and the proposer selection algorithm has changed. Since proposer information is
|
||||
never included in the blockchain, this change only affects the peer layer.
|
||||
|
||||
### Go API Changes
|
||||
|
||||
#### libs/db
|
||||
|
||||
The ReverseIterator API has changed the meaning of `start` and `end`.
|
||||
Before, iteration was from `start` to `end`, where
|
||||
`start > end`. Now, iteration is from `end` to `start`, where `start < end`.
|
||||
The iterator also excludes `end`. This change allows a simplified and more
|
||||
intuitive logic, aligning the semantic meaning of `start` and `end` in the
|
||||
`Iterator` and `ReverseIterator`.
|
||||
|
||||
### Applications
|
||||
|
||||
This release enforces a new consensus parameter, the
|
||||
ValidatorParams.PubKeyTypes. Applications must ensure that they only return
|
||||
validator updates with the allowed PubKeyTypes. If a validator update includes a
|
||||
pubkey type that is not included in the ConsensusParams.Validator.PubKeyTypes,
|
||||
block execution will fail and the consensus will halt.
|
||||
|
||||
By default, only Ed25519 pubkeys may be used for validators. Enabling
|
||||
Secp256k1 requires explicit modification of the ConsensusParams.
|
||||
Please update your application accordingly (ie. restrict validators to only be
|
||||
able to use Ed25519 keys, or explicitly add additional key types to the genesis
|
||||
file).
|
||||
|
||||
## v0.26.0
|
||||
|
||||
New 0.26.0 release contains a lot of changes to core data types and protocols. It is not
|
||||
This release contains a lot of changes to core data types and protocols. It is not
|
||||
compatible to the old versions and there is no straight forward way to update
|
||||
old data to be compatible with the new version.
|
||||
|
||||
@@ -67,7 +108,7 @@ For more information, see:
|
||||
|
||||
### Go API Changes
|
||||
|
||||
#### crypto.merkle
|
||||
#### crypto/merkle
|
||||
|
||||
The `merkle.Hasher` interface was removed. Functions which used to take `Hasher`
|
||||
now simply take `[]byte`. This means that any objects being Merklized should be
|
||||
|
||||
@@ -42,7 +42,7 @@ func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.G
|
||||
}
|
||||
|
||||
func makeVote(header *types.Header, blockID types.BlockID, valset *types.ValidatorSet, privVal types.PrivValidator) *types.Vote {
|
||||
addr := privVal.GetAddress()
|
||||
addr := privVal.GetPubKey().Address()
|
||||
idx, _ := valset.GetByAddress(addr)
|
||||
vote := &types.Vote{
|
||||
ValidatorAddress: addr,
|
||||
|
||||
@@ -57,9 +57,10 @@ func initFilesWithConfig(config *cfg.Config) error {
|
||||
GenesisTime: tmtime.Now(),
|
||||
ConsensusParams: types.DefaultConsensusParams(),
|
||||
}
|
||||
key := pv.GetPubKey()
|
||||
genDoc.Validators = []types.GenesisValidator{{
|
||||
Address: pv.GetPubKey().Address(),
|
||||
PubKey: pv.GetPubKey(),
|
||||
Address: key.Address(),
|
||||
PubKey: key,
|
||||
Power: 10,
|
||||
}}
|
||||
|
||||
|
||||
@@ -2,12 +2,20 @@ package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"github.com/tendermint/tendermint/node"
|
||||
"github.com/tendermint/tendermint/privval"
|
||||
)
|
||||
|
||||
func init() {
|
||||
ShowValidatorCmd.Flags().String("priv_validator_laddr", config.PrivValidatorListenAddr, "Socket address to listen on for connections from external priv_validator process")
|
||||
}
|
||||
|
||||
// ShowValidatorCmd adds capabilities for showing the validator info.
|
||||
var ShowValidatorCmd = &cobra.Command{
|
||||
Use: "show_validator",
|
||||
@@ -16,7 +24,25 @@ var ShowValidatorCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
func showValidator(cmd *cobra.Command, args []string) {
|
||||
privValidator := privval.LoadOrGenFilePV(config.PrivValidatorFile())
|
||||
pubKeyJSONBytes, _ := cdc.MarshalJSON(privValidator.GetPubKey())
|
||||
var pubKey crypto.PubKey
|
||||
if config.PrivValidatorListenAddr != "" {
|
||||
// If an address is provided, listen on the socket for a connection from an
|
||||
// external signing process and request the public key from there.
|
||||
privValidator, err := node.CreateAndStartPrivValidatorSocketClient(config.PrivValidatorListenAddr, logger)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to get private validator's public key: %v", err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
if pvsc, ok := privValidator.(cmn.Service); ok {
|
||||
if err := pvsc.Stop(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to get stop private validator client: %v", err)
|
||||
}
|
||||
}
|
||||
pubKey = privValidator.GetPubKey()
|
||||
} else {
|
||||
privValidator := privval.LoadOrGenFilePV(config.PrivValidatorFile())
|
||||
pubKey = privValidator.GetPubKey()
|
||||
}
|
||||
pubKeyJSONBytes, _ := cdc.MarshalJSON(pubKey)
|
||||
fmt.Println(string(pubKeyJSONBytes))
|
||||
}
|
||||
|
||||
@@ -90,9 +90,10 @@ func testnetFiles(cmd *cobra.Command, args []string) error {
|
||||
|
||||
pvFile := filepath.Join(nodeDir, config.BaseConfig.PrivValidator)
|
||||
pv := privval.LoadFilePV(pvFile)
|
||||
pubKey := pv.GetPubKey()
|
||||
genVals[i] = types.GenesisValidator{
|
||||
Address: pv.GetPubKey().Address(),
|
||||
PubKey: pv.GetPubKey(),
|
||||
Address: pubKey.Address(),
|
||||
PubKey: pubKey,
|
||||
Power: 1,
|
||||
Name: nodeDirName,
|
||||
}
|
||||
@@ -127,14 +128,31 @@ func testnetFiles(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Gather persistent peer addresses.
|
||||
var (
|
||||
persistentPeers string
|
||||
err error
|
||||
)
|
||||
if populatePersistentPeers {
|
||||
err := populatePersistentPeersInConfigAndWriteIt(config)
|
||||
persistentPeers, err = persistentPeersString(config)
|
||||
if err != nil {
|
||||
_ = os.RemoveAll(outputDir)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Overwrite default config.
|
||||
for i := 0; i < nValidators+nNonValidators; i++ {
|
||||
nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i))
|
||||
config.SetRoot(nodeDir)
|
||||
config.P2P.AddrBookStrict = false
|
||||
if populatePersistentPeers {
|
||||
config.P2P.PersistentPeers = persistentPeers
|
||||
}
|
||||
|
||||
cfg.WriteConfigFile(filepath.Join(nodeDir, "config", "config.toml"), config)
|
||||
}
|
||||
|
||||
fmt.Printf("Successfully initialized %v node directories\n", nValidators+nNonValidators)
|
||||
return nil
|
||||
}
|
||||
@@ -157,28 +175,16 @@ func hostnameOrIP(i int) string {
|
||||
return fmt.Sprintf("%s%d", hostnamePrefix, i)
|
||||
}
|
||||
|
||||
func populatePersistentPeersInConfigAndWriteIt(config *cfg.Config) error {
|
||||
func persistentPeersString(config *cfg.Config) (string, error) {
|
||||
persistentPeers := make([]string, nValidators+nNonValidators)
|
||||
for i := 0; i < nValidators+nNonValidators; i++ {
|
||||
nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i))
|
||||
config.SetRoot(nodeDir)
|
||||
nodeKey, err := p2p.LoadNodeKey(config.NodeKeyFile())
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
persistentPeers[i] = p2p.IDAddressString(nodeKey.ID(), fmt.Sprintf("%s:%d", hostnameOrIP(i), p2pPort))
|
||||
}
|
||||
persistentPeersList := strings.Join(persistentPeers, ",")
|
||||
|
||||
for i := 0; i < nValidators+nNonValidators; i++ {
|
||||
nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i))
|
||||
config.SetRoot(nodeDir)
|
||||
config.P2P.PersistentPeers = persistentPeersList
|
||||
config.P2P.AddrBookStrict = false
|
||||
|
||||
// overwrite default config
|
||||
cfg.WriteConfigFile(filepath.Join(nodeDir, "config", "config.toml"), config)
|
||||
}
|
||||
|
||||
return nil
|
||||
return strings.Join(persistentPeers, ","), nil
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
abcicli "github.com/tendermint/tendermint/abci/client"
|
||||
"github.com/tendermint/tendermint/abci/client"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
bc "github.com/tendermint/tendermint/blockchain"
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
@@ -72,9 +72,10 @@ func NewValidatorStub(privValidator types.PrivValidator, valIndex int) *validato
|
||||
}
|
||||
|
||||
func (vs *validatorStub) signVote(voteType types.SignedMsgType, hash []byte, header types.PartSetHeader) (*types.Vote, error) {
|
||||
addr := vs.PrivValidator.GetPubKey().Address()
|
||||
vote := &types.Vote{
|
||||
ValidatorIndex: vs.Index,
|
||||
ValidatorAddress: vs.PrivValidator.GetAddress(),
|
||||
ValidatorAddress: addr,
|
||||
Height: vs.Height,
|
||||
Round: vs.Round,
|
||||
Timestamp: tmtime.Now(),
|
||||
@@ -151,8 +152,9 @@ func signAddVotes(to *ConsensusState, voteType types.SignedMsgType, hash []byte,
|
||||
|
||||
func validatePrevote(t *testing.T, cs *ConsensusState, round int, privVal *validatorStub, blockHash []byte) {
|
||||
prevotes := cs.Votes.Prevotes(round)
|
||||
address := privVal.GetPubKey().Address()
|
||||
var vote *types.Vote
|
||||
if vote = prevotes.GetByAddress(privVal.GetAddress()); vote == nil {
|
||||
if vote = prevotes.GetByAddress(address); vote == nil {
|
||||
panic("Failed to find prevote from validator")
|
||||
}
|
||||
if blockHash == nil {
|
||||
@@ -168,8 +170,9 @@ func validatePrevote(t *testing.T, cs *ConsensusState, round int, privVal *valid
|
||||
|
||||
func validateLastPrecommit(t *testing.T, cs *ConsensusState, privVal *validatorStub, blockHash []byte) {
|
||||
votes := cs.LastCommit
|
||||
address := privVal.GetPubKey().Address()
|
||||
var vote *types.Vote
|
||||
if vote = votes.GetByAddress(privVal.GetAddress()); vote == nil {
|
||||
if vote = votes.GetByAddress(address); vote == nil {
|
||||
panic("Failed to find precommit from validator")
|
||||
}
|
||||
if !bytes.Equal(vote.BlockID.Hash, blockHash) {
|
||||
@@ -179,8 +182,9 @@ func validateLastPrecommit(t *testing.T, cs *ConsensusState, privVal *validatorS
|
||||
|
||||
func validatePrecommit(t *testing.T, cs *ConsensusState, thisRound, lockRound int, privVal *validatorStub, votedBlockHash, lockedBlockHash []byte) {
|
||||
precommits := cs.Votes.Precommits(thisRound)
|
||||
address := privVal.GetPubKey().Address()
|
||||
var vote *types.Vote
|
||||
if vote = precommits.GetByAddress(privVal.GetAddress()); vote == nil {
|
||||
if vote = precommits.GetByAddress(address); vote == nil {
|
||||
panic("Failed to find precommit from validator")
|
||||
}
|
||||
|
||||
@@ -425,20 +429,6 @@ func ensureNewRound(roundCh <-chan interface{}, height int64, round int) {
|
||||
}
|
||||
}
|
||||
|
||||
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)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ensureNewTimeout(timeoutCh <-chan interface{}, height int64, round int, timeout int64) {
|
||||
timeoutDuration := time.Duration(timeout*3) * time.Nanosecond
|
||||
ensureNewEvent(timeoutCh, height, round, timeoutDuration,
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
amino "github.com/tendermint/go-amino"
|
||||
"github.com/tendermint/go-amino"
|
||||
cstypes "github.com/tendermint/tendermint/consensus/types"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
tmevents "github.com/tendermint/tendermint/libs/events"
|
||||
@@ -264,11 +264,6 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
|
||||
BlockID: msg.BlockID,
|
||||
Votes: ourVotes,
|
||||
}))
|
||||
case *ProposalHeartbeatMessage:
|
||||
hb := msg.Heartbeat
|
||||
conR.Logger.Debug("Received proposal heartbeat message",
|
||||
"height", hb.Height, "round", hb.Round, "sequence", hb.Sequence,
|
||||
"valIdx", hb.ValidatorIndex, "valAddr", hb.ValidatorAddress)
|
||||
default:
|
||||
conR.Logger.Error(fmt.Sprintf("Unknown message type %v", reflect.TypeOf(msg)))
|
||||
}
|
||||
@@ -369,8 +364,8 @@ func (conR *ConsensusReactor) FastSync() bool {
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
// subscribeToBroadcastEvents subscribes for new round steps, votes and
|
||||
// proposal heartbeats using internal pubsub defined on state to broadcast
|
||||
// subscribeToBroadcastEvents subscribes for new round steps and votes
|
||||
// using internal pubsub defined on state to broadcast
|
||||
// them to peers upon receiving.
|
||||
func (conR *ConsensusReactor) subscribeToBroadcastEvents() {
|
||||
const subscriber = "consensus-reactor"
|
||||
@@ -389,10 +384,6 @@ func (conR *ConsensusReactor) subscribeToBroadcastEvents() {
|
||||
conR.broadcastHasVoteMessage(data.(*types.Vote))
|
||||
})
|
||||
|
||||
conR.conS.evsw.AddListenerForEvent(subscriber, types.EventProposalHeartbeat,
|
||||
func(data tmevents.EventData) {
|
||||
conR.broadcastProposalHeartbeatMessage(data.(*types.Heartbeat))
|
||||
})
|
||||
}
|
||||
|
||||
func (conR *ConsensusReactor) unsubscribeFromBroadcastEvents() {
|
||||
@@ -400,13 +391,6 @@ func (conR *ConsensusReactor) unsubscribeFromBroadcastEvents() {
|
||||
conR.conS.evsw.RemoveListener(subscriber)
|
||||
}
|
||||
|
||||
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)
|
||||
msg := &ProposalHeartbeatMessage{hb}
|
||||
conR.Switch.Broadcast(StateChannel, cdc.MustMarshalBinaryBare(msg))
|
||||
}
|
||||
|
||||
func (conR *ConsensusReactor) broadcastNewRoundStepMessage(rs *cstypes.RoundState) {
|
||||
nrsMsg := makeRoundStepMessage(rs)
|
||||
conR.Switch.Broadcast(StateChannel, cdc.MustMarshalBinaryBare(nrsMsg))
|
||||
@@ -1387,7 +1371,6 @@ func RegisterConsensusMessages(cdc *amino.Codec) {
|
||||
cdc.RegisterConcrete(&HasVoteMessage{}, "tendermint/HasVote", nil)
|
||||
cdc.RegisterConcrete(&VoteSetMaj23Message{}, "tendermint/VoteSetMaj23", nil)
|
||||
cdc.RegisterConcrete(&VoteSetBitsMessage{}, "tendermint/VoteSetBits", nil)
|
||||
cdc.RegisterConcrete(&ProposalHeartbeatMessage{}, "tendermint/ProposalHeartbeat", nil)
|
||||
}
|
||||
|
||||
func decodeMsg(bz []byte) (msg ConsensusMessage, err error) {
|
||||
@@ -1664,18 +1647,3 @@ func (m *VoteSetBitsMessage) String() string {
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
// ProposalHeartbeatMessage is sent to signal that a node is alive and waiting for transactions for a proposal.
|
||||
type ProposalHeartbeatMessage struct {
|
||||
Heartbeat *types.Heartbeat
|
||||
}
|
||||
|
||||
// ValidateBasic performs basic validation.
|
||||
func (m *ProposalHeartbeatMessage) ValidateBasic() error {
|
||||
return m.Heartbeat.ValidateBasic()
|
||||
}
|
||||
|
||||
// String returns a string representation.
|
||||
func (m *ProposalHeartbeatMessage) String() string {
|
||||
return fmt.Sprintf("[HEARTBEAT %v]", m.Heartbeat)
|
||||
}
|
||||
|
||||
@@ -143,7 +143,8 @@ func TestReactorWithEvidence(t *testing.T) {
|
||||
// mock the evidence pool
|
||||
// everyone includes evidence of another double signing
|
||||
vIdx := (i + 1) % nValidators
|
||||
evpool := newMockEvidencePool(privVals[vIdx].GetAddress())
|
||||
addr := privVals[vIdx].GetPubKey().Address()
|
||||
evpool := newMockEvidencePool(addr)
|
||||
|
||||
// Make ConsensusState
|
||||
blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyAppConnCon, mempool, evpool)
|
||||
@@ -213,8 +214,8 @@ func (m *mockEvidencePool) Update(block *types.Block, state sm.State) {
|
||||
|
||||
//------------------------------------
|
||||
|
||||
// Ensure a testnet sends proposal heartbeats and makes blocks when there are txs
|
||||
func TestReactorProposalHeartbeats(t *testing.T) {
|
||||
// Ensure a testnet makes blocks when there are txs
|
||||
func TestReactorCreatesBlockWhenEmptyBlocksFalse(t *testing.T) {
|
||||
N := 4
|
||||
css := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter,
|
||||
func(c *cfg.Config) {
|
||||
@@ -222,17 +223,6 @@ func TestReactorProposalHeartbeats(t *testing.T) {
|
||||
})
|
||||
reactors, eventChans, eventBuses := startConsensusNet(t, css, N)
|
||||
defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses)
|
||||
heartbeatChans := make([]chan interface{}, N)
|
||||
var err error
|
||||
for i := 0; i < N; i++ {
|
||||
heartbeatChans[i] = make(chan interface{}, 1)
|
||||
err = eventBuses[i].Subscribe(context.Background(), testSubscriber, types.EventQueryProposalHeartbeat, heartbeatChans[i])
|
||||
require.NoError(t, err)
|
||||
}
|
||||
// wait till everyone sends a proposal heartbeat
|
||||
timeoutWaitGroup(t, N, func(j int) {
|
||||
<-heartbeatChans[j]
|
||||
}, css)
|
||||
|
||||
// send a tx
|
||||
if err := css[3].mempool.CheckTx([]byte{1, 2, 3}, nil); err != nil {
|
||||
@@ -279,7 +269,8 @@ func TestReactorVotingPowerChange(t *testing.T) {
|
||||
// map of active validators
|
||||
activeVals := make(map[string]struct{})
|
||||
for i := 0; i < nVals; i++ {
|
||||
activeVals[string(css[i].privValidator.GetAddress())] = struct{}{}
|
||||
addr := css[i].privValidator.GetPubKey().Address()
|
||||
activeVals[string(addr)] = struct{}{}
|
||||
}
|
||||
|
||||
// wait till everyone makes block 1
|
||||
@@ -290,7 +281,9 @@ func TestReactorVotingPowerChange(t *testing.T) {
|
||||
//---------------------------------------------------------------------------
|
||||
logger.Debug("---------------------------- Testing changing the voting power of one validator a few times")
|
||||
|
||||
val1PubKey := css[0].privValidator.GetPubKey()
|
||||
pubKey := css[0].privValidator.GetPubKey()
|
||||
|
||||
val1PubKey := pubKey
|
||||
val1PubKeyABCI := types.TM2PB.PubKey(val1PubKey)
|
||||
updateValidatorTx := kvstore.MakeValSetChangeTx(val1PubKeyABCI, 25)
|
||||
previousTotalVotingPower := css[0].GetRoundState().LastValidators.TotalVotingPower()
|
||||
@@ -342,7 +335,8 @@ func TestReactorValidatorSetChanges(t *testing.T) {
|
||||
// map of active validators
|
||||
activeVals := make(map[string]struct{})
|
||||
for i := 0; i < nVals; i++ {
|
||||
activeVals[string(css[i].privValidator.GetAddress())] = struct{}{}
|
||||
addr := css[i].privValidator.GetPubKey().Address()
|
||||
activeVals[string(addr)] = struct{}{}
|
||||
}
|
||||
|
||||
// wait till everyone makes block 1
|
||||
@@ -353,7 +347,8 @@ func TestReactorValidatorSetChanges(t *testing.T) {
|
||||
//---------------------------------------------------------------------------
|
||||
logger.Info("---------------------------- Testing adding one validator")
|
||||
|
||||
newValidatorPubKey1 := css[nVals].privValidator.GetPubKey()
|
||||
pubKey := css[nVals].privValidator.GetPubKey()
|
||||
newValidatorPubKey1 := pubKey
|
||||
valPubKey1ABCI := types.TM2PB.PubKey(newValidatorPubKey1)
|
||||
newValidatorTx1 := kvstore.MakeValSetChangeTx(valPubKey1ABCI, testMinPower)
|
||||
|
||||
@@ -380,7 +375,8 @@ func TestReactorValidatorSetChanges(t *testing.T) {
|
||||
//---------------------------------------------------------------------------
|
||||
logger.Info("---------------------------- Testing changing the voting power of one validator")
|
||||
|
||||
updateValidatorPubKey1 := css[nVals].privValidator.GetPubKey()
|
||||
pubKey = css[nVals].privValidator.GetPubKey()
|
||||
updateValidatorPubKey1 := pubKey
|
||||
updatePubKey1ABCI := types.TM2PB.PubKey(updateValidatorPubKey1)
|
||||
updateValidatorTx1 := kvstore.MakeValSetChangeTx(updatePubKey1ABCI, 25)
|
||||
previousTotalVotingPower := css[nVals].GetRoundState().LastValidators.TotalVotingPower()
|
||||
@@ -397,11 +393,13 @@ func TestReactorValidatorSetChanges(t *testing.T) {
|
||||
//---------------------------------------------------------------------------
|
||||
logger.Info("---------------------------- Testing adding two validators at once")
|
||||
|
||||
newValidatorPubKey2 := css[nVals+1].privValidator.GetPubKey()
|
||||
pubKey = css[nVals+1].privValidator.GetPubKey()
|
||||
newValidatorPubKey2 := pubKey
|
||||
newVal2ABCI := types.TM2PB.PubKey(newValidatorPubKey2)
|
||||
newValidatorTx2 := kvstore.MakeValSetChangeTx(newVal2ABCI, testMinPower)
|
||||
|
||||
newValidatorPubKey3 := css[nVals+2].privValidator.GetPubKey()
|
||||
pubKey = css[nVals+2].privValidator.GetPubKey()
|
||||
newValidatorPubKey3 := pubKey
|
||||
newVal3ABCI := types.TM2PB.PubKey(newValidatorPubKey3)
|
||||
newValidatorTx3 := kvstore.MakeValSetChangeTx(newVal3ABCI, testMinPower)
|
||||
|
||||
|
||||
@@ -303,7 +303,13 @@ func (h *Handshaker) ReplayBlocks(
|
||||
}
|
||||
state.Validators = types.NewValidatorSet(vals)
|
||||
state.NextValidators = types.NewValidatorSet(vals)
|
||||
} else {
|
||||
// If validator set is not set in genesis and still empty after InitChain, exit.
|
||||
if len(h.genDoc.Validators) == 0 {
|
||||
return nil, fmt.Errorf("Validator set is nil in genesis and still empty after InitChain")
|
||||
}
|
||||
}
|
||||
|
||||
if res.ConsensusParams != nil {
|
||||
state.ConsensusParams = types.PB2TM.ConsensusParams(res.ConsensusParams)
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
|
||||
"github.com/tendermint/tendermint/abci/example/kvstore"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
crypto "github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
auto "github.com/tendermint/tendermint/libs/autofile"
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
"github.com/tendermint/tendermint/version"
|
||||
@@ -331,7 +331,9 @@ func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) {
|
||||
chain, commits, err := makeBlockchainFromWAL(wal)
|
||||
require.NoError(t, err)
|
||||
|
||||
stateDB, state, store := stateAndStore(config, privVal.GetPubKey(), kvstore.ProtocolVersion)
|
||||
pubKey := privVal.GetPubKey()
|
||||
require.NoError(t, err)
|
||||
stateDB, state, store := stateAndStore(config, pubKey, kvstore.ProtocolVersion)
|
||||
store.chain = chain
|
||||
store.commits = commits
|
||||
|
||||
@@ -346,7 +348,7 @@ func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) {
|
||||
// run nBlocks against a new client to build up the app state.
|
||||
// use a throwaway tendermint state
|
||||
proxyApp := proxy.NewAppConns(clientCreator2)
|
||||
stateDB, state, _ := stateAndStore(config, privVal.GetPubKey(), kvstore.ProtocolVersion)
|
||||
stateDB, state, _ := stateAndStore(config, pubKey, kvstore.ProtocolVersion)
|
||||
buildAppStateFromChain(proxyApp, stateDB, state, chain, nBlocks, mode)
|
||||
}
|
||||
|
||||
@@ -634,7 +636,8 @@ func TestInitChainUpdateValidators(t *testing.T) {
|
||||
|
||||
config := ResetConfig("proxy_test_")
|
||||
privVal := privval.LoadFilePV(config.PrivValidatorFile())
|
||||
stateDB, state, store := stateAndStore(config, privVal.GetPubKey(), 0x0)
|
||||
pubKey := privVal.GetPubKey()
|
||||
stateDB, state, store := stateAndStore(config, pubKey, 0x0)
|
||||
|
||||
oldValAddr := state.Validators.Validators[0].Address
|
||||
|
||||
@@ -659,12 +662,6 @@ func TestInitChainUpdateValidators(t *testing.T) {
|
||||
assert.Equal(t, newValAddr, expectValAddr)
|
||||
}
|
||||
|
||||
func newInitChainApp(vals []abci.ValidatorUpdate) *initChainApp {
|
||||
return &initChainApp{
|
||||
vals: vals,
|
||||
}
|
||||
}
|
||||
|
||||
// returns the vals on InitChain
|
||||
type initChainApp struct {
|
||||
abci.BaseApplication
|
||||
|
||||
@@ -2,13 +2,14 @@ package consensus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"github.com/tendermint/tendermint/libs/fail"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
@@ -22,13 +23,6 @@ import (
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Config
|
||||
|
||||
const (
|
||||
proposalHeartbeatIntervalSeconds = 2
|
||||
)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Errors
|
||||
|
||||
@@ -118,7 +112,7 @@ type ConsensusState struct {
|
||||
done chan struct{}
|
||||
|
||||
// synchronous pubsub between consensus state and reactor.
|
||||
// state only emits EventNewRoundStep, EventVote and EventProposalHeartbeat
|
||||
// state only emits EventNewRoundStep and EventVote
|
||||
evsw tmevents.EventSwitch
|
||||
|
||||
// for reporting metrics
|
||||
@@ -752,7 +746,7 @@ func (cs *ConsensusState) enterNewRound(height int64, round int) {
|
||||
validators := cs.Validators
|
||||
if cs.Round < round {
|
||||
validators = validators.Copy()
|
||||
validators.IncrementAccum(round - cs.Round)
|
||||
validators.IncrementProposerPriority(round - cs.Round)
|
||||
}
|
||||
|
||||
// Setup new round
|
||||
@@ -785,7 +779,6 @@ func (cs *ConsensusState) enterNewRound(height int64, round int) {
|
||||
cs.scheduleTimeout(cs.config.CreateEmptyBlocksInterval, height, round,
|
||||
cstypes.RoundStepNewRound)
|
||||
}
|
||||
go cs.proposalHeartbeat(height, round)
|
||||
} else {
|
||||
cs.enterPropose(height, round)
|
||||
}
|
||||
@@ -802,38 +795,6 @@ func (cs *ConsensusState) needProofBlock(height int64) bool {
|
||||
return !bytes.Equal(cs.state.AppHash, lastBlockMeta.Header.AppHash)
|
||||
}
|
||||
|
||||
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
|
||||
valIndex, _ := cs.Validators.GetByAddress(addr)
|
||||
chainID := cs.state.ChainID
|
||||
for {
|
||||
rs := cs.GetRoundState()
|
||||
// if we've already moved on, no need to send more heartbeats
|
||||
if rs.Step > cstypes.RoundStepNewRound || rs.Round > round || rs.Height > height {
|
||||
return
|
||||
}
|
||||
heartbeat := &types.Heartbeat{
|
||||
Height: rs.Height,
|
||||
Round: rs.Round,
|
||||
Sequence: counter,
|
||||
ValidatorAddress: addr,
|
||||
ValidatorIndex: valIndex,
|
||||
}
|
||||
cs.privValidator.SignHeartbeat(chainID, heartbeat)
|
||||
cs.eventBus.PublishEventProposalHeartbeat(types.EventDataProposalHeartbeat{heartbeat})
|
||||
cs.evsw.FireEvent(types.EventProposalHeartbeat, heartbeat)
|
||||
counter++
|
||||
time.Sleep(proposalHeartbeatIntervalSeconds * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
// Enter (CreateEmptyBlocks): from enterNewRound(height,round)
|
||||
// Enter (CreateEmptyBlocks, CreateEmptyBlocksInterval > 0 ): after enterNewRound(height,round), after timeout of CreateEmptyBlocksInterval
|
||||
// Enter (!CreateEmptyBlocks) : after enterNewRound(height,round), once txs are in the mempool
|
||||
@@ -869,13 +830,14 @@ func (cs *ConsensusState) enterPropose(height int64, round int) {
|
||||
}
|
||||
|
||||
// if not a validator, we're done
|
||||
if !cs.Validators.HasAddress(cs.privValidator.GetAddress()) {
|
||||
logger.Debug("This node is not a validator", "addr", cs.privValidator.GetAddress(), "vals", cs.Validators)
|
||||
address := cs.privValidator.GetPubKey().Address()
|
||||
if !cs.Validators.HasAddress(address) {
|
||||
logger.Debug("This node is not a validator", "addr", address, "vals", cs.Validators)
|
||||
return
|
||||
}
|
||||
logger.Debug("This node is a validator")
|
||||
|
||||
if cs.isProposer() {
|
||||
if cs.isProposer(address) {
|
||||
logger.Info("enterPropose: Our turn to propose", "proposer", cs.Validators.GetProposer().Address, "privValidator", cs.privValidator)
|
||||
cs.decideProposal(height, round)
|
||||
} else {
|
||||
@@ -883,8 +845,8 @@ func (cs *ConsensusState) enterPropose(height int64, round int) {
|
||||
}
|
||||
}
|
||||
|
||||
func (cs *ConsensusState) isProposer() bool {
|
||||
return bytes.Equal(cs.Validators.GetProposer().Address, cs.privValidator.GetAddress())
|
||||
func (cs *ConsensusState) isProposer(address []byte) bool {
|
||||
return bytes.Equal(cs.Validators.GetProposer().Address, address)
|
||||
}
|
||||
|
||||
func (cs *ConsensusState) defaultDecideProposal(height int64, round int) {
|
||||
@@ -969,7 +931,7 @@ func (cs *ConsensusState) createProposalBlock() (block *types.Block, blockParts
|
||||
cs.state.Validators.Size(),
|
||||
len(evidence),
|
||||
), maxGas)
|
||||
proposerAddr := cs.privValidator.GetAddress()
|
||||
proposerAddr := cs.privValidator.GetPubKey().Address()
|
||||
block, parts := cs.state.MakeBlock(cs.Height, txs, commit, evidence, proposerAddr)
|
||||
|
||||
return block, parts
|
||||
@@ -1514,7 +1476,8 @@ func (cs *ConsensusState) tryAddVote(vote *types.Vote, peerID p2p.ID) (bool, err
|
||||
if err == ErrVoteHeightMismatch {
|
||||
return added, err
|
||||
} else if voteErr, ok := err.(*types.ErrVoteConflictingVotes); ok {
|
||||
if bytes.Equal(vote.ValidatorAddress, cs.privValidator.GetAddress()) {
|
||||
addr := cs.privValidator.GetPubKey().Address()
|
||||
if bytes.Equal(vote.ValidatorAddress, addr) {
|
||||
cs.Logger.Error("Found conflicting vote from ourselves. Did you unsafe_reset a validator?", "height", vote.Height, "round", vote.Round, "type", vote.Type)
|
||||
return added, err
|
||||
}
|
||||
@@ -1679,7 +1642,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
|
||||
}
|
||||
|
||||
func (cs *ConsensusState) signVote(type_ types.SignedMsgType, hash []byte, header types.PartSetHeader) (*types.Vote, error) {
|
||||
addr := cs.privValidator.GetAddress()
|
||||
addr := cs.privValidator.GetPubKey().Address()
|
||||
valIndex, _ := cs.Validators.GetByAddress(addr)
|
||||
|
||||
vote := &types.Vote{
|
||||
@@ -1715,7 +1678,8 @@ func (cs *ConsensusState) voteTime() time.Time {
|
||||
// sign the vote and publish on internalMsgQueue
|
||||
func (cs *ConsensusState) signAddVote(type_ types.SignedMsgType, hash []byte, header types.PartSetHeader) *types.Vote {
|
||||
// if we don't have a key or we're not in the validator set, do nothing
|
||||
if cs.privValidator == nil || !cs.Validators.HasAddress(cs.privValidator.GetAddress()) {
|
||||
privValAddr := cs.privValidator.GetPubKey().Address()
|
||||
if cs.privValidator == nil || !cs.Validators.HasAddress(privValAddr) {
|
||||
return nil
|
||||
}
|
||||
vote, err := cs.signVote(type_, hash, header)
|
||||
|
||||
@@ -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"
|
||||
@@ -75,7 +73,8 @@ func TestStateProposerSelection0(t *testing.T) {
|
||||
|
||||
// Commit a block and ensure proposer for the next height is correct.
|
||||
prop := cs1.GetRoundState().Validators.GetProposer()
|
||||
if !bytes.Equal(prop.Address, cs1.privValidator.GetAddress()) {
|
||||
address := cs1.privValidator.GetPubKey().Address()
|
||||
if !bytes.Equal(prop.Address, address) {
|
||||
t.Fatalf("expected proposer to be validator %d. Got %X", 0, prop.Address)
|
||||
}
|
||||
|
||||
@@ -89,7 +88,8 @@ func TestStateProposerSelection0(t *testing.T) {
|
||||
ensureNewRound(newRoundCh, height+1, 0)
|
||||
|
||||
prop = cs1.GetRoundState().Validators.GetProposer()
|
||||
if !bytes.Equal(prop.Address, vss[1].GetAddress()) {
|
||||
addr := vss[1].GetPubKey().Address()
|
||||
if !bytes.Equal(prop.Address, addr) {
|
||||
panic(fmt.Sprintf("expected proposer to be validator %d. Got %X", 1, prop.Address))
|
||||
}
|
||||
}
|
||||
@@ -112,7 +112,8 @@ func TestStateProposerSelection2(t *testing.T) {
|
||||
// everyone just votes nil. we get a new proposer each round
|
||||
for i := 0; i < len(vss); i++ {
|
||||
prop := cs1.GetRoundState().Validators.GetProposer()
|
||||
correctProposer := vss[(i+round)%len(vss)].GetAddress()
|
||||
addr := vss[(i+round)%len(vss)].GetPubKey().Address()
|
||||
correctProposer := addr
|
||||
if !bytes.Equal(prop.Address, correctProposer) {
|
||||
panic(fmt.Sprintf("expected RoundState.Validators.GetProposer() to be validator %d. Got %X", (i+2)%len(vss), prop.Address))
|
||||
}
|
||||
@@ -507,7 +508,8 @@ func TestStateLockPOLRelock(t *testing.T) {
|
||||
|
||||
timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
|
||||
proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
|
||||
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
|
||||
addr := cs1.privValidator.GetPubKey().Address()
|
||||
voteCh := subscribeToVoter(cs1, addr)
|
||||
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
||||
newBlockCh := subscribe(cs1.eventBus, types.EventQueryNewBlockHeader)
|
||||
|
||||
@@ -598,7 +600,8 @@ func TestStateLockPOLUnlock(t *testing.T) {
|
||||
timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
|
||||
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
||||
unlockCh := subscribe(cs1.eventBus, types.EventQueryUnlock)
|
||||
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
|
||||
addr := cs1.privValidator.GetPubKey().Address()
|
||||
voteCh := subscribeToVoter(cs1, addr)
|
||||
|
||||
// everything done from perspective of cs1
|
||||
|
||||
@@ -691,7 +694,8 @@ func TestStateLockPOLSafety1(t *testing.T) {
|
||||
timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
|
||||
timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
|
||||
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
||||
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
|
||||
addr := cs1.privValidator.GetPubKey().Address()
|
||||
voteCh := subscribeToVoter(cs1, addr)
|
||||
|
||||
// start round and wait for propose and prevote
|
||||
startTestRound(cs1, cs1.Height, round)
|
||||
@@ -807,7 +811,8 @@ func TestStateLockPOLSafety2(t *testing.T) {
|
||||
timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
|
||||
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
||||
unlockCh := subscribe(cs1.eventBus, types.EventQueryUnlock)
|
||||
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
|
||||
addr := cs1.privValidator.GetPubKey().Address()
|
||||
voteCh := subscribeToVoter(cs1, addr)
|
||||
|
||||
// the block for R0: gets polkad but we miss it
|
||||
// (even though we signed it, shhh)
|
||||
@@ -898,7 +903,8 @@ func TestProposeValidBlock(t *testing.T) {
|
||||
timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
|
||||
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
||||
unlockCh := subscribe(cs1.eventBus, types.EventQueryUnlock)
|
||||
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
|
||||
addr := cs1.privValidator.GetPubKey().Address()
|
||||
voteCh := subscribeToVoter(cs1, addr)
|
||||
|
||||
// start round and wait for propose and prevote
|
||||
startTestRound(cs1, cs1.Height, round)
|
||||
@@ -984,7 +990,8 @@ func TestSetValidBlockOnDelayedPrevote(t *testing.T) {
|
||||
timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
|
||||
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
||||
validBlockCh := subscribe(cs1.eventBus, types.EventQueryValidBlock)
|
||||
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
|
||||
addr := cs1.privValidator.GetPubKey().Address()
|
||||
voteCh := subscribeToVoter(cs1, addr)
|
||||
|
||||
// start round and wait for propose and prevote
|
||||
startTestRound(cs1, cs1.Height, round)
|
||||
@@ -1029,33 +1036,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.
|
||||
@@ -1070,7 +1050,8 @@ func TestSetValidBlockOnDelayedProposal(t *testing.T) {
|
||||
timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
|
||||
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
||||
validBlockCh := subscribe(cs1.eventBus, types.EventQueryValidBlock)
|
||||
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
|
||||
addr := cs1.privValidator.GetPubKey().Address()
|
||||
voteCh := subscribeToVoter(cs1, addr)
|
||||
proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
|
||||
|
||||
round = round + 1 // move to round in which P0 is not proposer
|
||||
@@ -1140,7 +1121,8 @@ func TestWaitingTimeoutProposeOnNewRound(t *testing.T) {
|
||||
|
||||
timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
|
||||
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
||||
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
|
||||
addr := cs1.privValidator.GetPubKey().Address()
|
||||
voteCh := subscribeToVoter(cs1, addr)
|
||||
|
||||
// start round
|
||||
startTestRound(cs1, height, round)
|
||||
@@ -1173,7 +1155,8 @@ func TestRoundSkipOnNilPolkaFromHigherRound(t *testing.T) {
|
||||
|
||||
timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
|
||||
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
||||
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
|
||||
addr := cs1.privValidator.GetPubKey().Address()
|
||||
voteCh := subscribeToVoter(cs1, addr)
|
||||
|
||||
// start round
|
||||
startTestRound(cs1, height, round)
|
||||
@@ -1206,7 +1189,8 @@ func TestWaitTimeoutProposeOnNilPolkaForTheCurrentRound(t *testing.T) {
|
||||
|
||||
timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
|
||||
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
||||
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
|
||||
addr := cs1.privValidator.GetPubKey().Address()
|
||||
voteCh := subscribeToVoter(cs1, addr)
|
||||
|
||||
// start round in which PO is not proposer
|
||||
startTestRound(cs1, height, round)
|
||||
@@ -1390,7 +1374,8 @@ func TestStateHalt1(t *testing.T) {
|
||||
timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
|
||||
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
||||
newBlockCh := subscribe(cs1.eventBus, types.EventQueryNewBlock)
|
||||
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
|
||||
addr := cs1.privValidator.GetPubKey().Address()
|
||||
voteCh := subscribeToVoter(cs1, addr)
|
||||
|
||||
// start round and wait for propose and prevote
|
||||
startTestRound(cs1, height, round)
|
||||
|
||||
@@ -50,8 +50,9 @@ func TestPeerCatchupRounds(t *testing.T) {
|
||||
|
||||
func makeVoteHR(t *testing.T, height int64, round int, privVals []types.PrivValidator, valIndex int) *types.Vote {
|
||||
privVal := privVals[valIndex]
|
||||
addr := privVal.GetPubKey().Address()
|
||||
vote := &types.Vote{
|
||||
ValidatorAddress: privVal.GetAddress(),
|
||||
ValidatorAddress: addr,
|
||||
ValidatorIndex: valIndex,
|
||||
Height: height,
|
||||
Round: round,
|
||||
|
||||
@@ -8,7 +8,12 @@ module.exports = {
|
||||
lineNumbers: true
|
||||
},
|
||||
themeConfig: {
|
||||
lastUpdated: "Last Updated",
|
||||
lastUpdated: true,
|
||||
algolia: {
|
||||
apiKey: '59f0e2deb984aa9cdf2b3a5fd24ac501',
|
||||
indexName: 'tendermint',
|
||||
debug: false
|
||||
},
|
||||
nav: [{ text: "Back to Tendermint", link: "https://tendermint.com" }],
|
||||
sidebar: [
|
||||
{
|
||||
|
||||
@@ -11,13 +11,10 @@ Make sure you [have Go installed](https://golang.org/doc/install).
|
||||
Next, install the `abci-cli` tool and example applications:
|
||||
|
||||
```
|
||||
go get github.com/tendermint/tendermint
|
||||
```
|
||||
|
||||
to get vendored dependencies:
|
||||
|
||||
```
|
||||
cd $GOPATH/src/github.com/tendermint/tendermint
|
||||
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
|
||||
|
||||
@@ -5,7 +5,7 @@ Tendermint blockchain application.
|
||||
|
||||
The following diagram provides a superb example:
|
||||
|
||||
<https://drive.google.com/open?id=1yR2XpRi9YCY9H9uMfcw8-RMJpvDyvjz9>
|
||||

|
||||
|
||||
The end-user application here is the Cosmos Voyager, at the bottom left.
|
||||
Voyager communicates with a REST API exposed by a local Light-Client
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
],
|
||||
"abciServers": [
|
||||
{
|
||||
"name": "abci",
|
||||
"name": "go-abci",
|
||||
"url": "https://github.com/tendermint/tendermint/tree/master/abci",
|
||||
"language": "Go",
|
||||
"author": "Tendermint"
|
||||
@@ -133,6 +133,12 @@
|
||||
"language": "Javascript",
|
||||
"author": "Tendermint"
|
||||
},
|
||||
{
|
||||
"name": "rust-tsp",
|
||||
"url": "https://github.com/tendermint/rust-tsp",
|
||||
"language": "Rust",
|
||||
"author": "Tendermint"
|
||||
},
|
||||
{
|
||||
"name": "cpp-tmsp",
|
||||
"url": "https://github.com/mdyring/cpp-tmsp",
|
||||
@@ -164,7 +170,7 @@
|
||||
"author": "Dave Bryson"
|
||||
},
|
||||
{
|
||||
"name": "tm-abci",
|
||||
"name": "tm-abci (fork of py-abci with async IO)",
|
||||
"url": "https://github.com/SoftblocksCo/tm-abci",
|
||||
"language": "Python",
|
||||
"author": "Softblocks"
|
||||
@@ -175,5 +181,13 @@
|
||||
"language": "Javascript",
|
||||
"author": "Dennis McKinnon"
|
||||
}
|
||||
],
|
||||
"aminoLibraries": [
|
||||
{
|
||||
"name": "JS-Amino",
|
||||
"url": "https://github.com/TanNgocDo/Js-Amino",
|
||||
"language": "Javascript",
|
||||
"author": "TanNgocDo"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
# Ecosystem
|
||||
|
||||
The growing list of applications built using various pieces of the
|
||||
Tendermint stack can be found at:
|
||||
Tendermint stack can be found at the [ecosystem page](https://tendermint.com/ecosystem).
|
||||
|
||||
- https://tendermint.com/ecosystem
|
||||
|
||||
We thank the community for their contributions thus far and welcome the
|
||||
We thank the community for their contributions 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.
|
||||
|
||||
@@ -252,14 +252,12 @@ we'll run a Javascript version of the `counter`. To run it, you'll need
|
||||
to [install node](https://nodejs.org/en/download/).
|
||||
|
||||
You'll also need to fetch the relevant repository, from
|
||||
[here](https://github.com/tendermint/js-abci) then install it. As go
|
||||
devs, we keep all our code under the `$GOPATH`, so run:
|
||||
[here](https://github.com/tendermint/js-abci), then install it:
|
||||
|
||||
```
|
||||
go get github.com/tendermint/js-abci &> /dev/null
|
||||
cd $GOPATH/src/github.com/tendermint/js-abci/example
|
||||
npm install
|
||||
cd ..
|
||||
git clone https://github.com/tendermint/js-abci.git
|
||||
cd js-abci
|
||||
npm install abci
|
||||
```
|
||||
|
||||
Kill the previous `counter` and `tendermint` processes. Now run the app:
|
||||
@@ -276,13 +274,16 @@ tendermint node
|
||||
```
|
||||
|
||||
Once again, you should see blocks streaming by - but now, our
|
||||
application is written in javascript! Try sending some transactions, and
|
||||
application is written in Javascript! Try sending some transactions, and
|
||||
like before - the results should be the same:
|
||||
|
||||
```
|
||||
curl localhost:26657/broadcast_tx_commit?tx=0x00 # ok
|
||||
curl localhost:26657/broadcast_tx_commit?tx=0x05 # invalid nonce
|
||||
curl localhost:26657/broadcast_tx_commit?tx=0x01 # ok
|
||||
# ok
|
||||
curl localhost:26657/broadcast_tx_commit?tx=0x00
|
||||
# invalid nonce
|
||||
curl localhost:26657/broadcast_tx_commit?tx=0x05
|
||||
# ok
|
||||
curl localhost:26657/broadcast_tx_commit?tx=0x01
|
||||
```
|
||||
|
||||
Neat, eh?
|
||||
|
||||
@@ -54,7 +54,7 @@ Response:
|
||||
"value": "ww0z4WaZ0Xg+YI10w43wTWbBmM3dpVza4mmSQYsd0ck="
|
||||
},
|
||||
"voting_power": "10",
|
||||
"accum": "0"
|
||||
"proposer_priority": "0"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
BIN
docs/imgs/cosmos-tendermint-stack-4k.jpg
Normal file
BIN
docs/imgs/cosmos-tendermint-stack-4k.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 625 KiB |
@@ -79,11 +79,9 @@ make install
|
||||
|
||||
Install [LevelDB](https://github.com/google/leveldb) (minimum version is 1.7).
|
||||
|
||||
Build Tendermint with C libraries: `make build_c`.
|
||||
|
||||
### Ubuntu
|
||||
|
||||
Install LevelDB with snappy:
|
||||
Install LevelDB with snappy (optionally):
|
||||
|
||||
```
|
||||
sudo apt-get update
|
||||
@@ -112,5 +110,13 @@ db_backend = "cleveldb"
|
||||
To install Tendermint, run
|
||||
|
||||
```
|
||||
CGO_LDFLAGS="-lsnappy" go install -ldflags "-X github.com/tendermint/tendermint/version.GitCommit=`git rev-parse --short=8 HEAD`" -tags "tendermint gcc" -o build/tendermint ./cmd/tendermint/
|
||||
CGO_LDFLAGS="-lsnappy" make install_c
|
||||
```
|
||||
|
||||
or run
|
||||
|
||||
```
|
||||
CGO_LDFLAGS="-lsnappy" make build_c
|
||||
```
|
||||
|
||||
to put the binary in `./build`.
|
||||
|
||||
@@ -40,7 +40,11 @@ These files are found in `$HOME/.tendermint`:
|
||||
```
|
||||
$ ls $HOME/.tendermint
|
||||
|
||||
config.toml data genesis.json priv_validator.json
|
||||
config data
|
||||
|
||||
$ ls $HOME/.tendermint/config/
|
||||
|
||||
config.toml genesis.json node_key.json priv_validator.json
|
||||
```
|
||||
|
||||
For a single, local node, no further configuration is required.
|
||||
@@ -110,7 +114,18 @@ source ~/.profile
|
||||
|
||||
This will install `go` and other dependencies, get the Tendermint source code, then compile the `tendermint` binary.
|
||||
|
||||
Next, use the `tendermint testnet` command to create four directories of config files (found in `./mytestnet`) and copy each directory to the relevant machine in the cloud, so that each machine has `$HOME/mytestnet/node[0-3]` directory. Then from each machine, run:
|
||||
Next, use the `tendermint testnet` command to create four directories of config files (found in `./mytestnet`) and copy each directory to the relevant machine in the cloud, so that each machine has `$HOME/mytestnet/node[0-3]` directory.
|
||||
|
||||
Before you can start the network, you'll need peers identifiers (IPs are not enough and can change). We'll refer to them as ID1, ID2, ID3, ID4.
|
||||
|
||||
```
|
||||
tendermint show_node_id --home ./mytestnet/node0
|
||||
tendermint show_node_id --home ./mytestnet/node1
|
||||
tendermint show_node_id --home ./mytestnet/node2
|
||||
tendermint show_node_id --home ./mytestnet/node3
|
||||
```
|
||||
|
||||
Finally, from each machine, run:
|
||||
|
||||
```
|
||||
tendermint node --home ./mytestnet/node0 --proxy_app=kvstore --p2p.persistent_peers="ID1@IP1:26656,ID2@IP2:26656,ID3@IP3:26656,ID4@IP4:26656"
|
||||
@@ -121,6 +136,6 @@ tendermint node --home ./mytestnet/node3 --proxy_app=kvstore --p2p.persistent_pe
|
||||
|
||||
Note that after the third node is started, blocks will start to stream in
|
||||
because >2/3 of validators (defined in the `genesis.json`) have come online.
|
||||
Seeds can also be specified in the `config.toml`. See [here](../tendermint-core/configuration.md) for more information about configuration options.
|
||||
Persistent peers can also be specified in the `config.toml`. See [here](../tendermint-core/configuration.md) for more information about configuration options.
|
||||
|
||||
Transactions can then be sent as covered in the single, local node example above.
|
||||
|
||||
@@ -70,10 +70,6 @@ Tendermint is in essence similar software, but with two key differences:
|
||||
the application logic that's right for them, from key-value store to
|
||||
cryptocurrency to e-voting platform and beyond.
|
||||
|
||||
The layout of this Tendermint website content is also ripped directly
|
||||
and without shame from [consul.io](https://www.consul.io/) and the other
|
||||
[Hashicorp sites](https://www.hashicorp.com/#tools).
|
||||
|
||||
### Bitcoin, Ethereum, etc.
|
||||
|
||||
Tendermint emerged in the tradition of cryptocurrencies like Bitcoin,
|
||||
|
||||
@@ -14,31 +14,31 @@ please submit them to our [bug bounty](https://tendermint.com/security)!
|
||||
|
||||
### Data Structures
|
||||
|
||||
- [Encoding and Digests](https://github.com/tendermint/tendermint/blob/master/docs/spec/blockchain/encoding.md)
|
||||
- [Blockchain](https://github.com/tendermint/tendermint/blob/master/docs/spec/blockchain/blockchain.md)
|
||||
- [State](https://github.com/tendermint/tendermint/blob/master/docs/spec/blockchain/state.md)
|
||||
- [Encoding and Digests](./blockchain/encoding.md)
|
||||
- [Blockchain](./blockchain/blockchain.md)
|
||||
- [State](./blockchain/state.md)
|
||||
|
||||
### Consensus Protocol
|
||||
|
||||
- [Consensus Algorithm](/docs/spec/consensus/consensus.md)
|
||||
- [Creating a proposal](/docs/spec/consensus/creating-proposal.md)
|
||||
- [Time](/docs/spec/consensus/bft-time.md)
|
||||
- [Light-Client](/docs/spec/consensus/light-client.md)
|
||||
- [Consensus Algorithm](./consensus/consensus.md)
|
||||
- [Creating a proposal](./consensus/creating-proposal.md)
|
||||
- [Time](./consensus/bft-time.md)
|
||||
- [Light-Client](./consensus/light-client.md)
|
||||
|
||||
### P2P and Network Protocols
|
||||
|
||||
- [The Base P2P Layer](https://github.com/tendermint/tendermint/tree/master/docs/spec/p2p): multiplex the protocols ("reactors") on authenticated and encrypted TCP connections
|
||||
- [Peer Exchange (PEX)](https://github.com/tendermint/tendermint/tree/master/docs/spec/reactors/pex): gossip known peer addresses so peers can find each other
|
||||
- [Block Sync](https://github.com/tendermint/tendermint/tree/master/docs/spec/reactors/block_sync): gossip blocks so peers can catch up quickly
|
||||
- [Consensus](https://github.com/tendermint/tendermint/tree/master/docs/spec/reactors/consensus): gossip votes and block parts so new blocks can be committed
|
||||
- [Mempool](https://github.com/tendermint/tendermint/tree/master/docs/spec/reactors/mempool): gossip transactions so they get included in blocks
|
||||
- Evidence: Forthcoming, see [this issue](https://github.com/tendermint/tendermint/issues/2329).
|
||||
- [The Base P2P Layer](./p2p/): multiplex the protocols ("reactors") on authenticated and encrypted TCP connections
|
||||
- [Peer Exchange (PEX)](./reactors/pex/): gossip known peer addresses so peers can find each other
|
||||
- [Block Sync](./reactors/block_sync/): gossip blocks so peers can catch up quickly
|
||||
- [Consensus](./reactors/consensus/): gossip votes and block parts so new blocks can be committed
|
||||
- [Mempool](./reactors/mempool/): gossip transactions so they get included in blocks
|
||||
- [Evidence](./reactors/evidence/): sending invalid evidence will stop the peer
|
||||
|
||||
### Software
|
||||
|
||||
- [ABCI](/docs/spec/software/abci.md): Details about interactions between the
|
||||
- [ABCI](./software/abci.md): Details about interactions between the
|
||||
application and consensus engine over ABCI
|
||||
- [Write-Ahead Log](/docs/spec/software/wal.md): Details about how the consensus
|
||||
- [Write-Ahead Log](./software/wal.md): Details about how the consensus
|
||||
engine preserves data and recovers from crash failures
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -98,6 +98,10 @@ type Evidence struct {
|
||||
type Validator struct {
|
||||
PubKeyTypes []string
|
||||
}
|
||||
|
||||
type ValidatorParams struct {
|
||||
PubKeyTypes []string
|
||||
}
|
||||
```
|
||||
|
||||
#### BlockSize
|
||||
|
||||
@@ -338,12 +338,11 @@ BlockID has seen +2/3 votes. This routine is based on the local RoundState (`rs`
|
||||
|
||||
## Broadcast routine
|
||||
|
||||
The Broadcast routine subscribes to an internal event bus to receive new round steps, votes messages and proposal
|
||||
heartbeat messages, and broadcasts messages to peers upon receiving those events.
|
||||
The Broadcast routine subscribes to an internal event bus to receive new round steps and votes messages, and broadcasts messages to peers upon receiving those
|
||||
events.
|
||||
It broadcasts `NewRoundStepMessage` or `CommitStepMessage` upon new round state event. Note that
|
||||
broadcasting these messages does not depend on the PeerRoundState; it is sent on the StateChannel.
|
||||
Upon receiving VoteMessage it broadcasts `HasVoteMessage` message to its peers on the StateChannel.
|
||||
`ProposalHeartbeatMessage` is sent the same way on the StateChannel.
|
||||
|
||||
## Channels
|
||||
|
||||
|
||||
@@ -89,33 +89,6 @@ type BlockPartMessage struct {
|
||||
}
|
||||
```
|
||||
|
||||
## ProposalHeartbeatMessage
|
||||
|
||||
ProposalHeartbeatMessage is sent to signal that a node is alive and waiting for transactions
|
||||
to be able to create a next block proposal.
|
||||
|
||||
```go
|
||||
type ProposalHeartbeatMessage struct {
|
||||
Heartbeat Heartbeat
|
||||
}
|
||||
```
|
||||
|
||||
### Heartbeat
|
||||
|
||||
Heartbeat contains validator information (address and index),
|
||||
height, round and sequence number. It is signed by the private key of the validator.
|
||||
|
||||
```go
|
||||
type Heartbeat struct {
|
||||
ValidatorAddress []byte
|
||||
ValidatorIndex int
|
||||
Height int64
|
||||
Round int
|
||||
Sequence int
|
||||
Signature Signature
|
||||
}
|
||||
```
|
||||
|
||||
## NewRoundStepMessage
|
||||
|
||||
NewRoundStepMessage is sent for every step transition during the core consensus algorithm execution.
|
||||
|
||||
@@ -519,18 +519,16 @@ developers guide](../app-dev/app-development.md) for more details.
|
||||
|
||||
### Local Network
|
||||
|
||||
To run a network locally, say on a single machine, you must change the
|
||||
`_laddr` fields in the `config.toml` (or using the flags) so that the
|
||||
listening addresses of the various sockets don't conflict. Additionally,
|
||||
you must set `addr_book_strict=false` in the `config.toml`, otherwise
|
||||
Tendermint's p2p library will deny making connections to peers with the
|
||||
same IP address.
|
||||
To run a network locally, say on a single machine, you must change the `_laddr`
|
||||
fields in the `config.toml` (or using the flags) so that the listening
|
||||
addresses of the various sockets don't conflict. Additionally, you must set
|
||||
`addr_book_strict=false` in the `config.toml`, otherwise Tendermint's p2p
|
||||
library will deny making connections to peers with the same IP address.
|
||||
|
||||
### Upgrading
|
||||
|
||||
The Tendermint development cycle currently includes a lot of breaking changes.
|
||||
Upgrading from an old version to a new version usually means throwing
|
||||
away the chain data. Try out the
|
||||
[tm-migrate](https://github.com/hxzqlh/tm-tools) tool written by
|
||||
[@hxzqlh](https://github.com/hxzqlh) if you are keen to preserve the
|
||||
state of your chain when upgrading to newer versions.
|
||||
See the
|
||||
[UPGRADING.md](https://github.com/tendermint/tendermint/blob/master/UPGRADING.md)
|
||||
guide. You may need to reset your chain between major breaking releases.
|
||||
Although, we expect Tendermint to have fewer breaking releases in the future
|
||||
(especially after 1.0 release).
|
||||
|
||||
@@ -35,7 +35,7 @@ func initializeValidatorState(valAddr []byte, height int64) dbm.DB {
|
||||
LastBlockHeight: 0,
|
||||
LastBlockTime: tmtime.Now(),
|
||||
Validators: valSet,
|
||||
NextValidators: valSet.CopyIncrementAccum(1),
|
||||
NextValidators: valSet.CopyIncrementProposerPriority(1),
|
||||
LastHeightValidatorsChanged: 1,
|
||||
ConsensusParams: types.ConsensusParams{
|
||||
Evidence: types.EvidenceParams{
|
||||
|
||||
@@ -61,3 +61,16 @@ func ASCIITrim(s string) string {
|
||||
}
|
||||
return string(r)
|
||||
}
|
||||
|
||||
// StringSliceEqual checks if string slices a and b are equal
|
||||
func StringSliceEqual(a, b []string) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(a); i++ {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package common
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -35,3 +37,22 @@ func TestASCIITrim(t *testing.T) {
|
||||
assert.Equal(t, ASCIITrim(" a "), "a")
|
||||
assert.Panics(t, func() { ASCIITrim("\xC2\xA2") })
|
||||
}
|
||||
|
||||
func TestStringSliceEqual(t *testing.T) {
|
||||
tests := []struct {
|
||||
a []string
|
||||
b []string
|
||||
want bool
|
||||
}{
|
||||
{[]string{"hello", "world"}, []string{"hello", "world"}, true},
|
||||
{[]string{"test"}, []string{"test"}, true},
|
||||
{[]string{"test1"}, []string{"test2"}, false},
|
||||
{[]string{"hello", "world."}, []string{"hello", "world!"}, false},
|
||||
{[]string{"only 1 word"}, []string{"two", "words!"}, false},
|
||||
{[]string{"two", "words!"}, []string{"only 1 word"}, false},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
require.Equal(t, tt.want, StringSliceEqual(tt.a, tt.b),
|
||||
"StringSliceEqual failed on test %d", i)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,13 +180,13 @@ func testDBIterator(t *testing.T, backend DBBackendType) {
|
||||
verifyIterator(t, db.ReverseIterator(nil, nil), []int64{9, 8, 7, 5, 4, 3, 2, 1, 0}, "reverse iterator")
|
||||
|
||||
verifyIterator(t, db.Iterator(nil, int642Bytes(0)), []int64(nil), "forward iterator to 0")
|
||||
verifyIterator(t, db.ReverseIterator(nil, int642Bytes(10)), []int64(nil), "reverse iterator 10")
|
||||
verifyIterator(t, db.ReverseIterator(int642Bytes(10), nil), []int64(nil), "reverse iterator from 10 (ex)")
|
||||
|
||||
verifyIterator(t, db.Iterator(int642Bytes(0), nil), []int64{0, 1, 2, 3, 4, 5, 7, 8, 9}, "forward iterator from 0")
|
||||
verifyIterator(t, db.Iterator(int642Bytes(1), nil), []int64{1, 2, 3, 4, 5, 7, 8, 9}, "forward iterator from 1")
|
||||
verifyIterator(t, db.ReverseIterator(int642Bytes(10), nil), []int64{9, 8, 7, 5, 4, 3, 2, 1, 0}, "reverse iterator from 10")
|
||||
verifyIterator(t, db.ReverseIterator(int642Bytes(9), nil), []int64{9, 8, 7, 5, 4, 3, 2, 1, 0}, "reverse iterator from 9")
|
||||
verifyIterator(t, db.ReverseIterator(int642Bytes(8), nil), []int64{8, 7, 5, 4, 3, 2, 1, 0}, "reverse iterator from 8")
|
||||
verifyIterator(t, db.ReverseIterator(nil, int642Bytes(10)), []int64{9, 8, 7, 5, 4, 3, 2, 1, 0}, "reverse iterator from 10 (ex)")
|
||||
verifyIterator(t, db.ReverseIterator(nil, int642Bytes(9)), []int64{8, 7, 5, 4, 3, 2, 1, 0}, "reverse iterator from 9 (ex)")
|
||||
verifyIterator(t, db.ReverseIterator(nil, int642Bytes(8)), []int64{7, 5, 4, 3, 2, 1, 0}, "reverse iterator from 8 (ex)")
|
||||
|
||||
verifyIterator(t, db.Iterator(int642Bytes(5), int642Bytes(6)), []int64{5}, "forward iterator from 5 to 6")
|
||||
verifyIterator(t, db.Iterator(int642Bytes(5), int642Bytes(7)), []int64{5}, "forward iterator from 5 to 7")
|
||||
@@ -195,20 +195,20 @@ func testDBIterator(t *testing.T, backend DBBackendType) {
|
||||
verifyIterator(t, db.Iterator(int642Bytes(6), int642Bytes(8)), []int64{7}, "forward iterator from 6 to 8")
|
||||
verifyIterator(t, db.Iterator(int642Bytes(7), int642Bytes(8)), []int64{7}, "forward iterator from 7 to 8")
|
||||
|
||||
verifyIterator(t, db.ReverseIterator(int642Bytes(5), int642Bytes(4)), []int64{5}, "reverse iterator from 5 to 4")
|
||||
verifyIterator(t, db.ReverseIterator(int642Bytes(6), int642Bytes(4)), []int64{5}, "reverse iterator from 6 to 4")
|
||||
verifyIterator(t, db.ReverseIterator(int642Bytes(7), int642Bytes(4)), []int64{7, 5}, "reverse iterator from 7 to 4")
|
||||
verifyIterator(t, db.ReverseIterator(int642Bytes(6), int642Bytes(5)), []int64(nil), "reverse iterator from 6 to 5")
|
||||
verifyIterator(t, db.ReverseIterator(int642Bytes(7), int642Bytes(5)), []int64{7}, "reverse iterator from 7 to 5")
|
||||
verifyIterator(t, db.ReverseIterator(int642Bytes(7), int642Bytes(6)), []int64{7}, "reverse iterator from 7 to 6")
|
||||
verifyIterator(t, db.ReverseIterator(int642Bytes(4), int642Bytes(5)), []int64{4}, "reverse iterator from 5 (ex) to 4")
|
||||
verifyIterator(t, db.ReverseIterator(int642Bytes(4), int642Bytes(6)), []int64{5, 4}, "reverse iterator from 6 (ex) to 4")
|
||||
verifyIterator(t, db.ReverseIterator(int642Bytes(4), int642Bytes(7)), []int64{5, 4}, "reverse iterator from 7 (ex) to 4")
|
||||
verifyIterator(t, db.ReverseIterator(int642Bytes(5), int642Bytes(6)), []int64{5}, "reverse iterator from 6 (ex) to 5")
|
||||
verifyIterator(t, db.ReverseIterator(int642Bytes(5), int642Bytes(7)), []int64{5}, "reverse iterator from 7 (ex) to 5")
|
||||
verifyIterator(t, db.ReverseIterator(int642Bytes(6), int642Bytes(7)), []int64(nil), "reverse iterator from 7 (ex) to 6")
|
||||
|
||||
verifyIterator(t, db.Iterator(int642Bytes(0), int642Bytes(1)), []int64{0}, "forward iterator from 0 to 1")
|
||||
verifyIterator(t, db.ReverseIterator(int642Bytes(9), int642Bytes(8)), []int64{9}, "reverse iterator from 9 to 8")
|
||||
verifyIterator(t, db.ReverseIterator(int642Bytes(8), int642Bytes(9)), []int64{8}, "reverse iterator from 9 (ex) to 8")
|
||||
|
||||
verifyIterator(t, db.Iterator(int642Bytes(2), int642Bytes(4)), []int64{2, 3}, "forward iterator from 2 to 4")
|
||||
verifyIterator(t, db.Iterator(int642Bytes(4), int642Bytes(2)), []int64(nil), "forward iterator from 4 to 2")
|
||||
verifyIterator(t, db.ReverseIterator(int642Bytes(4), int642Bytes(2)), []int64{4, 3}, "reverse iterator from 4 to 2")
|
||||
verifyIterator(t, db.ReverseIterator(int642Bytes(2), int642Bytes(4)), []int64(nil), "reverse iterator from 2 to 4")
|
||||
verifyIterator(t, db.ReverseIterator(int642Bytes(2), int642Bytes(4)), []int64{3, 2}, "reverse iterator from 4 (ex) to 2")
|
||||
verifyIterator(t, db.ReverseIterator(int642Bytes(4), int642Bytes(2)), []int64(nil), "reverse iterator from 2 (ex) to 4")
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -205,13 +205,13 @@ type cLevelDBIterator struct {
|
||||
|
||||
func newCLevelDBIterator(source *levigo.Iterator, start, end []byte, isReverse bool) *cLevelDBIterator {
|
||||
if isReverse {
|
||||
if start == nil {
|
||||
if end == nil {
|
||||
source.SeekToLast()
|
||||
} else {
|
||||
source.Seek(start)
|
||||
source.Seek(end)
|
||||
if source.Valid() {
|
||||
soakey := source.Key() // start or after key
|
||||
if bytes.Compare(start, soakey) < 0 {
|
||||
eoakey := source.Key() // end or after key
|
||||
if bytes.Compare(end, eoakey) <= 0 {
|
||||
source.Prev()
|
||||
}
|
||||
} else {
|
||||
@@ -255,10 +255,11 @@ func (itr cLevelDBIterator) Valid() bool {
|
||||
}
|
||||
|
||||
// If key is end or past it, invalid.
|
||||
var start = itr.start
|
||||
var end = itr.end
|
||||
var key = itr.source.Key()
|
||||
if itr.isReverse {
|
||||
if end != nil && bytes.Compare(key, end) <= 0 {
|
||||
if start != nil && bytes.Compare(key, start) < 0 {
|
||||
itr.isInvalid = true
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ func (db *FSDB) MakeIterator(start, end []byte, isReversed bool) Iterator {
|
||||
|
||||
// We need a copy of all of the keys.
|
||||
// Not the best, but probably not a bottleneck depending.
|
||||
keys, err := list(db.dir, start, end, isReversed)
|
||||
keys, err := list(db.dir, start, end)
|
||||
if err != nil {
|
||||
panic(errors.Wrapf(err, "Listing keys in %s", db.dir))
|
||||
}
|
||||
@@ -229,7 +229,7 @@ func remove(path string) error {
|
||||
|
||||
// List keys in a directory, stripping of escape sequences and dir portions.
|
||||
// CONTRACT: returns os errors directly without wrapping.
|
||||
func list(dirPath string, start, end []byte, isReversed bool) ([]string, error) {
|
||||
func list(dirPath string, start, end []byte) ([]string, error) {
|
||||
dir, err := os.Open(dirPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -247,7 +247,7 @@ func list(dirPath string, start, end []byte, isReversed bool) ([]string, error)
|
||||
return nil, fmt.Errorf("Failed to unescape %s while listing", name)
|
||||
}
|
||||
key := unescapeKey([]byte(n))
|
||||
if IsKeyInDomain(key, start, end, isReversed) {
|
||||
if IsKeyInDomain(key, start, end) {
|
||||
keys = append(keys, string(key))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,13 +213,13 @@ var _ Iterator = (*goLevelDBIterator)(nil)
|
||||
|
||||
func newGoLevelDBIterator(source iterator.Iterator, start, end []byte, isReverse bool) *goLevelDBIterator {
|
||||
if isReverse {
|
||||
if start == nil {
|
||||
if end == nil {
|
||||
source.Last()
|
||||
} else {
|
||||
valid := source.Seek(start)
|
||||
valid := source.Seek(end)
|
||||
if valid {
|
||||
soakey := source.Key() // start or after key
|
||||
if bytes.Compare(start, soakey) < 0 {
|
||||
eoakey := source.Key() // end or after key
|
||||
if bytes.Compare(end, eoakey) <= 0 {
|
||||
source.Prev()
|
||||
}
|
||||
} else {
|
||||
@@ -265,11 +265,12 @@ func (itr *goLevelDBIterator) Valid() bool {
|
||||
}
|
||||
|
||||
// If key is end or past it, invalid.
|
||||
var start = itr.start
|
||||
var end = itr.end
|
||||
var key = itr.source.Key()
|
||||
|
||||
if itr.isReverse {
|
||||
if end != nil && bytes.Compare(key, end) <= 0 {
|
||||
if start != nil && bytes.Compare(key, start) < 0 {
|
||||
itr.isInvalid = true
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -237,7 +237,7 @@ func (itr *memDBIterator) assertIsValid() {
|
||||
func (db *MemDB) getSortedKeys(start, end []byte, reverse bool) []string {
|
||||
keys := []string{}
|
||||
for key := range db.db {
|
||||
inDomain := IsKeyInDomain([]byte(key), start, end, reverse)
|
||||
inDomain := IsKeyInDomain([]byte(key), start, end)
|
||||
if inDomain {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
|
||||
@@ -131,27 +131,13 @@ func (pdb *prefixDB) ReverseIterator(start, end []byte) Iterator {
|
||||
defer pdb.mtx.Unlock()
|
||||
|
||||
var pstart, pend []byte
|
||||
if start == nil {
|
||||
// This may cause the underlying iterator to start with
|
||||
// an item which doesn't start with prefix. We will skip
|
||||
// that item later in this function. See 'skipOne'.
|
||||
pstart = cpIncr(pdb.prefix)
|
||||
} else {
|
||||
pstart = append(cp(pdb.prefix), start...)
|
||||
}
|
||||
pstart = append(cp(pdb.prefix), start...)
|
||||
if end == nil {
|
||||
// This may cause the underlying iterator to end with an
|
||||
// item which doesn't start with prefix. The
|
||||
// prefixIterator will terminate iteration
|
||||
// automatically upon detecting this.
|
||||
pend = cpDecr(pdb.prefix)
|
||||
pend = cpIncr(pdb.prefix)
|
||||
} else {
|
||||
pend = append(cp(pdb.prefix), end...)
|
||||
}
|
||||
ritr := pdb.db.ReverseIterator(pstart, pend)
|
||||
if start == nil {
|
||||
skipOne(ritr, cpIncr(pdb.prefix))
|
||||
}
|
||||
return newPrefixIterator(
|
||||
pdb.prefix,
|
||||
start,
|
||||
@@ -310,7 +296,6 @@ func (itr *prefixIterator) Next() {
|
||||
}
|
||||
itr.source.Next()
|
||||
if !itr.source.Valid() || !bytes.HasPrefix(itr.source.Key(), itr.prefix) {
|
||||
itr.source.Close()
|
||||
itr.valid = false
|
||||
return
|
||||
}
|
||||
@@ -345,13 +330,3 @@ func stripPrefix(key []byte, prefix []byte) (stripped []byte) {
|
||||
}
|
||||
return key[len(prefix):]
|
||||
}
|
||||
|
||||
// If the first iterator item is skipKey, then
|
||||
// skip it.
|
||||
func skipOne(itr Iterator, skipKey []byte) {
|
||||
if itr.Valid() {
|
||||
if bytes.Equal(itr.Key(), skipKey) {
|
||||
itr.Next()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,8 +113,46 @@ func TestPrefixDBReverseIterator2(t *testing.T) {
|
||||
db := mockDBWithStuff()
|
||||
pdb := NewPrefixDB(db, bz("key"))
|
||||
|
||||
itr := pdb.ReverseIterator(bz(""), nil)
|
||||
checkDomain(t, itr, bz(""), nil)
|
||||
checkItem(t, itr, bz("3"), bz("value3"))
|
||||
checkNext(t, itr, true)
|
||||
checkItem(t, itr, bz("2"), bz("value2"))
|
||||
checkNext(t, itr, true)
|
||||
checkItem(t, itr, bz("1"), bz("value1"))
|
||||
checkNext(t, itr, true)
|
||||
checkItem(t, itr, bz(""), bz("value"))
|
||||
checkNext(t, itr, false)
|
||||
checkInvalid(t, itr)
|
||||
itr.Close()
|
||||
}
|
||||
|
||||
func TestPrefixDBReverseIterator3(t *testing.T) {
|
||||
db := mockDBWithStuff()
|
||||
pdb := NewPrefixDB(db, bz("key"))
|
||||
|
||||
itr := pdb.ReverseIterator(nil, bz(""))
|
||||
checkDomain(t, itr, nil, bz(""))
|
||||
checkInvalid(t, itr)
|
||||
itr.Close()
|
||||
}
|
||||
|
||||
func TestPrefixDBReverseIterator4(t *testing.T) {
|
||||
db := mockDBWithStuff()
|
||||
pdb := NewPrefixDB(db, bz("key"))
|
||||
|
||||
itr := pdb.ReverseIterator(bz(""), bz(""))
|
||||
checkDomain(t, itr, bz(""), bz(""))
|
||||
checkInvalid(t, itr)
|
||||
itr.Close()
|
||||
}
|
||||
|
||||
func TestPrefixDBReverseIterator5(t *testing.T) {
|
||||
db := mockDBWithStuff()
|
||||
pdb := NewPrefixDB(db, bz("key"))
|
||||
|
||||
itr := pdb.ReverseIterator(bz("1"), nil)
|
||||
checkDomain(t, itr, bz("1"), nil)
|
||||
checkItem(t, itr, bz("3"), bz("value3"))
|
||||
checkNext(t, itr, true)
|
||||
checkItem(t, itr, bz("2"), bz("value2"))
|
||||
@@ -125,23 +163,30 @@ func TestPrefixDBReverseIterator2(t *testing.T) {
|
||||
itr.Close()
|
||||
}
|
||||
|
||||
func TestPrefixDBReverseIterator3(t *testing.T) {
|
||||
func TestPrefixDBReverseIterator6(t *testing.T) {
|
||||
db := mockDBWithStuff()
|
||||
pdb := NewPrefixDB(db, bz("key"))
|
||||
|
||||
itr := pdb.ReverseIterator(bz(""), nil)
|
||||
checkDomain(t, itr, bz(""), nil)
|
||||
checkItem(t, itr, bz(""), bz("value"))
|
||||
itr := pdb.ReverseIterator(bz("2"), nil)
|
||||
checkDomain(t, itr, bz("2"), nil)
|
||||
checkItem(t, itr, bz("3"), bz("value3"))
|
||||
checkNext(t, itr, true)
|
||||
checkItem(t, itr, bz("2"), bz("value2"))
|
||||
checkNext(t, itr, false)
|
||||
checkInvalid(t, itr)
|
||||
itr.Close()
|
||||
}
|
||||
|
||||
func TestPrefixDBReverseIterator4(t *testing.T) {
|
||||
func TestPrefixDBReverseIterator7(t *testing.T) {
|
||||
db := mockDBWithStuff()
|
||||
pdb := NewPrefixDB(db, bz("key"))
|
||||
|
||||
itr := pdb.ReverseIterator(bz(""), bz(""))
|
||||
itr := pdb.ReverseIterator(nil, bz("2"))
|
||||
checkDomain(t, itr, nil, bz("2"))
|
||||
checkItem(t, itr, bz("1"), bz("value1"))
|
||||
checkNext(t, itr, true)
|
||||
checkItem(t, itr, bz(""), bz("value"))
|
||||
checkNext(t, itr, false)
|
||||
checkInvalid(t, itr)
|
||||
itr.Close()
|
||||
}
|
||||
|
||||
@@ -34,9 +34,9 @@ type DB interface {
|
||||
Iterator(start, end []byte) Iterator
|
||||
|
||||
// Iterate over a domain of keys in descending order. End is exclusive.
|
||||
// Start must be greater than end, or the Iterator is invalid.
|
||||
// If start is nil, iterates from the last/greatest item (inclusive).
|
||||
// If end is nil, iterates up to the first/least item (inclusive).
|
||||
// Start must be less than end, or the Iterator is invalid.
|
||||
// If start is nil, iterates up to the first/least item (inclusive).
|
||||
// If end is nil, iterates from the last/greatest item (inclusive).
|
||||
// CONTRACT: No writes may happen within a domain while an iterator exists over it.
|
||||
// CONTRACT: start, end readonly []byte
|
||||
ReverseIterator(start, end []byte) Iterator
|
||||
|
||||
@@ -33,46 +33,13 @@ func cpIncr(bz []byte) (ret []byte) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns a slice of the same length (big endian)
|
||||
// except decremented by one.
|
||||
// Returns nil on underflow (e.g. if bz bytes are all 0x00)
|
||||
// CONTRACT: len(bz) > 0
|
||||
func cpDecr(bz []byte) (ret []byte) {
|
||||
if len(bz) == 0 {
|
||||
panic("cpDecr expects non-zero bz length")
|
||||
}
|
||||
ret = cp(bz)
|
||||
for i := len(bz) - 1; i >= 0; i-- {
|
||||
if ret[i] > byte(0x00) {
|
||||
ret[i]--
|
||||
return
|
||||
}
|
||||
ret[i] = byte(0xFF)
|
||||
if i == 0 {
|
||||
// Underflow
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// See DB interface documentation for more information.
|
||||
func IsKeyInDomain(key, start, end []byte, isReverse bool) bool {
|
||||
if !isReverse {
|
||||
if bytes.Compare(key, start) < 0 {
|
||||
return false
|
||||
}
|
||||
if end != nil && bytes.Compare(end, key) <= 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
if start != nil && bytes.Compare(start, key) < 0 {
|
||||
return false
|
||||
}
|
||||
if end != nil && bytes.Compare(key, end) <= 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
func IsKeyInDomain(key, start, end []byte) bool {
|
||||
if bytes.Compare(key, start) < 0 {
|
||||
return false
|
||||
}
|
||||
if end != nil && bytes.Compare(end, key) <= 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
@@ -19,12 +20,22 @@ var (
|
||||
// inside a test (not in the init func) because
|
||||
// verbose flag only set at the time of testing.
|
||||
func TestingLogger() Logger {
|
||||
return TestingLoggerWithOutput(os.Stdout)
|
||||
}
|
||||
|
||||
// TestingLoggerWOutput returns a TMLogger which writes to (w io.Writer) if testing being run
|
||||
// with the verbose (-v) flag, NopLogger otherwise.
|
||||
//
|
||||
// Note that the call to TestingLoggerWithOutput(w io.Writer) must be made
|
||||
// inside a test (not in the init func) because
|
||||
// verbose flag only set at the time of testing.
|
||||
func TestingLoggerWithOutput(w io.Writer) Logger {
|
||||
if _testingLogger != nil {
|
||||
return _testingLogger
|
||||
}
|
||||
|
||||
if testing.Verbose() {
|
||||
_testingLogger = NewTMLogger(NewSyncWriter(os.Stdout))
|
||||
_testingLogger = NewTMLogger(NewSyncWriter(w))
|
||||
} else {
|
||||
_testingLogger = NewNopLogger()
|
||||
}
|
||||
|
||||
@@ -105,8 +105,8 @@ func (dbp *DBProvider) LatestFullCommit(chainID string, minHeight, maxHeight int
|
||||
}
|
||||
|
||||
itr := dbp.db.ReverseIterator(
|
||||
signedHeaderKey(chainID, maxHeight),
|
||||
signedHeaderKey(chainID, minHeight-1),
|
||||
signedHeaderKey(chainID, minHeight),
|
||||
append(signedHeaderKey(chainID, maxHeight), byte(0x00)),
|
||||
)
|
||||
defer itr.Close()
|
||||
|
||||
@@ -190,8 +190,8 @@ func (dbp *DBProvider) deleteAfterN(chainID string, after int) error {
|
||||
dbp.logger.Info("DBProvider.deleteAfterN()...", "chainID", chainID, "after", after)
|
||||
|
||||
itr := dbp.db.ReverseIterator(
|
||||
signedHeaderKey(chainID, 1<<63-1),
|
||||
signedHeaderKey(chainID, 0),
|
||||
signedHeaderKey(chainID, 1),
|
||||
append(signedHeaderKey(chainID, 1<<63-1), byte(0x00)),
|
||||
)
|
||||
defer itr.Close()
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ If we cannot update directly from H -> H' because there was too much change to
|
||||
the validator set, then we can look for some Hm (H < Hm < H') with a validator
|
||||
set Vm. Then we try to update H -> Hm and then Hm -> H' in two steps. If one
|
||||
of these steps doesn't work, then we continue bisecting, until we eventually
|
||||
have to externally validate the valdiator set changes at every block.
|
||||
have to externally validate the validator set changes at every block.
|
||||
|
||||
Since we never trust any server in this protocol, only the signatures
|
||||
themselves, it doesn't matter if the seed comes from a (possibly malicious)
|
||||
|
||||
@@ -108,6 +108,10 @@ func PostCheckMaxGas(maxGas int64) PostCheckFunc {
|
||||
if maxGas == -1 {
|
||||
return nil
|
||||
}
|
||||
if res.GasWanted < 0 {
|
||||
return fmt.Errorf("gas wanted %d is negative",
|
||||
res.GasWanted)
|
||||
}
|
||||
if res.GasWanted > maxGas {
|
||||
return fmt.Errorf("gas wanted %d is greater than max gas %d",
|
||||
res.GasWanted, maxGas)
|
||||
@@ -486,11 +490,15 @@ func (mem *Mempool) ReapMaxBytesMaxGas(maxBytes, maxGas int64) types.Txs {
|
||||
return txs
|
||||
}
|
||||
totalBytes += int64(len(memTx.tx)) + aminoOverhead
|
||||
// Check total gas requirement
|
||||
if maxGas > -1 && totalGas+memTx.gasWanted > maxGas {
|
||||
// Check total gas requirement.
|
||||
// If maxGas is negative, skip this check.
|
||||
// Since newTotalGas < masGas, which
|
||||
// must be non-negative, it follows that this won't overflow.
|
||||
newTotalGas := totalGas + memTx.gasWanted
|
||||
if maxGas > -1 && newTotalGas > maxGas {
|
||||
return txs
|
||||
}
|
||||
totalGas += memTx.gasWanted
|
||||
totalGas = newTotalGas
|
||||
txs = append(txs, memTx.tx)
|
||||
}
|
||||
return txs
|
||||
|
||||
37
node/node.go
37
node/node.go
@@ -210,13 +210,18 @@ func NewNode(config *cfg.Config,
|
||||
// what happened during block replay).
|
||||
state = sm.LoadState(stateDB)
|
||||
|
||||
// Ensure the state's block version matches that of the software.
|
||||
// Log the version info.
|
||||
logger.Info("Version info",
|
||||
"software", version.TMCoreSemVer,
|
||||
"block", version.BlockProtocol,
|
||||
"p2p", version.P2PProtocol,
|
||||
)
|
||||
|
||||
// If the state and software differ in block version, at least log it.
|
||||
if state.Version.Consensus.Block != version.BlockProtocol {
|
||||
return nil, fmt.Errorf(
|
||||
"Block version of the software does not match that of the state.\n"+
|
||||
"Got version.BlockProtocol=%v, state.Version.Consensus.Block=%v",
|
||||
version.BlockProtocol,
|
||||
state.Version.Consensus.Block,
|
||||
logger.Info("Software and state have different block protocols",
|
||||
"software", version.BlockProtocol,
|
||||
"state", state.Version.Consensus.Block,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -224,7 +229,7 @@ func NewNode(config *cfg.Config,
|
||||
// If an address is provided, listen on the socket for a connection from an
|
||||
// external signing process.
|
||||
// FIXME: we should start services inside OnStart
|
||||
privValidator, err = createAndStartPrivValidatorSocketClient(config.PrivValidatorListenAddr, logger)
|
||||
privValidator, err = CreateAndStartPrivValidatorSocketClient(config.PrivValidatorListenAddr, logger)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Error with private validator socket client")
|
||||
}
|
||||
@@ -235,16 +240,19 @@ func NewNode(config *cfg.Config,
|
||||
fastSync := config.FastSync
|
||||
if state.Validators.Size() == 1 {
|
||||
addr, _ := state.Validators.GetByIndex(0)
|
||||
if bytes.Equal(privValidator.GetAddress(), addr) {
|
||||
privValAddr := privValidator.GetPubKey().Address()
|
||||
if bytes.Equal(privValAddr, addr) {
|
||||
fastSync = false
|
||||
}
|
||||
}
|
||||
|
||||
pubKey := privValidator.GetPubKey()
|
||||
addr := pubKey.Address()
|
||||
// Log whether this node is a validator or an observer
|
||||
if state.Validators.HasAddress(privValidator.GetAddress()) {
|
||||
consensusLogger.Info("This node is a validator", "addr", privValidator.GetAddress(), "pubKey", privValidator.GetPubKey())
|
||||
if state.Validators.HasAddress(addr) {
|
||||
consensusLogger.Info("This node is a validator", "addr", addr, "pubKey", pubKey)
|
||||
} else {
|
||||
consensusLogger.Info("This node is not a validator", "addr", privValidator.GetAddress(), "pubKey", privValidator.GetPubKey())
|
||||
consensusLogger.Info("This node is not a validator", "addr", addr, "pubKey", pubKey)
|
||||
}
|
||||
|
||||
csMetrics, p2pMetrics, memplMetrics, smMetrics := metricsProvider()
|
||||
@@ -454,7 +462,7 @@ func NewNode(config *cfg.Config,
|
||||
Seeds: splitAndTrimEmpty(config.P2P.Seeds, ",", " "),
|
||||
SeedMode: config.P2P.SeedMode,
|
||||
})
|
||||
pexReactor.SetLogger(p2pLogger)
|
||||
pexReactor.SetLogger(logger.With("module", "pex"))
|
||||
sw.AddReactor("PEX", pexReactor)
|
||||
}
|
||||
|
||||
@@ -611,7 +619,8 @@ func (n *Node) ConfigureRPC() {
|
||||
rpccore.SetEvidencePool(n.evidencePool)
|
||||
rpccore.SetP2PPeers(n.sw)
|
||||
rpccore.SetP2PTransport(n)
|
||||
rpccore.SetPubKey(n.privValidator.GetPubKey())
|
||||
pubKey := n.privValidator.GetPubKey()
|
||||
rpccore.SetPubKey(pubKey)
|
||||
rpccore.SetGenesisDoc(n.genesisDoc)
|
||||
rpccore.SetAddrBook(n.addrBook)
|
||||
rpccore.SetProxyAppQuery(n.proxyApp.Query())
|
||||
@@ -844,7 +853,7 @@ func saveGenesisDoc(db dbm.DB, genDoc *types.GenesisDoc) {
|
||||
db.SetSync(genesisDocKey, bytes)
|
||||
}
|
||||
|
||||
func createAndStartPrivValidatorSocketClient(
|
||||
func CreateAndStartPrivValidatorSocketClient(
|
||||
listenAddr string,
|
||||
logger log.Logger,
|
||||
) (types.PrivValidator, error) {
|
||||
|
||||
@@ -160,6 +160,7 @@ func NewMConnectionWithConfig(conn net.Conn, chDescs []*ChannelDescriptor, onRec
|
||||
onReceive: onReceive,
|
||||
onError: onError,
|
||||
config: config,
|
||||
created: time.Now(),
|
||||
}
|
||||
|
||||
// Create channels
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
crypto "github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
)
|
||||
|
||||
@@ -62,7 +62,7 @@ func PrometheusMetrics(namespace string) *Metrics {
|
||||
// NopMetrics returns no-op Metrics.
|
||||
func NopMetrics() *Metrics {
|
||||
return &Metrics{
|
||||
Peers: discard.NewGauge(),
|
||||
Peers: discard.NewGauge(),
|
||||
PeerReceiveBytesTotal: discard.NewCounter(),
|
||||
PeerSendBytesTotal: discard.NewCounter(),
|
||||
PeerPendingSendBytes: discard.NewGauge(),
|
||||
|
||||
@@ -98,13 +98,15 @@ func (ps *PeerSet) Get(peerKey ID) Peer {
|
||||
}
|
||||
|
||||
// Remove discards peer by its Key, if the peer was previously memoized.
|
||||
func (ps *PeerSet) Remove(peer Peer) {
|
||||
// Returns true if the peer was removed, and false if it was not found.
|
||||
// in the set.
|
||||
func (ps *PeerSet) Remove(peer Peer) bool {
|
||||
ps.mtx.Lock()
|
||||
defer ps.mtx.Unlock()
|
||||
|
||||
item := ps.lookup[peer.ID()]
|
||||
if item == nil {
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
index := item.index
|
||||
@@ -116,7 +118,7 @@ func (ps *PeerSet) Remove(peer Peer) {
|
||||
if index == len(ps.list)-1 {
|
||||
ps.list = newList
|
||||
delete(ps.lookup, peer.ID())
|
||||
return
|
||||
return true
|
||||
}
|
||||
|
||||
// Replace the popped item with the last item in the old list.
|
||||
@@ -127,6 +129,7 @@ func (ps *PeerSet) Remove(peer Peer) {
|
||||
lastPeerItem.index = index
|
||||
ps.list = newList
|
||||
delete(ps.lookup, peer.ID())
|
||||
return true
|
||||
}
|
||||
|
||||
// Size returns the number of unique items in the peerSet.
|
||||
|
||||
@@ -60,13 +60,15 @@ func TestPeerSetAddRemoveOne(t *testing.T) {
|
||||
n := len(peerList)
|
||||
// 1. Test removing from the front
|
||||
for i, peerAtFront := range peerList {
|
||||
peerSet.Remove(peerAtFront)
|
||||
removed := peerSet.Remove(peerAtFront)
|
||||
assert.True(t, removed)
|
||||
wantSize := n - i - 1
|
||||
for j := 0; j < 2; j++ {
|
||||
assert.Equal(t, false, peerSet.Has(peerAtFront.ID()), "#%d Run #%d: failed to remove peer", i, j)
|
||||
assert.Equal(t, wantSize, peerSet.Size(), "#%d Run #%d: failed to remove peer and decrement size", i, j)
|
||||
// Test the route of removing the now non-existent element
|
||||
peerSet.Remove(peerAtFront)
|
||||
removed := peerSet.Remove(peerAtFront)
|
||||
assert.False(t, removed)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +83,8 @@ func TestPeerSetAddRemoveOne(t *testing.T) {
|
||||
// b) In reverse, remove each element
|
||||
for i := n - 1; i >= 0; i-- {
|
||||
peerAtEnd := peerList[i]
|
||||
peerSet.Remove(peerAtEnd)
|
||||
removed := peerSet.Remove(peerAtEnd)
|
||||
assert.True(t, removed)
|
||||
assert.Equal(t, false, peerSet.Has(peerAtEnd.ID()), "#%d: failed to remove item at end", i)
|
||||
assert.Equal(t, i, peerSet.Size(), "#%d: differing sizes after peerSet.Remove(atEndPeer)", i)
|
||||
}
|
||||
@@ -105,7 +108,8 @@ func TestPeerSetAddRemoveMany(t *testing.T) {
|
||||
}
|
||||
|
||||
for i, peer := range peers {
|
||||
peerSet.Remove(peer)
|
||||
removed := peerSet.Remove(peer)
|
||||
assert.True(t, removed)
|
||||
if peerSet.Has(peer.ID()) {
|
||||
t.Errorf("Failed to remove peer")
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
crypto "github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
crypto "github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
)
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
crypto "github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
|
||||
@@ -211,7 +211,9 @@ func (sw *Switch) OnStop() {
|
||||
// Stop peers
|
||||
for _, p := range sw.peers.List() {
|
||||
p.Stop()
|
||||
sw.peers.Remove(p)
|
||||
if sw.peers.Remove(p) {
|
||||
sw.metrics.Peers.Add(float64(-1))
|
||||
}
|
||||
}
|
||||
|
||||
// Stop reactors
|
||||
@@ -299,8 +301,9 @@ func (sw *Switch) StopPeerGracefully(peer Peer) {
|
||||
}
|
||||
|
||||
func (sw *Switch) stopAndRemovePeer(peer Peer, reason interface{}) {
|
||||
sw.peers.Remove(peer)
|
||||
sw.metrics.Peers.Add(float64(-1))
|
||||
if sw.peers.Remove(peer) {
|
||||
sw.metrics.Peers.Add(float64(-1))
|
||||
}
|
||||
peer.Stop()
|
||||
for _, reactor := range sw.reactors {
|
||||
reactor.RemovePeer(peer, reason)
|
||||
@@ -505,6 +508,12 @@ func (sw *Switch) acceptRoutine() {
|
||||
"err", err,
|
||||
"numPeers", sw.peers.Size(),
|
||||
)
|
||||
// We could instead have a retry loop around the acceptRoutine,
|
||||
// but that would need to stop and let the node shutdown eventually.
|
||||
// So might as well panic and let process managers restart the node.
|
||||
// There's no point in letting the node run without the acceptRoutine,
|
||||
// since it won't be able to accept new connections.
|
||||
panic(fmt.Errorf("accept routine exited: %v", err))
|
||||
}
|
||||
|
||||
break
|
||||
|
||||
@@ -3,10 +3,17 @@ package p2p
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@@ -335,6 +342,54 @@ func TestSwitchStopsNonPersistentPeerOnError(t *testing.T) {
|
||||
assert.False(p.IsRunning())
|
||||
}
|
||||
|
||||
func TestSwitchStopPeerForError(t *testing.T) {
|
||||
s := httptest.NewServer(stdprometheus.UninstrumentedHandler())
|
||||
defer s.Close()
|
||||
|
||||
scrapeMetrics := func() string {
|
||||
resp, _ := http.Get(s.URL)
|
||||
buf, _ := ioutil.ReadAll(resp.Body)
|
||||
return string(buf)
|
||||
}
|
||||
|
||||
namespace, subsystem, name := config.TestInstrumentationConfig().Namespace, MetricsSubsystem, "peers"
|
||||
re := regexp.MustCompile(namespace + `_` + subsystem + `_` + name + ` ([0-9\.]+)`)
|
||||
peersMetricValue := func() float64 {
|
||||
matches := re.FindStringSubmatch(scrapeMetrics())
|
||||
f, _ := strconv.ParseFloat(matches[1], 64)
|
||||
return f
|
||||
}
|
||||
|
||||
p2pMetrics := PrometheusMetrics(namespace)
|
||||
|
||||
// make two connected switches
|
||||
sw1, sw2 := MakeSwitchPair(t, func(i int, sw *Switch) *Switch {
|
||||
// set metrics on sw1
|
||||
if i == 0 {
|
||||
opt := WithMetrics(p2pMetrics)
|
||||
opt(sw)
|
||||
}
|
||||
return initSwitchFunc(i, sw)
|
||||
})
|
||||
|
||||
assert.Equal(t, len(sw1.Peers().List()), 1)
|
||||
assert.EqualValues(t, 1, peersMetricValue())
|
||||
|
||||
// send messages to the peer from sw1
|
||||
p := sw1.Peers().List()[0]
|
||||
p.Send(0x1, []byte("here's a message to send"))
|
||||
|
||||
// stop sw2. this should cause the p to fail,
|
||||
// which results in calling StopPeerForError internally
|
||||
sw2.Stop()
|
||||
|
||||
// now call StopPeerForError explicitly, eg. from a reactor
|
||||
sw1.StopPeerForError(p, fmt.Errorf("some err"))
|
||||
|
||||
assert.Equal(t, len(sw1.Peers().List()), 0)
|
||||
assert.EqualValues(t, 0, peersMetricValue())
|
||||
}
|
||||
|
||||
func TestSwitchReconnectsToPersistentPeer(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
crypto "github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
@@ -184,7 +184,7 @@ func MakeSwitch(
|
||||
|
||||
// TODO: let the config be passed in?
|
||||
sw := initSwitch(i, NewSwitch(cfg, t, opts...))
|
||||
sw.SetLogger(log.TestingLogger())
|
||||
sw.SetLogger(log.TestingLogger().With("switch", i))
|
||||
sw.SetNodeKey(&nodeKey)
|
||||
|
||||
ni := nodeInfo.(DefaultNodeInfo)
|
||||
|
||||
@@ -67,7 +67,10 @@ func (sc *IPCVal) OnStart() error {
|
||||
return err
|
||||
}
|
||||
|
||||
sc.RemoteSignerClient = NewRemoteSignerClient(sc.conn)
|
||||
sc.RemoteSignerClient, err = NewRemoteSignerClient(sc.conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Start a routine to keep the connection alive
|
||||
sc.cancelPing = make(chan struct{}, 1)
|
||||
|
||||
@@ -56,12 +56,6 @@ type FilePV struct {
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
// GetAddress returns the address of the validator.
|
||||
// Implements PrivValidator.
|
||||
func (pv *FilePV) GetAddress() types.Address {
|
||||
return pv.Address
|
||||
}
|
||||
|
||||
// GetPubKey returns the public key of the validator.
|
||||
// Implements PrivValidator.
|
||||
func (pv *FilePV) GetPubKey() crypto.PubKey {
|
||||
@@ -290,22 +284,11 @@ func (pv *FilePV) saveSigned(height int64, round int, step int8,
|
||||
pv.save()
|
||||
}
|
||||
|
||||
// SignHeartbeat signs a canonical representation of the heartbeat, along with the chainID.
|
||||
// Implements PrivValidator.
|
||||
func (pv *FilePV) SignHeartbeat(chainID string, heartbeat *types.Heartbeat) error {
|
||||
pv.mtx.Lock()
|
||||
defer pv.mtx.Unlock()
|
||||
sig, err := pv.PrivKey.Sign(heartbeat.SignBytes(chainID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
heartbeat.Signature = sig
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns a string representation of the FilePV.
|
||||
func (pv *FilePV) String() string {
|
||||
return fmt.Sprintf("PrivValidator{%v LH:%v, LR:%v, LS:%v}", pv.GetAddress(), pv.LastHeight, pv.LastRound, pv.LastStep)
|
||||
// does not error in FilePV:
|
||||
addr := pv.GetPubKey().Address()
|
||||
return fmt.Sprintf("PrivValidator{%v LH:%v, LR:%v, LS:%v}", addr, pv.LastHeight, pv.LastRound, pv.LastStep)
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
@@ -25,10 +25,11 @@ func TestGenLoadValidator(t *testing.T) {
|
||||
height := int64(100)
|
||||
privVal.LastHeight = height
|
||||
privVal.Save()
|
||||
addr := privVal.GetAddress()
|
||||
addr := privVal.GetPubKey().Address()
|
||||
|
||||
privVal = LoadFilePV(tempFile.Name())
|
||||
assert.Equal(addr, privVal.GetAddress(), "expected privval addr to be the same")
|
||||
loadedAddr := privVal.GetPubKey().Address()
|
||||
assert.Equal(addr, loadedAddr)
|
||||
assert.Equal(height, privVal.LastHeight, "expected privval.LastHeight to have been saved")
|
||||
}
|
||||
|
||||
@@ -42,9 +43,10 @@ func TestLoadOrGenValidator(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
privVal := LoadOrGenFilePV(tempFilePath)
|
||||
addr := privVal.GetAddress()
|
||||
addr := privVal.GetPubKey().Address()
|
||||
privVal = LoadOrGenFilePV(tempFilePath)
|
||||
assert.Equal(addr, privVal.GetAddress(), "expected privval addr to be the same")
|
||||
loadedAddr := privVal.GetPubKey().Address()
|
||||
assert.Equal(addr, loadedAddr)
|
||||
}
|
||||
|
||||
func TestUnmarshalValidator(t *testing.T) {
|
||||
@@ -81,8 +83,10 @@ func TestUnmarshalValidator(t *testing.T) {
|
||||
require.Nil(err, "%+v", err)
|
||||
|
||||
// make sure the values match
|
||||
assert.EqualValues(addr, val.GetAddress())
|
||||
assert.EqualValues(pubKey, val.GetPubKey())
|
||||
loadedAddr := val.GetPubKey().Address()
|
||||
assert.EqualValues(addr, loadedAddr)
|
||||
loadedKey := val.GetPubKey()
|
||||
assert.EqualValues(pubKey, loadedKey)
|
||||
assert.EqualValues(privKey, val.PrivKey)
|
||||
|
||||
// export it and make sure it is the same
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/tendermint/go-amino"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
@@ -15,8 +17,9 @@ import (
|
||||
// RemoteSignerClient implements PrivValidator, it uses a socket to request signatures
|
||||
// from an external process.
|
||||
type RemoteSignerClient struct {
|
||||
conn net.Conn
|
||||
lock sync.Mutex
|
||||
conn net.Conn
|
||||
consensusPubKey crypto.PubKey
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
// Check that RemoteSignerClient implements PrivValidator.
|
||||
@@ -25,38 +28,29 @@ var _ types.PrivValidator = (*RemoteSignerClient)(nil)
|
||||
// NewRemoteSignerClient returns an instance of RemoteSignerClient.
|
||||
func NewRemoteSignerClient(
|
||||
conn net.Conn,
|
||||
) *RemoteSignerClient {
|
||||
) (*RemoteSignerClient, error) {
|
||||
sc := &RemoteSignerClient{
|
||||
conn: conn,
|
||||
}
|
||||
return sc
|
||||
}
|
||||
|
||||
// GetAddress implements PrivValidator.
|
||||
func (sc *RemoteSignerClient) GetAddress() types.Address {
|
||||
pubKey, err := sc.getPubKey()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return nil, cmn.ErrorWrap(err, "error while retrieving public key for remote signer")
|
||||
}
|
||||
|
||||
return pubKey.Address()
|
||||
// retrieve and memoize the consensus public key once:
|
||||
sc.consensusPubKey = pubKey
|
||||
return sc, nil
|
||||
}
|
||||
|
||||
// GetPubKey implements PrivValidator.
|
||||
func (sc *RemoteSignerClient) GetPubKey() crypto.PubKey {
|
||||
pubKey, err := sc.getPubKey()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return pubKey
|
||||
return sc.consensusPubKey
|
||||
}
|
||||
|
||||
func (sc *RemoteSignerClient) getPubKey() (crypto.PubKey, error) {
|
||||
sc.lock.Lock()
|
||||
defer sc.lock.Unlock()
|
||||
|
||||
err := writeMsg(sc.conn, &PubKeyMsg{})
|
||||
err := writeMsg(sc.conn, &PubKeyRequest{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -65,8 +59,16 @@ func (sc *RemoteSignerClient) getPubKey() (crypto.PubKey, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pubKeyResp, ok := res.(*PubKeyResponse)
|
||||
if !ok {
|
||||
return nil, errors.Wrap(ErrUnexpectedResponse, "response is not PubKeyResponse")
|
||||
}
|
||||
|
||||
return res.(*PubKeyMsg).PubKey, nil
|
||||
if pubKeyResp.Error != nil {
|
||||
return nil, errors.Wrap(pubKeyResp.Error, "failed to get private validator's public key")
|
||||
}
|
||||
|
||||
return pubKeyResp.PubKey, nil
|
||||
}
|
||||
|
||||
// SignVote implements PrivValidator.
|
||||
@@ -125,35 +127,6 @@ func (sc *RemoteSignerClient) SignProposal(
|
||||
return nil
|
||||
}
|
||||
|
||||
// SignHeartbeat implements PrivValidator.
|
||||
func (sc *RemoteSignerClient) SignHeartbeat(
|
||||
chainID string,
|
||||
heartbeat *types.Heartbeat,
|
||||
) error {
|
||||
sc.lock.Lock()
|
||||
defer sc.lock.Unlock()
|
||||
|
||||
err := writeMsg(sc.conn, &SignHeartbeatRequest{Heartbeat: heartbeat})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := readMsg(sc.conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp, ok := res.(*SignedHeartbeatResponse)
|
||||
if !ok {
|
||||
return ErrUnexpectedResponse
|
||||
}
|
||||
if resp.Error != nil {
|
||||
return resp.Error
|
||||
}
|
||||
*heartbeat = *resp.Heartbeat
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ping is used to check connection health.
|
||||
func (sc *RemoteSignerClient) Ping() error {
|
||||
sc.lock.Lock()
|
||||
@@ -181,20 +154,23 @@ type RemoteSignerMsg interface{}
|
||||
|
||||
func RegisterRemoteSignerMsg(cdc *amino.Codec) {
|
||||
cdc.RegisterInterface((*RemoteSignerMsg)(nil), nil)
|
||||
cdc.RegisterConcrete(&PubKeyMsg{}, "tendermint/remotesigner/PubKeyMsg", nil)
|
||||
cdc.RegisterConcrete(&PubKeyRequest{}, "tendermint/remotesigner/PubKeyRequest", nil)
|
||||
cdc.RegisterConcrete(&PubKeyResponse{}, "tendermint/remotesigner/PubKeyResponse", nil)
|
||||
cdc.RegisterConcrete(&SignVoteRequest{}, "tendermint/remotesigner/SignVoteRequest", nil)
|
||||
cdc.RegisterConcrete(&SignedVoteResponse{}, "tendermint/remotesigner/SignedVoteResponse", nil)
|
||||
cdc.RegisterConcrete(&SignProposalRequest{}, "tendermint/remotesigner/SignProposalRequest", nil)
|
||||
cdc.RegisterConcrete(&SignedProposalResponse{}, "tendermint/remotesigner/SignedProposalResponse", nil)
|
||||
cdc.RegisterConcrete(&SignHeartbeatRequest{}, "tendermint/remotesigner/SignHeartbeatRequest", nil)
|
||||
cdc.RegisterConcrete(&SignedHeartbeatResponse{}, "tendermint/remotesigner/SignedHeartbeatResponse", nil)
|
||||
cdc.RegisterConcrete(&PingRequest{}, "tendermint/remotesigner/PingRequest", nil)
|
||||
cdc.RegisterConcrete(&PingResponse{}, "tendermint/remotesigner/PingResponse", nil)
|
||||
}
|
||||
|
||||
// PubKeyMsg is a PrivValidatorSocket message containing the public key.
|
||||
type PubKeyMsg struct {
|
||||
// PubKeyRequest requests the consensus public key from the remote signer.
|
||||
type PubKeyRequest struct{}
|
||||
|
||||
// PubKeyResponse is a PrivValidatorSocket message containing the public key.
|
||||
type PubKeyResponse struct {
|
||||
PubKey crypto.PubKey
|
||||
Error *RemoteSignerError
|
||||
}
|
||||
|
||||
// SignVoteRequest is a PrivValidatorSocket message containing a vote.
|
||||
@@ -218,16 +194,6 @@ type SignedProposalResponse struct {
|
||||
Error *RemoteSignerError
|
||||
}
|
||||
|
||||
// SignHeartbeatRequest is a PrivValidatorSocket message containing a Heartbeat.
|
||||
type SignHeartbeatRequest struct {
|
||||
Heartbeat *types.Heartbeat
|
||||
}
|
||||
|
||||
type SignedHeartbeatResponse struct {
|
||||
Heartbeat *types.Heartbeat
|
||||
Error *RemoteSignerError
|
||||
}
|
||||
|
||||
// PingRequest is a PrivValidatorSocket message to keep the connection alive.
|
||||
type PingRequest struct {
|
||||
}
|
||||
@@ -264,14 +230,15 @@ func writeMsg(w io.Writer, msg interface{}) (err error) {
|
||||
}
|
||||
|
||||
func handleRequest(req RemoteSignerMsg, chainID string, privVal types.PrivValidator) (RemoteSignerMsg, error) {
|
||||
fmt.Println("handle req", req)
|
||||
var res RemoteSignerMsg
|
||||
var err error
|
||||
|
||||
switch r := req.(type) {
|
||||
case *PubKeyMsg:
|
||||
case *PubKeyRequest:
|
||||
var p crypto.PubKey
|
||||
p = privVal.GetPubKey()
|
||||
res = &PubKeyMsg{p}
|
||||
res = &PubKeyResponse{p, nil}
|
||||
case *SignVoteRequest:
|
||||
err = privVal.SignVote(chainID, r.Vote)
|
||||
if err != nil {
|
||||
@@ -286,13 +253,6 @@ func handleRequest(req RemoteSignerMsg, chainID string, privVal types.PrivValida
|
||||
} else {
|
||||
res = &SignedProposalResponse{r.Proposal, nil}
|
||||
}
|
||||
case *SignHeartbeatRequest:
|
||||
err = privVal.SignHeartbeat(chainID, r.Heartbeat)
|
||||
if err != nil {
|
||||
res = &SignedHeartbeatResponse{nil, &RemoteSignerError{0, err.Error()}}
|
||||
} else {
|
||||
res = &SignedHeartbeatResponse{r.Heartbeat, nil}
|
||||
}
|
||||
case *PingRequest:
|
||||
res = &PingResponse{}
|
||||
default:
|
||||
|
||||
@@ -107,16 +107,21 @@ func (sc *TCPVal) OnStart() error {
|
||||
}
|
||||
|
||||
sc.conn = conn
|
||||
|
||||
sc.RemoteSignerClient = NewRemoteSignerClient(sc.conn)
|
||||
sc.RemoteSignerClient, err = NewRemoteSignerClient(sc.conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Start a routine to keep the connection alive
|
||||
sc.cancelPing = make(chan struct{}, 1)
|
||||
sc.pingTicker = time.NewTicker(sc.connHeartbeat)
|
||||
go func() {
|
||||
/*go func() {
|
||||
for {
|
||||
select {
|
||||
case <-sc.pingTicker.C:
|
||||
sc.Logger.Error(
|
||||
"Pinging",
|
||||
)
|
||||
err := sc.Ping()
|
||||
if err != nil {
|
||||
sc.Logger.Error(
|
||||
@@ -125,11 +130,14 @@ func (sc *TCPVal) OnStart() error {
|
||||
)
|
||||
}
|
||||
case <-sc.cancelPing:
|
||||
sc.Logger.Error(
|
||||
"Pinging Stopped",
|
||||
)
|
||||
sc.pingTicker.Stop()
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}()*/
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package privval
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
@@ -63,13 +64,7 @@ func NewRemoteSigner(
|
||||
|
||||
// OnStart implements cmn.Service.
|
||||
func (rs *RemoteSigner) OnStart() error {
|
||||
conn, err := rs.connect()
|
||||
if err != nil {
|
||||
rs.Logger.Error("OnStart", "err", err)
|
||||
return err
|
||||
}
|
||||
|
||||
go rs.handleConnection(conn)
|
||||
go rs.handleConnection()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -86,75 +81,86 @@ func (rs *RemoteSigner) OnStop() {
|
||||
}
|
||||
|
||||
func (rs *RemoteSigner) connect() (net.Conn, error) {
|
||||
for retries := rs.connRetries; retries > 0; retries-- {
|
||||
// Don't sleep if it is the first retry.
|
||||
if retries != rs.connRetries {
|
||||
time.Sleep(rs.connDeadline)
|
||||
}
|
||||
//for retries := rs.connRetries; retries > 0; retries-- {
|
||||
// Don't sleep if it is the first retry.
|
||||
//if retries != rs.connRetries {
|
||||
// time.Sleep(rs.connDeadline)
|
||||
//}
|
||||
|
||||
conn, err := cmn.Connect(rs.addr)
|
||||
if err != nil {
|
||||
rs.Logger.Error(
|
||||
"connect",
|
||||
"addr", rs.addr,
|
||||
"err", err,
|
||||
)
|
||||
conn, err := cmn.Connect(rs.addr)
|
||||
if err != nil {
|
||||
rs.Logger.Error(
|
||||
"connect",
|
||||
"addr", rs.addr,
|
||||
"err", err,
|
||||
)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if err := conn.SetDeadline(time.Now().Add(connTimeout)); err != nil {
|
||||
rs.Logger.Error(
|
||||
"connect",
|
||||
"err", err,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
conn, err = p2pconn.MakeSecretConnection(conn, rs.privKey)
|
||||
if err != nil {
|
||||
rs.Logger.Error(
|
||||
"connect",
|
||||
"err", err,
|
||||
)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//if err := conn.SetDeadline(time.Now().Add(connTimeout)); err != nil {
|
||||
// rs.Logger.Error(
|
||||
// "connect",
|
||||
// "err", err,
|
||||
// )
|
||||
// //continue
|
||||
// return nil, err
|
||||
//}
|
||||
|
||||
conn, err = p2pconn.MakeSecretConnection(conn, rs.privKey)
|
||||
if err != nil {
|
||||
rs.Logger.Error(
|
||||
"connect",
|
||||
"err", err,
|
||||
)
|
||||
|
||||
return nil, err
|
||||
}
|
||||
fmt.Println("connected", conn.RemoteAddr())
|
||||
return conn, nil
|
||||
//}
|
||||
|
||||
return nil, ErrDialRetryMax
|
||||
}
|
||||
|
||||
func (rs *RemoteSigner) handleConnection(conn net.Conn) {
|
||||
for {
|
||||
func (rs *RemoteSigner) handleConnection() {
|
||||
for { // establish connection loop:
|
||||
if !rs.IsRunning() {
|
||||
return // Ignore error from listener closing.
|
||||
}
|
||||
|
||||
fmt.Println("Connecting again ...")
|
||||
conn, err := rs.connect()
|
||||
if err != nil {
|
||||
rs.Logger.Error("OnStart", "err", err)
|
||||
fmt.Println("failed to connect", err)
|
||||
time.Sleep(rs.connDeadline)
|
||||
continue
|
||||
}
|
||||
// Reset the connection deadline
|
||||
conn.SetDeadline(time.Now().Add(rs.connDeadline))
|
||||
//conn.SetDeadline(time.Now().Add(rs.connDeadline*10))
|
||||
|
||||
req, err := readMsg(conn)
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
rs.Logger.Error("handleConnection", "err", err)
|
||||
for { // handle request loop
|
||||
req, err := readMsg(conn)
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
rs.Logger.Error("handleConnection readMsg", "err", err)
|
||||
}
|
||||
conn.Close()
|
||||
break
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
res, err := handleRequest(req, rs.chainID, rs.privVal)
|
||||
res, err := handleRequest(req, rs.chainID, rs.privVal)
|
||||
|
||||
if err != nil {
|
||||
// only log the error; we'll reply with an error in res
|
||||
rs.Logger.Error("handleConnection", "err", err)
|
||||
}
|
||||
if err != nil {
|
||||
// only log the error; we'll reply with an error in res
|
||||
rs.Logger.Error("handleConnection handleRequest", "err", err)
|
||||
}
|
||||
|
||||
err = writeMsg(conn, res)
|
||||
if err != nil {
|
||||
rs.Logger.Error("handleConnection", "err", err)
|
||||
return
|
||||
err = writeMsg(conn, res)
|
||||
if err != nil {
|
||||
rs.Logger.Error("handleConnection writeMsg", "err", err)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,15 +25,10 @@ func TestSocketPVAddress(t *testing.T) {
|
||||
defer sc.Stop()
|
||||
defer rs.Stop()
|
||||
|
||||
serverAddr := rs.privVal.GetAddress()
|
||||
|
||||
clientAddr := sc.GetAddress()
|
||||
serverAddr := rs.privVal.GetPubKey().Address()
|
||||
clientAddr := sc.GetPubKey().Address()
|
||||
|
||||
assert.Equal(t, serverAddr, clientAddr)
|
||||
|
||||
// TODO(xla): Remove when PrivValidator2 replaced PrivValidator.
|
||||
assert.Equal(t, serverAddr, sc.GetAddress())
|
||||
|
||||
}
|
||||
|
||||
func TestSocketPVPubKey(t *testing.T) {
|
||||
@@ -47,12 +42,10 @@ func TestSocketPVPubKey(t *testing.T) {
|
||||
clientKey, err := sc.getPubKey()
|
||||
require.NoError(t, err)
|
||||
|
||||
privKey := rs.privVal.GetPubKey()
|
||||
privvalPubKey := rs.privVal.GetPubKey()
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, privKey, clientKey)
|
||||
|
||||
// TODO(xla): Remove when PrivValidator2 replaced PrivValidator.
|
||||
assert.Equal(t, privKey, sc.GetPubKey())
|
||||
assert.Equal(t, privvalPubKey, clientKey)
|
||||
}
|
||||
|
||||
func TestSocketPVProposal(t *testing.T) {
|
||||
@@ -137,22 +130,6 @@ func TestSocketPVVoteKeepalive(t *testing.T) {
|
||||
assert.Equal(t, want.Signature, have.Signature)
|
||||
}
|
||||
|
||||
func TestSocketPVHeartbeat(t *testing.T) {
|
||||
var (
|
||||
chainID = cmn.RandStr(12)
|
||||
sc, rs = testSetupSocketPair(t, chainID, types.NewMockPV())
|
||||
|
||||
want = &types.Heartbeat{}
|
||||
have = &types.Heartbeat{}
|
||||
)
|
||||
defer sc.Stop()
|
||||
defer rs.Stop()
|
||||
|
||||
require.NoError(t, rs.privVal.SignHeartbeat(chainID, want))
|
||||
require.NoError(t, sc.SignHeartbeat(chainID, have))
|
||||
assert.Equal(t, want.Signature, have.Signature)
|
||||
}
|
||||
|
||||
func TestSocketPVDeadline(t *testing.T) {
|
||||
var (
|
||||
addr = testFreeAddr(t)
|
||||
@@ -169,9 +146,9 @@ func TestSocketPVDeadline(t *testing.T) {
|
||||
go func(sc *TCPVal) {
|
||||
defer close(listenc)
|
||||
|
||||
require.NoError(t, sc.Start())
|
||||
assert.Equal(t, sc.Start().(cmn.Error).Data(), ErrConnTimeout)
|
||||
|
||||
assert.True(t, sc.IsRunning())
|
||||
assert.False(t, sc.IsRunning())
|
||||
}(sc)
|
||||
|
||||
for {
|
||||
@@ -190,9 +167,6 @@ func TestSocketPVDeadline(t *testing.T) {
|
||||
}
|
||||
|
||||
<-listenc
|
||||
|
||||
_, err := sc.getPubKey()
|
||||
assert.Equal(t, err.(cmn.Error).Data(), ErrConnTimeout)
|
||||
}
|
||||
|
||||
func TestRemoteSignerRetry(t *testing.T) {
|
||||
@@ -301,32 +275,6 @@ func TestRemoteSignProposalErrors(t *testing.T) {
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestRemoteSignHeartbeatErrors(t *testing.T) {
|
||||
var (
|
||||
chainID = cmn.RandStr(12)
|
||||
sc, rs = testSetupSocketPair(t, chainID, types.NewErroringMockPV())
|
||||
hb = &types.Heartbeat{}
|
||||
)
|
||||
defer sc.Stop()
|
||||
defer rs.Stop()
|
||||
|
||||
err := writeMsg(sc.conn, &SignHeartbeatRequest{Heartbeat: hb})
|
||||
require.NoError(t, err)
|
||||
|
||||
res, err := readMsg(sc.conn)
|
||||
require.NoError(t, err)
|
||||
|
||||
resp := *res.(*SignedHeartbeatResponse)
|
||||
require.NotNil(t, resp.Error)
|
||||
require.Equal(t, resp.Error.Description, types.ErroringMockPVErr.Error())
|
||||
|
||||
err = rs.privVal.SignHeartbeat(chainID, hb)
|
||||
require.Error(t, err)
|
||||
|
||||
err = sc.SignHeartbeat(chainID, hb)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestErrUnexpectedResponse(t *testing.T) {
|
||||
var (
|
||||
addr = testFreeAddr(t)
|
||||
@@ -352,32 +300,23 @@ func TestErrUnexpectedResponse(t *testing.T) {
|
||||
testStartSocketPV(t, readyc, sc)
|
||||
defer sc.Stop()
|
||||
RemoteSignerConnDeadline(time.Millisecond)(rs)
|
||||
RemoteSignerConnRetries(1e6)(rs)
|
||||
|
||||
RemoteSignerConnRetries(100)(rs)
|
||||
// we do not want to Start() the remote signer here and instead use the connection to
|
||||
// reply with intentionally wrong replies below:
|
||||
rsConn, err := rs.connect()
|
||||
defer rsConn.Close()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, rsConn)
|
||||
// send over public key to get the remote signer running:
|
||||
go testReadWriteResponse(t, &PubKeyResponse{}, rsConn)
|
||||
<-readyc
|
||||
|
||||
// Heartbeat:
|
||||
go func(errc chan error) {
|
||||
errc <- sc.SignHeartbeat(chainID, &types.Heartbeat{})
|
||||
}(errc)
|
||||
// read request and write wrong response:
|
||||
go testReadWriteResponse(t, &SignedVoteResponse{}, rsConn)
|
||||
err = <-errc
|
||||
require.Error(t, err)
|
||||
require.Equal(t, err, ErrUnexpectedResponse)
|
||||
|
||||
// Proposal:
|
||||
go func(errc chan error) {
|
||||
errc <- sc.SignProposal(chainID, &types.Proposal{})
|
||||
}(errc)
|
||||
// read request and write wrong response:
|
||||
go testReadWriteResponse(t, &SignedHeartbeatResponse{}, rsConn)
|
||||
go testReadWriteResponse(t, &SignedVoteResponse{}, rsConn)
|
||||
err = <-errc
|
||||
require.Error(t, err)
|
||||
require.Equal(t, err, ErrUnexpectedResponse)
|
||||
@@ -387,7 +326,7 @@ func TestErrUnexpectedResponse(t *testing.T) {
|
||||
errc <- sc.SignVote(chainID, &types.Vote{})
|
||||
}(errc)
|
||||
// read request and write wrong response:
|
||||
go testReadWriteResponse(t, &SignedHeartbeatResponse{}, rsConn)
|
||||
go testReadWriteResponse(t, &SignedProposalResponse{}, rsConn)
|
||||
err = <-errc
|
||||
require.Error(t, err)
|
||||
require.Equal(t, err, ErrUnexpectedResponse)
|
||||
|
||||
@@ -109,6 +109,24 @@ func (c *HTTP) broadcastTX(route string, tx types.Tx) (*ctypes.ResultBroadcastTx
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c *HTTP) UnconfirmedTxs(limit int) (*ctypes.ResultUnconfirmedTxs, error) {
|
||||
result := new(ctypes.ResultUnconfirmedTxs)
|
||||
_, err := c.rpc.Call("unconfirmed_txs", map[string]interface{}{"limit": limit}, result)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unconfirmed_txs")
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c *HTTP) NumUnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error) {
|
||||
result := new(ctypes.ResultUnconfirmedTxs)
|
||||
_, err := c.rpc.Call("num_unconfirmed_txs", map[string]interface{}{}, result)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "num_unconfirmed_txs")
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c *HTTP) NetInfo() (*ctypes.ResultNetInfo, error) {
|
||||
result := new(ctypes.ResultNetInfo)
|
||||
_, err := c.rpc.Call("net_info", map[string]interface{}{}, result)
|
||||
|
||||
@@ -93,3 +93,9 @@ type NetworkClient interface {
|
||||
type EventsClient interface {
|
||||
types.EventBusSubscriber
|
||||
}
|
||||
|
||||
// MempoolClient shows us data about current mempool state.
|
||||
type MempoolClient interface {
|
||||
UnconfirmedTxs(limit int) (*ctypes.ResultUnconfirmedTxs, error)
|
||||
NumUnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error)
|
||||
}
|
||||
|
||||
@@ -76,6 +76,14 @@ func (Local) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
|
||||
return core.BroadcastTxSync(tx)
|
||||
}
|
||||
|
||||
func (Local) UnconfirmedTxs(limit int) (*ctypes.ResultUnconfirmedTxs, error) {
|
||||
return core.UnconfirmedTxs(limit)
|
||||
}
|
||||
|
||||
func (Local) NumUnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error) {
|
||||
return core.NumUnconfirmedTxs()
|
||||
}
|
||||
|
||||
func (Local) NetInfo() (*ctypes.ResultNetInfo, error) {
|
||||
return core.NetInfo()
|
||||
}
|
||||
|
||||
@@ -281,6 +281,42 @@ func TestBroadcastTxCommit(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnconfirmedTxs(t *testing.T) {
|
||||
_, _, tx := MakeTxKV()
|
||||
|
||||
mempool := node.MempoolReactor().Mempool
|
||||
_ = mempool.CheckTx(tx, nil)
|
||||
|
||||
for i, c := range GetClients() {
|
||||
mc, ok := c.(client.MempoolClient)
|
||||
require.True(t, ok, "%d", i)
|
||||
txs, err := mc.UnconfirmedTxs(1)
|
||||
require.Nil(t, err, "%d: %+v", i, err)
|
||||
assert.Exactly(t, types.Txs{tx}, types.Txs(txs.Txs))
|
||||
}
|
||||
|
||||
mempool.Flush()
|
||||
}
|
||||
|
||||
func TestNumUnconfirmedTxs(t *testing.T) {
|
||||
_, _, tx := MakeTxKV()
|
||||
|
||||
mempool := node.MempoolReactor().Mempool
|
||||
_ = mempool.CheckTx(tx, nil)
|
||||
mempoolSize := mempool.Size()
|
||||
|
||||
for i, c := range GetClients() {
|
||||
mc, ok := c.(client.MempoolClient)
|
||||
require.True(t, ok, "%d", i)
|
||||
res, err := mc.NumUnconfirmedTxs()
|
||||
require.Nil(t, err, "%d: %+v", i, err)
|
||||
|
||||
assert.Equal(t, mempoolSize, res.N)
|
||||
}
|
||||
|
||||
mempool.Flush()
|
||||
}
|
||||
|
||||
func TestTx(t *testing.T) {
|
||||
// first we broadcast a tx
|
||||
c := getHTTPClient()
|
||||
|
||||
@@ -15,6 +15,11 @@ import (
|
||||
//
|
||||
// ```go
|
||||
// client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
|
||||
// err := client.Start()
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// defer client.Stop()
|
||||
// result, err := client.ABCIQuery("", "abcd", true)
|
||||
// ```
|
||||
//
|
||||
@@ -69,6 +74,11 @@ func ABCIQuery(path string, data cmn.HexBytes, height int64, prove bool) (*ctype
|
||||
//
|
||||
// ```go
|
||||
// client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
|
||||
// err := client.Start()
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// defer client.Stop()
|
||||
// info, err := client.ABCIInfo()
|
||||
// ```
|
||||
//
|
||||
|
||||
@@ -18,6 +18,11 @@ import (
|
||||
//
|
||||
// ```go
|
||||
// client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
|
||||
// err := client.Start()
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// defer client.Stop()
|
||||
// info, err := client.BlockchainInfo(10, 10)
|
||||
// ```
|
||||
//
|
||||
@@ -123,6 +128,11 @@ func filterMinMax(height, min, max, limit int64) (int64, int64, error) {
|
||||
//
|
||||
// ```go
|
||||
// client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
|
||||
// err := client.Start()
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// defer client.Stop()
|
||||
// info, err := client.Block(10)
|
||||
// ```
|
||||
//
|
||||
@@ -235,6 +245,11 @@ func Block(heightPtr *int64) (*ctypes.ResultBlock, error) {
|
||||
//
|
||||
// ```go
|
||||
// client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
|
||||
// err := client.Start()
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// defer client.Stop()
|
||||
// info, err := client.Commit(11)
|
||||
// ```
|
||||
//
|
||||
@@ -329,6 +344,11 @@ func Commit(heightPtr *int64) (*ctypes.ResultCommit, error) {
|
||||
//
|
||||
// ```go
|
||||
// client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
|
||||
// err := client.Start()
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// defer client.Stop()
|
||||
// info, err := client.BlockResults(10)
|
||||
// ```
|
||||
//
|
||||
|
||||
@@ -16,6 +16,11 @@ import (
|
||||
//
|
||||
// ```go
|
||||
// client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
|
||||
// err := client.Start()
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// defer client.Stop()
|
||||
// state, err := client.Validators()
|
||||
// ```
|
||||
//
|
||||
@@ -27,7 +32,7 @@ import (
|
||||
// "result": {
|
||||
// "validators": [
|
||||
// {
|
||||
// "accum": "0",
|
||||
// "proposer_priority": "0",
|
||||
// "voting_power": "10",
|
||||
// "pub_key": {
|
||||
// "data": "68DFDA7E50F82946E7E8546BED37944A422CD1B831E70DF66BA3B8430593944D",
|
||||
@@ -67,6 +72,11 @@ func Validators(heightPtr *int64) (*ctypes.ResultValidators, error) {
|
||||
//
|
||||
// ```go
|
||||
// client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
|
||||
// err := client.Start()
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// defer client.Stop()
|
||||
// state, err := client.DumpConsensusState()
|
||||
// ```
|
||||
//
|
||||
@@ -92,7 +102,7 @@ func Validators(heightPtr *int64) (*ctypes.ResultValidators, error) {
|
||||
// "value": "SBctdhRBcXtBgdI/8a/alTsUhGXqGs9k5ylV1u5iKHg="
|
||||
// },
|
||||
// "voting_power": "10",
|
||||
// "accum": "0"
|
||||
// "proposer_priority": "0"
|
||||
// }
|
||||
// ],
|
||||
// "proposer": {
|
||||
@@ -102,7 +112,7 @@ func Validators(heightPtr *int64) (*ctypes.ResultValidators, error) {
|
||||
// "value": "SBctdhRBcXtBgdI/8a/alTsUhGXqGs9k5ylV1u5iKHg="
|
||||
// },
|
||||
// "voting_power": "10",
|
||||
// "accum": "0"
|
||||
// "proposer_priority": "0"
|
||||
// }
|
||||
// },
|
||||
// "proposal": null,
|
||||
@@ -138,7 +148,7 @@ func Validators(heightPtr *int64) (*ctypes.ResultValidators, error) {
|
||||
// "value": "SBctdhRBcXtBgdI/8a/alTsUhGXqGs9k5ylV1u5iKHg="
|
||||
// },
|
||||
// "voting_power": "10",
|
||||
// "accum": "0"
|
||||
// "proposer_priority": "0"
|
||||
// }
|
||||
// ],
|
||||
// "proposer": {
|
||||
@@ -148,7 +158,7 @@ func Validators(heightPtr *int64) (*ctypes.ResultValidators, error) {
|
||||
// "value": "SBctdhRBcXtBgdI/8a/alTsUhGXqGs9k5ylV1u5iKHg="
|
||||
// },
|
||||
// "voting_power": "10",
|
||||
// "accum": "0"
|
||||
// "proposer_priority": "0"
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
@@ -225,6 +235,11 @@ func DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) {
|
||||
//
|
||||
// ```go
|
||||
// client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
|
||||
// err := client.Start()
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// defer client.Stop()
|
||||
// state, err := client.ConsensusState()
|
||||
// ```
|
||||
//
|
||||
@@ -273,6 +288,11 @@ func ConsensusState() (*ctypes.ResultConsensusState, error) {
|
||||
//
|
||||
// ```go
|
||||
// client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
|
||||
// err := client.Start()
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// defer client.Stop()
|
||||
// state, err := client.ConsensusParams()
|
||||
// ```
|
||||
//
|
||||
|
||||
@@ -54,11 +54,16 @@ import (
|
||||
// import "github.com/tendermint/tendermint/types"
|
||||
//
|
||||
// client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
|
||||
// err := client.Start()
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// defer client.Stop()
|
||||
// ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
// defer cancel()
|
||||
// query := query.MustParse("tm.event = 'Tx' AND tx.height = 3")
|
||||
// txs := make(chan interface{})
|
||||
// err := client.Subscribe(ctx, "test-client", query, txs)
|
||||
// err = client.Subscribe(ctx, "test-client", query, txs)
|
||||
//
|
||||
// go func() {
|
||||
// for e := range txs {
|
||||
@@ -116,7 +121,12 @@ func Subscribe(wsCtx rpctypes.WSRPCContext, query string) (*ctypes.ResultSubscri
|
||||
//
|
||||
// ```go
|
||||
// client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
|
||||
// err := client.Unsubscribe("test-client", query)
|
||||
// err := client.Start()
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// defer client.Stop()
|
||||
// err = client.Unsubscribe("test-client", query)
|
||||
// ```
|
||||
//
|
||||
// > The above command returns JSON structured like this:
|
||||
@@ -155,7 +165,12 @@ func Unsubscribe(wsCtx rpctypes.WSRPCContext, query string) (*ctypes.ResultUnsub
|
||||
//
|
||||
// ```go
|
||||
// client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
|
||||
// err := client.UnsubscribeAll("test-client")
|
||||
// err := client.Start()
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// defer client.Stop()
|
||||
// err = client.UnsubscribeAll("test-client")
|
||||
// ```
|
||||
//
|
||||
// > The above command returns JSON structured like this:
|
||||
|
||||
@@ -13,6 +13,11 @@ import (
|
||||
//
|
||||
// ```go
|
||||
// client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
|
||||
// err := client.Start()
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// defer client.Stop()
|
||||
// result, err := client.Health()
|
||||
// ```
|
||||
//
|
||||
|
||||
@@ -24,6 +24,11 @@ import (
|
||||
//
|
||||
// ```go
|
||||
// client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
|
||||
// err := client.Start()
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// defer client.Stop()
|
||||
// result, err := client.BroadcastTxAsync("123")
|
||||
// ```
|
||||
//
|
||||
@@ -64,6 +69,11 @@ func BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
|
||||
//
|
||||
// ```go
|
||||
// client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
|
||||
// err := client.Start()
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// defer client.Stop()
|
||||
// result, err := client.BroadcastTxSync("456")
|
||||
// ```
|
||||
//
|
||||
@@ -118,6 +128,11 @@ func BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
|
||||
//
|
||||
// ```go
|
||||
// client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
|
||||
// err := client.Start()
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// defer client.Stop()
|
||||
// result, err := client.BroadcastTxCommit("789")
|
||||
// ```
|
||||
//
|
||||
@@ -198,7 +213,10 @@ func BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
|
||||
// TODO: configurable?
|
||||
var deliverTxTimeout = rpcserver.WriteTimeout / 2
|
||||
select {
|
||||
case deliverTxResMsg := <-deliverTxResCh: // The tx was included in a block.
|
||||
case deliverTxResMsg, ok := <-deliverTxResCh: // The tx was included in a block.
|
||||
if !ok {
|
||||
return nil, errors.New("Error on broadcastTxCommit: expected DeliverTxResult, got nil. Did the Tendermint stop?")
|
||||
}
|
||||
deliverTxRes := deliverTxResMsg.(types.EventDataTx)
|
||||
return &ctypes.ResultBroadcastTxCommit{
|
||||
CheckTx: *checkTxRes,
|
||||
@@ -225,6 +243,11 @@ func BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
|
||||
//
|
||||
// ```go
|
||||
// client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
|
||||
// err := client.Start()
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// defer client.Stop()
|
||||
// result, err := client.UnconfirmedTxs()
|
||||
// ```
|
||||
//
|
||||
@@ -263,6 +286,11 @@ func UnconfirmedTxs(limit int) (*ctypes.ResultUnconfirmedTxs, error) {
|
||||
//
|
||||
// ```go
|
||||
// client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
|
||||
// err := client.Start()
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// defer client.Stop()
|
||||
// result, err := client.UnconfirmedTxs()
|
||||
// ```
|
||||
//
|
||||
|
||||
@@ -17,6 +17,11 @@ import (
|
||||
//
|
||||
// ```go
|
||||
// client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
|
||||
// err := client.Start()
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// defer client.Stop()
|
||||
// info, err := client.NetInfo()
|
||||
// ```
|
||||
//
|
||||
@@ -95,6 +100,11 @@ func UnsafeDialPeers(peers []string, persistent bool) (*ctypes.ResultDialPeers,
|
||||
//
|
||||
// ```go
|
||||
// client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
|
||||
// err := client.Start()
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// defer client.Stop()
|
||||
// genesis, err := client.Genesis()
|
||||
// ```
|
||||
//
|
||||
|
||||
@@ -2,7 +2,7 @@ package core
|
||||
|
||||
import (
|
||||
"github.com/tendermint/tendermint/consensus"
|
||||
crypto "github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
mempl "github.com/tendermint/tendermint/mempool"
|
||||
|
||||
@@ -20,6 +20,11 @@ import (
|
||||
//
|
||||
// ```go
|
||||
// client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
|
||||
// err := client.Start()
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// defer client.Stop()
|
||||
// result, err := client.Status()
|
||||
// ```
|
||||
//
|
||||
|
||||
@@ -21,6 +21,11 @@ import (
|
||||
//
|
||||
// ```go
|
||||
// client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
|
||||
// err := client.Start()
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// defer client.Stop()
|
||||
// tx, err := client.Tx([]byte("2B8EC32BA2579B3B8606E42C06DE2F7AFA2556EF"), true)
|
||||
// ```
|
||||
//
|
||||
@@ -115,6 +120,11 @@ func Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) {
|
||||
//
|
||||
// ```go
|
||||
// client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
|
||||
// err := client.Start()
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// defer client.Stop()
|
||||
// q, err := tmquery.New("account.owner='Ivan'")
|
||||
// tx, err := client.TxSearch(q, true)
|
||||
// ```
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
crypto "github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
|
||||
@@ -107,8 +107,22 @@ func (blockExec *BlockExecutor) ApplyBlock(state State, blockID types.BlockID, b
|
||||
|
||||
fail.Fail() // XXX
|
||||
|
||||
// validate the validator updates and convert to tendermint types
|
||||
abciValUpdates := abciResponses.EndBlock.ValidatorUpdates
|
||||
err = validateValidatorUpdates(abciValUpdates, state.ConsensusParams.Validator)
|
||||
if err != nil {
|
||||
return state, fmt.Errorf("Error in validator updates: %v", err)
|
||||
}
|
||||
validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciValUpdates)
|
||||
if err != nil {
|
||||
return state, err
|
||||
}
|
||||
if len(validatorUpdates) > 0 {
|
||||
blockExec.logger.Info("Updates to validators", "updates", makeValidatorUpdatesLogString(validatorUpdates))
|
||||
}
|
||||
|
||||
// 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, validatorUpdates)
|
||||
if err != nil {
|
||||
return state, fmt.Errorf("Commit failed for application: %v", err)
|
||||
}
|
||||
@@ -132,7 +146,7 @@ func (blockExec *BlockExecutor) ApplyBlock(state State, blockID types.BlockID, b
|
||||
|
||||
// Events are fired after everything else.
|
||||
// NOTE: if we crash between Commit and Save, events wont be fired during replay
|
||||
fireEvents(blockExec.logger, blockExec.eventBus, block, abciResponses)
|
||||
fireEvents(blockExec.logger, blockExec.eventBus, block, abciResponses, validatorUpdates)
|
||||
|
||||
return state, nil
|
||||
}
|
||||
@@ -308,53 +322,98 @@ func getBeginBlockValidatorInfo(block *types.Block, lastValSet *types.ValidatorS
|
||||
|
||||
}
|
||||
|
||||
func validateValidatorUpdates(abciUpdates []abci.ValidatorUpdate,
|
||||
params types.ValidatorParams) error {
|
||||
for _, valUpdate := range abciUpdates {
|
||||
if valUpdate.GetPower() < 0 {
|
||||
return fmt.Errorf("Voting power can't be negative %v", valUpdate)
|
||||
} else if valUpdate.GetPower() == 0 {
|
||||
// continue, since this is deleting the validator, and thus there is no
|
||||
// pubkey to check
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if validator's pubkey matches an ABCI type in the consensus params
|
||||
thisKeyType := valUpdate.PubKey.Type
|
||||
if !params.IsValidPubkeyType(thisKeyType) {
|
||||
return fmt.Errorf("Validator %v is using pubkey %s, which is unsupported for consensus",
|
||||
valUpdate, thisKeyType)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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) {
|
||||
updates, err := types.PB2TM.ValidatorUpdates(abciUpdates)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// these are tendermint types now
|
||||
func updateValidators(currentSet *types.ValidatorSet, updates []*types.Validator) error {
|
||||
for _, valUpdate := range updates {
|
||||
// should already have been checked
|
||||
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
|
||||
_, val := currentSet.GetByAddress(address)
|
||||
if valUpdate.VotingPower == 0 {
|
||||
// remove val
|
||||
// valUpdate.VotingPower is ensured to be non-negative in validation method
|
||||
if valUpdate.VotingPower == 0 { // 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
|
||||
} else if val == nil { // add val
|
||||
// make sure we do not exceed MaxTotalVotingPower by adding this validator:
|
||||
totalVotingPower := currentSet.TotalVotingPower()
|
||||
updatedVotingPower := valUpdate.VotingPower + totalVotingPower
|
||||
overflow := updatedVotingPower > types.MaxTotalVotingPower || updatedVotingPower < 0
|
||||
if overflow {
|
||||
return fmt.Errorf(
|
||||
"Failed to add new validator %v. Adding it would exceed max allowed total voting power %v",
|
||||
valUpdate,
|
||||
types.MaxTotalVotingPower)
|
||||
}
|
||||
// TODO: issue #1558 update spec according to the following:
|
||||
// Set ProposerPriority to -C*totalVotingPower (with C ~= 1.125) to make sure validators can't
|
||||
// unbond/rebond to reset their (potentially previously negative) ProposerPriority to zero.
|
||||
//
|
||||
// Contract: totalVotingPower < MaxTotalVotingPower to ensure ProposerPriority does
|
||||
// not exceed the bounds of int64.
|
||||
//
|
||||
// Compute ProposerPriority = -1.125*totalVotingPower == -(totalVotingPower + (totalVotingPower >> 3)).
|
||||
valUpdate.ProposerPriority = -(totalVotingPower + (totalVotingPower >> 3))
|
||||
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
|
||||
} else { // update val
|
||||
// make sure we do not exceed MaxTotalVotingPower by updating this validator:
|
||||
totalVotingPower := currentSet.TotalVotingPower()
|
||||
curVotingPower := val.VotingPower
|
||||
updatedVotingPower := totalVotingPower - curVotingPower + valUpdate.VotingPower
|
||||
overflow := updatedVotingPower > types.MaxTotalVotingPower || updatedVotingPower < 0
|
||||
if overflow {
|
||||
return fmt.Errorf(
|
||||
"Failed to update existing validator %v. Updating it would exceed max allowed total voting power %v",
|
||||
valUpdate,
|
||||
types.MaxTotalVotingPower)
|
||||
}
|
||||
|
||||
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,
|
||||
abciResponses *ABCIResponses,
|
||||
validatorUpdates []*types.Validator,
|
||||
) (State, error) {
|
||||
|
||||
// Copy the valset so we can apply changes from EndBlock
|
||||
@@ -363,19 +422,17 @@ 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)
|
||||
if len(validatorUpdates) > 0 {
|
||||
err := updateValidators(nValSet, 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.
|
||||
nValSet.IncrementAccum(1)
|
||||
// Update validator proposer priority and set state variables.
|
||||
nValSet.IncrementProposerPriority(1)
|
||||
|
||||
// Update the params with the latest abciResponses.
|
||||
nextParams := state.ConsensusParams
|
||||
@@ -417,7 +474,7 @@ func updateState(
|
||||
// Fire NewBlock, NewBlockHeader.
|
||||
// Fire TxEvent for every tx.
|
||||
// NOTE: if Tendermint crashes before commit, some or all of these events may be published again.
|
||||
func fireEvents(logger log.Logger, eventBus types.BlockEventPublisher, block *types.Block, abciResponses *ABCIResponses) {
|
||||
func fireEvents(logger log.Logger, eventBus types.BlockEventPublisher, block *types.Block, abciResponses *ABCIResponses, validatorUpdates []*types.Validator) {
|
||||
eventBus.PublishEventNewBlock(types.EventDataNewBlock{
|
||||
Block: block,
|
||||
ResultBeginBlock: *abciResponses.BeginBlock,
|
||||
@@ -438,12 +495,9 @@ func fireEvents(logger log.Logger, eventBus types.BlockEventPublisher, block *ty
|
||||
}})
|
||||
}
|
||||
|
||||
abciValUpdates := abciResponses.EndBlock.ValidatorUpdates
|
||||
if len(abciValUpdates) > 0 {
|
||||
// if there were an error, we would've stopped in updateValidators
|
||||
updates, _ := types.PB2TM.ValidatorUpdates(abciValUpdates)
|
||||
if len(validatorUpdates) > 0 {
|
||||
eventBus.PublishEventValidatorSetUpdates(
|
||||
types.EventDataValidatorSetUpdates{ValidatorUpdates: updates})
|
||||
types.EventDataValidatorSetUpdates{ValidatorUpdates: validatorUpdates})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/tendermint/tendermint/abci/example/kvstore"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
@@ -152,6 +153,76 @@ func TestBeginBlockByzantineValidators(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateValidatorUpdates(t *testing.T) {
|
||||
pubkey1 := ed25519.GenPrivKey().PubKey()
|
||||
pubkey2 := ed25519.GenPrivKey().PubKey()
|
||||
|
||||
secpKey := secp256k1.GenPrivKey().PubKey()
|
||||
|
||||
defaultValidatorParams := types.ValidatorParams{[]string{types.ABCIPubKeyTypeEd25519}}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
|
||||
abciUpdates []abci.ValidatorUpdate
|
||||
validatorParams types.ValidatorParams
|
||||
|
||||
shouldErr bool
|
||||
}{
|
||||
{
|
||||
"adding a validator is OK",
|
||||
|
||||
[]abci.ValidatorUpdate{{PubKey: types.TM2PB.PubKey(pubkey2), Power: 20}},
|
||||
defaultValidatorParams,
|
||||
|
||||
false,
|
||||
},
|
||||
{
|
||||
"updating a validator is OK",
|
||||
|
||||
[]abci.ValidatorUpdate{{PubKey: types.TM2PB.PubKey(pubkey1), Power: 20}},
|
||||
defaultValidatorParams,
|
||||
|
||||
false,
|
||||
},
|
||||
{
|
||||
"removing a validator is OK",
|
||||
|
||||
[]abci.ValidatorUpdate{{PubKey: types.TM2PB.PubKey(pubkey2), Power: 0}},
|
||||
defaultValidatorParams,
|
||||
|
||||
false,
|
||||
},
|
||||
{
|
||||
"adding a validator with negative power results in error",
|
||||
|
||||
[]abci.ValidatorUpdate{{PubKey: types.TM2PB.PubKey(pubkey2), Power: -100}},
|
||||
defaultValidatorParams,
|
||||
|
||||
true,
|
||||
},
|
||||
{
|
||||
"adding a validator with pubkey thats not in validator params results in error",
|
||||
|
||||
[]abci.ValidatorUpdate{{PubKey: types.TM2PB.PubKey(secpKey), Power: -100}},
|
||||
defaultValidatorParams,
|
||||
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := validateValidatorUpdates(tc.abciUpdates, tc.validatorParams)
|
||||
if tc.shouldErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateValidators(t *testing.T) {
|
||||
pubkey1 := ed25519.GenPrivKey().PubKey()
|
||||
val1 := types.NewValidator(pubkey1, 10)
|
||||
@@ -194,7 +265,6 @@ func TestUpdateValidators(t *testing.T) {
|
||||
types.NewValidatorSet([]*types.Validator{val1}),
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
"removing a non-existing validator results in error",
|
||||
|
||||
@@ -204,24 +274,17 @@ func TestUpdateValidators(t *testing.T) {
|
||||
types.NewValidatorSet([]*types.Validator{val1}),
|
||||
true,
|
||||
},
|
||||
|
||||
{
|
||||
"adding a validator with negative power results in error",
|
||||
|
||||
types.NewValidatorSet([]*types.Validator{val1}),
|
||||
[]abci.ValidatorUpdate{{PubKey: types.TM2PB.PubKey(pubkey2), Power: -100}},
|
||||
|
||||
types.NewValidatorSet([]*types.Validator{val1}),
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
_, err := updateValidators(tc.currentSet, tc.abciUpdates)
|
||||
updates, err := types.PB2TM.ValidatorUpdates(tc.abciUpdates)
|
||||
assert.NoError(t, err)
|
||||
err = updateValidators(tc.currentSet, updates)
|
||||
if tc.shouldErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
require.Equal(t, tc.resultingSet.Size(), tc.currentSet.Size())
|
||||
|
||||
assert.Equal(t, tc.resultingSet.TotalVotingPower(), tc.currentSet.TotalVotingPower())
|
||||
|
||||
@@ -226,7 +226,7 @@ func MakeGenesisState(genDoc *types.GenesisDoc) (State, error) {
|
||||
validators[i] = types.NewValidator(val.PubKey, val.Power)
|
||||
}
|
||||
validatorSet = types.NewValidatorSet(validators)
|
||||
nextValidatorSet = types.NewValidatorSet(validators).CopyIncrementAccum(1)
|
||||
nextValidatorSet = types.NewValidatorSet(validators).CopyIncrementProposerPriority(1)
|
||||
}
|
||||
|
||||
return State{
|
||||
|
||||
@@ -5,12 +5,11 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
crypto "github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
@@ -134,8 +133,8 @@ func TestABCIResponsesSaveLoad2(t *testing.T) {
|
||||
{Code: 383},
|
||||
{Data: []byte("Gotcha!"),
|
||||
Tags: []cmn.KVPair{
|
||||
cmn.KVPair{Key: []byte("a"), Value: []byte("1")},
|
||||
cmn.KVPair{Key: []byte("build"), Value: []byte("stuff")},
|
||||
{Key: []byte("a"), Value: []byte("1")},
|
||||
{Key: []byte("build"), Value: []byte("stuff")},
|
||||
}},
|
||||
},
|
||||
types.ABCIResults{
|
||||
@@ -223,6 +222,7 @@ func TestOneValidatorChangesSaveLoad(t *testing.T) {
|
||||
_, val := state.Validators.GetByIndex(0)
|
||||
power := val.VotingPower
|
||||
var err error
|
||||
var validatorUpdates []*types.Validator
|
||||
for i := int64(1); i < highestHeight; i++ {
|
||||
// When we get to a change height, use the next pubkey.
|
||||
if changeIndex < len(changeHeights) && i == changeHeights[changeIndex] {
|
||||
@@ -230,8 +230,10 @@ func TestOneValidatorChangesSaveLoad(t *testing.T) {
|
||||
power++
|
||||
}
|
||||
header, blockID, responses := makeHeaderPartsResponsesValPowerChange(state, i, power)
|
||||
state, err = updateState(log.TestingLogger(), state, blockID, &header, responses)
|
||||
assert.Nil(t, err)
|
||||
validatorUpdates, err = types.PB2TM.ValidatorUpdates(responses.EndBlock.ValidatorUpdates)
|
||||
require.NoError(t, err)
|
||||
state, err = updateState(state, blockID, &header, responses, validatorUpdates)
|
||||
require.NoError(t, err)
|
||||
nextHeight := state.LastBlockHeight + 1
|
||||
saveValidatorsInfo(stateDB, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators)
|
||||
}
|
||||
@@ -261,11 +263,11 @@ func TestOneValidatorChangesSaveLoad(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestStoreLoadValidatorsIncrementsAccum(t *testing.T) {
|
||||
func TestStoreLoadValidatorsIncrementsProposerPriority(t *testing.T) {
|
||||
const valSetSize = 2
|
||||
tearDown, stateDB, state := setupTestCase(t)
|
||||
state.Validators = genValSet(valSetSize)
|
||||
state.NextValidators = state.Validators.CopyIncrementAccum(1)
|
||||
state.NextValidators = state.Validators.CopyIncrementProposerPriority(1)
|
||||
SaveState(stateDB, state)
|
||||
defer tearDown(t)
|
||||
|
||||
@@ -273,13 +275,13 @@ func TestStoreLoadValidatorsIncrementsAccum(t *testing.T) {
|
||||
|
||||
v0, err := LoadValidators(stateDB, nextHeight)
|
||||
assert.Nil(t, err)
|
||||
acc0 := v0.Validators[0].Accum
|
||||
acc0 := v0.Validators[0].ProposerPriority
|
||||
|
||||
v1, err := LoadValidators(stateDB, nextHeight+1)
|
||||
assert.Nil(t, err)
|
||||
acc1 := v1.Validators[0].Accum
|
||||
acc1 := v1.Validators[0].ProposerPriority
|
||||
|
||||
assert.NotEqual(t, acc1, acc0, "expected Accum value to change between heights")
|
||||
assert.NotEqual(t, acc1, acc0, "expected ProposerPriority value to change between heights")
|
||||
}
|
||||
|
||||
// TestValidatorChangesSaveLoad tests saving and loading a validator set with
|
||||
@@ -289,7 +291,7 @@ func TestManyValidatorChangesSaveLoad(t *testing.T) {
|
||||
tearDown, stateDB, state := setupTestCase(t)
|
||||
require.Equal(t, int64(0), state.LastBlockHeight)
|
||||
state.Validators = genValSet(valSetSize)
|
||||
state.NextValidators = state.Validators.CopyIncrementAccum(1)
|
||||
state.NextValidators = state.Validators.CopyIncrementProposerPriority(1)
|
||||
SaveState(stateDB, state)
|
||||
defer tearDown(t)
|
||||
|
||||
@@ -303,7 +305,10 @@ func TestManyValidatorChangesSaveLoad(t *testing.T) {
|
||||
|
||||
// Save state etc.
|
||||
var err error
|
||||
state, err = updateState(log.TestingLogger(), state, blockID, &header, responses)
|
||||
var validatorUpdates []*types.Validator
|
||||
validatorUpdates, err = types.PB2TM.ValidatorUpdates(responses.EndBlock.ValidatorUpdates)
|
||||
require.NoError(t, err)
|
||||
state, err = updateState(state, blockID, &header, responses, validatorUpdates)
|
||||
require.Nil(t, err)
|
||||
nextHeight := state.LastBlockHeight + 1
|
||||
saveValidatorsInfo(stateDB, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators)
|
||||
@@ -375,6 +380,7 @@ func TestConsensusParamsChangesSaveLoad(t *testing.T) {
|
||||
changeIndex := 0
|
||||
cp := params[changeIndex]
|
||||
var err error
|
||||
var validatorUpdates []*types.Validator
|
||||
for i := int64(1); i < highestHeight; i++ {
|
||||
// When we get to a change height, use the next params.
|
||||
if changeIndex < len(changeHeights) && i == changeHeights[changeIndex] {
|
||||
@@ -382,7 +388,9 @@ func TestConsensusParamsChangesSaveLoad(t *testing.T) {
|
||||
cp = params[changeIndex]
|
||||
}
|
||||
header, blockID, responses := makeHeaderPartsResponsesParams(state, i, cp)
|
||||
state, err = updateState(log.TestingLogger(), state, blockID, &header, responses)
|
||||
validatorUpdates, err = types.PB2TM.ValidatorUpdates(responses.EndBlock.ValidatorUpdates)
|
||||
require.NoError(t, err)
|
||||
state, err = updateState(state, blockID, &header, responses, validatorUpdates)
|
||||
|
||||
require.Nil(t, err)
|
||||
nextHeight := state.LastBlockHeight + 1
|
||||
|
||||
@@ -194,7 +194,7 @@ func LoadValidators(db dbm.DB, height int64) (*types.ValidatorSet, error) {
|
||||
),
|
||||
)
|
||||
}
|
||||
valInfo2.ValidatorSet.IncrementAccum(int(height - valInfo.LastHeightChanged)) // mutate
|
||||
valInfo2.ValidatorSet.IncrementProposerPriority(int(height - valInfo.LastHeightChanged)) // mutate
|
||||
valInfo = valInfo2
|
||||
}
|
||||
|
||||
|
||||
@@ -172,10 +172,10 @@ func (txi *TxIndex) Search(q *query.Query) ([]*types.TxResult, error) {
|
||||
|
||||
for _, r := range ranges {
|
||||
if !hashesInitialized {
|
||||
hashes = txi.matchRange(r, []byte(r.key))
|
||||
hashes = txi.matchRange(r, startKey(r.key))
|
||||
hashesInitialized = true
|
||||
} else {
|
||||
hashes = intersect(hashes, txi.matchRange(r, []byte(r.key)))
|
||||
hashes = intersect(hashes, txi.matchRange(r, startKey(r.key)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -190,10 +190,10 @@ func (txi *TxIndex) Search(q *query.Query) ([]*types.TxResult, error) {
|
||||
}
|
||||
|
||||
if !hashesInitialized {
|
||||
hashes = txi.match(c, startKey(c, height))
|
||||
hashes = txi.match(c, startKeyForCondition(c, height))
|
||||
hashesInitialized = true
|
||||
} else {
|
||||
hashes = intersect(hashes, txi.match(c, startKey(c, height)))
|
||||
hashes = intersect(hashes, txi.match(c, startKeyForCondition(c, height)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,18 +332,18 @@ func isRangeOperation(op query.Operator) bool {
|
||||
}
|
||||
}
|
||||
|
||||
func (txi *TxIndex) match(c query.Condition, startKey []byte) (hashes [][]byte) {
|
||||
func (txi *TxIndex) match(c query.Condition, startKeyBz []byte) (hashes [][]byte) {
|
||||
if c.Op == query.OpEqual {
|
||||
it := dbm.IteratePrefix(txi.store, startKey)
|
||||
it := dbm.IteratePrefix(txi.store, startKeyBz)
|
||||
defer it.Close()
|
||||
for ; it.Valid(); it.Next() {
|
||||
hashes = append(hashes, it.Value())
|
||||
}
|
||||
} else if c.Op == query.OpContains {
|
||||
// XXX: doing full scan because startKey does not apply here
|
||||
// For example, if startKey = "account.owner=an" and search query = "accoutn.owner CONSISTS an"
|
||||
// we can't iterate with prefix "account.owner=an" because we might miss keys like "account.owner=Ulan"
|
||||
it := txi.store.Iterator(nil, nil)
|
||||
// XXX: startKey does not apply here.
|
||||
// For example, if startKey = "account.owner/an/" and search query = "accoutn.owner CONTAINS an"
|
||||
// we can't iterate with prefix "account.owner/an/" because we might miss keys like "account.owner/Ulan/"
|
||||
it := dbm.IteratePrefix(txi.store, startKey(c.Tag))
|
||||
defer it.Close()
|
||||
for ; it.Valid(); it.Next() {
|
||||
if !isTagKey(it.Key()) {
|
||||
@@ -359,14 +359,14 @@ func (txi *TxIndex) match(c query.Condition, startKey []byte) (hashes [][]byte)
|
||||
return
|
||||
}
|
||||
|
||||
func (txi *TxIndex) matchRange(r queryRange, prefix []byte) (hashes [][]byte) {
|
||||
func (txi *TxIndex) matchRange(r queryRange, startKey []byte) (hashes [][]byte) {
|
||||
// create a map to prevent duplicates
|
||||
hashesMap := make(map[string][]byte)
|
||||
|
||||
lowerBound := r.lowerBoundValue()
|
||||
upperBound := r.upperBoundValue()
|
||||
|
||||
it := dbm.IteratePrefix(txi.store, prefix)
|
||||
it := dbm.IteratePrefix(txi.store, startKey)
|
||||
defer it.Close()
|
||||
LOOP:
|
||||
for ; it.Valid(); it.Next() {
|
||||
@@ -409,16 +409,6 @@ LOOP:
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Keys
|
||||
|
||||
func startKey(c query.Condition, height int64) []byte {
|
||||
var key string
|
||||
if height > 0 {
|
||||
key = fmt.Sprintf("%s/%v/%d/", c.Tag, c.Operand, height)
|
||||
} else {
|
||||
key = fmt.Sprintf("%s/%v/", c.Tag, c.Operand)
|
||||
}
|
||||
return []byte(key)
|
||||
}
|
||||
|
||||
func isTagKey(key []byte) bool {
|
||||
return strings.Count(string(key), tagKeySeparator) == 3
|
||||
}
|
||||
@@ -429,11 +419,36 @@ func extractValueFromKey(key []byte) string {
|
||||
}
|
||||
|
||||
func keyForTag(tag cmn.KVPair, result *types.TxResult) []byte {
|
||||
return []byte(fmt.Sprintf("%s/%s/%d/%d", tag.Key, tag.Value, result.Height, result.Index))
|
||||
return []byte(fmt.Sprintf("%s/%s/%d/%d",
|
||||
tag.Key,
|
||||
tag.Value,
|
||||
result.Height,
|
||||
result.Index,
|
||||
))
|
||||
}
|
||||
|
||||
func keyForHeight(result *types.TxResult) []byte {
|
||||
return []byte(fmt.Sprintf("%s/%d/%d/%d", types.TxHeightKey, result.Height, result.Height, result.Index))
|
||||
return []byte(fmt.Sprintf("%s/%d/%d/%d",
|
||||
types.TxHeightKey,
|
||||
result.Height,
|
||||
result.Height,
|
||||
result.Index,
|
||||
))
|
||||
}
|
||||
|
||||
func startKeyForCondition(c query.Condition, height int64) []byte {
|
||||
if height > 0 {
|
||||
return startKey(c.Tag, c.Operand, height)
|
||||
}
|
||||
return startKey(c.Tag, c.Operand)
|
||||
}
|
||||
|
||||
func startKey(fields ...interface{}) []byte {
|
||||
var b bytes.Buffer
|
||||
for _, f := range fields {
|
||||
b.Write([]byte(fmt.Sprintf("%v", f) + tagKeySeparator))
|
||||
}
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -89,8 +89,10 @@ func TestTxSearch(t *testing.T) {
|
||||
{"account.date >= TIME 2013-05-03T14:45:00Z", 0},
|
||||
// search using CONTAINS
|
||||
{"account.owner CONTAINS 'an'", 1},
|
||||
// search using CONTAINS
|
||||
// search for non existing value using CONTAINS
|
||||
{"account.owner CONTAINS 'Vlad'", 0},
|
||||
// search using the wrong tag (of numeric type) using CONTAINS
|
||||
{"account.number CONTAINS 'Iv'", 0},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
@@ -126,7 +128,7 @@ func TestTxSearchOneTxWithMultipleSameTagsButDifferentValues(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTxSearchMultipleTxs(t *testing.T) {
|
||||
allowedTags := []string{"account.number"}
|
||||
allowedTags := []string{"account.number", "account.number.id"}
|
||||
indexer := NewTxIndex(db.NewMemDB(), IndexTags(allowedTags))
|
||||
|
||||
// indexed first, but bigger height (to test the order of transactions)
|
||||
@@ -160,6 +162,17 @@ func TestTxSearchMultipleTxs(t *testing.T) {
|
||||
err = indexer.Index(txResult3)
|
||||
require.NoError(t, err)
|
||||
|
||||
// indexed fourth (to test we don't include txs with similar tags)
|
||||
// https://github.com/tendermint/tendermint/issues/2908
|
||||
txResult4 := txResultWithTags([]cmn.KVPair{
|
||||
{Key: []byte("account.number.id"), Value: []byte("1")},
|
||||
})
|
||||
txResult4.Tx = types.Tx("Mike's account")
|
||||
txResult4.Height = 2
|
||||
txResult4.Index = 2
|
||||
err = indexer.Index(txResult4)
|
||||
require.NoError(t, err)
|
||||
|
||||
results, err := indexer.Search(query.MustParse("account.number >= 1"))
|
||||
assert.NoError(t, err)
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
crypto "github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/libs/events"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
|
||||
@@ -41,16 +41,6 @@ type CanonicalVote struct {
|
||||
ChainID string
|
||||
}
|
||||
|
||||
type CanonicalHeartbeat struct {
|
||||
Type byte
|
||||
Height int64 `binary:"fixed64"`
|
||||
Round int `binary:"fixed64"`
|
||||
Sequence int `binary:"fixed64"`
|
||||
ValidatorAddress Address
|
||||
ValidatorIndex int
|
||||
ChainID string
|
||||
}
|
||||
|
||||
//-----------------------------------
|
||||
// Canonicalize the structs
|
||||
|
||||
@@ -91,18 +81,6 @@ func CanonicalizeVote(chainID string, vote *Vote) CanonicalVote {
|
||||
}
|
||||
}
|
||||
|
||||
func CanonicalizeHeartbeat(chainID string, heartbeat *Heartbeat) CanonicalHeartbeat {
|
||||
return CanonicalHeartbeat{
|
||||
Type: byte(HeartbeatType),
|
||||
Height: heartbeat.Height,
|
||||
Round: heartbeat.Round,
|
||||
Sequence: heartbeat.Sequence,
|
||||
ValidatorAddress: heartbeat.ValidatorAddress,
|
||||
ValidatorIndex: heartbeat.ValidatorIndex,
|
||||
ChainID: chainID,
|
||||
}
|
||||
}
|
||||
|
||||
// CanonicalTime can be used to stringify time in a canonical way.
|
||||
func CanonicalTime(t time.Time) string {
|
||||
// Note that sending time over amino resets it to
|
||||
|
||||
@@ -146,10 +146,6 @@ func (b *EventBus) PublishEventTx(data EventDataTx) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *EventBus) PublishEventProposalHeartbeat(data EventDataProposalHeartbeat) error {
|
||||
return b.Publish(EventProposalHeartbeat, data)
|
||||
}
|
||||
|
||||
func (b *EventBus) PublishEventNewRoundStep(data EventDataRoundState) error {
|
||||
return b.Publish(EventNewRoundStep, data)
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ func TestEventBusPublish(t *testing.T) {
|
||||
err = eventBus.Subscribe(context.Background(), "test", tmquery.Empty{}, eventsCh)
|
||||
require.NoError(t, err)
|
||||
|
||||
const numEventsExpected = 15
|
||||
const numEventsExpected = 14
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
numEvents := 0
|
||||
@@ -172,8 +172,6 @@ func TestEventBusPublish(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
err = eventBus.PublishEventVote(EventDataVote{})
|
||||
require.NoError(t, err)
|
||||
err = eventBus.PublishEventProposalHeartbeat(EventDataProposalHeartbeat{})
|
||||
require.NoError(t, err)
|
||||
err = eventBus.PublishEventNewRoundStep(EventDataRoundState{})
|
||||
require.NoError(t, err)
|
||||
err = eventBus.PublishEventTimeoutPropose(EventDataRoundState{})
|
||||
|
||||
@@ -18,7 +18,6 @@ const (
|
||||
EventNewRound = "NewRound"
|
||||
EventNewRoundStep = "NewRoundStep"
|
||||
EventPolka = "Polka"
|
||||
EventProposalHeartbeat = "ProposalHeartbeat"
|
||||
EventRelock = "Relock"
|
||||
EventTimeoutPropose = "TimeoutPropose"
|
||||
EventTimeoutWait = "TimeoutWait"
|
||||
@@ -47,7 +46,6 @@ func RegisterEventDatas(cdc *amino.Codec) {
|
||||
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)
|
||||
cdc.RegisterConcrete(EventDataString(""), "tendermint/event/ProposalString", nil)
|
||||
}
|
||||
@@ -75,10 +73,6 @@ type EventDataTx struct {
|
||||
TxResult
|
||||
}
|
||||
|
||||
type EventDataProposalHeartbeat struct {
|
||||
Heartbeat *Heartbeat
|
||||
}
|
||||
|
||||
// NOTE: This goes into the replay WAL
|
||||
type EventDataRoundState struct {
|
||||
Height int64 `json:"height"`
|
||||
@@ -143,7 +137,6 @@ var (
|
||||
EventQueryNewRound = QueryForEvent(EventNewRound)
|
||||
EventQueryNewRoundStep = QueryForEvent(EventNewRoundStep)
|
||||
EventQueryPolka = QueryForEvent(EventPolka)
|
||||
EventQueryProposalHeartbeat = QueryForEvent(EventProposalHeartbeat)
|
||||
EventQueryRelock = QueryForEvent(EventRelock)
|
||||
EventQueryTimeoutPropose = QueryForEvent(EventTimeoutPropose)
|
||||
EventQueryTimeoutWait = QueryForEvent(EventTimeoutWait)
|
||||
|
||||
@@ -17,8 +17,9 @@ type voteData struct {
|
||||
}
|
||||
|
||||
func makeVote(val PrivValidator, chainID string, valIndex int, height int64, round, step int, blockID BlockID) *Vote {
|
||||
addr := val.GetPubKey().Address()
|
||||
v := &Vote{
|
||||
ValidatorAddress: val.GetAddress(),
|
||||
ValidatorAddress: addr,
|
||||
ValidatorIndex: valIndex,
|
||||
Height: height,
|
||||
Round: round,
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
)
|
||||
|
||||
// Heartbeat is a simple vote-like structure so validators can
|
||||
// alert others that they are alive and waiting for transactions.
|
||||
// Note: We aren't adding ",omitempty" to Heartbeat's
|
||||
// json field tags because we always want the JSON
|
||||
// representation to be in its canonical form.
|
||||
type Heartbeat struct {
|
||||
ValidatorAddress Address `json:"validator_address"`
|
||||
ValidatorIndex int `json:"validator_index"`
|
||||
Height int64 `json:"height"`
|
||||
Round int `json:"round"`
|
||||
Sequence int `json:"sequence"`
|
||||
Signature []byte `json:"signature"`
|
||||
}
|
||||
|
||||
// SignBytes returns the Heartbeat bytes for signing.
|
||||
// It panics if the Heartbeat is nil.
|
||||
func (heartbeat *Heartbeat) SignBytes(chainID string) []byte {
|
||||
bz, err := cdc.MarshalBinaryLengthPrefixed(CanonicalizeHeartbeat(chainID, heartbeat))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bz
|
||||
}
|
||||
|
||||
// Copy makes a copy of the Heartbeat.
|
||||
func (heartbeat *Heartbeat) Copy() *Heartbeat {
|
||||
if heartbeat == nil {
|
||||
return nil
|
||||
}
|
||||
heartbeatCopy := *heartbeat
|
||||
return &heartbeatCopy
|
||||
}
|
||||
|
||||
// String returns a string representation of the Heartbeat.
|
||||
func (heartbeat *Heartbeat) String() string {
|
||||
if heartbeat == nil {
|
||||
return "nil-heartbeat"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Heartbeat{%v:%X %v/%02d (%v) %v}",
|
||||
heartbeat.ValidatorIndex, cmn.Fingerprint(heartbeat.ValidatorAddress),
|
||||
heartbeat.Height, heartbeat.Round, heartbeat.Sequence,
|
||||
fmt.Sprintf("/%X.../", cmn.Fingerprint(heartbeat.Signature[:])))
|
||||
}
|
||||
|
||||
// ValidateBasic performs basic validation.
|
||||
func (heartbeat *Heartbeat) ValidateBasic() error {
|
||||
if len(heartbeat.ValidatorAddress) != crypto.AddressSize {
|
||||
return fmt.Errorf("Expected ValidatorAddress size to be %d bytes, got %d bytes",
|
||||
crypto.AddressSize,
|
||||
len(heartbeat.ValidatorAddress),
|
||||
)
|
||||
}
|
||||
if heartbeat.ValidatorIndex < 0 {
|
||||
return errors.New("Negative ValidatorIndex")
|
||||
}
|
||||
if heartbeat.Height < 0 {
|
||||
return errors.New("Negative Height")
|
||||
}
|
||||
if heartbeat.Round < 0 {
|
||||
return errors.New("Negative Round")
|
||||
}
|
||||
if heartbeat.Sequence < 0 {
|
||||
return errors.New("Negative Sequence")
|
||||
}
|
||||
if len(heartbeat.Signature) == 0 {
|
||||
return errors.New("Signature is missing")
|
||||
}
|
||||
if len(heartbeat.Signature) > MaxSignatureSize {
|
||||
return fmt.Errorf("Signature is too big (max: %d)", MaxSignatureSize)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
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) {
|
||||
hb := &Heartbeat{ValidatorIndex: 1, Height: 10, Round: 1}
|
||||
hbCopy := hb.Copy()
|
||||
require.Equal(t, hbCopy, hb, "heartbeat copy should be the same")
|
||||
hbCopy.Round = hb.Round + 10
|
||||
require.NotEqual(t, hbCopy, hb, "heartbeat copy mutation should not change original")
|
||||
|
||||
var nilHb *Heartbeat
|
||||
nilHbCopy := nilHb.Copy()
|
||||
require.Nil(t, nilHbCopy, "copy of nil should also return nil")
|
||||
}
|
||||
|
||||
func TestHeartbeatString(t *testing.T) {
|
||||
var nilHb *Heartbeat
|
||||
require.Contains(t, nilHb.String(), "nil", "expecting a string and no panic")
|
||||
|
||||
hb := &Heartbeat{ValidatorIndex: 1, Height: 11, Round: 2}
|
||||
require.Equal(t, "Heartbeat{1:000000000000 11/02 (0) /000000000000.../}", hb.String())
|
||||
|
||||
var key ed25519.PrivKeyEd25519
|
||||
sig, err := key.Sign([]byte("Tendermint"))
|
||||
require.NoError(t, err)
|
||||
hb.Signature = sig
|
||||
require.Equal(t, "Heartbeat{1:000000000000 11/02 (0) /FF41E371B9BF.../}", hb.String())
|
||||
}
|
||||
|
||||
func TestHeartbeatWriteSignBytes(t *testing.T) {
|
||||
chainID := "test_chain_id"
|
||||
|
||||
{
|
||||
testHeartbeat := &Heartbeat{ValidatorIndex: 1, Height: 10, Round: 1}
|
||||
signBytes := testHeartbeat.SignBytes(chainID)
|
||||
expected, err := cdc.MarshalBinaryLengthPrefixed(CanonicalizeHeartbeat(chainID, testHeartbeat))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expected, signBytes, "Got unexpected sign bytes for Heartbeat")
|
||||
}
|
||||
|
||||
{
|
||||
testHeartbeat := &Heartbeat{}
|
||||
signBytes := testHeartbeat.SignBytes(chainID)
|
||||
expected, err := cdc.MarshalBinaryLengthPrefixed(CanonicalizeHeartbeat(chainID, testHeartbeat))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expected, signBytes, "Got unexpected sign bytes for Heartbeat")
|
||||
}
|
||||
|
||||
require.Panics(t, func() {
|
||||
var nilHb *Heartbeat
|
||||
signBytes := nilHb.SignBytes(chainID)
|
||||
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")
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -69,6 +69,15 @@ func DefaultValidatorParams() ValidatorParams {
|
||||
return ValidatorParams{[]string{ABCIPubKeyTypeEd25519}}
|
||||
}
|
||||
|
||||
func (params *ValidatorParams) IsValidPubkeyType(pubkeyType string) bool {
|
||||
for i := 0; i < len(params.PubKeyTypes); i++ {
|
||||
if params.PubKeyTypes[i] == pubkeyType {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Validate validates the ConsensusParams to ensure all values are within their
|
||||
// allowed limits, and returns an error if they are not.
|
||||
func (params *ConsensusParams) Validate() error {
|
||||
@@ -124,19 +133,7 @@ func (params *ConsensusParams) Hash() []byte {
|
||||
func (params *ConsensusParams) Equals(params2 *ConsensusParams) bool {
|
||||
return params.BlockSize == params2.BlockSize &&
|
||||
params.Evidence == params2.Evidence &&
|
||||
stringSliceEqual(params.Validator.PubKeyTypes, params2.Validator.PubKeyTypes)
|
||||
}
|
||||
|
||||
func stringSliceEqual(a, b []string) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(a); i++ {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
cmn.StringSliceEqual(params.Validator.PubKeyTypes, params2.Validator.PubKeyTypes)
|
||||
}
|
||||
|
||||
// Update returns a copy of the params with updates from the non-zero fields of p2.
|
||||
@@ -157,7 +154,9 @@ func (params ConsensusParams) Update(params2 *abci.ConsensusParams) ConsensusPar
|
||||
res.Evidence.MaxAge = params2.Evidence.MaxAge
|
||||
}
|
||||
if params2.Validator != nil {
|
||||
res.Validator.PubKeyTypes = params2.Validator.PubKeyTypes
|
||||
// Copy params2.Validator.PubkeyTypes, and set result's value to the copy.
|
||||
// This avoids having to initialize the slice to 0 values, and then write to it again.
|
||||
res.Validator.PubKeyTypes = append([]string{}, params2.Validator.PubKeyTypes...)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -10,14 +10,12 @@ import (
|
||||
)
|
||||
|
||||
// PrivValidator defines the functionality of a local Tendermint validator
|
||||
// that signs votes, proposals, and heartbeats, and never double signs.
|
||||
// that signs votes and proposals, and never double signs.
|
||||
type PrivValidator interface {
|
||||
GetAddress() Address // redundant since .PubKey().Address()
|
||||
GetPubKey() crypto.PubKey
|
||||
|
||||
SignVote(chainID string, vote *Vote) error
|
||||
SignProposal(chainID string, proposal *Proposal) error
|
||||
SignHeartbeat(chainID string, heartbeat *Heartbeat) error
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
@@ -30,7 +28,7 @@ func (pvs PrivValidatorsByAddress) Len() int {
|
||||
}
|
||||
|
||||
func (pvs PrivValidatorsByAddress) Less(i, j int) bool {
|
||||
return bytes.Compare(pvs[i].GetAddress(), pvs[j].GetAddress()) == -1
|
||||
return bytes.Compare(pvs[i].GetPubKey().Address(), pvs[j].GetPubKey().Address()) == -1
|
||||
}
|
||||
|
||||
func (pvs PrivValidatorsByAddress) Swap(i, j int) {
|
||||
@@ -52,11 +50,6 @@ func NewMockPV() *MockPV {
|
||||
return &MockPV{ed25519.GenPrivKey()}
|
||||
}
|
||||
|
||||
// Implements PrivValidator.
|
||||
func (pv *MockPV) GetAddress() Address {
|
||||
return pv.privKey.PubKey().Address()
|
||||
}
|
||||
|
||||
// Implements PrivValidator.
|
||||
func (pv *MockPV) GetPubKey() crypto.PubKey {
|
||||
return pv.privKey.PubKey()
|
||||
@@ -84,19 +77,10 @@ func (pv *MockPV) SignProposal(chainID string, proposal *Proposal) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// signHeartbeat signs the heartbeat without any checking.
|
||||
func (pv *MockPV) SignHeartbeat(chainID string, heartbeat *Heartbeat) error {
|
||||
sig, err := pv.privKey.Sign(heartbeat.SignBytes(chainID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
heartbeat.Signature = sig
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns a string representation of the MockPV.
|
||||
func (pv *MockPV) String() string {
|
||||
return fmt.Sprintf("MockPV{%v}", pv.GetAddress())
|
||||
addr := pv.GetPubKey().Address()
|
||||
return fmt.Sprintf("MockPV{%v}", addr)
|
||||
}
|
||||
|
||||
// XXX: Implement.
|
||||
@@ -121,11 +105,6 @@ func (pv *erroringMockPV) SignProposal(chainID string, proposal *Proposal) error
|
||||
return ErroringMockPVErr
|
||||
}
|
||||
|
||||
// signHeartbeat signs the heartbeat without any checking.
|
||||
func (pv *erroringMockPV) SignHeartbeat(chainID string, heartbeat *Heartbeat) error {
|
||||
return ErroringMockPVErr
|
||||
}
|
||||
|
||||
// NewErroringMockPV returns a MockPV that fails on each signing request. Again, for testing only.
|
||||
func NewErroringMockPV() *erroringMockPV {
|
||||
return &erroringMockPV{&MockPV{ed25519.GenPrivKey()}}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user