From 928d9dad99ee40e4974c32d0c476ddac77560561 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Mon, 17 Dec 2018 23:26:33 -0500 Subject: [PATCH] upgrade path --- config/config.go | 11 +++++ node/node.go | 21 ++++++++- privval/old_priv_validator.go | 80 +++++++++++++++++++++++++++++++++++ privval/priv_validator.go | 6 +-- scripts/privValUpgrade.go | 41 ++++++++++++++++++ 5 files changed, 155 insertions(+), 4 deletions(-) create mode 100644 privval/old_priv_validator.go create mode 100644 scripts/privValUpgrade.go diff --git a/config/config.go b/config/config.go index 85d55b556..4bdd87236 100644 --- a/config/config.go +++ b/config/config.go @@ -50,6 +50,11 @@ var ( 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 type Config struct { // Top level options use an anonymous struct @@ -236,6 +241,12 @@ 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 func (cfg BaseConfig) NodeKeyFile() string { return rootify(cfg.NodeKey, cfg.RootDir) diff --git a/node/node.go b/node/node.go index f03e5bab5..daa7d7c40 100644 --- a/node/node.go +++ b/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.PrivValidatorKeyFile(), config.PrivValidatorStateFile()), + privval.LoadOrGenFilePV(newPrivValKey, newPrivValState), nodeKey, proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()), DefaultGenesisDocProviderFunc(config), diff --git a/privval/old_priv_validator.go b/privval/old_priv_validator.go new file mode 100644 index 000000000..ec72c1834 --- /dev/null +++ b/privval/old_priv_validator.go @@ -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 +} diff --git a/privval/priv_validator.go b/privval/priv_validator.go index b4d4ba6e5..6473f10ee 100644 --- a/privval/priv_validator.go +++ b/privval/priv_validator.go @@ -141,7 +141,7 @@ type FilePV struct { // GenFilePV generates a new validator with randomly generated private key // and sets the filePaths, but does not call Save(). -func GenFilePV(keyFilePath string, stateFilePath string) *FilePV { +func GenFilePV(keyFilePath, stateFilePath string) *FilePV { privKey := ed25519.GenPrivKey() return &FilePV{ @@ -161,7 +161,7 @@ func GenFilePV(keyFilePath string, stateFilePath string) *FilePV { // LoadFilePV loads a FilePV from the filePaths. The FilePV handles double // signing prevention by persisting data to the stateFilePath. If the filePaths // do not exist, the FilePV must be created manually and saved. -func LoadFilePV(keyFilePath string, stateFilePath string) *FilePV { +func LoadFilePV(keyFilePath, stateFilePath string) *FilePV { keyJSONBytes, err := ioutil.ReadFile(keyFilePath) if err != nil { cmn.Exit(err.Error()) @@ -197,7 +197,7 @@ func LoadFilePV(keyFilePath string, stateFilePath string) *FilePV { // LoadOrGenFilePV loads a FilePV from the given filePaths // or else generates a new one and saves it to the filePaths. -func LoadOrGenFilePV(keyFilePath string, stateFilePath string) *FilePV { +func LoadOrGenFilePV(keyFilePath, stateFilePath string) *FilePV { var pv *FilePV if cmn.FileExists(keyFilePath) { pv = LoadFilePV(keyFilePath, stateFilePath) diff --git a/scripts/privValUpgrade.go b/scripts/privValUpgrade.go new file mode 100644 index 000000000..72ce505ee --- /dev/null +++ b/scripts/privValUpgrade.go @@ -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: ") + 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 +}