split immutable and mutable parts of priv_validator.json

This commit is contained in:
yutianwu 2018-11-17 18:48:33 +08:00
parent 725ed7969a
commit e8700152be
18 changed files with 352 additions and 186 deletions

View File

@ -34,6 +34,7 @@ program](https://hackerone.com/tendermint).
reset to 0 reset to 0
### FEATURES: ### FEATURES:
- [privval] \#1181 Split immutable and mutable parts of priv_validator.json
### IMPROVEMENTS: ### IMPROVEMENTS:

View File

@ -13,9 +13,10 @@ import (
func main() { func main() {
var ( var (
addr = flag.String("addr", ":26659", "Address of client to connect to") addr = flag.String("addr", ":26659", "Address of client to connect to")
chainID = flag.String("chain-id", "mychain", "chain id") chainID = flag.String("chain-id", "mychain", "chain id")
privValPath = flag.String("priv", "", "priv val file path") privValKeyPath = flag.String("priv-key", "", "priv val key file path")
privValStatePath = flag.String("priv-state", "", "priv val state file path")
logger = log.NewTMLogger( logger = log.NewTMLogger(
log.NewSyncWriter(os.Stdout), log.NewSyncWriter(os.Stdout),
@ -27,10 +28,11 @@ func main() {
"Starting private validator", "Starting private validator",
"addr", *addr, "addr", *addr,
"chainID", *chainID, "chainID", *chainID,
"privPath", *privValPath, "privKeyPath", *privValKeyPath,
"privStatePath", *privValStatePath,
) )
pv := privval.LoadFilePV(*privValPath) pv := privval.LoadFilePV(*privValKeyPath, *privValStatePath)
rs := privval.NewRemoteSigner( rs := privval.NewRemoteSigner(
logger, logger,

View File

@ -17,7 +17,7 @@ var GenValidatorCmd = &cobra.Command{
} }
func genValidator(cmd *cobra.Command, args []string) { func genValidator(cmd *cobra.Command, args []string) {
pv := privval.GenFilePV("") pv := privval.GenFilePV("", "")
jsbz, err := cdc.MarshalJSON(pv) jsbz, err := cdc.MarshalJSON(pv)
if err != nil { if err != nil {
panic(err) panic(err)

View File

@ -26,15 +26,18 @@ func initFiles(cmd *cobra.Command, args []string) error {
func initFilesWithConfig(config *cfg.Config) error { func initFilesWithConfig(config *cfg.Config) error {
// private validator // private validator
privValFile := config.PrivValidatorFile() privValkeyFile := config.PrivValidatorKeyFile()
privValStateFile := config.PrivValidatorStateFile()
var pv *privval.FilePV var pv *privval.FilePV
if cmn.FileExists(privValFile) { if cmn.FileExists(privValkeyFile) {
pv = privval.LoadFilePV(privValFile) pv = privval.LoadFilePV(privValkeyFile, privValStateFile)
logger.Info("Found private validator", "path", privValFile) logger.Info("Found private validator", "keyFile", privValkeyFile,
"stateFile", privValStateFile)
} else { } else {
pv = privval.GenFilePV(privValFile) pv = privval.GenFilePV(privValkeyFile, privValStateFile)
pv.Save() pv.Save()
logger.Info("Generated private validator", "path", privValFile) logger.Info("Generated private validator", "keyFile", privValkeyFile,
"stateFile", privValStateFile)
} }
nodeKeyFile := config.NodeKeyFile() nodeKeyFile := config.NodeKeyFile()

View File

@ -27,19 +27,20 @@ var ResetPrivValidatorCmd = &cobra.Command{
// XXX: this is totally unsafe. // XXX: this is totally unsafe.
// it's only suitable for testnets. // it's only suitable for testnets.
func resetAll(cmd *cobra.Command, args []string) { 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. // XXX: this is totally unsafe.
// it's only suitable for testnets. // it's only suitable for testnets.
func resetPrivValidator(cmd *cobra.Command, args []string) { 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 the privValidator and address book files plus all data.
// Exported so other CLI tools can use it. // Exported so other CLI tools can use it.
func ResetAll(dbDir, addrBookFile, privValFile string, logger log.Logger) { func ResetAll(dbDir, addrBookFile, privValKeyFile, privValStateFile string, logger log.Logger) {
resetFilePV(privValFile, logger) resetFilePV(privValKeyFile, privValStateFile, logger)
removeAddrBook(addrBookFile, logger) removeAddrBook(addrBookFile, logger)
if err := os.RemoveAll(dbDir); err == nil { if err := os.RemoveAll(dbDir); err == nil {
logger.Info("Removed all blockchain history", "dir", dbDir) logger.Info("Removed all blockchain history", "dir", dbDir)
@ -48,15 +49,17 @@ func ResetAll(dbDir, addrBookFile, privValFile string, logger log.Logger) {
} }
} }
func resetFilePV(privValFile string, logger log.Logger) { func resetFilePV(privValKeyFile, privValStateFile string, logger log.Logger) {
if _, err := os.Stat(privValFile); err == nil { if _, err := os.Stat(privValKeyFile); err == nil {
pv := privval.LoadFilePV(privValFile) pv := privval.LoadFilePV(privValKeyFile, privValStateFile)
pv.Reset() 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 { } else {
pv := privval.GenFilePV(privValFile) pv := privval.GenFilePV(privValKeyFile, privValStateFile)
pv.Save() pv.Save()
logger.Info("Generated private validator file", "file", privValFile) logger.Info("Generated private validator file", "file", "keyFile", privValKeyFile,
"stateFile", privValStateFile)
} }
} }

View File

@ -16,7 +16,7 @@ var ShowValidatorCmd = &cobra.Command{
} }
func showValidator(cmd *cobra.Command, args []string) { func showValidator(cmd *cobra.Command, args []string) {
privValidator := privval.LoadOrGenFilePV(config.PrivValidatorFile()) privValidator := privval.LoadOrGenFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile())
pubKeyJSONBytes, _ := cdc.MarshalJSON(privValidator.GetPubKey()) pubKeyJSONBytes, _ := cdc.MarshalJSON(privValidator.GetPubKey())
fmt.Println(string(pubKeyJSONBytes)) fmt.Println(string(pubKeyJSONBytes))
} }

View File

@ -88,8 +88,10 @@ func testnetFiles(cmd *cobra.Command, args []string) error {
initFilesWithConfig(config) initFilesWithConfig(config)
pvFile := filepath.Join(nodeDir, config.BaseConfig.PrivValidator) pvKeyFile := filepath.Join(nodeDir, config.BaseConfig.PrivValidatorKey)
pv := privval.LoadFilePV(pvFile) pvStateFile := filepath.Join(nodeDir, config.BaseConfig.PrivValidatorState)
pv := privval.LoadFilePV(pvKeyFile, pvStateFile)
genVals[i] = types.GenesisValidator{ genVals[i] = types.GenesisValidator{
Address: pv.GetPubKey().Address(), Address: pv.GetPubKey().Address(),
PubKey: pv.GetPubKey(), PubKey: pv.GetPubKey(),

View File

@ -35,15 +35,19 @@ var (
defaultConfigFileName = "config.toml" defaultConfigFileName = "config.toml"
defaultGenesisJSONName = "genesis.json" defaultGenesisJSONName = "genesis.json"
defaultPrivValName = "priv_validator.json" defaultPrivValKeyName = "priv_validator_key.json"
defaultPrivValStateName = "priv_validator_state.json"
defaultNodeKeyName = "node_key.json" defaultNodeKeyName = "node_key.json"
defaultAddrBookName = "addrbook.json" defaultAddrBookName = "addrbook.json"
defaultConfigFilePath = filepath.Join(defaultConfigDir, defaultConfigFileName) defaultConfigFilePath = filepath.Join(defaultConfigDir, defaultConfigFileName)
defaultGenesisJSONPath = filepath.Join(defaultConfigDir, defaultGenesisJSONName) defaultGenesisJSONPath = filepath.Join(defaultConfigDir, defaultGenesisJSONName)
defaultPrivValPath = filepath.Join(defaultConfigDir, defaultPrivValName) defaultPrivValKeyPath = filepath.Join(defaultConfigDir, defaultPrivValKeyName)
defaultNodeKeyPath = filepath.Join(defaultConfigDir, defaultNodeKeyName) defaultPrivValStatePath = filepath.Join(defaultDataDir, defaultPrivValStateName)
defaultAddrBookPath = filepath.Join(defaultConfigDir, defaultAddrBookName)
defaultNodeKeyPath = filepath.Join(defaultConfigDir, defaultNodeKeyName)
defaultAddrBookPath = filepath.Join(defaultConfigDir, defaultAddrBookName)
) )
// Config defines the top level configuration for a Tendermint node // Config defines the top level configuration for a Tendermint node
@ -160,7 +164,10 @@ type BaseConfig struct {
Genesis string `mapstructure:"genesis_file"` Genesis string `mapstructure:"genesis_file"`
// Path to the JSON file containing the private key to use as a validator in the consensus protocol // 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 // TCP or UNIX socket address for Tendermint to listen on for
// connections from an external PrivValidator process // connections from an external PrivValidator process
@ -183,19 +190,20 @@ type BaseConfig struct {
// DefaultBaseConfig returns a default base configuration for a Tendermint node // DefaultBaseConfig returns a default base configuration for a Tendermint node
func DefaultBaseConfig() BaseConfig { func DefaultBaseConfig() BaseConfig {
return BaseConfig{ return BaseConfig{
Genesis: defaultGenesisJSONPath, Genesis: defaultGenesisJSONPath,
PrivValidator: defaultPrivValPath, PrivValidatorKey: defaultPrivValKeyPath,
NodeKey: defaultNodeKeyPath, PrivValidatorState: defaultPrivValStatePath,
Moniker: defaultMoniker, NodeKey: defaultNodeKeyPath,
ProxyApp: "tcp://127.0.0.1:26658", Moniker: defaultMoniker,
ABCI: "socket", ProxyApp: "tcp://127.0.0.1:26658",
LogLevel: DefaultPackageLogLevels(), ABCI: "socket",
LogFormat: LogFormatPlain, LogLevel: DefaultPackageLogLevels(),
ProfListenAddress: "", LogFormat: LogFormatPlain,
FastSync: true, ProfListenAddress: "",
FilterPeers: false, FastSync: true,
DBBackend: "leveldb", FilterPeers: false,
DBPath: "data", DBBackend: "leveldb",
DBPath: "data",
} }
} }
@ -218,9 +226,14 @@ func (cfg BaseConfig) GenesisFile() string {
return rootify(cfg.Genesis, cfg.RootDir) return rootify(cfg.Genesis, cfg.RootDir)
} }
// PrivValidatorFile returns the full path to the priv_validator.json file // PrivValidatorKeyFile returns the full path to the priv_validator_key.json file
func (cfg BaseConfig) PrivValidatorFile() string { func (cfg BaseConfig) PrivValidatorKeyFile() string {
return rootify(cfg.PrivValidator, cfg.RootDir) 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)
} }
// NodeKeyFile returns the full path to the node_key.json file // NodeKeyFile returns the full path to the node_key.json file

View File

@ -95,7 +95,10 @@ log_format = "{{ .BaseConfig.LogFormat }}"
genesis_file = "{{ js .BaseConfig.Genesis }}" genesis_file = "{{ js .BaseConfig.Genesis }}"
# Path to the JSON file containing the private key to use as a validator in the consensus protocol # 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 # TCP or UNIX socket address for Tendermint to listen on for
# connections from an external PrivValidator process # connections from an external PrivValidator process
@ -342,7 +345,8 @@ func ResetTestRoot(testName string) *Config {
baseConfig := DefaultBaseConfig() baseConfig := DefaultBaseConfig()
configFilePath := filepath.Join(rootDir, defaultConfigFilePath) configFilePath := filepath.Join(rootDir, defaultConfigFilePath)
genesisFilePath := filepath.Join(rootDir, baseConfig.Genesis) 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. // Write default config file if missing.
if !cmn.FileExists(configFilePath) { if !cmn.FileExists(configFilePath) {
@ -352,7 +356,8 @@ func ResetTestRoot(testName string) *Config {
cmn.MustWriteFile(genesisFilePath, []byte(testGenesis), 0644) cmn.MustWriteFile(genesisFilePath, []byte(testGenesis), 0644)
} }
// we always overwrite the priv val // 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) config := TestConfig().SetRoot(rootDir)
return config return config
@ -374,7 +379,7 @@ var testGenesis = `{
"app_hash": "" "app_hash": ""
}` }`
var testPrivValidator = `{ var testPrivValidatorKey = `{
"address": "A3258DCBF45DCA0DF052981870F2D1441A36D145", "address": "A3258DCBF45DCA0DF052981870F2D1441A36D145",
"pub_key": { "pub_key": {
"type": "tendermint/PubKeyEd25519", "type": "tendermint/PubKeyEd25519",
@ -383,8 +388,11 @@ var testPrivValidator = `{
"priv_key": { "priv_key": {
"type": "tendermint/PrivKeyEd25519", "type": "tendermint/PrivKeyEd25519",
"value": "EVkqJO/jIXp3rkASXfh9YnyToYXRXhBr6g9cQVxPFnQBP/5povV4HTjvsy530kybxKHwEi85iU8YL0qQhSYVoQ==" "value": "EVkqJO/jIXp3rkASXfh9YnyToYXRXhBr6g9cQVxPFnQBP/5povV4HTjvsy530kybxKHwEi85iU8YL0qQhSYVoQ=="
}, }
"last_height": "0", }`
"last_round": "0",
"last_step": 0 var testPrivValidatorState = `{
"height": "0",
"round": "0",
"step": 0
}` }`

View File

@ -60,7 +60,7 @@ func TestEnsureTestRoot(t *testing.T) {
// TODO: make sure the cfg returned and testconfig are the same! // TODO: make sure the cfg returned and testconfig are the same!
baseConfig := DefaultBaseConfig() 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 { func checkConfig(configFile string) bool {

View File

@ -281,9 +281,10 @@ func newConsensusStateWithConfigAndBlockStore(thisConfig *cfg.Config, state sm.S
} }
func loadPrivValidator(config *cfg.Config) *privval.FilePV { func loadPrivValidator(config *cfg.Config) *privval.FilePV {
privValidatorFile := config.PrivValidatorFile() privValidatorKeyFile := config.PrivValidatorKeyFile()
ensureDir(path.Dir(privValidatorFile), 0700) ensureDir(path.Dir(privValidatorKeyFile), 0700)
privValidator := privval.LoadOrGenFilePV(privValidatorFile) privValidatorStateFile := config.PrivValidatorStateFile()
privValidator := privval.LoadOrGenFilePV(privValidatorKeyFile, privValidatorStateFile)
privValidator.Reset() privValidator.Reset()
return privValidator return privValidator
} }
@ -617,11 +618,16 @@ func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerF
if i < nValidators { if i < nValidators {
privVal = privVals[i] privVal = privVals[i]
} else { } else {
tempFile, err := ioutil.TempFile("", "priv_validator_") tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_")
if err != nil { if err != nil {
panic(err) 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() app := appFunc()

View File

@ -319,7 +319,7 @@ func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) {
walFile := tempWALWithData(walBody) walFile := tempWALWithData(walBody)
config.Consensus.SetWalFile(walFile) config.Consensus.SetWalFile(walFile)
privVal := privval.LoadFilePV(config.PrivValidatorFile()) privVal := privval.LoadFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile())
wal, err := NewWAL(walFile) wal, err := NewWAL(walFile)
require.NoError(t, err) require.NoError(t, err)
@ -633,7 +633,7 @@ func TestInitChainUpdateValidators(t *testing.T) {
clientCreator := proxy.NewLocalClientCreator(app) clientCreator := proxy.NewLocalClientCreator(app)
config := ResetConfig("proxy_test_") config := ResetConfig("proxy_test_")
privVal := privval.LoadFilePV(config.PrivValidatorFile()) privVal := privval.LoadFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile())
stateDB, state, store := stateAndStore(config, privVal.GetPubKey(), 0x0) stateDB, state, store := stateAndStore(config, privVal.GetPubKey(), 0x0)
oldValAddr := state.Validators.Validators[0].Address oldValAddr := state.Validators.Validators[0].Address

View File

@ -40,8 +40,9 @@ func WALGenerateNBlocks(wr io.Writer, numBlocks int) (err error) {
// COPY PASTE FROM node.go WITH A FEW MODIFICATIONS // COPY PASTE FROM node.go WITH A FEW MODIFICATIONS
// NOTE: we can't import node package because of circular dependency. // 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. // NOTE: we don't do handshake so need to set state.Version.Consensus.App directly.
privValidatorFile := config.PrivValidatorFile() privValidatorKeyFile := config.PrivValidatorKeyFile()
privValidator := privval.LoadOrGenFilePV(privValidatorFile) privValidatorStateFile := config.PrivValidatorStateFile()
privValidator := privval.LoadOrGenFilePV(privValidatorKeyFile, privValidatorStateFile)
genDoc, err := types.GenesisDocFromFile(config.GenesisFile()) genDoc, err := types.GenesisDocFromFile(config.GenesisFile())
if err != nil { if err != nil {
return errors.Wrap(err, "failed to read genesis file") return errors.Wrap(err, "failed to read genesis file")

View File

@ -87,7 +87,7 @@ func DefaultNewNode(config *cfg.Config, logger log.Logger) (*Node, error) {
return nil, err return nil, err
} }
return NewNode(config, return NewNode(config,
privval.LoadOrGenFilePV(config.PrivValidatorFile()), privval.LoadOrGenFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile()),
nodeKey, nodeKey,
proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()), proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()),
DefaultGenesisDocProviderFunc(config), DefaultGenesisDocProviderFunc(config),

View File

@ -37,21 +37,31 @@ func voteToStep(vote *types.Vote) int8 {
// FilePV implements PrivValidator using data persisted to disk // FilePV implements PrivValidator using data persisted to disk
// to prevent double signing. // to prevent double signing.
// NOTE: the directory containing the pv.filePath must already exist. // 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 // 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. // if the process crashes after signing but before the resulting consensus message is processed.
type FilePV struct { type FilePV struct {
Address types.Address `json:"address"` Key FilePVKey
PubKey crypto.PubKey `json:"pub_key"` LastSignState FilePVLastSignState
LastHeight int64 `json:"last_height"` }
LastRound int `json:"last_round"`
LastStep int8 `json:"last_step"` // FilePVKey stores the immutable part of PrivValidator
LastSignature []byte `json:"last_signature,omitempty"` type FilePVKey struct {
LastSignBytes cmn.HexBytes `json:"last_signbytes,omitempty"` Address types.Address `json:"address"`
PrivKey crypto.PrivKey `json:"priv_key"` PubKey crypto.PubKey `json:"pub_key"`
PrivKey crypto.PrivKey `json:"priv_key"`
filePath string
}
// FilePVState stores the mutable part of PrivValidator
type FilePVLastSignState struct {
Height int64 `json:"height"`
Round int `json:"round"`
Step int8 `json:"step"`
Signature []byte `json:"signature,omitempty"`
SignBytes cmn.HexBytes `json:"signbytes,omitempty"`
// For persistence.
// Overloaded for testing.
filePath string filePath string
mtx sync.Mutex mtx sync.Mutex
} }
@ -59,58 +69,82 @@ type FilePV struct {
// GetAddress returns the address of the validator. // GetAddress returns the address of the validator.
// Implements PrivValidator. // Implements PrivValidator.
func (pv *FilePV) GetAddress() types.Address { func (pv *FilePV) GetAddress() types.Address {
return pv.Address return pv.Key.Address
} }
// GetPubKey returns the public key of the validator. // GetPubKey returns the public key of the validator.
// Implements PrivValidator. // Implements PrivValidator.
func (pv *FilePV) GetPubKey() crypto.PubKey { func (pv *FilePV) GetPubKey() crypto.PubKey {
return pv.PubKey return pv.Key.PubKey
} }
// GenFilePV generates a new validator with randomly generated private key // GenFilePV generates a new validator with randomly generated private key
// and sets the filePath, but does not call Save(). // and sets the filePaths, but does not call Save().
func GenFilePV(filePath string) *FilePV { func GenFilePV(keyFilePath string, stateFilePath string) *FilePV {
privKey := ed25519.GenPrivKey() privKey := ed25519.GenPrivKey()
return &FilePV{ return &FilePV{
Address: privKey.PubKey().Address(), Key: FilePVKey{
PubKey: privKey.PubKey(), Address: privKey.PubKey().Address(),
PrivKey: privKey, PubKey: privKey.PubKey(),
LastStep: stepNone, PrivKey: privKey,
filePath: filePath, filePath: keyFilePath,
},
LastSignState: FilePVLastSignState{
Step: stepNone,
filePath: stateFilePath,
},
} }
} }
// LoadFilePV loads a FilePV from the filePath. The FilePV handles double // LoadFilePV loads a FilePV from the filePaths. The FilePV handles double
// signing prevention by persisting data to the filePath. If the filePath does // signing prevention by persisting data to the stateFilePath. If the filePaths does
// not exist, the FilePV must be created manually and saved. // not exist, the FilePV must be created manually and saved.
func LoadFilePV(filePath string) *FilePV { func LoadFilePV(keyFilePath string, stateFilePath string) *FilePV {
pvJSONBytes, err := ioutil.ReadFile(filePath) keyJSONBytes, err := ioutil.ReadFile(keyFilePath)
if err != nil { if err != nil {
cmn.Exit(err.Error()) cmn.Exit(err.Error())
} }
pv := &FilePV{} pvKey := FilePVKey{}
err = cdc.UnmarshalJSON(pvJSONBytes, &pv) err = cdc.UnmarshalJSON(keyJSONBytes, &pvKey)
if err != nil { if err != nil {
cmn.Exit(fmt.Sprintf("Error reading PrivValidator from %v: %v\n", filePath, err)) cmn.Exit(fmt.Sprintf("Error reading PrivValidator key from %v: %v\n", keyFilePath, err))
} }
// overwrite pubkey and address for convenience // overwrite pubkey and address for convenience
pv.PubKey = pv.PrivKey.PubKey() pvKey.PubKey = pvKey.PrivKey.PubKey()
pv.Address = pv.PubKey.Address() pvKey.Address = pvKey.PubKey.Address()
pvKey.filePath = keyFilePath
stateJSONBytes, err := ioutil.ReadFile(stateFilePath)
if err != nil {
cmn.Exit(err.Error())
}
pvState := FilePVLastSignState{}
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
pv := &FilePV{}
pv.Key = pvKey
pv.LastSignState = pvState
pv.filePath = filePath
return pv return pv
} }
// LoadOrGenFilePV loads a FilePV from the given filePath // LoadOrGenFilePV loads a FilePV from the given filePaths
// or else generates a new one and saves it to the filePath. // or else generates a new one and saves it to the filePaths.
func LoadOrGenFilePV(filePath string) *FilePV { func LoadOrGenFilePV(keyFilePath string, stateFilePath string) *FilePV {
var pv *FilePV var pv *FilePV
if cmn.FileExists(filePath) { if cmn.FileExists(keyFilePath) {
pv = LoadFilePV(filePath) println(keyFilePath)
pv = LoadFilePV(keyFilePath, stateFilePath)
} else { } else {
pv = GenFilePV(filePath) pv = GenFilePV(keyFilePath, stateFilePath)
pv.Save() pv.Save()
} }
return pv return pv
@ -118,17 +152,36 @@ func LoadOrGenFilePV(filePath string) *FilePV {
// Save persists the FilePV to disk. // Save persists the FilePV to disk.
func (pv *FilePV) Save() { func (pv *FilePV) Save() {
pv.mtx.Lock() pv.saveKey()
defer pv.mtx.Unlock()
pv.save() pv.LastSignState.mtx.Lock()
defer pv.LastSignState.mtx.Unlock()
pv.saveState()
} }
func (pv *FilePV) save() { func (pv *FilePV) saveKey() {
outFile := pv.filePath outFile := pv.Key.filePath
if outFile == "" { 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(pv.Key, "", " ")
if err != nil {
panic(err)
}
err = cmn.WriteFileAtomic(outFile, jsonBytes, 0600)
if err != nil {
panic(err)
}
}
func (pv *FilePV) saveState() {
outFile := pv.LastSignState.filePath
if outFile == "" {
panic("Cannot save PrivValidator state: filePath not set")
}
jsonBytes, err := cdc.MarshalJSONIndent(pv.LastSignState, "", " ")
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -142,19 +195,19 @@ func (pv *FilePV) save() {
// NOTE: Unsafe! // NOTE: Unsafe!
func (pv *FilePV) Reset() { func (pv *FilePV) Reset() {
var sig []byte var sig []byte
pv.LastHeight = 0 pv.LastSignState.Height = 0
pv.LastRound = 0 pv.LastSignState.Round = 0
pv.LastStep = 0 pv.LastSignState.Step = 0
pv.LastSignature = sig pv.LastSignState.Signature = sig
pv.LastSignBytes = nil pv.LastSignState.SignBytes = nil
pv.Save() pv.Save()
} }
// SignVote signs a canonical representation of the vote, along with the // SignVote signs a canonical representation of the vote, along with the
// chainID. Implements PrivValidator. // chainID. Implements PrivValidator.
func (pv *FilePV) SignVote(chainID string, vote *types.Vote) error { func (pv *FilePV) SignVote(chainID string, vote *types.Vote) error {
pv.mtx.Lock() pv.LastSignState.mtx.Lock()
defer pv.mtx.Unlock() defer pv.LastSignState.mtx.Unlock()
if err := pv.signVote(chainID, vote); err != nil { if err := pv.signVote(chainID, vote); err != nil {
return fmt.Errorf("Error signing vote: %v", err) return fmt.Errorf("Error signing vote: %v", err)
} }
@ -164,8 +217,8 @@ func (pv *FilePV) SignVote(chainID string, vote *types.Vote) error {
// SignProposal signs a canonical representation of the proposal, along with // SignProposal signs a canonical representation of the proposal, along with
// the chainID. Implements PrivValidator. // the chainID. Implements PrivValidator.
func (pv *FilePV) SignProposal(chainID string, proposal *types.Proposal) error { func (pv *FilePV) SignProposal(chainID string, proposal *types.Proposal) error {
pv.mtx.Lock() pv.LastSignState.mtx.Lock()
defer pv.mtx.Unlock() defer pv.LastSignState.mtx.Unlock()
if err := pv.signProposal(chainID, proposal); err != nil { if err := pv.signProposal(chainID, proposal); err != nil {
return fmt.Errorf("Error signing proposal: %v", err) return fmt.Errorf("Error signing proposal: %v", err)
} }
@ -174,21 +227,21 @@ func (pv *FilePV) SignProposal(chainID string, proposal *types.Proposal) error {
// returns error if HRS regression or no LastSignBytes. returns true if HRS is unchanged // 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) { func (pv *FilePV) checkHRS(height int64, round int, step int8) (bool, error) {
if pv.LastHeight > height { if pv.LastSignState.Height > height {
return false, errors.New("Height regression") return false, errors.New("Height regression")
} }
if pv.LastHeight == height { if pv.LastSignState.Height == height {
if pv.LastRound > round { if pv.LastSignState.Round > round {
return false, errors.New("Round regression") return false, errors.New("Round regression")
} }
if pv.LastRound == round { if pv.LastSignState.Round == round {
if pv.LastStep > step { if pv.LastSignState.Step > step {
return false, errors.New("Step regression") return false, errors.New("Step regression")
} else if pv.LastStep == step { } else if pv.LastSignState.Step == step {
if pv.LastSignBytes != nil { if pv.LastSignState.SignBytes != nil {
if pv.LastSignature == nil { if pv.LastSignState.Signature == nil {
panic("pv: LastSignature is nil but LastSignBytes is not!") panic("pv: LastSignature is nil but LastSignBytes is not!")
} }
return true, nil return true, nil
@ -218,11 +271,11 @@ func (pv *FilePV) signVote(chainID string, vote *types.Vote) error {
// If they only differ by timestamp, use last timestamp and signature // If they only differ by timestamp, use last timestamp and signature
// Otherwise, return error // Otherwise, return error
if sameHRS { if sameHRS {
if bytes.Equal(signBytes, pv.LastSignBytes) { if bytes.Equal(signBytes, pv.LastSignState.SignBytes) {
vote.Signature = pv.LastSignature vote.Signature = pv.LastSignState.Signature
} else if timestamp, ok := checkVotesOnlyDifferByTimestamp(pv.LastSignBytes, signBytes); ok { } else if timestamp, ok := checkVotesOnlyDifferByTimestamp(pv.LastSignState.SignBytes, signBytes); ok {
vote.Timestamp = timestamp vote.Timestamp = timestamp
vote.Signature = pv.LastSignature vote.Signature = pv.LastSignState.Signature
} else { } else {
err = fmt.Errorf("Conflicting data") err = fmt.Errorf("Conflicting data")
} }
@ -230,7 +283,7 @@ func (pv *FilePV) signVote(chainID string, vote *types.Vote) error {
} }
// It passed the checks. Sign the vote // It passed the checks. Sign the vote
sig, err := pv.PrivKey.Sign(signBytes) sig, err := pv.Key.PrivKey.Sign(signBytes)
if err != nil { if err != nil {
return err return err
} }
@ -257,11 +310,11 @@ func (pv *FilePV) signProposal(chainID string, proposal *types.Proposal) error {
// If they only differ by timestamp, use last timestamp and signature // If they only differ by timestamp, use last timestamp and signature
// Otherwise, return error // Otherwise, return error
if sameHRS { if sameHRS {
if bytes.Equal(signBytes, pv.LastSignBytes) { if bytes.Equal(signBytes, pv.LastSignState.SignBytes) {
proposal.Signature = pv.LastSignature proposal.Signature = pv.LastSignState.Signature
} else if timestamp, ok := checkProposalsOnlyDifferByTimestamp(pv.LastSignBytes, signBytes); ok { } else if timestamp, ok := checkProposalsOnlyDifferByTimestamp(pv.LastSignState.SignBytes, signBytes); ok {
proposal.Timestamp = timestamp proposal.Timestamp = timestamp
proposal.Signature = pv.LastSignature proposal.Signature = pv.LastSignState.Signature
} else { } else {
err = fmt.Errorf("Conflicting data") err = fmt.Errorf("Conflicting data")
} }
@ -269,7 +322,7 @@ func (pv *FilePV) signProposal(chainID string, proposal *types.Proposal) error {
} }
// It passed the checks. Sign the proposal // It passed the checks. Sign the proposal
sig, err := pv.PrivKey.Sign(signBytes) sig, err := pv.Key.PrivKey.Sign(signBytes)
if err != nil { if err != nil {
return err return err
} }
@ -282,17 +335,17 @@ func (pv *FilePV) signProposal(chainID string, proposal *types.Proposal) error {
func (pv *FilePV) saveSigned(height int64, round int, step int8, func (pv *FilePV) saveSigned(height int64, round int, step int8,
signBytes []byte, sig []byte) { signBytes []byte, sig []byte) {
pv.LastHeight = height pv.LastSignState.Height = height
pv.LastRound = round pv.LastSignState.Round = round
pv.LastStep = step pv.LastSignState.Step = step
pv.LastSignature = sig pv.LastSignState.Signature = sig
pv.LastSignBytes = signBytes pv.LastSignState.SignBytes = signBytes
pv.save() pv.saveState()
} }
// String returns a string representation of the FilePV. // String returns a string representation of the FilePV.
func (pv *FilePV) String() string { func (pv *FilePV) String() string {
return fmt.Sprintf("PrivValidator{%v LH:%v, LR:%v, LS:%v}", pv.GetAddress(), pv.LastHeight, pv.LastRound, pv.LastStep) return fmt.Sprintf("PrivValidator{%v LH:%v, LR:%v, LS:%v}", pv.GetAddress(), pv.LastSignState.Height, pv.LastSignState.Round, pv.LastSignState.Step)
} }
//------------------------------------- //-------------------------------------

View File

@ -18,36 +18,72 @@ import (
func TestGenLoadValidator(t *testing.T) { func TestGenLoadValidator(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
tempFile, err := ioutil.TempFile("", "priv_validator_") tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_")
require.Nil(t, err) 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) height := int64(100)
privVal.LastHeight = height privVal.LastSignState.Height = height
privVal.Save() privVal.Save()
addr := privVal.GetAddress() 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(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 TestLoadOrGenValidator(t *testing.T) { func TestLoadOrGenValidator(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
tempFile, err := ioutil.TempFile("", "priv_validator_") tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_")
require.Nil(t, err) require.Nil(t, err)
tempFilePath := tempFile.Name() tempStateFile, err := ioutil.TempFile("", "priv_validator_state_")
if err := os.Remove(tempFilePath); err != nil { require.Nil(t, err)
tempKeyFilePath := tempKeyFile.Name()
if err := os.Remove(tempKeyFilePath); err != nil {
t.Error(err) 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() addr := privVal.GetAddress()
privVal = LoadOrGenFilePV(tempFilePath) privVal = LoadOrGenFilePV(tempKeyFilePath, tempStateFilePath)
assert.Equal(addr, privVal.GetAddress(), "expected privval addr to be the same") 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 := fmt.Sprintf(`{
"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) assert, require := assert.New(t), require.New(t)
// create some fixed values // create some fixed values
@ -67,22 +103,19 @@ func TestUnmarshalValidator(t *testing.T) {
"type": "tendermint/PubKeyEd25519", "type": "tendermint/PubKeyEd25519",
"value": "%s" "value": "%s"
}, },
"last_height": "0",
"last_round": "0",
"last_step": 0,
"priv_key": { "priv_key": {
"type": "tendermint/PrivKeyEd25519", "type": "tendermint/PrivKeyEd25519",
"value": "%s" "value": "%s"
} }
}`, addr, pubB64, privB64) }`, addr, pubB64, privB64)
val := FilePV{} val := FilePVKey{}
err := cdc.UnmarshalJSON([]byte(serialized), &val) err := cdc.UnmarshalJSON([]byte(serialized), &val)
require.Nil(err, "%+v", err) require.Nil(err, "%+v", err)
// make sure the values match // make sure the values match
assert.EqualValues(addr, val.GetAddress()) assert.EqualValues(addr, val.Address)
assert.EqualValues(pubKey, val.GetPubKey()) assert.EqualValues(pubKey, val.PubKey)
assert.EqualValues(privKey, val.PrivKey) assert.EqualValues(privKey, val.PrivKey)
// export it and make sure it is the same // export it and make sure it is the same
@ -94,9 +127,12 @@ func TestUnmarshalValidator(t *testing.T) {
func TestSignVote(t *testing.T) { func TestSignVote(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
tempFile, err := ioutil.TempFile("", "priv_validator_") tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_")
require.Nil(t, err) 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{}} block1 := types.BlockID{[]byte{1, 2, 3}, types.PartSetHeader{}}
block2 := types.BlockID{[]byte{3, 2, 1}, types.PartSetHeader{}} block2 := types.BlockID{[]byte{3, 2, 1}, types.PartSetHeader{}}
@ -104,7 +140,7 @@ func TestSignVote(t *testing.T) {
voteType := byte(types.PrevoteType) voteType := byte(types.PrevoteType)
// sign a vote for first time // 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) err = privVal.SignVote("mychainid", vote)
assert.NoError(err, "expected no error signing vote") assert.NoError(err, "expected no error signing vote")
@ -114,10 +150,10 @@ func TestSignVote(t *testing.T) {
// now try some bad votes // now try some bad votes
cases := []*types.Vote{ cases := []*types.Vote{
newVote(privVal.Address, 0, height, round-1, voteType, block1), // round regression newVote(privVal.Key.Address, 0, height, round-1, voteType, block1), // round regression
newVote(privVal.Address, 0, height-1, round, voteType, block1), // height regression newVote(privVal.Key.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.Key.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, voteType, block2), // different block
} }
for _, c := range cases { for _, c := range cases {
@ -136,9 +172,12 @@ func TestSignVote(t *testing.T) {
func TestSignProposal(t *testing.T) { func TestSignProposal(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
tempFile, err := ioutil.TempFile("", "priv_validator_") tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_")
require.Nil(t, err) 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}}} 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}}} block2 := types.BlockID{[]byte{3, 2, 1}, types.PartSetHeader{10, []byte{3, 2, 1}}}
@ -175,9 +214,12 @@ func TestSignProposal(t *testing.T) {
} }
func TestDifferByTimestamp(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) 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}}} block1 := types.BlockID{[]byte{1, 2, 3}, types.PartSetHeader{5, []byte{1, 2, 3}}}
height, round := int64(10), 1 height, round := int64(10), 1
@ -208,7 +250,7 @@ func TestDifferByTimestamp(t *testing.T) {
{ {
voteType := byte(types.PrevoteType) voteType := byte(types.PrevoteType)
blockID := types.BlockID{[]byte{1, 2, 3}, types.PartSetHeader{}} 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) err := privVal.SignVote("mychainid", vote)
assert.NoError(t, err, "expected no error signing vote") assert.NoError(t, err, "expected no error signing vote")

View File

@ -119,8 +119,9 @@ func NewTendermint(app abci.Application) *nm.Node {
config := GetConfig() config := GetConfig()
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
logger = log.NewFilter(logger, log.AllowError()) logger = log.NewFilter(logger, log.AllowError())
pvFile := config.PrivValidatorFile() pvKeyFile := config.PrivValidatorKeyFile()
pv := privval.LoadOrGenFilePV(pvFile) pvKeyStateFile := config.PrivValidatorStateFile()
pv := privval.LoadOrGenFilePV(pvKeyFile, pvKeyStateFile)
papp := proxy.NewLocalClientCreator(app) papp := proxy.NewLocalClientCreator(app)
nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile()) nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
if err != nil { if err != nil {

View File

@ -48,6 +48,18 @@ type PrivVal struct {
PrivKey Data `json:"priv_key"` PrivKey Data `json:"priv_key"`
} }
type PrivValKey struct {
Address cmn.HexBytes `json:"address"`
PubKey Data `json:"pub_key"`
PrivKey Data `json:"priv_key"`
}
type PrivValState struct {
LastHeight int64 `json:"last_height"`
LastRound int `json:"last_round"`
LastStep int8 `json:"last_step"`
}
type Data struct { type Data struct {
Type string `json:"type"` Type string `json:"type"`
Data cmn.HexBytes `json:"data"` Data cmn.HexBytes `json:"data"`
@ -72,8 +84,8 @@ func convertNodeKey(cdc *amino.Codec, jsonBytes []byte) ([]byte, error) {
return bz, nil return bz, nil
} }
func convertPrivVal(cdc *amino.Codec, jsonBytes []byte) ([]byte, error) { func convertPrivValKey(cdc *amino.Codec, jsonBytes []byte) ([]byte, error) {
var privVal PrivVal var privVal PrivValKey
err := json.Unmarshal(jsonBytes, &privVal) err := json.Unmarshal(jsonBytes, &privVal)
if err != nil { if err != nil {
return nil, err return nil, err
@ -85,13 +97,30 @@ func convertPrivVal(cdc *amino.Codec, jsonBytes []byte) ([]byte, error) {
var pubKey ed25519.PubKeyEd25519 var pubKey ed25519.PubKeyEd25519
copy(pubKey[:], privVal.PubKey.Data) copy(pubKey[:], privVal.PubKey.Data)
privValNew := privval.FilePV{ privValNew := privval.FilePVKey{
Address: pubKey.Address(), Address: pubKey.Address(),
PubKey: pubKey, PubKey: pubKey,
LastHeight: privVal.LastHeight, PrivKey: privKey,
LastRound: privVal.LastRound, }
LastStep: privVal.LastStep,
PrivKey: privKey, bz, err := cdc.MarshalJSON(privValNew)
if err != nil {
return nil, err
}
return bz, nil
}
func convertPrivValState(cdc *amino.Codec, jsonBytes []byte) ([]byte, error) {
var privVal PrivValState
err := json.Unmarshal(jsonBytes, &privVal)
if err != nil {
return nil, err
}
privValNew := privval.FilePVLastSignState{
Height: privVal.LastHeight,
Round: privVal.LastRound,
Step: privVal.LastStep,
} }
bz, err := cdc.MarshalJSON(privValNew) bz, err := cdc.MarshalJSON(privValNew)
@ -165,8 +194,10 @@ func main() {
switch fileName { switch fileName {
case "node_key.json": case "node_key.json":
bz, err = convertNodeKey(cdc, fileBytes) bz, err = convertNodeKey(cdc, fileBytes)
case "priv_validator.json": case "priv_validator_key.json":
bz, err = convertPrivVal(cdc, fileBytes) bz, err = convertPrivValKey(cdc, fileBytes)
case "priv_validator_state.json":
bz, err = convertPrivValState(cdc, fileBytes)
case "genesis.json": case "genesis.json":
bz, err = convertGenesis(cdc, fileBytes) bz, err = convertGenesis(cdc, fileBytes)
default: default: