mirror of
https://github.com/fluencelabs/tendermint
synced 2025-07-22 07:41:57 +00:00
Compare commits
22 Commits
v0.27.2
...
multisig_d
Author | SHA1 | Date | |
---|---|---|---|
|
8eef647bfb | ||
|
616c3a4bae | ||
|
04e97f599a | ||
|
56a4fb4d72 | ||
|
49017a5787 | ||
|
6a80412a01 | ||
|
2348f38927 | ||
|
41e2eeee9c | ||
|
a88e283a9d | ||
|
1e1ca15bcc | ||
|
c6604b5a9b | ||
|
c510f823e7 | ||
|
daddebac29 | ||
|
30f346fe44 | ||
|
4d8f29f79c | ||
|
2182f6a702 | ||
|
a06912b579 | ||
|
0ff715125b | ||
|
385977d5e8 | ||
|
0138530df2 | ||
|
0533c73a50 | ||
|
1beb45511c |
@@ -3,7 +3,7 @@ version: 2
|
||||
defaults: &defaults
|
||||
working_directory: /go/src/github.com/tendermint/tendermint
|
||||
docker:
|
||||
- image: circleci/golang:1.10.3
|
||||
- image: circleci/golang:1.11.4
|
||||
environment:
|
||||
GOBIN: /tmp/workspace/bin
|
||||
|
||||
|
31
CHANGELOG.md
31
CHANGELOG.md
@@ -1,5 +1,26 @@
|
||||
# Changelog
|
||||
|
||||
## v0.27.4
|
||||
|
||||
*December 21st, 2018*
|
||||
|
||||
### BUG FIXES:
|
||||
|
||||
- [mempool] [\#3036](https://github.com/tendermint/tendermint/issues/3036) Fix
|
||||
LRU cache by popping the least recently used item when the cache is full,
|
||||
not the most recently used one!
|
||||
|
||||
## v0.27.3
|
||||
|
||||
*December 16th, 2018*
|
||||
|
||||
### BREAKING CHANGES:
|
||||
|
||||
* Go API
|
||||
|
||||
- [dep] [\#3027](https://github.com/tendermint/tendermint/issues/3027) Revert to mainline Go crypto library, eliminating the modified
|
||||
`bcrypt.GenerateFromPassword`
|
||||
|
||||
## v0.27.2
|
||||
|
||||
*December 16th, 2018*
|
||||
@@ -84,17 +105,17 @@ message.
|
||||
### 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
|
||||
- [node] [\#2959](https://github.com/tendermint/tendermint/issues/2959) Allow node to start even if software's BlockProtocol is
|
||||
different from state's BlockProtocol
|
||||
- [pex] \#2959 Pex reactor logger uses `module=pex`
|
||||
- [pex] [\#2959](https://github.com/tendermint/tendermint/issues/2959) Pex reactor logger uses `module=pex`
|
||||
|
||||
### BUG FIXES:
|
||||
|
||||
- [p2p] \#2968 Panic on transport error rather than continuing to run but not
|
||||
- [p2p] [\#2968](https://github.com/tendermint/tendermint/issues/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
|
||||
- [p2p] [\#2969](https://github.com/tendermint/tendermint/issues/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)
|
||||
- [rpc] [\#2408](https://github.com/tendermint/tendermint/issues/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
|
||||
|
@@ -1,4 +1,4 @@
|
||||
## v0.27.3
|
||||
## v0.27.4
|
||||
|
||||
*TBD*
|
||||
|
||||
@@ -7,18 +7,28 @@ Special thanks to external contributors on this release:
|
||||
### BREAKING CHANGES:
|
||||
|
||||
* CLI/RPC/Config
|
||||
- [cli] Removed `node` `--proxy_app=dummy` option. Use `kvstore` (`persistent_kvstore`) instead.
|
||||
- [cli] Renamed `node` `--proxy_app=nilapp` to `--proxy_app=noop`.
|
||||
- [config] \#2992 `allow_duplicate_ip` is now set to false
|
||||
|
||||
- [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
|
||||
|
||||
* P2P Protocol
|
||||
- multiple connections from the same IP are now disabled by default (see `allow_duplicate_ip` config option)
|
||||
|
||||
### FEATURES:
|
||||
- [privval] \#1181 Split immutable and mutable parts of priv_validator.json
|
||||
|
||||
### IMPROVEMENTS:
|
||||
|
||||
### BUG FIXES:
|
||||
|
||||
- [types] \#2926 do not panic if retrieving the private validator's public key fails
|
||||
|
@@ -3,7 +3,7 @@ set -e
|
||||
|
||||
# Get the tag from the version, or try to figure it out.
|
||||
if [ -z "$TAG" ]; then
|
||||
TAG=$(awk -F\" '/Version =/ { print $2; exit }' < ../version/version.go)
|
||||
TAG=$(awk -F\" '/TMCoreSemVer =/ { print $2; exit }' < ../version/version.go)
|
||||
fi
|
||||
if [ -z "$TAG" ]; then
|
||||
echo "Please specify a tag."
|
||||
|
@@ -3,7 +3,7 @@ set -e
|
||||
|
||||
# Get the tag from the version, or try to figure it out.
|
||||
if [ -z "$TAG" ]; then
|
||||
TAG=$(awk -F\" '/Version =/ { print $2; exit }' < ../version/version.go)
|
||||
TAG=$(awk -F\" '/TMCoreSemVer =/ { print $2; exit }' < ../version/version.go)
|
||||
fi
|
||||
if [ -z "$TAG" ]; then
|
||||
echo "Please specify a tag."
|
||||
|
5
Gopkg.lock
generated
5
Gopkg.lock
generated
@@ -376,7 +376,7 @@
|
||||
version = "v0.14.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:72b71e3a29775e5752ed7a8012052a3dee165e27ec18cedddae5288058f09acf"
|
||||
digest = "1:00d2b3e64cdc3fa69aa250dfbe4cc38c4837d4f37e62279be2ae52107ffbbb44"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = [
|
||||
"bcrypt",
|
||||
@@ -397,8 +397,7 @@
|
||||
"salsa20/salsa",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "3764759f34a542a3aef74d6b02e35be7ab893bba"
|
||||
source = "github.com/tendermint/crypto"
|
||||
revision = "505ab145d0a99da450461ae2c1a9f6cd10d1f447"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:d36f55a999540d29b6ea3c2ea29d71c76b1d9853fdcd3e5c5cb4836f2ba118f1"
|
||||
|
@@ -81,8 +81,7 @@
|
||||
|
||||
[[constraint]]
|
||||
name = "golang.org/x/crypto"
|
||||
source = "github.com/tendermint/crypto"
|
||||
revision = "3764759f34a542a3aef74d6b02e35be7ab893bba"
|
||||
revision = "505ab145d0a99da450461ae2c1a9f6cd10d1f447"
|
||||
|
||||
[[override]]
|
||||
name = "github.com/jmhodges/levigo"
|
||||
|
@@ -49,7 +49,7 @@ For examples of the kinds of bugs we're looking for, see [SECURITY.md](SECURITY.
|
||||
|
||||
Requirement|Notes
|
||||
---|---
|
||||
Go version | Go1.10 or higher
|
||||
Go version | Go1.11.4 or higher
|
||||
|
||||
## Documentation
|
||||
|
||||
|
@@ -58,7 +58,7 @@ var RootCmd = &cobra.Command{
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
switch cmd.Use {
|
||||
case "counter", "kvstore", "dummy": // for the examples apps, don't pre-run
|
||||
case "counter", "kvstore": // for the examples apps, don't pre-run
|
||||
return nil
|
||||
case "version": // skip running for version command
|
||||
return nil
|
||||
@@ -127,10 +127,6 @@ func addCounterFlags() {
|
||||
counterCmd.PersistentFlags().BoolVarP(&flagSerial, "serial", "", false, "enforce incrementing (serial) transactions")
|
||||
}
|
||||
|
||||
func addDummyFlags() {
|
||||
dummyCmd.PersistentFlags().StringVarP(&flagPersist, "persist", "", "", "directory to use for a database")
|
||||
}
|
||||
|
||||
func addKVStoreFlags() {
|
||||
kvstoreCmd.PersistentFlags().StringVarP(&flagPersist, "persist", "", "", "directory to use for a database")
|
||||
}
|
||||
@@ -152,10 +148,6 @@ func addCommands() {
|
||||
// examples
|
||||
addCounterFlags()
|
||||
RootCmd.AddCommand(counterCmd)
|
||||
// deprecated, left for backwards compatibility
|
||||
addDummyFlags()
|
||||
RootCmd.AddCommand(dummyCmd)
|
||||
// replaces dummy, see issue #196
|
||||
addKVStoreFlags()
|
||||
RootCmd.AddCommand(kvstoreCmd)
|
||||
}
|
||||
@@ -291,18 +283,6 @@ var counterCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
// deprecated, left for backwards compatibility
|
||||
var dummyCmd = &cobra.Command{
|
||||
Use: "dummy",
|
||||
Deprecated: "use: [abci-cli kvstore] instead",
|
||||
Short: "ABCI demo example",
|
||||
Long: "ABCI demo example",
|
||||
Args: cobra.ExactArgs(0),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return cmdKVStore(cmd, args)
|
||||
},
|
||||
}
|
||||
|
||||
var kvstoreCmd = &cobra.Command{
|
||||
Use: "kvstore",
|
||||
Short: "ABCI demo example",
|
||||
|
@@ -432,11 +432,7 @@ type bcBlockResponseMessage struct {
|
||||
|
||||
// ValidateBasic performs basic validation.
|
||||
func (m *bcBlockResponseMessage) ValidateBasic() error {
|
||||
if err := m.Block.ValidateBasic(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return m.Block.ValidateBasic()
|
||||
}
|
||||
|
||||
func (m *bcBlockResponseMessage) String() string {
|
||||
|
@@ -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,
|
||||
|
@@ -13,9 +13,10 @@ import (
|
||||
|
||||
func main() {
|
||||
var (
|
||||
addr = flag.String("addr", ":26659", "Address of client to connect to")
|
||||
chainID = flag.String("chain-id", "mychain", "chain id")
|
||||
privValPath = flag.String("priv", "", "priv val file path")
|
||||
addr = flag.String("addr", ":26659", "Address of client to connect to")
|
||||
chainID = flag.String("chain-id", "mychain", "chain id")
|
||||
privValKeyPath = flag.String("priv-key", "", "priv val key file path")
|
||||
privValStatePath = flag.String("priv-state", "", "priv val state file path")
|
||||
|
||||
logger = log.NewTMLogger(
|
||||
log.NewSyncWriter(os.Stdout),
|
||||
@@ -27,10 +28,11 @@ func main() {
|
||||
"Starting private validator",
|
||||
"addr", *addr,
|
||||
"chainID", *chainID,
|
||||
"privPath", *privValPath,
|
||||
"privKeyPath", *privValKeyPath,
|
||||
"privStatePath", *privValStatePath,
|
||||
)
|
||||
|
||||
pv := privval.LoadFilePV(*privValPath)
|
||||
pv := privval.LoadFilePV(*privValKeyPath, *privValStatePath)
|
||||
|
||||
rs := privval.NewRemoteSigner(
|
||||
logger,
|
||||
|
@@ -17,7 +17,7 @@ var GenValidatorCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
func genValidator(cmd *cobra.Command, args []string) {
|
||||
pv := privval.GenFilePV("")
|
||||
pv := privval.GenFilePV("", "")
|
||||
jsbz, err := cdc.MarshalJSON(pv)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
@@ -26,15 +25,18 @@ func initFiles(cmd *cobra.Command, args []string) error {
|
||||
|
||||
func initFilesWithConfig(config *cfg.Config) error {
|
||||
// private validator
|
||||
privValFile := config.PrivValidatorFile()
|
||||
privValKeyFile := config.PrivValidatorKeyFile()
|
||||
privValStateFile := config.PrivValidatorStateFile()
|
||||
var pv *privval.FilePV
|
||||
if cmn.FileExists(privValFile) {
|
||||
pv = privval.LoadFilePV(privValFile)
|
||||
logger.Info("Found private validator", "path", privValFile)
|
||||
if cmn.FileExists(privValKeyFile) {
|
||||
pv = privval.LoadFilePV(privValKeyFile, privValStateFile)
|
||||
logger.Info("Found private validator", "keyFile", privValKeyFile,
|
||||
"stateFile", privValStateFile)
|
||||
} else {
|
||||
pv = privval.GenFilePV(privValFile)
|
||||
pv = privval.GenFilePV(privValKeyFile, privValStateFile)
|
||||
pv.Save()
|
||||
logger.Info("Generated private validator", "path", privValFile)
|
||||
logger.Info("Generated private validator", "keyFile", privValKeyFile,
|
||||
"stateFile", privValStateFile)
|
||||
}
|
||||
|
||||
nodeKeyFile := config.NodeKeyFile()
|
||||
@@ -57,9 +59,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,
|
||||
}}
|
||||
|
||||
|
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
"github.com/tendermint/tendermint/privval"
|
||||
)
|
||||
@@ -27,36 +28,41 @@ var ResetPrivValidatorCmd = &cobra.Command{
|
||||
// XXX: this is totally unsafe.
|
||||
// it's only suitable for testnets.
|
||||
func resetAll(cmd *cobra.Command, args []string) {
|
||||
ResetAll(config.DBDir(), config.P2P.AddrBookFile(), config.PrivValidatorFile(), logger)
|
||||
ResetAll(config.DBDir(), config.P2P.AddrBookFile(), config.PrivValidatorKeyFile(),
|
||||
config.PrivValidatorStateFile(), logger)
|
||||
}
|
||||
|
||||
// XXX: this is totally unsafe.
|
||||
// it's only suitable for testnets.
|
||||
func resetPrivValidator(cmd *cobra.Command, args []string) {
|
||||
resetFilePV(config.PrivValidatorFile(), logger)
|
||||
resetFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile(), logger)
|
||||
}
|
||||
|
||||
// ResetAll removes the privValidator and address book files plus all data.
|
||||
// ResetAll removes address book files plus all data, and resets the privValdiator data.
|
||||
// Exported so other CLI tools can use it.
|
||||
func ResetAll(dbDir, addrBookFile, privValFile string, logger log.Logger) {
|
||||
resetFilePV(privValFile, logger)
|
||||
func ResetAll(dbDir, addrBookFile, privValKeyFile, privValStateFile string, logger log.Logger) {
|
||||
removeAddrBook(addrBookFile, logger)
|
||||
if err := os.RemoveAll(dbDir); err == nil {
|
||||
logger.Info("Removed all blockchain history", "dir", dbDir)
|
||||
} else {
|
||||
logger.Error("Error removing all blockchain history", "dir", dbDir, "err", err)
|
||||
}
|
||||
// recreate the dbDir since the privVal state needs to live there
|
||||
cmn.EnsureDir(dbDir, 0700)
|
||||
resetFilePV(privValKeyFile, privValStateFile, logger)
|
||||
}
|
||||
|
||||
func resetFilePV(privValFile string, logger log.Logger) {
|
||||
if _, err := os.Stat(privValFile); err == nil {
|
||||
pv := privval.LoadFilePV(privValFile)
|
||||
func resetFilePV(privValKeyFile, privValStateFile string, logger log.Logger) {
|
||||
if _, err := os.Stat(privValKeyFile); err == nil {
|
||||
pv := privval.LoadFilePVEmptyState(privValKeyFile, privValStateFile)
|
||||
pv.Reset()
|
||||
logger.Info("Reset private validator file to genesis state", "file", privValFile)
|
||||
logger.Info("Reset private validator file to genesis state", "keyFile", privValKeyFile,
|
||||
"stateFile", privValStateFile)
|
||||
} else {
|
||||
pv := privval.GenFilePV(privValFile)
|
||||
pv := privval.GenFilePV(privValKeyFile, privValStateFile)
|
||||
pv.Save()
|
||||
logger.Info("Generated private validator file", "file", privValFile)
|
||||
logger.Info("Generated private validator file", "file", "keyFile", privValKeyFile,
|
||||
"stateFile", privValStateFile)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -24,7 +24,7 @@ func AddNodeFlags(cmd *cobra.Command) {
|
||||
cmd.Flags().Bool("fast_sync", config.FastSync, "Fast blockchain syncing")
|
||||
|
||||
// abci flags
|
||||
cmd.Flags().String("proxy_app", config.ProxyApp, "Proxy app address, or 'nilapp' or 'kvstore' for local testing.")
|
||||
cmd.Flags().String("proxy_app", config.ProxyApp, "Proxy app address, or one of: 'kvstore', 'persistent_kvstore', 'counter', 'counter_serial' or 'noop' for local testing.")
|
||||
cmd.Flags().String("abci", config.ABCI, "Specify abci transport (socket | grpc)")
|
||||
|
||||
// rpc flags
|
||||
|
@@ -16,7 +16,7 @@ var ShowValidatorCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
func showValidator(cmd *cobra.Command, args []string) {
|
||||
privValidator := privval.LoadOrGenFilePV(config.PrivValidatorFile())
|
||||
privValidator := privval.LoadOrGenFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile())
|
||||
pubKeyJSONBytes, _ := cdc.MarshalJSON(privValidator.GetPubKey())
|
||||
fmt.Println(string(pubKeyJSONBytes))
|
||||
}
|
||||
|
@@ -85,11 +85,18 @@ func testnetFiles(cmd *cobra.Command, args []string) error {
|
||||
_ = os.RemoveAll(outputDir)
|
||||
return err
|
||||
}
|
||||
err = os.MkdirAll(filepath.Join(nodeDir, "data"), nodeDirPerm)
|
||||
if err != nil {
|
||||
_ = os.RemoveAll(outputDir)
|
||||
return err
|
||||
}
|
||||
|
||||
initFilesWithConfig(config)
|
||||
|
||||
pvFile := filepath.Join(nodeDir, config.BaseConfig.PrivValidator)
|
||||
pv := privval.LoadFilePV(pvFile)
|
||||
pvKeyFile := filepath.Join(nodeDir, config.BaseConfig.PrivValidatorKey)
|
||||
pvStateFile := filepath.Join(nodeDir, config.BaseConfig.PrivValidatorState)
|
||||
|
||||
pv := privval.LoadFilePV(pvKeyFile, pvStateFile)
|
||||
genVals[i] = types.GenesisValidator{
|
||||
Address: pv.GetPubKey().Address(),
|
||||
PubKey: pv.GetPubKey(),
|
||||
@@ -145,6 +152,7 @@ func testnetFiles(cmd *cobra.Command, args []string) error {
|
||||
nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i))
|
||||
config.SetRoot(nodeDir)
|
||||
config.P2P.AddrBookStrict = false
|
||||
config.P2P.AllowDuplicateIP = true
|
||||
if populatePersistentPeers {
|
||||
config.P2P.PersistentPeers = persistentPeers
|
||||
}
|
||||
|
@@ -35,15 +35,24 @@ var (
|
||||
defaultConfigFileName = "config.toml"
|
||||
defaultGenesisJSONName = "genesis.json"
|
||||
|
||||
defaultPrivValName = "priv_validator.json"
|
||||
defaultPrivValKeyName = "priv_validator_key.json"
|
||||
defaultPrivValStateName = "priv_validator_state.json"
|
||||
|
||||
defaultNodeKeyName = "node_key.json"
|
||||
defaultAddrBookName = "addrbook.json"
|
||||
|
||||
defaultConfigFilePath = filepath.Join(defaultConfigDir, defaultConfigFileName)
|
||||
defaultGenesisJSONPath = filepath.Join(defaultConfigDir, defaultGenesisJSONName)
|
||||
defaultPrivValPath = filepath.Join(defaultConfigDir, defaultPrivValName)
|
||||
defaultNodeKeyPath = filepath.Join(defaultConfigDir, defaultNodeKeyName)
|
||||
defaultAddrBookPath = filepath.Join(defaultConfigDir, defaultAddrBookName)
|
||||
defaultConfigFilePath = filepath.Join(defaultConfigDir, defaultConfigFileName)
|
||||
defaultGenesisJSONPath = filepath.Join(defaultConfigDir, defaultGenesisJSONName)
|
||||
defaultPrivValKeyPath = filepath.Join(defaultConfigDir, defaultPrivValKeyName)
|
||||
defaultPrivValStatePath = filepath.Join(defaultDataDir, defaultPrivValStateName)
|
||||
|
||||
defaultNodeKeyPath = filepath.Join(defaultConfigDir, defaultNodeKeyName)
|
||||
defaultAddrBookPath = filepath.Join(defaultConfigDir, defaultAddrBookName)
|
||||
)
|
||||
|
||||
var (
|
||||
oldPrivVal = "priv_validator.json"
|
||||
oldPrivValPath = filepath.Join(defaultConfigDir, oldPrivVal)
|
||||
)
|
||||
|
||||
// Config defines the top level configuration for a Tendermint node
|
||||
@@ -160,7 +169,10 @@ type BaseConfig struct {
|
||||
Genesis string `mapstructure:"genesis_file"`
|
||||
|
||||
// Path to the JSON file containing the private key to use as a validator in the consensus protocol
|
||||
PrivValidator string `mapstructure:"priv_validator_file"`
|
||||
PrivValidatorKey string `mapstructure:"priv_validator_key_file"`
|
||||
|
||||
// Path to the JSON file containing the last sign state of a validator
|
||||
PrivValidatorState string `mapstructure:"priv_validator_state_file"`
|
||||
|
||||
// TCP or UNIX socket address for Tendermint to listen on for
|
||||
// connections from an external PrivValidator process
|
||||
@@ -183,19 +195,20 @@ type BaseConfig struct {
|
||||
// DefaultBaseConfig returns a default base configuration for a Tendermint node
|
||||
func DefaultBaseConfig() BaseConfig {
|
||||
return BaseConfig{
|
||||
Genesis: defaultGenesisJSONPath,
|
||||
PrivValidator: defaultPrivValPath,
|
||||
NodeKey: defaultNodeKeyPath,
|
||||
Moniker: defaultMoniker,
|
||||
ProxyApp: "tcp://127.0.0.1:26658",
|
||||
ABCI: "socket",
|
||||
LogLevel: DefaultPackageLogLevels(),
|
||||
LogFormat: LogFormatPlain,
|
||||
ProfListenAddress: "",
|
||||
FastSync: true,
|
||||
FilterPeers: false,
|
||||
DBBackend: "leveldb",
|
||||
DBPath: "data",
|
||||
Genesis: defaultGenesisJSONPath,
|
||||
PrivValidatorKey: defaultPrivValKeyPath,
|
||||
PrivValidatorState: defaultPrivValStatePath,
|
||||
NodeKey: defaultNodeKeyPath,
|
||||
Moniker: defaultMoniker,
|
||||
ProxyApp: "tcp://127.0.0.1:26658",
|
||||
ABCI: "socket",
|
||||
LogLevel: DefaultPackageLogLevels(),
|
||||
LogFormat: LogFormatPlain,
|
||||
ProfListenAddress: "",
|
||||
FastSync: true,
|
||||
FilterPeers: false,
|
||||
DBBackend: "leveldb",
|
||||
DBPath: "data",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,9 +231,20 @@ func (cfg BaseConfig) GenesisFile() string {
|
||||
return rootify(cfg.Genesis, cfg.RootDir)
|
||||
}
|
||||
|
||||
// PrivValidatorFile returns the full path to the priv_validator.json file
|
||||
func (cfg BaseConfig) PrivValidatorFile() string {
|
||||
return rootify(cfg.PrivValidator, cfg.RootDir)
|
||||
// PrivValidatorKeyFile returns the full path to the priv_validator_key.json file
|
||||
func (cfg BaseConfig) PrivValidatorKeyFile() string {
|
||||
return rootify(cfg.PrivValidatorKey, cfg.RootDir)
|
||||
}
|
||||
|
||||
// PrivValidatorFile returns the full path to the priv_validator_state.json file
|
||||
func (cfg BaseConfig) PrivValidatorStateFile() string {
|
||||
return rootify(cfg.PrivValidatorState, cfg.RootDir)
|
||||
}
|
||||
|
||||
// OldPrivValidatorFile returns the full path of the priv_validator.json from pre v0.28.0.
|
||||
// TODO: eventually remove.
|
||||
func (cfg BaseConfig) OldPrivValidatorFile() string {
|
||||
return rootify(oldPrivValPath, cfg.RootDir)
|
||||
}
|
||||
|
||||
// NodeKeyFile returns the full path to the node_key.json file
|
||||
@@ -434,7 +458,7 @@ func DefaultP2PConfig() *P2PConfig {
|
||||
RecvRate: 5120000, // 5 mB/s
|
||||
PexReactor: true,
|
||||
SeedMode: false,
|
||||
AllowDuplicateIP: true, // so non-breaking yet
|
||||
AllowDuplicateIP: false,
|
||||
HandshakeTimeout: 20 * time.Second,
|
||||
DialTimeout: 3 * time.Second,
|
||||
TestDialFail: false,
|
||||
|
@@ -95,7 +95,10 @@ log_format = "{{ .BaseConfig.LogFormat }}"
|
||||
genesis_file = "{{ js .BaseConfig.Genesis }}"
|
||||
|
||||
# Path to the JSON file containing the private key to use as a validator in the consensus protocol
|
||||
priv_validator_file = "{{ js .BaseConfig.PrivValidator }}"
|
||||
priv_validator_key_file = "{{ js .BaseConfig.PrivValidatorKey }}"
|
||||
|
||||
# Path to the JSON file containing the last sign state of a validator
|
||||
priv_validator_state_file = "{{ js .BaseConfig.PrivValidatorState }}"
|
||||
|
||||
# TCP or UNIX socket address for Tendermint to listen on for
|
||||
# connections from an external PrivValidator process
|
||||
@@ -342,7 +345,8 @@ func ResetTestRoot(testName string) *Config {
|
||||
baseConfig := DefaultBaseConfig()
|
||||
configFilePath := filepath.Join(rootDir, defaultConfigFilePath)
|
||||
genesisFilePath := filepath.Join(rootDir, baseConfig.Genesis)
|
||||
privFilePath := filepath.Join(rootDir, baseConfig.PrivValidator)
|
||||
privKeyFilePath := filepath.Join(rootDir, baseConfig.PrivValidatorKey)
|
||||
privStateFilePath := filepath.Join(rootDir, baseConfig.PrivValidatorState)
|
||||
|
||||
// Write default config file if missing.
|
||||
if !cmn.FileExists(configFilePath) {
|
||||
@@ -352,7 +356,8 @@ func ResetTestRoot(testName string) *Config {
|
||||
cmn.MustWriteFile(genesisFilePath, []byte(testGenesis), 0644)
|
||||
}
|
||||
// we always overwrite the priv val
|
||||
cmn.MustWriteFile(privFilePath, []byte(testPrivValidator), 0644)
|
||||
cmn.MustWriteFile(privKeyFilePath, []byte(testPrivValidatorKey), 0644)
|
||||
cmn.MustWriteFile(privStateFilePath, []byte(testPrivValidatorState), 0644)
|
||||
|
||||
config := TestConfig().SetRoot(rootDir)
|
||||
return config
|
||||
@@ -374,7 +379,7 @@ var testGenesis = `{
|
||||
"app_hash": ""
|
||||
}`
|
||||
|
||||
var testPrivValidator = `{
|
||||
var testPrivValidatorKey = `{
|
||||
"address": "A3258DCBF45DCA0DF052981870F2D1441A36D145",
|
||||
"pub_key": {
|
||||
"type": "tendermint/PubKeyEd25519",
|
||||
@@ -383,8 +388,11 @@ var testPrivValidator = `{
|
||||
"priv_key": {
|
||||
"type": "tendermint/PrivKeyEd25519",
|
||||
"value": "EVkqJO/jIXp3rkASXfh9YnyToYXRXhBr6g9cQVxPFnQBP/5povV4HTjvsy530kybxKHwEi85iU8YL0qQhSYVoQ=="
|
||||
},
|
||||
"last_height": "0",
|
||||
"last_round": "0",
|
||||
"last_step": 0
|
||||
}
|
||||
}`
|
||||
|
||||
var testPrivValidatorState = `{
|
||||
"height": "0",
|
||||
"round": "0",
|
||||
"step": 0
|
||||
}`
|
||||
|
@@ -60,7 +60,7 @@ func TestEnsureTestRoot(t *testing.T) {
|
||||
|
||||
// TODO: make sure the cfg returned and testconfig are the same!
|
||||
baseConfig := DefaultBaseConfig()
|
||||
ensureFiles(t, rootDir, defaultDataDir, baseConfig.Genesis, baseConfig.PrivValidator)
|
||||
ensureFiles(t, rootDir, defaultDataDir, baseConfig.Genesis, baseConfig.PrivValidatorKey, baseConfig.PrivValidatorState)
|
||||
}
|
||||
|
||||
func checkConfig(configFile string) bool {
|
||||
|
@@ -6,14 +6,18 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/log/term"
|
||||
|
||||
abcicli "github.com/tendermint/tendermint/abci/client"
|
||||
"github.com/tendermint/tendermint/abci/example/counter"
|
||||
"github.com/tendermint/tendermint/abci/example/kvstore"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
bc "github.com/tendermint/tendermint/blockchain"
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
@@ -27,11 +31,6 @@ import (
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
|
||||
"github.com/tendermint/tendermint/abci/example/counter"
|
||||
"github.com/tendermint/tendermint/abci/example/kvstore"
|
||||
|
||||
"github.com/go-kit/kit/log/term"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -72,9 +71,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 +151,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 +169,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 +181,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")
|
||||
}
|
||||
|
||||
@@ -281,9 +284,10 @@ func newConsensusStateWithConfigAndBlockStore(thisConfig *cfg.Config, state sm.S
|
||||
}
|
||||
|
||||
func loadPrivValidator(config *cfg.Config) *privval.FilePV {
|
||||
privValidatorFile := config.PrivValidatorFile()
|
||||
ensureDir(path.Dir(privValidatorFile), 0700)
|
||||
privValidator := privval.LoadOrGenFilePV(privValidatorFile)
|
||||
privValidatorKeyFile := config.PrivValidatorKeyFile()
|
||||
ensureDir(filepath.Dir(privValidatorKeyFile), 0700)
|
||||
privValidatorStateFile := config.PrivValidatorStateFile()
|
||||
privValidator := privval.LoadOrGenFilePV(privValidatorKeyFile, privValidatorStateFile)
|
||||
privValidator.Reset()
|
||||
return privValidator
|
||||
}
|
||||
@@ -591,7 +595,7 @@ func randConsensusNet(nValidators int, testName string, tickerFunc func() Timeou
|
||||
for _, opt := range configOpts {
|
||||
opt(thisConfig)
|
||||
}
|
||||
ensureDir(path.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal
|
||||
ensureDir(filepath.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal
|
||||
app := appFunc()
|
||||
vals := types.TM2PB.ValidatorUpdates(state.Validators)
|
||||
app.InitChain(abci.RequestInitChain{Validators: vals})
|
||||
@@ -612,16 +616,21 @@ func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerF
|
||||
stateDB := dbm.NewMemDB() // each state needs its own db
|
||||
state, _ := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
|
||||
thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i))
|
||||
ensureDir(path.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal
|
||||
ensureDir(filepath.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal
|
||||
var privVal types.PrivValidator
|
||||
if i < nValidators {
|
||||
privVal = privVals[i]
|
||||
} else {
|
||||
tempFile, err := ioutil.TempFile("", "priv_validator_")
|
||||
tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
privVal = privval.GenFilePV(tempFile.Name())
|
||||
tempStateFile, err := ioutil.TempFile("", "priv_validator_state_")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
privVal = privval.GenFilePV(tempKeyFile.Name(), tempStateFile.Name())
|
||||
}
|
||||
|
||||
app := appFunc()
|
||||
|
@@ -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)
|
||||
@@ -268,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
|
||||
@@ -331,7 +333,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
|
||||
|
@@ -319,7 +319,7 @@ func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) {
|
||||
walFile := tempWALWithData(walBody)
|
||||
config.Consensus.SetWalFile(walFile)
|
||||
|
||||
privVal := privval.LoadFilePV(config.PrivValidatorFile())
|
||||
privVal := privval.LoadFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile())
|
||||
|
||||
wal, err := NewWAL(walFile)
|
||||
require.NoError(t, err)
|
||||
@@ -633,7 +633,7 @@ func TestInitChainUpdateValidators(t *testing.T) {
|
||||
clientCreator := proxy.NewLocalClientCreator(app)
|
||||
|
||||
config := ResetConfig("proxy_test_")
|
||||
privVal := privval.LoadFilePV(config.PrivValidatorFile())
|
||||
privVal := privval.LoadFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile())
|
||||
stateDB, state, store := stateAndStore(config, privVal.GetPubKey(), 0x0)
|
||||
|
||||
oldValAddr := state.Validators.Validators[0].Address
|
||||
@@ -659,12 +659,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"
|
||||
@@ -829,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 {
|
||||
@@ -843,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) {
|
||||
@@ -929,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
|
||||
@@ -1474,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
|
||||
}
|
||||
@@ -1526,7 +1529,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
|
||||
// Not necessarily a bad peer, but not favourable behaviour.
|
||||
if vote.Height != cs.Height {
|
||||
err = ErrVoteHeightMismatch
|
||||
cs.Logger.Info("Vote ignored and not added", "voteHeight", vote.Height, "csHeight", cs.Height, "err", err)
|
||||
cs.Logger.Info("Vote ignored and not added", "voteHeight", vote.Height, "csHeight", cs.Height, "peerID", peerID)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1639,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{
|
||||
@@ -1675,7 +1678,7 @@ 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()) {
|
||||
if cs.privValidator == nil || !cs.Validators.HasAddress(cs.privValidator.GetPubKey().Address()) {
|
||||
return nil
|
||||
}
|
||||
vote, err := cs.signVote(type_, hash, header)
|
||||
|
@@ -73,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)
|
||||
}
|
||||
|
||||
@@ -87,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))
|
||||
}
|
||||
}
|
||||
@@ -110,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))
|
||||
}
|
||||
@@ -505,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)
|
||||
|
||||
@@ -596,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
|
||||
|
||||
@@ -689,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)
|
||||
@@ -805,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)
|
||||
@@ -896,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)
|
||||
@@ -982,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)
|
||||
@@ -1041,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
|
||||
@@ -1111,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)
|
||||
@@ -1144,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)
|
||||
@@ -1177,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)
|
||||
@@ -1361,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,
|
||||
|
@@ -40,8 +40,9 @@ func WALGenerateNBlocks(wr io.Writer, numBlocks int) (err error) {
|
||||
// COPY PASTE FROM node.go WITH A FEW MODIFICATIONS
|
||||
// NOTE: we can't import node package because of circular dependency.
|
||||
// NOTE: we don't do handshake so need to set state.Version.Consensus.App directly.
|
||||
privValidatorFile := config.PrivValidatorFile()
|
||||
privValidator := privval.LoadOrGenFilePV(privValidatorFile)
|
||||
privValidatorKeyFile := config.PrivValidatorKeyFile()
|
||||
privValidatorStateFile := config.PrivValidatorStateFile()
|
||||
privValidator := privval.LoadOrGenFilePV(privValidatorKeyFile, privValidatorStateFile)
|
||||
genDoc, err := types.GenesisDocFromFile(config.GenesisFile())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to read genesis file")
|
||||
|
@@ -5,7 +5,7 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"golang.org/x/crypto/openpgp/armor" // forked to github.com/tendermint/crypto
|
||||
"golang.org/x/crypto/openpgp/armor"
|
||||
)
|
||||
|
||||
func EncodeArmor(blockType string, headers map[string]string, data []byte) string {
|
||||
|
@@ -7,7 +7,7 @@ import (
|
||||
"io"
|
||||
|
||||
amino "github.com/tendermint/go-amino"
|
||||
"golang.org/x/crypto/ed25519" // forked to github.com/tendermint/crypto
|
||||
"golang.org/x/crypto/ed25519"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
|
@@ -3,7 +3,7 @@ package crypto
|
||||
import (
|
||||
"crypto/sha256"
|
||||
|
||||
"golang.org/x/crypto/ripemd160" // forked to github.com/tendermint/crypto
|
||||
"golang.org/x/crypto/ripemd160"
|
||||
)
|
||||
|
||||
func Sha256(bytes []byte) []byte {
|
||||
|
@@ -95,6 +95,22 @@ func TestMultiSigPubKeyEquality(t *testing.T) {
|
||||
require.False(t, multisigKey.Equals(multisigKey2))
|
||||
}
|
||||
|
||||
func TestPubKeyMultisigThresholdAminoToIface(t *testing.T) {
|
||||
msg := []byte{1, 2, 3, 4}
|
||||
pubkeys, _ := generatePubKeysAndSignatures(5, msg)
|
||||
multisigKey := NewPubKeyMultisigThreshold(2, pubkeys)
|
||||
|
||||
ab, err := cdc.MarshalBinaryLengthPrefixed(multisigKey)
|
||||
require.NoError(t, err)
|
||||
// like other crypto.Pubkey implementations (e.g. ed25519.PubKeyEd25519),
|
||||
// PubKeyMultisigThreshold should be deserializable into a crypto.PubKey:
|
||||
var pubKey crypto.PubKey
|
||||
err = cdc.UnmarshalBinaryLengthPrefixed(ab, &pubKey)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, multisigKey, pubKey)
|
||||
}
|
||||
|
||||
func generatePubKeysAndSignatures(n int, msg []byte) (pubkeys []crypto.PubKey, signatures [][]byte) {
|
||||
pubkeys = make([]crypto.PubKey, n)
|
||||
signatures = make([][]byte, n)
|
||||
|
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
secp256k1 "github.com/tendermint/btcd/btcec"
|
||||
amino "github.com/tendermint/go-amino"
|
||||
"golang.org/x/crypto/ripemd160" // forked to github.com/tendermint/crypto
|
||||
"golang.org/x/crypto/ripemd160"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
)
|
||||
|
@@ -8,7 +8,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/crypto/chacha20poly1305" // forked to github.com/tendermint/crypto
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
)
|
||||
|
||||
// Implements crypto.AEAD
|
||||
|
@@ -4,7 +4,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/crypto/nacl/secretbox" // forked to github.com/tendermint/crypto
|
||||
"golang.org/x/crypto/nacl/secretbox"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
|
@@ -6,7 +6,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"golang.org/x/crypto/bcrypt" // forked to github.com/tendermint/crypto
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
)
|
||||
@@ -30,9 +30,7 @@ func TestSimpleWithKDF(t *testing.T) {
|
||||
|
||||
plaintext := []byte("sometext")
|
||||
secretPass := []byte("somesecret")
|
||||
salt := []byte("somesaltsomesalt") // len 16
|
||||
// NOTE: we use a fork of x/crypto so we can inject our own randomness for salt
|
||||
secret, err := bcrypt.GenerateFromPassword(salt, secretPass, 12)
|
||||
secret, err := bcrypt.GenerateFromPassword(secretPass, 12)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
@@ -19,7 +19,10 @@ module.exports = {
|
||||
indexName: 'tendermint',
|
||||
debug: false
|
||||
},
|
||||
nav: [{ text: "Back to Tendermint", link: "https://tendermint.com" }],
|
||||
nav: [
|
||||
{ text: "Back to Tendermint", link: "https://tendermint.com" },
|
||||
{ text: "RPC", link: "../rpc/" }
|
||||
],
|
||||
sidebar: [
|
||||
{
|
||||
title: "Introduction",
|
||||
@@ -31,6 +34,20 @@ module.exports = {
|
||||
"/introduction/what-is-tendermint"
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Apps",
|
||||
collapsable: false,
|
||||
children: [
|
||||
"/app-dev/getting-started",
|
||||
"/app-dev/abci-cli",
|
||||
"/app-dev/app-architecture",
|
||||
"/app-dev/app-development",
|
||||
"/app-dev/subscribing-to-events-via-websocket",
|
||||
"/app-dev/indexing-transactions",
|
||||
"/app-dev/abci-spec",
|
||||
"/app-dev/ecosystem"
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Tendermint Core",
|
||||
collapsable: false,
|
||||
@@ -49,15 +66,6 @@ module.exports = {
|
||||
"/tendermint-core/validators"
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Tools",
|
||||
collapsable: false,
|
||||
children: [
|
||||
"/tools/",
|
||||
"/tools/benchmarking",
|
||||
"/tools/monitoring"
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Networks",
|
||||
collapsable: false,
|
||||
@@ -68,18 +76,13 @@ module.exports = {
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Apps",
|
||||
title: "Tools",
|
||||
collapsable: false,
|
||||
children: [
|
||||
"/app-dev/getting-started",
|
||||
"/app-dev/abci-cli",
|
||||
"/app-dev/app-architecture",
|
||||
"/app-dev/app-development",
|
||||
"/app-dev/subscribing-to-events-via-websocket",
|
||||
"/app-dev/indexing-transactions",
|
||||
"/app-dev/abci-spec",
|
||||
"/app-dev/ecosystem"
|
||||
]
|
||||
children: [
|
||||
"/tools/",
|
||||
"/tools/benchmarking",
|
||||
"/tools/monitoring"
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Tendermint Spec",
|
||||
|
4670
docs/package-lock.json
generated
4670
docs/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,40 +0,0 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"prettier": "^1.13.7",
|
||||
"remark-cli": "^5.0.0",
|
||||
"remark-lint-no-dead-urls": "^0.3.0",
|
||||
"remark-lint-write-good": "^1.0.3",
|
||||
"textlint": "^10.2.1",
|
||||
"textlint-rule-stop-words": "^1.0.3"
|
||||
},
|
||||
"name": "tendermint",
|
||||
"description": "Tendermint Core Documentation",
|
||||
"version": "0.0.1",
|
||||
"main": "README.md",
|
||||
"devDependencies": {},
|
||||
"scripts": {
|
||||
"lint:json": "prettier \"**/*.json\" --write",
|
||||
"lint:md": "prettier \"**/*.md\" --write && remark . && textlint \"md/**\"",
|
||||
"lint": "yarn lint:json && yarn lint:md"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/tendermint/tendermint.git"
|
||||
},
|
||||
"keywords": [
|
||||
"tendermint",
|
||||
"blockchain"
|
||||
],
|
||||
"author": "Tendermint",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/tendermint/tendermint/issues"
|
||||
},
|
||||
"homepage": "https://tendermint.com/docs/",
|
||||
"remarkConfig": {
|
||||
"plugins": [
|
||||
"remark-lint-no-dead-urls",
|
||||
"remark-lint-write-good"
|
||||
]
|
||||
}
|
||||
}
|
@@ -407,21 +407,22 @@ If `storeBlockHeight == stateBlockHeight && appBlockHeight < storeBlockHeight`,
|
||||
replay all blocks in full from `appBlockHeight` to `storeBlockHeight`.
|
||||
This happens if we completed processing the block, but the app forgot its height.
|
||||
|
||||
If `storeBlockHeight == stateBlockHeight && appBlockHeight == storeBlockHeight`, we're done
|
||||
If `storeBlockHeight == stateBlockHeight && appBlockHeight == storeBlockHeight`, we're done.
|
||||
This happens if we crashed at an opportune spot.
|
||||
|
||||
If `storeBlockHeight == stateBlockHeight+1`
|
||||
This happens if we started processing the block but didn't finish.
|
||||
|
||||
If `appBlockHeight < stateBlockHeight`
|
||||
replay all blocks in full from `appBlockHeight` to `storeBlockHeight-1`,
|
||||
and replay the block at `storeBlockHeight` using the WAL.
|
||||
This happens if the app forgot the last block it committed.
|
||||
If `appBlockHeight < stateBlockHeight`
|
||||
replay all blocks in full from `appBlockHeight` to `storeBlockHeight-1`,
|
||||
and replay the block at `storeBlockHeight` using the WAL.
|
||||
This happens if the app forgot the last block it committed.
|
||||
|
||||
If `appBlockHeight == stateBlockHeight`,
|
||||
replay the last block (storeBlockHeight) in full.
|
||||
This happens if we crashed before the app finished Commit
|
||||
If `appBlockHeight == stateBlockHeight`,
|
||||
replay the last block (storeBlockHeight) in full.
|
||||
This happens if we crashed before the app finished Commit
|
||||
|
||||
If `appBlockHeight == storeBlockHeight`
|
||||
update the state using the saved ABCI responses but dont run the block against the real app.
|
||||
This happens if we crashed after the app finished Commit but before Tendermint saved the state.
|
||||
|
||||
If appBlockHeight == storeBlockHeight {
|
||||
update the state using the saved ABCI responses but dont run the block against the real app.
|
||||
This happens if we crashed after the app finished Commit but before Tendermint saved the state.
|
||||
|
@@ -230,7 +230,7 @@ The block version must match the state version.
|
||||
len(block.ChainID) < 50
|
||||
```
|
||||
|
||||
ChainID must be maximum 50 UTF-8 symbols.
|
||||
ChainID must be less than 50 bytes.
|
||||
|
||||
### Height
|
||||
|
||||
|
205
docs/spec/consensus/signing.md
Normal file
205
docs/spec/consensus/signing.md
Normal file
@@ -0,0 +1,205 @@
|
||||
# Validator Signing
|
||||
|
||||
Here we specify the rules for validating a proposal and vote before signing.
|
||||
First we include some general notes on validating data structures common to both types.
|
||||
We then provide specific validation rules for each. Finally, we include validation rules to prevent double-sigining.
|
||||
|
||||
## SignedMsgType
|
||||
|
||||
The `SignedMsgType` is a single byte that refers to the type of the message
|
||||
being signed. It is defined in Go as follows:
|
||||
|
||||
```
|
||||
// SignedMsgType is a type of signed message in the consensus.
|
||||
type SignedMsgType byte
|
||||
|
||||
const (
|
||||
// Votes
|
||||
PrevoteType SignedMsgType = 0x01
|
||||
PrecommitType SignedMsgType = 0x02
|
||||
|
||||
// Proposals
|
||||
ProposalType SignedMsgType = 0x20
|
||||
)
|
||||
```
|
||||
|
||||
All signed messages must correspond to one of these types.
|
||||
|
||||
## Timestamp
|
||||
|
||||
Timestamp validation is subtle and there are currently no bounds placed on the
|
||||
timestamp included in a proposal or vote. It is expected that validators will honestly
|
||||
report their local clock time. The median of all timestamps
|
||||
included in a commit is used as the timestamp for the next block height.
|
||||
|
||||
Timestamps are expected to be strictly monotonic for a given validator, though
|
||||
this is not currently enforced.
|
||||
|
||||
## ChainID
|
||||
|
||||
ChainID is an unstructured string with a max length of 50-bytes.
|
||||
In the future, the ChainID may become structured, and may take on longer lengths.
|
||||
For now, it is recommended that signers be configured for a particular ChainID,
|
||||
and to only sign votes and proposals corresponding to that ChainID.
|
||||
|
||||
## BlockID
|
||||
|
||||
BlockID is the structure used to represent the block:
|
||||
|
||||
```
|
||||
type BlockID struct {
|
||||
Hash []byte
|
||||
PartsHeader PartSetHeader
|
||||
}
|
||||
|
||||
type PartSetHeader struct {
|
||||
Hash []byte
|
||||
Total int
|
||||
}
|
||||
```
|
||||
|
||||
To be included in a valid vote or proposal, BlockID must either represent a `nil` block, or a complete one.
|
||||
We introduce two methods, `BlockID.IsNil()` and `BlockID.IsComplete()` for these cases, respectively.
|
||||
|
||||
`BlockID.IsNil()` returns true for BlockID `b` if each of the following
|
||||
are true:
|
||||
|
||||
```
|
||||
b.Hash == nil
|
||||
b.PartsHeader.Total == 0
|
||||
b.PartsHeader.Hash == nil
|
||||
```
|
||||
|
||||
`BlockID.IsComplete()` returns true for BlockID `b` if each of the following
|
||||
are true:
|
||||
|
||||
```
|
||||
len(b.Hash) == 32
|
||||
b.PartsHeader.Total > 0
|
||||
len(b.PartsHeader.Hash) == 32
|
||||
```
|
||||
|
||||
## Proposals
|
||||
|
||||
The structure of a propsal for signing looks like:
|
||||
|
||||
```
|
||||
type CanonicalProposal struct {
|
||||
Type SignedMsgType // type alias for byte
|
||||
Height int64 `binary:"fixed64"`
|
||||
Round int64 `binary:"fixed64"`
|
||||
POLRound int64 `binary:"fixed64"`
|
||||
BlockID BlockID
|
||||
Timestamp time.Time
|
||||
ChainID string
|
||||
}
|
||||
```
|
||||
|
||||
A proposal is valid if each of the following lines evaluates to true for proposal `p`:
|
||||
|
||||
```
|
||||
p.Type == 0x20
|
||||
p.Height > 0
|
||||
p.Round >= 0
|
||||
p.POLRound >= -1
|
||||
p.BlockID.IsComplete()
|
||||
```
|
||||
|
||||
In other words, a proposal is valid for signing if it contains the type of a Proposal
|
||||
(0x20), has a positive, non-zero height, a
|
||||
non-negative round, a POLRound not less than -1, and a complete BlockID.
|
||||
|
||||
## Votes
|
||||
|
||||
The structure of a vote for signing looks like:
|
||||
|
||||
```
|
||||
type CanonicalVote struct {
|
||||
Type SignedMsgType // type alias for byte
|
||||
Height int64 `binary:"fixed64"`
|
||||
Round int64 `binary:"fixed64"`
|
||||
Timestamp time.Time
|
||||
BlockID BlockID
|
||||
ChainID string
|
||||
}
|
||||
```
|
||||
|
||||
A vote is valid if each of the following lines evaluates to true for vote `v`:
|
||||
|
||||
```
|
||||
v.Type == 0x1 || v.Type == 0x2
|
||||
v.Height > 0
|
||||
v.Round >= 0
|
||||
v.BlockID.IsNil() || v.BlockID.IsValid()
|
||||
```
|
||||
|
||||
In other words, a vote is valid for signing if it contains the type of a Prevote
|
||||
or Precommit (0x1 or 0x2, respectively), has a positive, non-zero height, a
|
||||
non-negative round, and an empty or valid BlockID.
|
||||
|
||||
## Invalid Votes and Proposals
|
||||
|
||||
Votes and proposals which do not satisfy the above rules are considered invalid.
|
||||
Peers gossipping invalid votes and proposals may be disconnected from other peers on the network.
|
||||
Note, however, that there is not currently any explicit mechanism to punish validators signing votes or proposals that fail
|
||||
these basic validation rules.
|
||||
|
||||
## Double Signing
|
||||
|
||||
Signers must be careful not to sign conflicting messages, also known as "double signing" or "equivocating".
|
||||
Tendermint has mechanisms to publish evidence of validators that signed conflicting votes, so they can be punished
|
||||
by the application. Note Tendermint does not currently handle evidence of conflciting proposals, though it may in the future.
|
||||
|
||||
### State
|
||||
|
||||
To prevent such double signing, signers must track the height, round, and type of the last message signed.
|
||||
Assume the signer keeps the following state, `s`:
|
||||
|
||||
```
|
||||
type LastSigned struct {
|
||||
Height int64
|
||||
Round int64
|
||||
Type SignedMsgType // byte
|
||||
}
|
||||
```
|
||||
|
||||
After signing a vote or proposal `m`, the signer sets:
|
||||
|
||||
```
|
||||
s.Height = m.Height
|
||||
s.Round = m.Round
|
||||
s.Type = m.Type
|
||||
```
|
||||
|
||||
### Proposals
|
||||
|
||||
A signer should only sign a proposal `p` if any of the following lines are true:
|
||||
|
||||
```
|
||||
p.Height > s.Height
|
||||
p.Height == s.Height && p.Round > s.Round
|
||||
```
|
||||
|
||||
In other words, a proposal should only be signed if it's at a higher height, or a higher round for the same height.
|
||||
Once a proposal or vote has been signed for a given height and round, a proposal should never be signed for the same height and round.
|
||||
|
||||
### Votes
|
||||
|
||||
A signer should only sign a vote `v` if any of the following lines are true:
|
||||
|
||||
```
|
||||
v.Height > s.Height
|
||||
v.Height == s.Height && v.Round > s.Round
|
||||
v.Height == s.Height && v.Round == s.Round && v.Step == 0x1 && s.Step == 0x20
|
||||
v.Height == s.Height && v.Round == s.Round && v.Step == 0x2 && s.Step != 0x2
|
||||
```
|
||||
|
||||
In other words, a vote should only be signed if it's:
|
||||
|
||||
- at a higher height
|
||||
- at a higher round for the same height
|
||||
- a prevote for the same height and round where we haven't signed a prevote or precommit (but have signed a proposal)
|
||||
- a precommit for the same height and round where we haven't signed a precommit (but have signed a proposal and/or a prevote)
|
||||
|
||||
This means that once a validator signs a prevote for a given height and round, the only other message it can sign for that height and round is a precommit.
|
||||
And once a validator signs a precommit for a given height and round, it must not sign any other message for that same height and round.
|
@@ -170,7 +170,7 @@ seed_mode = false
|
||||
private_peer_ids = ""
|
||||
|
||||
# Toggle to disable guard against peers connecting from the same ip.
|
||||
allow_duplicate_ip = true
|
||||
allow_duplicate_ip = false
|
||||
|
||||
# Peer connection configuration.
|
||||
handshake_timeout = "20s"
|
||||
|
@@ -113,7 +113,7 @@ blocks are produced regularly, even if there are no transactions. See
|
||||
_No Empty Blocks_, below, to modify this setting.
|
||||
|
||||
Tendermint supports in-process versions of the `counter`, `kvstore` and
|
||||
`nil` apps that ship as examples with `abci-cli`. It's easy to compile
|
||||
`noop` apps that ship as examples with `abci-cli`. It's easy to compile
|
||||
your own app in-process with Tendermint if it's written in Go. If your
|
||||
app is not written in Go, simply run it in another process, and use the
|
||||
`--proxy_app` flag to specify the address of the socket it is listening
|
||||
|
2611
docs/yarn.lock
2611
docs/yarn.lock
File diff suppressed because it is too large
Load Diff
@@ -676,7 +676,7 @@ func (cache *mapTxCache) Push(tx types.Tx) bool {
|
||||
// Use the tx hash in the cache
|
||||
txHash := sha256.Sum256(tx)
|
||||
if moved, exists := cache.map_[txHash]; exists {
|
||||
cache.list.MoveToFront(moved)
|
||||
cache.list.MoveToBack(moved)
|
||||
return false
|
||||
}
|
||||
|
||||
|
35
node/node.go
35
node/node.go
@@ -7,6 +7,7 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -86,8 +87,26 @@ func DefaultNewNode(config *cfg.Config, logger log.Logger) (*Node, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert old PrivValidator if it exists.
|
||||
oldPrivVal := config.OldPrivValidatorFile()
|
||||
newPrivValKey := config.PrivValidatorKeyFile()
|
||||
newPrivValState := config.PrivValidatorStateFile()
|
||||
if _, err := os.Stat(oldPrivVal); !os.IsNotExist(err) {
|
||||
oldPV, err := privval.LoadOldFilePV(oldPrivVal)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error reading OldPrivValidator from %v: %v\n", oldPrivVal, err)
|
||||
}
|
||||
logger.Info("Upgrading PrivValidator file",
|
||||
"old", oldPrivVal,
|
||||
"newKey", newPrivValKey,
|
||||
"newState", newPrivValState,
|
||||
)
|
||||
oldPV.Upgrade(newPrivValKey, newPrivValState)
|
||||
}
|
||||
|
||||
return NewNode(config,
|
||||
privval.LoadOrGenFilePV(config.PrivValidatorFile()),
|
||||
privval.LoadOrGenFilePV(newPrivValKey, newPrivValState),
|
||||
nodeKey,
|
||||
proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()),
|
||||
DefaultGenesisDocProviderFunc(config),
|
||||
@@ -240,16 +259,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()
|
||||
@@ -617,7 +639,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())
|
||||
|
@@ -10,7 +10,6 @@ import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
// forked to github.com/tendermint/crypto
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
"golang.org/x/crypto/curve25519"
|
||||
"golang.org/x/crypto/nacl/box"
|
||||
|
@@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
maxNodeInfoSize = 10240 // 10Kb
|
||||
maxNodeInfoSize = 10240 // 10KB
|
||||
maxNumChannels = 16 // plenty of room for upgrades, for now
|
||||
)
|
||||
|
||||
|
@@ -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)
|
||||
|
80
privval/old_priv_validator.go
Normal file
80
privval/old_priv_validator.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package privval
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
// OldFilePV is the old version of the FilePV, pre v0.28.0.
|
||||
type OldFilePV struct {
|
||||
Address types.Address `json:"address"`
|
||||
PubKey crypto.PubKey `json:"pub_key"`
|
||||
LastHeight int64 `json:"last_height"`
|
||||
LastRound int `json:"last_round"`
|
||||
LastStep int8 `json:"last_step"`
|
||||
LastSignature []byte `json:"last_signature,omitempty"`
|
||||
LastSignBytes cmn.HexBytes `json:"last_signbytes,omitempty"`
|
||||
PrivKey crypto.PrivKey `json:"priv_key"`
|
||||
|
||||
filePath string
|
||||
}
|
||||
|
||||
// LoadOldFilePV loads an OldFilePV from the filePath.
|
||||
func LoadOldFilePV(filePath string) (*OldFilePV, error) {
|
||||
pvJSONBytes, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pv := &OldFilePV{}
|
||||
err = cdc.UnmarshalJSON(pvJSONBytes, &pv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// overwrite pubkey and address for convenience
|
||||
pv.PubKey = pv.PrivKey.PubKey()
|
||||
pv.Address = pv.PubKey.Address()
|
||||
|
||||
pv.filePath = filePath
|
||||
return pv, nil
|
||||
}
|
||||
|
||||
// Upgrade convets the OldFilePV to the new FilePV, separating the immutable and mutable components,
|
||||
// and persisting them to the keyFilePath and stateFilePath, respectively.
|
||||
// It renames the original file by adding ".bak".
|
||||
func (oldFilePV *OldFilePV) Upgrade(keyFilePath, stateFilePath string) *FilePV {
|
||||
privKey := oldFilePV.PrivKey
|
||||
pvKey := FilePVKey{
|
||||
PrivKey: privKey,
|
||||
PubKey: privKey.PubKey(),
|
||||
Address: privKey.PubKey().Address(),
|
||||
filePath: keyFilePath,
|
||||
}
|
||||
|
||||
pvState := FilePVLastSignState{
|
||||
Height: oldFilePV.LastHeight,
|
||||
Round: oldFilePV.LastRound,
|
||||
Step: oldFilePV.LastStep,
|
||||
Signature: oldFilePV.LastSignature,
|
||||
SignBytes: oldFilePV.LastSignBytes,
|
||||
filePath: stateFilePath,
|
||||
}
|
||||
|
||||
// Save the new PV files
|
||||
pv := &FilePV{
|
||||
Key: pvKey,
|
||||
LastSignState: pvState,
|
||||
}
|
||||
pv.Save()
|
||||
|
||||
// Rename the old PV file
|
||||
err := os.Rename(oldFilePV.filePath, oldFilePV.filePath+".bak")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return pv
|
||||
}
|
77
privval/old_priv_validator_test.go
Normal file
77
privval/old_priv_validator_test.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package privval_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tendermint/privval"
|
||||
)
|
||||
|
||||
const oldPrivvalContent = `{
|
||||
"address": "1D8089FAFDFAE4A637F3D616E17B92905FA2D91D",
|
||||
"pub_key": {
|
||||
"type": "tendermint/PubKeyEd25519",
|
||||
"value": "r3Yg2AhDZ745CNTpavsGU+mRZ8WpRXqoJuyqjN8mJq0="
|
||||
},
|
||||
"last_height": "5",
|
||||
"last_round": "0",
|
||||
"last_step": 3,
|
||||
"last_signature": "CTr7b9ZQlrJJf+12rPl5t/YSCUc/KqV7jQogCfFJA24e7hof69X6OMT7eFLVQHyodPjD/QTA298XHV5ejxInDQ==",
|
||||
"last_signbytes": "750802110500000000000000220B08B398F3E00510F48DA6402A480A20FC258973076512999C3E6839A22E9FBDB1B77CF993E8A9955412A41A59D4CAD312240A20C971B286ACB8AAA6FCA0365EB0A660B189EDC08B46B5AF2995DEFA51A28D215B10013211746573742D636861696E2D533245415533",
|
||||
"priv_key": {
|
||||
"type": "tendermint/PrivKeyEd25519",
|
||||
"value": "7MwvTGEWWjsYwjn2IpRb+GYsWi9nnFsw8jPLLY1UtP6vdiDYCENnvjkI1Olq+wZT6ZFnxalFeqgm7KqM3yYmrQ=="
|
||||
}
|
||||
}`
|
||||
|
||||
func TestLoadAndUpgrade(t *testing.T) {
|
||||
|
||||
oldFilePath := initTmpOldFile(t)
|
||||
defer os.Remove(oldFilePath)
|
||||
newStateFile, err := ioutil.TempFile("", "priv_validator_state*.json")
|
||||
defer os.Remove(newStateFile.Name())
|
||||
require.NoError(t, err)
|
||||
newKeyFile, err := ioutil.TempFile("", "priv_validator_key*.json")
|
||||
defer os.Remove(newKeyFile.Name())
|
||||
require.NoError(t, err)
|
||||
|
||||
oldPV, err := privval.LoadOldFilePV(oldFilePath)
|
||||
assert.NoError(t, err)
|
||||
newPV := oldPV.Upgrade(newKeyFile.Name(), newStateFile.Name())
|
||||
|
||||
assertEqualPV(t, oldPV, newPV)
|
||||
assert.NoError(t, err)
|
||||
upgradedPV := privval.LoadFilePV(newKeyFile.Name(), newStateFile.Name())
|
||||
assertEqualPV(t, oldPV, upgradedPV)
|
||||
oldPV, err = privval.LoadOldFilePV(oldFilePath + ".bak")
|
||||
require.NoError(t, err)
|
||||
assertEqualPV(t, oldPV, upgradedPV)
|
||||
}
|
||||
|
||||
func assertEqualPV(t *testing.T, oldPV *privval.OldFilePV, newPV *privval.FilePV) {
|
||||
assert.Equal(t, oldPV.Address, newPV.Key.Address)
|
||||
assert.Equal(t, oldPV.Address, newPV.GetAddress())
|
||||
assert.Equal(t, oldPV.PubKey, newPV.Key.PubKey)
|
||||
assert.Equal(t, oldPV.PubKey, newPV.GetPubKey())
|
||||
assert.Equal(t, oldPV.PrivKey, newPV.Key.PrivKey)
|
||||
|
||||
assert.Equal(t, oldPV.LastHeight, newPV.LastSignState.Height)
|
||||
assert.Equal(t, oldPV.LastRound, newPV.LastSignState.Round)
|
||||
assert.Equal(t, oldPV.LastSignature, newPV.LastSignState.Signature)
|
||||
assert.Equal(t, oldPV.LastSignBytes, newPV.LastSignState.SignBytes)
|
||||
assert.Equal(t, oldPV.LastStep, newPV.LastSignState.Step)
|
||||
}
|
||||
|
||||
func initTmpOldFile(t *testing.T) string {
|
||||
tmpfile, err := ioutil.TempFile("", "priv_validator_*.json")
|
||||
require.NoError(t, err)
|
||||
t.Logf("created test file %s", tmpfile.Name())
|
||||
_, err = tmpfile.WriteString(oldPrivvalContent)
|
||||
require.NoError(t, err)
|
||||
|
||||
return tmpfile.Name()
|
||||
}
|
@@ -5,7 +5,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
@@ -35,100 +34,90 @@ func voteToStep(vote *types.Vote) int8 {
|
||||
}
|
||||
}
|
||||
|
||||
// FilePV implements PrivValidator using data persisted to disk
|
||||
// to prevent double signing.
|
||||
// NOTE: the directory containing the pv.filePath must already exist.
|
||||
// It includes the LastSignature and LastSignBytes so we don't lose the signature
|
||||
// if the process crashes after signing but before the resulting consensus message is processed.
|
||||
type FilePV struct {
|
||||
Address types.Address `json:"address"`
|
||||
PubKey crypto.PubKey `json:"pub_key"`
|
||||
LastHeight int64 `json:"last_height"`
|
||||
LastRound int `json:"last_round"`
|
||||
LastStep int8 `json:"last_step"`
|
||||
LastSignature []byte `json:"last_signature,omitempty"`
|
||||
LastSignBytes cmn.HexBytes `json:"last_signbytes,omitempty"`
|
||||
PrivKey crypto.PrivKey `json:"priv_key"`
|
||||
//-------------------------------------------------------------------------------
|
||||
|
||||
// FilePVKey stores the immutable part of PrivValidator.
|
||||
type FilePVKey struct {
|
||||
Address types.Address `json:"address"`
|
||||
PubKey crypto.PubKey `json:"pub_key"`
|
||||
PrivKey crypto.PrivKey `json:"priv_key"`
|
||||
|
||||
// For persistence.
|
||||
// Overloaded for testing.
|
||||
filePath string
|
||||
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 {
|
||||
return pv.PubKey
|
||||
}
|
||||
|
||||
// GenFilePV generates a new validator with randomly generated private key
|
||||
// and sets the filePath, but does not call Save().
|
||||
func GenFilePV(filePath string) *FilePV {
|
||||
privKey := ed25519.GenPrivKey()
|
||||
return &FilePV{
|
||||
Address: privKey.PubKey().Address(),
|
||||
PubKey: privKey.PubKey(),
|
||||
PrivKey: privKey,
|
||||
LastStep: stepNone,
|
||||
filePath: filePath,
|
||||
}
|
||||
}
|
||||
|
||||
// LoadFilePV loads a FilePV from the filePath. The FilePV handles double
|
||||
// signing prevention by persisting data to the filePath. If the filePath does
|
||||
// not exist, the FilePV must be created manually and saved.
|
||||
func LoadFilePV(filePath string) *FilePV {
|
||||
pvJSONBytes, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
cmn.Exit(err.Error())
|
||||
}
|
||||
pv := &FilePV{}
|
||||
err = cdc.UnmarshalJSON(pvJSONBytes, &pv)
|
||||
if err != nil {
|
||||
cmn.Exit(fmt.Sprintf("Error reading PrivValidator from %v: %v\n", filePath, err))
|
||||
}
|
||||
|
||||
// overwrite pubkey and address for convenience
|
||||
pv.PubKey = pv.PrivKey.PubKey()
|
||||
pv.Address = pv.PubKey.Address()
|
||||
|
||||
pv.filePath = filePath
|
||||
return pv
|
||||
}
|
||||
|
||||
// LoadOrGenFilePV loads a FilePV from the given filePath
|
||||
// or else generates a new one and saves it to the filePath.
|
||||
func LoadOrGenFilePV(filePath string) *FilePV {
|
||||
var pv *FilePV
|
||||
if cmn.FileExists(filePath) {
|
||||
pv = LoadFilePV(filePath)
|
||||
} else {
|
||||
pv = GenFilePV(filePath)
|
||||
pv.Save()
|
||||
}
|
||||
return pv
|
||||
}
|
||||
|
||||
// Save persists the FilePV to disk.
|
||||
func (pv *FilePV) Save() {
|
||||
pv.mtx.Lock()
|
||||
defer pv.mtx.Unlock()
|
||||
pv.save()
|
||||
}
|
||||
|
||||
func (pv *FilePV) save() {
|
||||
outFile := pv.filePath
|
||||
// Save persists the FilePVKey to its filePath.
|
||||
func (pvKey FilePVKey) Save() {
|
||||
outFile := pvKey.filePath
|
||||
if outFile == "" {
|
||||
panic("Cannot save PrivValidator: filePath not set")
|
||||
panic("Cannot save PrivValidator key: filePath not set")
|
||||
}
|
||||
jsonBytes, err := cdc.MarshalJSONIndent(pv, "", " ")
|
||||
|
||||
jsonBytes, err := cdc.MarshalJSONIndent(pvKey, "", " ")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = cmn.WriteFileAtomic(outFile, jsonBytes, 0600)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
|
||||
// FilePVLastSignState stores the mutable part of PrivValidator.
|
||||
type FilePVLastSignState struct {
|
||||
Height int64 `json:"height"`
|
||||
Round int `json:"round"`
|
||||
Step int8 `json:"step"`
|
||||
Signature []byte `json:"signature,omitempty"`
|
||||
SignBytes cmn.HexBytes `json:"signbytes,omitempty"`
|
||||
|
||||
filePath string
|
||||
}
|
||||
|
||||
// CheckHRS checks the given height, round, step (HRS) against that of the
|
||||
// FilePVLastSignState. It returns an error if the arguments constitute a regression,
|
||||
// or if they match but the SignBytes are empty.
|
||||
// The returned boolean indicates whether the last Signature should be reused -
|
||||
// it returns true if the HRS matches the arguments and the SignBytes are not empty (indicating
|
||||
// we have already signed for this HRS, and can reuse the existing signature).
|
||||
// It panics if the HRS matches the arguments, there's a SignBytes, but no Signature.
|
||||
func (lss *FilePVLastSignState) CheckHRS(height int64, round int, step int8) (bool, error) {
|
||||
|
||||
if lss.Height > height {
|
||||
return false, errors.New("Height regression")
|
||||
}
|
||||
|
||||
if lss.Height == height {
|
||||
if lss.Round > round {
|
||||
return false, errors.New("Round regression")
|
||||
}
|
||||
|
||||
if lss.Round == round {
|
||||
if lss.Step > step {
|
||||
return false, errors.New("Step regression")
|
||||
} else if lss.Step == step {
|
||||
if lss.SignBytes != nil {
|
||||
if lss.Signature == nil {
|
||||
panic("pv: Signature is nil but SignBytes is not!")
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
return false, errors.New("No SignBytes found")
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Save persists the FilePvLastSignState to its filePath.
|
||||
func (lss *FilePVLastSignState) Save() {
|
||||
outFile := lss.filePath
|
||||
if outFile == "" {
|
||||
panic("Cannot save FilePVLastSignState: filePath not set")
|
||||
}
|
||||
jsonBytes, err := cdc.MarshalJSONIndent(lss, "", " ")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -138,23 +127,115 @@ func (pv *FilePV) save() {
|
||||
}
|
||||
}
|
||||
|
||||
// Reset resets all fields in the FilePV.
|
||||
// NOTE: Unsafe!
|
||||
func (pv *FilePV) Reset() {
|
||||
var sig []byte
|
||||
pv.LastHeight = 0
|
||||
pv.LastRound = 0
|
||||
pv.LastStep = 0
|
||||
pv.LastSignature = sig
|
||||
pv.LastSignBytes = nil
|
||||
pv.Save()
|
||||
//-------------------------------------------------------------------------------
|
||||
|
||||
// FilePV implements PrivValidator using data persisted to disk
|
||||
// to prevent double signing.
|
||||
// NOTE: the directories containing pv.Key.filePath and pv.LastSignState.filePath must already exist.
|
||||
// It includes the LastSignature and LastSignBytes so we don't lose the signature
|
||||
// if the process crashes after signing but before the resulting consensus message is processed.
|
||||
type FilePV struct {
|
||||
Key FilePVKey
|
||||
LastSignState FilePVLastSignState
|
||||
}
|
||||
|
||||
// GenFilePV generates a new validator with randomly generated private key
|
||||
// and sets the filePaths, but does not call Save().
|
||||
func GenFilePV(keyFilePath, stateFilePath string) *FilePV {
|
||||
privKey := ed25519.GenPrivKey()
|
||||
|
||||
return &FilePV{
|
||||
Key: FilePVKey{
|
||||
Address: privKey.PubKey().Address(),
|
||||
PubKey: privKey.PubKey(),
|
||||
PrivKey: privKey,
|
||||
filePath: keyFilePath,
|
||||
},
|
||||
LastSignState: FilePVLastSignState{
|
||||
Step: stepNone,
|
||||
filePath: stateFilePath,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// LoadFilePV loads a FilePV from the filePaths. The FilePV handles double
|
||||
// signing prevention by persisting data to the stateFilePath. If either file path
|
||||
// does not exist, the program will exit.
|
||||
func LoadFilePV(keyFilePath, stateFilePath string) *FilePV {
|
||||
return loadFilePV(keyFilePath, stateFilePath, true)
|
||||
}
|
||||
|
||||
// LoadFilePVEmptyState loads a FilePV from the given keyFilePath, with an empty LastSignState.
|
||||
// If the keyFilePath does not exist, the program will exit.
|
||||
func LoadFilePVEmptyState(keyFilePath, stateFilePath string) *FilePV {
|
||||
return loadFilePV(keyFilePath, stateFilePath, false)
|
||||
}
|
||||
|
||||
// If loadState is true, we load from the stateFilePath. Otherwise, we use an empty LastSignState.
|
||||
func loadFilePV(keyFilePath, stateFilePath string, loadState bool) *FilePV {
|
||||
keyJSONBytes, err := ioutil.ReadFile(keyFilePath)
|
||||
if err != nil {
|
||||
cmn.Exit(err.Error())
|
||||
}
|
||||
pvKey := FilePVKey{}
|
||||
err = cdc.UnmarshalJSON(keyJSONBytes, &pvKey)
|
||||
if err != nil {
|
||||
cmn.Exit(fmt.Sprintf("Error reading PrivValidator key from %v: %v\n", keyFilePath, err))
|
||||
}
|
||||
|
||||
// overwrite pubkey and address for convenience
|
||||
pvKey.PubKey = pvKey.PrivKey.PubKey()
|
||||
pvKey.Address = pvKey.PubKey.Address()
|
||||
pvKey.filePath = keyFilePath
|
||||
|
||||
pvState := FilePVLastSignState{}
|
||||
if loadState {
|
||||
stateJSONBytes, err := ioutil.ReadFile(stateFilePath)
|
||||
if err != nil {
|
||||
cmn.Exit(err.Error())
|
||||
}
|
||||
err = cdc.UnmarshalJSON(stateJSONBytes, &pvState)
|
||||
if err != nil {
|
||||
cmn.Exit(fmt.Sprintf("Error reading PrivValidator state from %v: %v\n", stateFilePath, err))
|
||||
}
|
||||
}
|
||||
|
||||
pvState.filePath = stateFilePath
|
||||
|
||||
return &FilePV{
|
||||
Key: pvKey,
|
||||
LastSignState: pvState,
|
||||
}
|
||||
}
|
||||
|
||||
// LoadOrGenFilePV loads a FilePV from the given filePaths
|
||||
// or else generates a new one and saves it to the filePaths.
|
||||
func LoadOrGenFilePV(keyFilePath, stateFilePath string) *FilePV {
|
||||
var pv *FilePV
|
||||
if cmn.FileExists(keyFilePath) {
|
||||
pv = LoadFilePV(keyFilePath, stateFilePath)
|
||||
} else {
|
||||
pv = GenFilePV(keyFilePath, stateFilePath)
|
||||
pv.Save()
|
||||
}
|
||||
return pv
|
||||
}
|
||||
|
||||
// GetAddress returns the address of the validator.
|
||||
// Implements PrivValidator.
|
||||
func (pv *FilePV) GetAddress() types.Address {
|
||||
return pv.Key.Address
|
||||
}
|
||||
|
||||
// GetPubKey returns the public key of the validator.
|
||||
// Implements PrivValidator.
|
||||
func (pv *FilePV) GetPubKey() crypto.PubKey {
|
||||
return pv.Key.PubKey
|
||||
}
|
||||
|
||||
// SignVote signs a canonical representation of the vote, along with the
|
||||
// chainID. Implements PrivValidator.
|
||||
func (pv *FilePV) SignVote(chainID string, vote *types.Vote) error {
|
||||
pv.mtx.Lock()
|
||||
defer pv.mtx.Unlock()
|
||||
if err := pv.signVote(chainID, vote); err != nil {
|
||||
return fmt.Errorf("Error signing vote: %v", err)
|
||||
}
|
||||
@@ -164,65 +245,63 @@ func (pv *FilePV) SignVote(chainID string, vote *types.Vote) error {
|
||||
// SignProposal signs a canonical representation of the proposal, along with
|
||||
// the chainID. Implements PrivValidator.
|
||||
func (pv *FilePV) SignProposal(chainID string, proposal *types.Proposal) error {
|
||||
pv.mtx.Lock()
|
||||
defer pv.mtx.Unlock()
|
||||
if err := pv.signProposal(chainID, proposal); err != nil {
|
||||
return fmt.Errorf("Error signing proposal: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// returns error if HRS regression or no LastSignBytes. returns true if HRS is unchanged
|
||||
func (pv *FilePV) checkHRS(height int64, round int, step int8) (bool, error) {
|
||||
if pv.LastHeight > height {
|
||||
return false, errors.New("Height regression")
|
||||
}
|
||||
|
||||
if pv.LastHeight == height {
|
||||
if pv.LastRound > round {
|
||||
return false, errors.New("Round regression")
|
||||
}
|
||||
|
||||
if pv.LastRound == round {
|
||||
if pv.LastStep > step {
|
||||
return false, errors.New("Step regression")
|
||||
} else if pv.LastStep == step {
|
||||
if pv.LastSignBytes != nil {
|
||||
if pv.LastSignature == nil {
|
||||
panic("pv: LastSignature is nil but LastSignBytes is not!")
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
return false, errors.New("No LastSignature found")
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
// Save persists the FilePV to disk.
|
||||
func (pv *FilePV) Save() {
|
||||
pv.Key.Save()
|
||||
pv.LastSignState.Save()
|
||||
}
|
||||
|
||||
// Reset resets all fields in the FilePV.
|
||||
// NOTE: Unsafe!
|
||||
func (pv *FilePV) Reset() {
|
||||
var sig []byte
|
||||
pv.LastSignState.Height = 0
|
||||
pv.LastSignState.Round = 0
|
||||
pv.LastSignState.Step = 0
|
||||
pv.LastSignState.Signature = sig
|
||||
pv.LastSignState.SignBytes = nil
|
||||
pv.Save()
|
||||
}
|
||||
|
||||
// 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.LastSignState.Height, pv.LastSignState.Round, pv.LastSignState.Step)
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
|
||||
// signVote checks if the vote is good to sign and sets the vote signature.
|
||||
// It may need to set the timestamp as well if the vote is otherwise the same as
|
||||
// a previously signed vote (ie. we crashed after signing but before the vote hit the WAL).
|
||||
func (pv *FilePV) signVote(chainID string, vote *types.Vote) error {
|
||||
height, round, step := vote.Height, vote.Round, voteToStep(vote)
|
||||
signBytes := vote.SignBytes(chainID)
|
||||
|
||||
sameHRS, err := pv.checkHRS(height, round, step)
|
||||
lss := pv.LastSignState
|
||||
|
||||
sameHRS, err := lss.CheckHRS(height, round, step)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
signBytes := vote.SignBytes(chainID)
|
||||
|
||||
// We might crash before writing to the wal,
|
||||
// causing us to try to re-sign for the same HRS.
|
||||
// If signbytes are the same, use the last signature.
|
||||
// If they only differ by timestamp, use last timestamp and signature
|
||||
// Otherwise, return error
|
||||
if sameHRS {
|
||||
if bytes.Equal(signBytes, pv.LastSignBytes) {
|
||||
vote.Signature = pv.LastSignature
|
||||
} else if timestamp, ok := checkVotesOnlyDifferByTimestamp(pv.LastSignBytes, signBytes); ok {
|
||||
if bytes.Equal(signBytes, lss.SignBytes) {
|
||||
vote.Signature = lss.Signature
|
||||
} else if timestamp, ok := checkVotesOnlyDifferByTimestamp(lss.SignBytes, signBytes); ok {
|
||||
vote.Timestamp = timestamp
|
||||
vote.Signature = pv.LastSignature
|
||||
vote.Signature = lss.Signature
|
||||
} else {
|
||||
err = fmt.Errorf("Conflicting data")
|
||||
}
|
||||
@@ -230,7 +309,7 @@ func (pv *FilePV) signVote(chainID string, vote *types.Vote) error {
|
||||
}
|
||||
|
||||
// It passed the checks. Sign the vote
|
||||
sig, err := pv.PrivKey.Sign(signBytes)
|
||||
sig, err := pv.Key.PrivKey.Sign(signBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -244,24 +323,27 @@ func (pv *FilePV) signVote(chainID string, vote *types.Vote) error {
|
||||
// a previously signed proposal ie. we crashed after signing but before the proposal hit the WAL).
|
||||
func (pv *FilePV) signProposal(chainID string, proposal *types.Proposal) error {
|
||||
height, round, step := proposal.Height, proposal.Round, stepPropose
|
||||
signBytes := proposal.SignBytes(chainID)
|
||||
|
||||
sameHRS, err := pv.checkHRS(height, round, step)
|
||||
lss := pv.LastSignState
|
||||
|
||||
sameHRS, err := lss.CheckHRS(height, round, step)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
signBytes := proposal.SignBytes(chainID)
|
||||
|
||||
// We might crash before writing to the wal,
|
||||
// causing us to try to re-sign for the same HRS.
|
||||
// If signbytes are the same, use the last signature.
|
||||
// If they only differ by timestamp, use last timestamp and signature
|
||||
// Otherwise, return error
|
||||
if sameHRS {
|
||||
if bytes.Equal(signBytes, pv.LastSignBytes) {
|
||||
proposal.Signature = pv.LastSignature
|
||||
} else if timestamp, ok := checkProposalsOnlyDifferByTimestamp(pv.LastSignBytes, signBytes); ok {
|
||||
if bytes.Equal(signBytes, lss.SignBytes) {
|
||||
proposal.Signature = lss.Signature
|
||||
} else if timestamp, ok := checkProposalsOnlyDifferByTimestamp(lss.SignBytes, signBytes); ok {
|
||||
proposal.Timestamp = timestamp
|
||||
proposal.Signature = pv.LastSignature
|
||||
proposal.Signature = lss.Signature
|
||||
} else {
|
||||
err = fmt.Errorf("Conflicting data")
|
||||
}
|
||||
@@ -269,7 +351,7 @@ func (pv *FilePV) signProposal(chainID string, proposal *types.Proposal) error {
|
||||
}
|
||||
|
||||
// It passed the checks. Sign the proposal
|
||||
sig, err := pv.PrivKey.Sign(signBytes)
|
||||
sig, err := pv.Key.PrivKey.Sign(signBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -282,20 +364,15 @@ func (pv *FilePV) signProposal(chainID string, proposal *types.Proposal) error {
|
||||
func (pv *FilePV) saveSigned(height int64, round int, step int8,
|
||||
signBytes []byte, sig []byte) {
|
||||
|
||||
pv.LastHeight = height
|
||||
pv.LastRound = round
|
||||
pv.LastStep = step
|
||||
pv.LastSignature = sig
|
||||
pv.LastSignBytes = signBytes
|
||||
pv.save()
|
||||
pv.LastSignState.Height = height
|
||||
pv.LastSignState.Round = round
|
||||
pv.LastSignState.Step = step
|
||||
pv.LastSignState.Signature = sig
|
||||
pv.LastSignState.SignBytes = signBytes
|
||||
pv.LastSignState.Save()
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
//-----------------------------------------------------------------------------------------
|
||||
|
||||
// returns the timestamp from the lastSignBytes.
|
||||
// returns true if the only difference in the votes is their timestamp.
|
||||
|
@@ -18,36 +18,100 @@ import (
|
||||
func TestGenLoadValidator(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
tempFile, err := ioutil.TempFile("", "priv_validator_")
|
||||
tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_")
|
||||
require.Nil(t, err)
|
||||
privVal := GenFilePV(tempFile.Name())
|
||||
tempStateFile, err := ioutil.TempFile("", "priv_validator_state_")
|
||||
require.Nil(t, err)
|
||||
|
||||
privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name())
|
||||
|
||||
height := int64(100)
|
||||
privVal.LastHeight = height
|
||||
privVal.LastSignState.Height = height
|
||||
privVal.Save()
|
||||
addr := privVal.GetAddress()
|
||||
|
||||
privVal = LoadFilePV(tempFile.Name())
|
||||
privVal = LoadFilePV(tempKeyFile.Name(), tempStateFile.Name())
|
||||
assert.Equal(addr, privVal.GetAddress(), "expected privval addr to be the same")
|
||||
assert.Equal(height, privVal.LastHeight, "expected privval.LastHeight to have been saved")
|
||||
assert.Equal(height, privVal.LastSignState.Height, "expected privval.LastHeight to have been saved")
|
||||
}
|
||||
|
||||
func TestResetValidator(t *testing.T) {
|
||||
tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_")
|
||||
require.Nil(t, err)
|
||||
tempStateFile, err := ioutil.TempFile("", "priv_validator_state_")
|
||||
require.Nil(t, err)
|
||||
|
||||
privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name())
|
||||
emptyState := FilePVLastSignState{filePath: tempStateFile.Name()}
|
||||
|
||||
// new priv val has empty state
|
||||
assert.Equal(t, privVal.LastSignState, emptyState)
|
||||
|
||||
// test vote
|
||||
height, round := int64(10), 1
|
||||
voteType := byte(types.PrevoteType)
|
||||
blockID := types.BlockID{[]byte{1, 2, 3}, types.PartSetHeader{}}
|
||||
vote := newVote(privVal.Key.Address, 0, height, round, voteType, blockID)
|
||||
err = privVal.SignVote("mychainid", vote)
|
||||
assert.NoError(t, err, "expected no error signing vote")
|
||||
|
||||
// priv val after signing is not same as empty
|
||||
assert.NotEqual(t, privVal.LastSignState, emptyState)
|
||||
|
||||
// priv val after reset is same as empty
|
||||
privVal.Reset()
|
||||
assert.Equal(t, privVal.LastSignState, emptyState)
|
||||
}
|
||||
|
||||
func TestLoadOrGenValidator(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
tempFile, err := ioutil.TempFile("", "priv_validator_")
|
||||
tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_")
|
||||
require.Nil(t, err)
|
||||
tempFilePath := tempFile.Name()
|
||||
if err := os.Remove(tempFilePath); err != nil {
|
||||
tempStateFile, err := ioutil.TempFile("", "priv_validator_state_")
|
||||
require.Nil(t, err)
|
||||
|
||||
tempKeyFilePath := tempKeyFile.Name()
|
||||
if err := os.Remove(tempKeyFilePath); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
privVal := LoadOrGenFilePV(tempFilePath)
|
||||
tempStateFilePath := tempStateFile.Name()
|
||||
if err := os.Remove(tempStateFilePath); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
privVal := LoadOrGenFilePV(tempKeyFilePath, tempStateFilePath)
|
||||
addr := privVal.GetAddress()
|
||||
privVal = LoadOrGenFilePV(tempFilePath)
|
||||
privVal = LoadOrGenFilePV(tempKeyFilePath, tempStateFilePath)
|
||||
assert.Equal(addr, privVal.GetAddress(), "expected privval addr to be the same")
|
||||
}
|
||||
|
||||
func TestUnmarshalValidator(t *testing.T) {
|
||||
func TestUnmarshalValidatorState(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
// create some fixed values
|
||||
serialized := `{
|
||||
"height": "1",
|
||||
"round": "1",
|
||||
"step": 1
|
||||
}`
|
||||
|
||||
val := FilePVLastSignState{}
|
||||
err := cdc.UnmarshalJSON([]byte(serialized), &val)
|
||||
require.Nil(err, "%+v", err)
|
||||
|
||||
// make sure the values match
|
||||
assert.EqualValues(val.Height, 1)
|
||||
assert.EqualValues(val.Round, 1)
|
||||
assert.EqualValues(val.Step, 1)
|
||||
|
||||
// export it and make sure it is the same
|
||||
out, err := cdc.MarshalJSON(val)
|
||||
require.Nil(err, "%+v", err)
|
||||
assert.JSONEq(serialized, string(out))
|
||||
}
|
||||
|
||||
func TestUnmarshalValidatorKey(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
// create some fixed values
|
||||
@@ -67,22 +131,19 @@ func TestUnmarshalValidator(t *testing.T) {
|
||||
"type": "tendermint/PubKeyEd25519",
|
||||
"value": "%s"
|
||||
},
|
||||
"last_height": "0",
|
||||
"last_round": "0",
|
||||
"last_step": 0,
|
||||
"priv_key": {
|
||||
"type": "tendermint/PrivKeyEd25519",
|
||||
"value": "%s"
|
||||
}
|
||||
}`, addr, pubB64, privB64)
|
||||
|
||||
val := FilePV{}
|
||||
val := FilePVKey{}
|
||||
err := cdc.UnmarshalJSON([]byte(serialized), &val)
|
||||
require.Nil(err, "%+v", err)
|
||||
|
||||
// make sure the values match
|
||||
assert.EqualValues(addr, val.GetAddress())
|
||||
assert.EqualValues(pubKey, val.GetPubKey())
|
||||
assert.EqualValues(addr, val.Address)
|
||||
assert.EqualValues(pubKey, val.PubKey)
|
||||
assert.EqualValues(privKey, val.PrivKey)
|
||||
|
||||
// export it and make sure it is the same
|
||||
@@ -94,9 +155,12 @@ func TestUnmarshalValidator(t *testing.T) {
|
||||
func TestSignVote(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
tempFile, err := ioutil.TempFile("", "priv_validator_")
|
||||
tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_")
|
||||
require.Nil(t, err)
|
||||
privVal := GenFilePV(tempFile.Name())
|
||||
tempStateFile, err := ioutil.TempFile("", "priv_validator_state_")
|
||||
require.Nil(t, err)
|
||||
|
||||
privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name())
|
||||
|
||||
block1 := types.BlockID{[]byte{1, 2, 3}, types.PartSetHeader{}}
|
||||
block2 := types.BlockID{[]byte{3, 2, 1}, types.PartSetHeader{}}
|
||||
@@ -104,7 +168,7 @@ func TestSignVote(t *testing.T) {
|
||||
voteType := byte(types.PrevoteType)
|
||||
|
||||
// sign a vote for first time
|
||||
vote := newVote(privVal.Address, 0, height, round, voteType, block1)
|
||||
vote := newVote(privVal.Key.Address, 0, height, round, voteType, block1)
|
||||
err = privVal.SignVote("mychainid", vote)
|
||||
assert.NoError(err, "expected no error signing vote")
|
||||
|
||||
@@ -114,10 +178,10 @@ func TestSignVote(t *testing.T) {
|
||||
|
||||
// now try some bad votes
|
||||
cases := []*types.Vote{
|
||||
newVote(privVal.Address, 0, height, round-1, voteType, block1), // round regression
|
||||
newVote(privVal.Address, 0, height-1, round, voteType, block1), // height regression
|
||||
newVote(privVal.Address, 0, height-2, round+4, voteType, block1), // height regression and different round
|
||||
newVote(privVal.Address, 0, height, round, voteType, block2), // different block
|
||||
newVote(privVal.Key.Address, 0, height, round-1, voteType, block1), // round regression
|
||||
newVote(privVal.Key.Address, 0, height-1, round, voteType, block1), // height regression
|
||||
newVote(privVal.Key.Address, 0, height-2, round+4, voteType, block1), // height regression and different round
|
||||
newVote(privVal.Key.Address, 0, height, round, voteType, block2), // different block
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
@@ -136,9 +200,12 @@ func TestSignVote(t *testing.T) {
|
||||
func TestSignProposal(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
tempFile, err := ioutil.TempFile("", "priv_validator_")
|
||||
tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_")
|
||||
require.Nil(t, err)
|
||||
privVal := GenFilePV(tempFile.Name())
|
||||
tempStateFile, err := ioutil.TempFile("", "priv_validator_state_")
|
||||
require.Nil(t, err)
|
||||
|
||||
privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name())
|
||||
|
||||
block1 := types.BlockID{[]byte{1, 2, 3}, types.PartSetHeader{5, []byte{1, 2, 3}}}
|
||||
block2 := types.BlockID{[]byte{3, 2, 1}, types.PartSetHeader{10, []byte{3, 2, 1}}}
|
||||
@@ -175,9 +242,12 @@ func TestSignProposal(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDifferByTimestamp(t *testing.T) {
|
||||
tempFile, err := ioutil.TempFile("", "priv_validator_")
|
||||
tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_")
|
||||
require.Nil(t, err)
|
||||
privVal := GenFilePV(tempFile.Name())
|
||||
tempStateFile, err := ioutil.TempFile("", "priv_validator_state_")
|
||||
require.Nil(t, err)
|
||||
|
||||
privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name())
|
||||
|
||||
block1 := types.BlockID{[]byte{1, 2, 3}, types.PartSetHeader{5, []byte{1, 2, 3}}}
|
||||
height, round := int64(10), 1
|
||||
@@ -208,7 +278,7 @@ func TestDifferByTimestamp(t *testing.T) {
|
||||
{
|
||||
voteType := byte(types.PrevoteType)
|
||||
blockID := types.BlockID{[]byte{1, 2, 3}, types.PartSetHeader{}}
|
||||
vote := newVote(privVal.Address, 0, height, round, voteType, blockID)
|
||||
vote := newVote(privVal.Key.Address, 0, height, round, voteType, blockID)
|
||||
err := privVal.SignVote("mychainid", vote)
|
||||
assert.NoError(t, err, "expected no error signing vote")
|
||||
|
||||
|
@@ -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
|
||||
mtx 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()
|
||||
sc.mtx.Lock()
|
||||
defer sc.mtx.Unlock()
|
||||
|
||||
err := writeMsg(sc.conn, &PubKeyMsg{})
|
||||
err := writeMsg(sc.conn, &PubKeyRequest{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -65,14 +59,22 @@ 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.
|
||||
func (sc *RemoteSignerClient) SignVote(chainID string, vote *types.Vote) error {
|
||||
sc.lock.Lock()
|
||||
defer sc.lock.Unlock()
|
||||
sc.mtx.Lock()
|
||||
defer sc.mtx.Unlock()
|
||||
|
||||
err := writeMsg(sc.conn, &SignVoteRequest{Vote: vote})
|
||||
if err != nil {
|
||||
@@ -101,8 +103,8 @@ func (sc *RemoteSignerClient) SignProposal(
|
||||
chainID string,
|
||||
proposal *types.Proposal,
|
||||
) error {
|
||||
sc.lock.Lock()
|
||||
defer sc.lock.Unlock()
|
||||
sc.mtx.Lock()
|
||||
defer sc.mtx.Unlock()
|
||||
|
||||
err := writeMsg(sc.conn, &SignProposalRequest{Proposal: proposal})
|
||||
if err != nil {
|
||||
@@ -127,8 +129,8 @@ func (sc *RemoteSignerClient) SignProposal(
|
||||
|
||||
// Ping is used to check connection health.
|
||||
func (sc *RemoteSignerClient) Ping() error {
|
||||
sc.lock.Lock()
|
||||
defer sc.lock.Unlock()
|
||||
sc.mtx.Lock()
|
||||
defer sc.mtx.Unlock()
|
||||
|
||||
err := writeMsg(sc.conn, &PingRequest{})
|
||||
if err != nil {
|
||||
@@ -152,7 +154,8 @@ 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)
|
||||
@@ -161,9 +164,13 @@ func RegisterRemoteSignerMsg(cdc *amino.Codec) {
|
||||
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.
|
||||
@@ -227,10 +234,10 @@ func handleRequest(req RemoteSignerMsg, chainID string, privVal types.PrivValida
|
||||
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 {
|
||||
|
@@ -107,8 +107,10 @@ 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)
|
||||
|
@@ -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,9 @@ func TestSocketPVPubKey(t *testing.T) {
|
||||
clientKey, err := sc.getPubKey()
|
||||
require.NoError(t, err)
|
||||
|
||||
privKey := rs.privVal.GetPubKey()
|
||||
privvalPubKey := rs.privVal.GetPubKey()
|
||||
|
||||
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) {
|
||||
@@ -153,9 +145,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 {
|
||||
@@ -174,9 +166,6 @@ func TestSocketPVDeadline(t *testing.T) {
|
||||
}
|
||||
|
||||
<-listenc
|
||||
|
||||
_, err := sc.getPubKey()
|
||||
assert.Equal(t, err.(cmn.Error).Data(), ErrConnTimeout)
|
||||
}
|
||||
|
||||
func TestRemoteSignerRetry(t *testing.T) {
|
||||
@@ -310,14 +299,15 @@ 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
|
||||
|
||||
// Proposal:
|
||||
|
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
abcicli "github.com/tendermint/tendermint/abci/client"
|
||||
"github.com/tendermint/tendermint/abci/example/counter"
|
||||
"github.com/tendermint/tendermint/abci/example/kvstore"
|
||||
"github.com/tendermint/tendermint/abci/types"
|
||||
)
|
||||
@@ -64,15 +65,15 @@ func (r *remoteClientCreator) NewABCIClient() (abcicli.Client, error) {
|
||||
|
||||
func DefaultClientCreator(addr, transport, dbDir string) ClientCreator {
|
||||
switch addr {
|
||||
case "counter":
|
||||
return NewLocalClientCreator(counter.NewCounterApplication(false))
|
||||
case "counter_serial":
|
||||
return NewLocalClientCreator(counter.NewCounterApplication(true))
|
||||
case "kvstore":
|
||||
fallthrough
|
||||
case "dummy":
|
||||
return NewLocalClientCreator(kvstore.NewKVStoreApplication())
|
||||
case "persistent_kvstore":
|
||||
fallthrough
|
||||
case "persistent_dummy":
|
||||
return NewLocalClientCreator(kvstore.NewPersistentKVStoreApplication(dbDir))
|
||||
case "nilapp":
|
||||
case "noop":
|
||||
return NewLocalClientCreator(types.NewBaseApplication())
|
||||
default:
|
||||
mustConnect := false // loop retrying
|
||||
|
@@ -119,8 +119,9 @@ func NewTendermint(app abci.Application) *nm.Node {
|
||||
config := GetConfig()
|
||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
|
||||
logger = log.NewFilter(logger, log.AllowError())
|
||||
pvFile := config.PrivValidatorFile()
|
||||
pv := privval.LoadOrGenFilePV(pvFile)
|
||||
pvKeyFile := config.PrivValidatorKeyFile()
|
||||
pvKeyStateFile := config.PrivValidatorStateFile()
|
||||
pv := privval.LoadOrGenFilePV(pvKeyFile, pvKeyStateFile)
|
||||
papp := proxy.NewLocalClientCreator(app)
|
||||
nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
|
||||
if err != nil {
|
||||
|
@@ -6,7 +6,7 @@ set -e
|
||||
|
||||
# Get the version from the environment, or try to figure it out.
|
||||
if [ -z $VERSION ]; then
|
||||
VERSION=$(awk -F\" '/Version =/ { print $2; exit }' < version/version.go)
|
||||
VERSION=$(awk -F\" 'TMCoreSemVer =/ { print $2; exit }' < version/version.go)
|
||||
fi
|
||||
if [ -z "$VERSION" ]; then
|
||||
echo "Please specify a version."
|
||||
|
@@ -1,19 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# XXX: this script is intended to be run from
|
||||
# a fresh Digital Ocean droplet with Ubuntu
|
||||
|
||||
# upon its completion, you must either reset
|
||||
# your terminal or run `source ~/.profile`
|
||||
|
||||
# as written, this script will install
|
||||
# tendermint core from master branch
|
||||
REPO=github.com/tendermint/tendermint
|
||||
|
||||
# change this to a specific release or branch
|
||||
BRANCH=master
|
||||
|
||||
GO_VERSION=1.11.2
|
||||
GO_VERSION=1.11.4
|
||||
|
||||
sudo apt-get update -y
|
||||
|
||||
|
@@ -16,7 +16,7 @@
|
||||
set BRANCH=master
|
||||
set REPO=github.com/tendermint/tendermint
|
||||
|
||||
set GO_VERSION=1.11.2
|
||||
set GO_VERSION=1.11.4
|
||||
|
||||
sudo pkg update
|
||||
|
||||
|
@@ -13,7 +13,7 @@ REPO=github.com/tendermint/tendermint
|
||||
# change this to a specific release or branch
|
||||
BRANCH=master
|
||||
|
||||
GO_VERSION=1.11.2
|
||||
GO_VERSION=1.11.4
|
||||
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y make
|
||||
|
41
scripts/privValUpgrade.go
Normal file
41
scripts/privValUpgrade.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
"github.com/tendermint/tendermint/privval"
|
||||
)
|
||||
|
||||
var (
|
||||
logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout))
|
||||
)
|
||||
|
||||
func main() {
|
||||
args := os.Args[1:]
|
||||
if len(args) != 3 {
|
||||
fmt.Println("Expected three args: <old path> <new key path> <new state path>")
|
||||
fmt.Println("Eg. ~/.tendermint/config/priv_validator.json ~/.tendermint/config/priv_validator_key.json ~/.tendermint/data/priv_validator_state.json")
|
||||
os.Exit(1)
|
||||
}
|
||||
err := loadAndUpgrade(args[0], args[1], args[2])
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func loadAndUpgrade(oldPVPath, newPVKeyPath, newPVStatePath string) error {
|
||||
oldPV, err := privval.LoadOldFilePV(oldPVPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error reading OldPrivValidator from %v: %v\n", oldPVPath, err)
|
||||
}
|
||||
logger.Info("Upgrading PrivValidator file",
|
||||
"old", oldPVPath,
|
||||
"newKey", newPVKeyPath,
|
||||
"newState", newPVStatePath,
|
||||
)
|
||||
oldPV.Upgrade(newPVKeyPath, newPVStatePath)
|
||||
return nil
|
||||
}
|
121
scripts/privValUpgrade_test.go
Normal file
121
scripts/privValUpgrade_test.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/tendermint/tendermint/privval"
|
||||
)
|
||||
|
||||
const oldPrivvalContent = `{
|
||||
"address": "1D8089FAFDFAE4A637F3D616E17B92905FA2D91D",
|
||||
"pub_key": {
|
||||
"type": "tendermint/PubKeyEd25519",
|
||||
"value": "r3Yg2AhDZ745CNTpavsGU+mRZ8WpRXqoJuyqjN8mJq0="
|
||||
},
|
||||
"last_height": "5",
|
||||
"last_round": "0",
|
||||
"last_step": 3,
|
||||
"last_signature": "CTr7b9ZQlrJJf+12rPl5t/YSCUc/KqV7jQogCfFJA24e7hof69X6OMT7eFLVQHyodPjD/QTA298XHV5ejxInDQ==",
|
||||
"last_signbytes": "750802110500000000000000220B08B398F3E00510F48DA6402A480A20FC258973076512999C3E6839A22E9FBDB1B77CF993E8A9955412A41A59D4CAD312240A20C971B286ACB8AAA6FCA0365EB0A660B189EDC08B46B5AF2995DEFA51A28D215B10013211746573742D636861696E2D533245415533",
|
||||
"priv_key": {
|
||||
"type": "tendermint/PrivKeyEd25519",
|
||||
"value": "7MwvTGEWWjsYwjn2IpRb+GYsWi9nnFsw8jPLLY1UtP6vdiDYCENnvjkI1Olq+wZT6ZFnxalFeqgm7KqM3yYmrQ=="
|
||||
}
|
||||
}`
|
||||
|
||||
func TestLoadAndUpgrade(t *testing.T) {
|
||||
|
||||
oldFilePath := initTmpOldFile(t)
|
||||
defer os.Remove(oldFilePath)
|
||||
newStateFile, err := ioutil.TempFile("", "priv_validator_state*.json")
|
||||
defer os.Remove(newStateFile.Name())
|
||||
require.NoError(t, err)
|
||||
newKeyFile, err := ioutil.TempFile("", "priv_validator_key*.json")
|
||||
defer os.Remove(newKeyFile.Name())
|
||||
require.NoError(t, err)
|
||||
emptyOldFile, err := ioutil.TempFile("", "priv_validator_empty*.json")
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(emptyOldFile.Name())
|
||||
|
||||
type args struct {
|
||||
oldPVPath string
|
||||
newPVKeyPath string
|
||||
newPVStatePath string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
wantPanic bool
|
||||
}{
|
||||
{"successful upgrade",
|
||||
args{oldPVPath: oldFilePath, newPVKeyPath: newKeyFile.Name(), newPVStatePath: newStateFile.Name()},
|
||||
false, false,
|
||||
},
|
||||
{"unsuccessful upgrade: empty old privval file",
|
||||
args{oldPVPath: emptyOldFile.Name(), newPVKeyPath: newKeyFile.Name(), newPVStatePath: newStateFile.Name()},
|
||||
true, false,
|
||||
},
|
||||
{"unsuccessful upgrade: invalid new paths (1/3)",
|
||||
args{oldPVPath: oldFilePath, newPVKeyPath: "", newPVStatePath: newStateFile.Name()},
|
||||
false, true,
|
||||
},
|
||||
{"unsuccessful upgrade: invalid new paths (2/3)",
|
||||
args{oldPVPath: oldFilePath, newPVKeyPath: newKeyFile.Name(), newPVStatePath: ""},
|
||||
false, true,
|
||||
},
|
||||
{"unsuccessful upgrade: invalid new paths (3/3)",
|
||||
args{oldPVPath: oldFilePath, newPVKeyPath: "", newPVStatePath: ""},
|
||||
false, true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// need to re-write the file everytime because upgrading renames it
|
||||
err := ioutil.WriteFile(oldFilePath, []byte(oldPrivvalContent), 0600)
|
||||
require.NoError(t, err)
|
||||
if tt.wantPanic {
|
||||
require.Panics(t, func() { loadAndUpgrade(tt.args.oldPVPath, tt.args.newPVKeyPath, tt.args.newPVStatePath) })
|
||||
} else {
|
||||
err = loadAndUpgrade(tt.args.oldPVPath, tt.args.newPVKeyPath, tt.args.newPVStatePath)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
fmt.Println("ERR", err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
upgradedPV := privval.LoadFilePV(tt.args.newPVKeyPath, tt.args.newPVStatePath)
|
||||
oldPV, err := privval.LoadOldFilePV(tt.args.oldPVPath + ".bak")
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, oldPV.Address, upgradedPV.Key.Address)
|
||||
assert.Equal(t, oldPV.Address, upgradedPV.GetAddress())
|
||||
assert.Equal(t, oldPV.PubKey, upgradedPV.Key.PubKey)
|
||||
assert.Equal(t, oldPV.PubKey, upgradedPV.GetPubKey())
|
||||
assert.Equal(t, oldPV.PrivKey, upgradedPV.Key.PrivKey)
|
||||
|
||||
assert.Equal(t, oldPV.LastHeight, upgradedPV.LastSignState.Height)
|
||||
assert.Equal(t, oldPV.LastRound, upgradedPV.LastSignState.Round)
|
||||
assert.Equal(t, oldPV.LastSignature, upgradedPV.LastSignState.Signature)
|
||||
assert.Equal(t, oldPV.LastSignBytes, upgradedPV.LastSignState.SignBytes)
|
||||
assert.Equal(t, oldPV.LastStep, upgradedPV.LastSignState.Step)
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func initTmpOldFile(t *testing.T) string {
|
||||
tmpfile, err := ioutil.TempFile("", "priv_validator_*.json")
|
||||
require.NoError(t, err)
|
||||
t.Logf("created test file %s", tmpfile.Name())
|
||||
_, err = tmpfile.WriteString(oldPrivvalContent)
|
||||
require.NoError(t, err)
|
||||
|
||||
return tmpfile.Name()
|
||||
}
|
@@ -6,7 +6,7 @@ DIST_DIR=./build/dist
|
||||
|
||||
# Get the version from the environment, or try to figure it out.
|
||||
if [ -z $VERSION ]; then
|
||||
VERSION=$(awk -F\" '/Version =/ { print $2; exit }' < version/version.go)
|
||||
VERSION=$(awk -F\" 'TMCoreSemVer =/ { print $2; exit }' < version/version.go)
|
||||
fi
|
||||
if [ -z "$VERSION" ]; then
|
||||
echo "Please specify a version."
|
||||
|
@@ -3,7 +3,7 @@ set -e
|
||||
|
||||
# Get the version from the environment, or try to figure it out.
|
||||
if [ -z $VERSION ]; then
|
||||
VERSION=$(awk -F\" '/Version =/ { print $2; exit }' < version/version.go)
|
||||
VERSION=$(awk -F\" 'TMCoreSemVer =/ { print $2; exit }' < version/version.go)
|
||||
fi
|
||||
if [ -z "$VERSION" ]; then
|
||||
echo "Please specify a version."
|
||||
|
@@ -1,182 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/go-amino"
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino"
|
||||
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
"github.com/tendermint/tendermint/privval"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
type GenesisValidator struct {
|
||||
PubKey Data `json:"pub_key"`
|
||||
Power int64 `json:"power"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type Genesis struct {
|
||||
GenesisTime time.Time `json:"genesis_time"`
|
||||
ChainID string `json:"chain_id"`
|
||||
ConsensusParams *types.ConsensusParams `json:"consensus_params,omitempty"`
|
||||
Validators []GenesisValidator `json:"validators"`
|
||||
AppHash cmn.HexBytes `json:"app_hash"`
|
||||
AppState json.RawMessage `json:"app_state,omitempty"`
|
||||
AppOptions json.RawMessage `json:"app_options,omitempty"` // DEPRECATED
|
||||
}
|
||||
|
||||
type NodeKey struct {
|
||||
PrivKey Data `json:"priv_key"`
|
||||
}
|
||||
|
||||
type PrivVal struct {
|
||||
Address cmn.HexBytes `json:"address"`
|
||||
LastHeight int64 `json:"last_height"`
|
||||
LastRound int `json:"last_round"`
|
||||
LastStep int8 `json:"last_step"`
|
||||
PubKey Data `json:"pub_key"`
|
||||
PrivKey Data `json:"priv_key"`
|
||||
}
|
||||
|
||||
type Data struct {
|
||||
Type string `json:"type"`
|
||||
Data cmn.HexBytes `json:"data"`
|
||||
}
|
||||
|
||||
func convertNodeKey(cdc *amino.Codec, jsonBytes []byte) ([]byte, error) {
|
||||
var nodeKey NodeKey
|
||||
err := json.Unmarshal(jsonBytes, &nodeKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var privKey ed25519.PrivKeyEd25519
|
||||
copy(privKey[:], nodeKey.PrivKey.Data)
|
||||
|
||||
nodeKeyNew := p2p.NodeKey{privKey}
|
||||
|
||||
bz, err := cdc.MarshalJSON(nodeKeyNew)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bz, nil
|
||||
}
|
||||
|
||||
func convertPrivVal(cdc *amino.Codec, jsonBytes []byte) ([]byte, error) {
|
||||
var privVal PrivVal
|
||||
err := json.Unmarshal(jsonBytes, &privVal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var privKey ed25519.PrivKeyEd25519
|
||||
copy(privKey[:], privVal.PrivKey.Data)
|
||||
|
||||
var pubKey ed25519.PubKeyEd25519
|
||||
copy(pubKey[:], privVal.PubKey.Data)
|
||||
|
||||
privValNew := privval.FilePV{
|
||||
Address: pubKey.Address(),
|
||||
PubKey: pubKey,
|
||||
LastHeight: privVal.LastHeight,
|
||||
LastRound: privVal.LastRound,
|
||||
LastStep: privVal.LastStep,
|
||||
PrivKey: privKey,
|
||||
}
|
||||
|
||||
bz, err := cdc.MarshalJSON(privValNew)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bz, nil
|
||||
}
|
||||
|
||||
func convertGenesis(cdc *amino.Codec, jsonBytes []byte) ([]byte, error) {
|
||||
var genesis Genesis
|
||||
err := json.Unmarshal(jsonBytes, &genesis)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
genesisNew := types.GenesisDoc{
|
||||
GenesisTime: genesis.GenesisTime,
|
||||
ChainID: genesis.ChainID,
|
||||
ConsensusParams: genesis.ConsensusParams,
|
||||
// Validators
|
||||
AppHash: genesis.AppHash,
|
||||
AppState: genesis.AppState,
|
||||
}
|
||||
|
||||
if genesis.AppOptions != nil {
|
||||
genesisNew.AppState = genesis.AppOptions
|
||||
}
|
||||
|
||||
for _, v := range genesis.Validators {
|
||||
var pubKey ed25519.PubKeyEd25519
|
||||
copy(pubKey[:], v.PubKey.Data)
|
||||
genesisNew.Validators = append(
|
||||
genesisNew.Validators,
|
||||
types.GenesisValidator{
|
||||
PubKey: pubKey,
|
||||
Power: v.Power,
|
||||
Name: v.Name,
|
||||
},
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
bz, err := cdc.MarshalJSON(genesisNew)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bz, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
cdc := amino.NewCodec()
|
||||
cryptoAmino.RegisterAmino(cdc)
|
||||
|
||||
args := os.Args[1:]
|
||||
if len(args) != 1 {
|
||||
fmt.Println("Please specify a file to convert")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
filePath := args[0]
|
||||
fileName := filepath.Base(filePath)
|
||||
|
||||
fileBytes, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var bz []byte
|
||||
|
||||
switch fileName {
|
||||
case "node_key.json":
|
||||
bz, err = convertNodeKey(cdc, fileBytes)
|
||||
case "priv_validator.json":
|
||||
bz, err = convertPrivVal(cdc, fileBytes)
|
||||
case "genesis.json":
|
||||
bz, err = convertGenesis(cdc, fileBytes)
|
||||
default:
|
||||
fmt.Println("Expected file name to be in (node_key.json, priv_validator.json, genesis.json)")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(string(bz))
|
||||
|
||||
}
|
@@ -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,
|
||||
|
@@ -12,7 +12,6 @@ import (
|
||||
// PrivValidator defines the functionality of a local Tendermint validator
|
||||
// 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
|
||||
@@ -29,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) {
|
||||
@@ -51,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()
|
||||
@@ -85,7 +79,8 @@ func (pv *MockPV) SignProposal(chainID string, proposal *Proposal) error {
|
||||
|
||||
// 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.
|
||||
|
@@ -142,14 +142,15 @@ func TestABCIEvidence(t *testing.T) {
|
||||
blockID := makeBlockID([]byte("blockhash"), 1000, []byte("partshash"))
|
||||
blockID2 := makeBlockID([]byte("blockhash2"), 1000, []byte("partshash"))
|
||||
const chainID = "mychain"
|
||||
pubKey := val.GetPubKey()
|
||||
ev := &DuplicateVoteEvidence{
|
||||
PubKey: val.GetPubKey(),
|
||||
PubKey: pubKey,
|
||||
VoteA: makeVote(val, chainID, 0, 10, 2, 1, blockID),
|
||||
VoteB: makeVote(val, chainID, 0, 10, 2, 1, blockID2),
|
||||
}
|
||||
abciEv := TM2PB.Evidence(
|
||||
ev,
|
||||
NewValidatorSet([]*Validator{NewValidator(val.GetPubKey(), 10)}),
|
||||
NewValidatorSet([]*Validator{NewValidator(pubKey, 10)}),
|
||||
time.Now(),
|
||||
)
|
||||
|
||||
|
@@ -10,9 +10,9 @@ func MakeCommit(blockID BlockID, height int64, round int,
|
||||
|
||||
// all sign
|
||||
for i := 0; i < len(validators); i++ {
|
||||
|
||||
addr := validators[i].GetPubKey().Address()
|
||||
vote := &Vote{
|
||||
ValidatorAddress: validators[i].GetAddress(),
|
||||
ValidatorAddress: addr,
|
||||
ValidatorIndex: i,
|
||||
Height: height,
|
||||
Round: round,
|
||||
|
@@ -103,9 +103,9 @@ func TestComputeTxsOverhead(t *testing.T) {
|
||||
}{
|
||||
{Txs{[]byte{6, 6, 6, 6, 6, 6}}, 2},
|
||||
// one 21 Mb transaction:
|
||||
{Txs{make([]byte, 22020096, 22020096)}, 5},
|
||||
{Txs{make([]byte, 22020096)}, 5},
|
||||
// two 21Mb/2 sized transactions:
|
||||
{Txs{make([]byte, 11010048, 11010048), make([]byte, 11010048, 11010048)}, 10},
|
||||
{Txs{make([]byte, 11010048), make([]byte, 11010048)}, 10},
|
||||
{Txs{[]byte{1, 2, 3}, []byte{1, 2, 3}, []byte{4, 5, 6}}, 6},
|
||||
{Txs{[]byte{100, 5, 64}, []byte{42, 116, 118}, []byte{6, 6, 6}, []byte{6, 6, 6}}, 8},
|
||||
}
|
||||
|
@@ -101,6 +101,7 @@ func RandValidator(randPower bool, minPower int64) (*Validator, PrivValidator) {
|
||||
if randPower {
|
||||
votePower += int64(cmn.RandUint32())
|
||||
}
|
||||
val := NewValidator(privVal.GetPubKey(), votePower)
|
||||
pubKey := privVal.GetPubKey()
|
||||
val := NewValidator(pubKey, votePower)
|
||||
return val, privVal
|
||||
}
|
||||
|
@@ -66,7 +66,8 @@ func TestAddVote(t *testing.T) {
|
||||
|
||||
// t.Logf(">> %v", voteSet)
|
||||
|
||||
if voteSet.GetByAddress(val0.GetAddress()) != nil {
|
||||
val0Addr := val0.GetPubKey().Address()
|
||||
if voteSet.GetByAddress(val0Addr) != nil {
|
||||
t.Errorf("Expected GetByAddress(val0.Address) to be nil")
|
||||
}
|
||||
if voteSet.BitArray().GetIndex(0) {
|
||||
@@ -78,7 +79,7 @@ func TestAddVote(t *testing.T) {
|
||||
}
|
||||
|
||||
vote := &Vote{
|
||||
ValidatorAddress: val0.GetAddress(),
|
||||
ValidatorAddress: val0Addr,
|
||||
ValidatorIndex: 0, // since privValidators are in order
|
||||
Height: height,
|
||||
Round: round,
|
||||
@@ -91,7 +92,7 @@ func TestAddVote(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if voteSet.GetByAddress(val0.GetAddress()) == nil {
|
||||
if voteSet.GetByAddress(val0Addr) == nil {
|
||||
t.Errorf("Expected GetByAddress(val0.Address) to be present")
|
||||
}
|
||||
if !voteSet.BitArray().GetIndex(0) {
|
||||
@@ -118,7 +119,8 @@ func Test2_3Majority(t *testing.T) {
|
||||
}
|
||||
// 6 out of 10 voted for nil.
|
||||
for i := 0; i < 6; i++ {
|
||||
vote := withValidator(voteProto, privValidators[i].GetAddress(), i)
|
||||
addr := privValidators[i].GetPubKey().Address()
|
||||
vote := withValidator(voteProto, addr, i)
|
||||
_, err := signAddVote(privValidators[i], vote, voteSet)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
@@ -131,7 +133,8 @@ func Test2_3Majority(t *testing.T) {
|
||||
|
||||
// 7th validator voted for some blockhash
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[6].GetAddress(), 6)
|
||||
addr := privValidators[6].GetPubKey().Address()
|
||||
vote := withValidator(voteProto, addr, 6)
|
||||
_, err := signAddVote(privValidators[6], withBlockHash(vote, cmn.RandBytes(32)), voteSet)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
@@ -144,7 +147,8 @@ func Test2_3Majority(t *testing.T) {
|
||||
|
||||
// 8th validator voted for nil.
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[7].GetAddress(), 7)
|
||||
addr := privValidators[7].GetPubKey().Address()
|
||||
vote := withValidator(voteProto, addr, 7)
|
||||
_, err := signAddVote(privValidators[7], vote, voteSet)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
@@ -176,7 +180,8 @@ func Test2_3MajorityRedux(t *testing.T) {
|
||||
|
||||
// 66 out of 100 voted for nil.
|
||||
for i := 0; i < 66; i++ {
|
||||
vote := withValidator(voteProto, privValidators[i].GetAddress(), i)
|
||||
addr := privValidators[i].GetPubKey().Address()
|
||||
vote := withValidator(voteProto, addr, i)
|
||||
_, err := signAddVote(privValidators[i], vote, voteSet)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
@@ -189,7 +194,8 @@ func Test2_3MajorityRedux(t *testing.T) {
|
||||
|
||||
// 67th validator voted for nil
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[66].GetAddress(), 66)
|
||||
adrr := privValidators[66].GetPubKey().Address()
|
||||
vote := withValidator(voteProto, adrr, 66)
|
||||
_, err := signAddVote(privValidators[66], withBlockHash(vote, nil), voteSet)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
@@ -202,7 +208,8 @@ func Test2_3MajorityRedux(t *testing.T) {
|
||||
|
||||
// 68th validator voted for a different BlockParts PartSetHeader
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[67].GetAddress(), 67)
|
||||
addr := privValidators[67].GetPubKey().Address()
|
||||
vote := withValidator(voteProto, addr, 67)
|
||||
blockPartsHeader := PartSetHeader{blockPartsTotal, crypto.CRandBytes(32)}
|
||||
_, err := signAddVote(privValidators[67], withBlockPartsHeader(vote, blockPartsHeader), voteSet)
|
||||
if err != nil {
|
||||
@@ -216,7 +223,8 @@ func Test2_3MajorityRedux(t *testing.T) {
|
||||
|
||||
// 69th validator voted for different BlockParts Total
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[68].GetAddress(), 68)
|
||||
addr := privValidators[68].GetPubKey().Address()
|
||||
vote := withValidator(voteProto, addr, 68)
|
||||
blockPartsHeader := PartSetHeader{blockPartsTotal + 1, blockPartsHeader.Hash}
|
||||
_, err := signAddVote(privValidators[68], withBlockPartsHeader(vote, blockPartsHeader), voteSet)
|
||||
if err != nil {
|
||||
@@ -230,7 +238,8 @@ func Test2_3MajorityRedux(t *testing.T) {
|
||||
|
||||
// 70th validator voted for different BlockHash
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[69].GetAddress(), 69)
|
||||
addr := privValidators[69].GetPubKey().Address()
|
||||
vote := withValidator(voteProto, addr, 69)
|
||||
_, err := signAddVote(privValidators[69], withBlockHash(vote, cmn.RandBytes(32)), voteSet)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
@@ -243,7 +252,8 @@ func Test2_3MajorityRedux(t *testing.T) {
|
||||
|
||||
// 71st validator voted for the right BlockHash & BlockPartsHeader
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[70].GetAddress(), 70)
|
||||
addr := privValidators[70].GetPubKey().Address()
|
||||
vote := withValidator(voteProto, addr, 70)
|
||||
_, err := signAddVote(privValidators[70], vote, voteSet)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
@@ -271,7 +281,8 @@ func TestBadVotes(t *testing.T) {
|
||||
|
||||
// val0 votes for nil.
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[0].GetAddress(), 0)
|
||||
addr := privValidators[0].GetPubKey().Address()
|
||||
vote := withValidator(voteProto, addr, 0)
|
||||
added, err := signAddVote(privValidators[0], vote, voteSet)
|
||||
if !added || err != nil {
|
||||
t.Errorf("Expected VoteSet.Add to succeed")
|
||||
@@ -280,7 +291,8 @@ func TestBadVotes(t *testing.T) {
|
||||
|
||||
// val0 votes again for some block.
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[0].GetAddress(), 0)
|
||||
addr := privValidators[0].GetPubKey().Address()
|
||||
vote := withValidator(voteProto, addr, 0)
|
||||
added, err := signAddVote(privValidators[0], withBlockHash(vote, cmn.RandBytes(32)), voteSet)
|
||||
if added || err == nil {
|
||||
t.Errorf("Expected VoteSet.Add to fail, conflicting vote.")
|
||||
@@ -289,7 +301,8 @@ func TestBadVotes(t *testing.T) {
|
||||
|
||||
// val1 votes on another height
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[1].GetAddress(), 1)
|
||||
addr := privValidators[1].GetPubKey().Address()
|
||||
vote := withValidator(voteProto, addr, 1)
|
||||
added, err := signAddVote(privValidators[1], withHeight(vote, height+1), voteSet)
|
||||
if added || err == nil {
|
||||
t.Errorf("Expected VoteSet.Add to fail, wrong height")
|
||||
@@ -298,7 +311,8 @@ func TestBadVotes(t *testing.T) {
|
||||
|
||||
// val2 votes on another round
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[2].GetAddress(), 2)
|
||||
addr := privValidators[2].GetPubKey().Address()
|
||||
vote := withValidator(voteProto, addr, 2)
|
||||
added, err := signAddVote(privValidators[2], withRound(vote, round+1), voteSet)
|
||||
if added || err == nil {
|
||||
t.Errorf("Expected VoteSet.Add to fail, wrong round")
|
||||
@@ -307,7 +321,8 @@ func TestBadVotes(t *testing.T) {
|
||||
|
||||
// val3 votes of another type.
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[3].GetAddress(), 3)
|
||||
addr := privValidators[3].GetPubKey().Address()
|
||||
vote := withValidator(voteProto, addr, 3)
|
||||
added, err := signAddVote(privValidators[3], withType(vote, byte(PrecommitType)), voteSet)
|
||||
if added || err == nil {
|
||||
t.Errorf("Expected VoteSet.Add to fail, wrong type")
|
||||
@@ -331,9 +346,10 @@ func TestConflicts(t *testing.T) {
|
||||
BlockID: BlockID{nil, PartSetHeader{}},
|
||||
}
|
||||
|
||||
val0Addr := privValidators[0].GetPubKey().Address()
|
||||
// val0 votes for nil.
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[0].GetAddress(), 0)
|
||||
vote := withValidator(voteProto, val0Addr, 0)
|
||||
added, err := signAddVote(privValidators[0], vote, voteSet)
|
||||
if !added || err != nil {
|
||||
t.Errorf("Expected VoteSet.Add to succeed")
|
||||
@@ -342,7 +358,7 @@ func TestConflicts(t *testing.T) {
|
||||
|
||||
// val0 votes again for blockHash1.
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[0].GetAddress(), 0)
|
||||
vote := withValidator(voteProto, val0Addr, 0)
|
||||
added, err := signAddVote(privValidators[0], withBlockHash(vote, blockHash1), voteSet)
|
||||
if added {
|
||||
t.Errorf("Expected VoteSet.Add to fail, conflicting vote.")
|
||||
@@ -357,7 +373,7 @@ func TestConflicts(t *testing.T) {
|
||||
|
||||
// val0 votes again for blockHash1.
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[0].GetAddress(), 0)
|
||||
vote := withValidator(voteProto, val0Addr, 0)
|
||||
added, err := signAddVote(privValidators[0], withBlockHash(vote, blockHash1), voteSet)
|
||||
if !added {
|
||||
t.Errorf("Expected VoteSet.Add to succeed, called SetPeerMaj23().")
|
||||
@@ -372,7 +388,7 @@ func TestConflicts(t *testing.T) {
|
||||
|
||||
// val0 votes again for blockHash1.
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[0].GetAddress(), 0)
|
||||
vote := withValidator(voteProto, val0Addr, 0)
|
||||
added, err := signAddVote(privValidators[0], withBlockHash(vote, blockHash2), voteSet)
|
||||
if added {
|
||||
t.Errorf("Expected VoteSet.Add to fail, duplicate SetPeerMaj23() from peerA")
|
||||
@@ -384,7 +400,8 @@ func TestConflicts(t *testing.T) {
|
||||
|
||||
// val1 votes for blockHash1.
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[1].GetAddress(), 1)
|
||||
addr := privValidators[1].GetPubKey().Address()
|
||||
vote := withValidator(voteProto, addr, 1)
|
||||
added, err := signAddVote(privValidators[1], withBlockHash(vote, blockHash1), voteSet)
|
||||
if !added || err != nil {
|
||||
t.Errorf("Expected VoteSet.Add to succeed")
|
||||
@@ -401,7 +418,8 @@ func TestConflicts(t *testing.T) {
|
||||
|
||||
// val2 votes for blockHash2.
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[2].GetAddress(), 2)
|
||||
addr := privValidators[2].GetPubKey().Address()
|
||||
vote := withValidator(voteProto, addr, 2)
|
||||
added, err := signAddVote(privValidators[2], withBlockHash(vote, blockHash2), voteSet)
|
||||
if !added || err != nil {
|
||||
t.Errorf("Expected VoteSet.Add to succeed")
|
||||
@@ -421,7 +439,8 @@ func TestConflicts(t *testing.T) {
|
||||
|
||||
// val2 votes for blockHash1.
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[2].GetAddress(), 2)
|
||||
addr := privValidators[2].GetPubKey().Address()
|
||||
vote := withValidator(voteProto, addr, 2)
|
||||
added, err := signAddVote(privValidators[2], withBlockHash(vote, blockHash1), voteSet)
|
||||
if !added {
|
||||
t.Errorf("Expected VoteSet.Add to succeed")
|
||||
@@ -462,7 +481,8 @@ func TestMakeCommit(t *testing.T) {
|
||||
|
||||
// 6 out of 10 voted for some block.
|
||||
for i := 0; i < 6; i++ {
|
||||
vote := withValidator(voteProto, privValidators[i].GetAddress(), i)
|
||||
addr := privValidators[i].GetPubKey().Address()
|
||||
vote := withValidator(voteProto, addr, i)
|
||||
_, err := signAddVote(privValidators[i], vote, voteSet)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
@@ -474,7 +494,8 @@ func TestMakeCommit(t *testing.T) {
|
||||
|
||||
// 7th voted for some other block.
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[6].GetAddress(), 6)
|
||||
addr := privValidators[6].GetPubKey().Address()
|
||||
vote := withValidator(voteProto, addr, 6)
|
||||
vote = withBlockHash(vote, cmn.RandBytes(32))
|
||||
vote = withBlockPartsHeader(vote, PartSetHeader{123, cmn.RandBytes(32)})
|
||||
|
||||
@@ -486,7 +507,8 @@ func TestMakeCommit(t *testing.T) {
|
||||
|
||||
// The 8th voted like everyone else.
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[7].GetAddress(), 7)
|
||||
addr := privValidators[7].GetPubKey().Address()
|
||||
vote := withValidator(voteProto, addr, 7)
|
||||
_, err := signAddVote(privValidators[7], vote, voteSet)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
@@ -18,7 +18,7 @@ const (
|
||||
// TMCoreSemVer is the current version of Tendermint Core.
|
||||
// It's the Semantic Version of the software.
|
||||
// Must be a string because scripts like dist.sh read this file.
|
||||
TMCoreSemVer = "0.27.2"
|
||||
TMCoreSemVer = "0.27.4"
|
||||
|
||||
// ABCISemVer is the semantic version of the ABCI library
|
||||
ABCISemVer = "0.15.0"
|
||||
|
Reference in New Issue
Block a user