github.com/noirx94/tendermintmp@v0.0.1/test/maverick/node/privval.go (about) 1 package node 2 3 import ( 4 "errors" 5 "fmt" 6 "io/ioutil" 7 8 "github.com/tendermint/tendermint/crypto" 9 "github.com/tendermint/tendermint/crypto/ed25519" 10 tmbytes "github.com/tendermint/tendermint/libs/bytes" 11 tmjson "github.com/tendermint/tendermint/libs/json" 12 tmos "github.com/tendermint/tendermint/libs/os" 13 "github.com/tendermint/tendermint/libs/tempfile" 14 tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 15 "github.com/tendermint/tendermint/types" 16 ) 17 18 // ******************************************************************************************************************* 19 // 20 // WARNING: FOR TESTING ONLY. DO NOT USE THIS FILE OUTSIDE MAVERICK 21 // 22 // ******************************************************************************************************************* 23 24 const ( 25 stepNone int8 = 0 // Used to distinguish the initial state 26 stepPropose int8 = 1 27 stepPrevote int8 = 2 28 stepPrecommit int8 = 3 29 ) 30 31 // A vote is either stepPrevote or stepPrecommit. 32 func voteToStep(vote *tmproto.Vote) int8 { 33 switch vote.Type { 34 case tmproto.PrevoteType: 35 return stepPrevote 36 case tmproto.PrecommitType: 37 return stepPrecommit 38 default: 39 panic(fmt.Sprintf("Unknown vote type: %v", vote.Type)) 40 } 41 } 42 43 //------------------------------------------------------------------------------- 44 45 // FilePVKey stores the immutable part of PrivValidator. 46 type FilePVKey struct { 47 Address types.Address `json:"address"` 48 PubKey crypto.PubKey `json:"pub_key"` 49 PrivKey crypto.PrivKey `json:"priv_key"` 50 51 filePath string 52 } 53 54 // Save persists the FilePVKey to its filePath. 55 func (pvKey FilePVKey) Save() { 56 outFile := pvKey.filePath 57 if outFile == "" { 58 panic("cannot save PrivValidator key: filePath not set") 59 } 60 61 jsonBytes, err := tmjson.MarshalIndent(pvKey, "", " ") 62 if err != nil { 63 panic(err) 64 } 65 err = tempfile.WriteFileAtomic(outFile, jsonBytes, 0600) 66 if err != nil { 67 panic(err) 68 } 69 70 } 71 72 //------------------------------------------------------------------------------- 73 74 // FilePVLastSignState stores the mutable part of PrivValidator. 75 type FilePVLastSignState struct { 76 Height int64 `json:"height"` 77 Round int32 `json:"round"` 78 Step int8 `json:"step"` 79 Signature []byte `json:"signature,omitempty"` 80 SignBytes tmbytes.HexBytes `json:"signbytes,omitempty"` 81 82 filePath string 83 } 84 85 // CheckHRS checks the given height, round, step (HRS) against that of the 86 // FilePVLastSignState. It returns an error if the arguments constitute a regression, 87 // or if they match but the SignBytes are empty. 88 // The returned boolean indicates whether the last Signature should be reused - 89 // it returns true if the HRS matches the arguments and the SignBytes are not empty (indicating 90 // we have already signed for this HRS, and can reuse the existing signature). 91 // It panics if the HRS matches the arguments, there's a SignBytes, but no Signature. 92 func (lss *FilePVLastSignState) CheckHRS(height int64, round int32, step int8) (bool, error) { 93 94 if lss.Height > height { 95 return false, fmt.Errorf("height regression. Got %v, last height %v", height, lss.Height) 96 } 97 98 if lss.Height == height { 99 if lss.Round > round { 100 return false, fmt.Errorf("round regression at height %v. Got %v, last round %v", height, round, lss.Round) 101 } 102 103 if lss.Round == round { 104 if lss.Step > step { 105 return false, fmt.Errorf( 106 "step regression at height %v round %v. Got %v, last step %v", 107 height, 108 round, 109 step, 110 lss.Step, 111 ) 112 } else if lss.Step == step { 113 if lss.SignBytes != nil { 114 if lss.Signature == nil { 115 panic("pv: Signature is nil but SignBytes is not!") 116 } 117 return true, nil 118 } 119 return false, errors.New("no SignBytes found") 120 } 121 } 122 } 123 return false, nil 124 } 125 126 // Save persists the FilePvLastSignState to its filePath. 127 func (lss *FilePVLastSignState) Save() { 128 outFile := lss.filePath 129 if outFile == "" { 130 panic("cannot save FilePVLastSignState: filePath not set") 131 } 132 jsonBytes, err := tmjson.MarshalIndent(lss, "", " ") 133 if err != nil { 134 panic(err) 135 } 136 err = tempfile.WriteFileAtomic(outFile, jsonBytes, 0600) 137 if err != nil { 138 panic(err) 139 } 140 } 141 142 //------------------------------------------------------------------------------- 143 144 // FilePV implements PrivValidator using data persisted to disk 145 // to prevent double signing. 146 // NOTE: the directories containing pv.Key.filePath and pv.LastSignState.filePath must already exist. 147 // It includes the LastSignature and LastSignBytes so we don't lose the signature 148 // if the process crashes after signing but before the resulting consensus message is processed. 149 type FilePV struct { 150 Key FilePVKey 151 LastSignState FilePVLastSignState 152 } 153 154 // GenFilePV generates a new validator with randomly generated private key 155 // and sets the filePaths, but does not call Save(). 156 func GenFilePV(keyFilePath, stateFilePath string) *FilePV { 157 privKey := ed25519.GenPrivKey() 158 159 return &FilePV{ 160 Key: FilePVKey{ 161 Address: privKey.PubKey().Address(), 162 PubKey: privKey.PubKey(), 163 PrivKey: privKey, 164 filePath: keyFilePath, 165 }, 166 LastSignState: FilePVLastSignState{ 167 Step: stepNone, 168 filePath: stateFilePath, 169 }, 170 } 171 } 172 173 // LoadFilePV loads a FilePV from the filePaths. The FilePV handles double 174 // signing prevention by persisting data to the stateFilePath. If either file path 175 // does not exist, the program will exit. 176 func LoadFilePV(keyFilePath, stateFilePath string) *FilePV { 177 return loadFilePV(keyFilePath, stateFilePath, true) 178 } 179 180 // LoadFilePVEmptyState loads a FilePV from the given keyFilePath, with an empty LastSignState. 181 // If the keyFilePath does not exist, the program will exit. 182 func LoadFilePVEmptyState(keyFilePath, stateFilePath string) *FilePV { 183 return loadFilePV(keyFilePath, stateFilePath, false) 184 } 185 186 // If loadState is true, we load from the stateFilePath. Otherwise, we use an empty LastSignState. 187 func loadFilePV(keyFilePath, stateFilePath string, loadState bool) *FilePV { 188 keyJSONBytes, err := ioutil.ReadFile(keyFilePath) 189 if err != nil { 190 tmos.Exit(err.Error()) 191 } 192 pvKey := FilePVKey{} 193 err = tmjson.Unmarshal(keyJSONBytes, &pvKey) 194 if err != nil { 195 tmos.Exit(fmt.Sprintf("Error reading PrivValidator key from %v: %v\n", keyFilePath, err)) 196 } 197 198 // overwrite pubkey and address for convenience 199 pvKey.PubKey = pvKey.PrivKey.PubKey() 200 pvKey.Address = pvKey.PubKey.Address() 201 pvKey.filePath = keyFilePath 202 203 pvState := FilePVLastSignState{} 204 205 if loadState { 206 stateJSONBytes, err := ioutil.ReadFile(stateFilePath) 207 if err != nil { 208 tmos.Exit(err.Error()) 209 } 210 err = tmjson.Unmarshal(stateJSONBytes, &pvState) 211 if err != nil { 212 tmos.Exit(fmt.Sprintf("Error reading PrivValidator state from %v: %v\n", stateFilePath, err)) 213 } 214 } 215 216 pvState.filePath = stateFilePath 217 218 return &FilePV{ 219 Key: pvKey, 220 LastSignState: pvState, 221 } 222 } 223 224 // LoadOrGenFilePV loads a FilePV from the given filePaths 225 // or else generates a new one and saves it to the filePaths. 226 func LoadOrGenFilePV(keyFilePath, stateFilePath string) *FilePV { 227 var pv *FilePV 228 if tmos.FileExists(keyFilePath) { 229 pv = LoadFilePV(keyFilePath, stateFilePath) 230 } else { 231 pv = GenFilePV(keyFilePath, stateFilePath) 232 pv.Save() 233 } 234 return pv 235 } 236 237 // GetAddress returns the address of the validator. 238 // Implements PrivValidator. 239 func (pv *FilePV) GetAddress() types.Address { 240 return pv.Key.Address 241 } 242 243 // GetPubKey returns the public key of the validator. 244 // Implements PrivValidator. 245 func (pv *FilePV) GetPubKey() (crypto.PubKey, error) { 246 return pv.Key.PubKey, nil 247 } 248 249 // SignVote signs a canonical representation of the vote, along with the 250 // chainID. Implements PrivValidator. 251 func (pv *FilePV) SignVote(chainID string, vote *tmproto.Vote) error { 252 if err := pv.signVote(chainID, vote); err != nil { 253 return fmt.Errorf("error signing vote: %v", err) 254 } 255 return nil 256 } 257 258 // SignProposal signs a canonical representation of the proposal, along with 259 // the chainID. Implements PrivValidator. 260 func (pv *FilePV) SignProposal(chainID string, proposal *tmproto.Proposal) error { 261 if err := pv.signProposal(chainID, proposal); err != nil { 262 return fmt.Errorf("error signing proposal: %v", err) 263 } 264 return nil 265 } 266 267 // Save persists the FilePV to disk. 268 func (pv *FilePV) Save() { 269 pv.Key.Save() 270 pv.LastSignState.Save() 271 } 272 273 // Reset resets all fields in the FilePV. 274 // NOTE: Unsafe! 275 func (pv *FilePV) Reset() { 276 var sig []byte 277 pv.LastSignState.Height = 0 278 pv.LastSignState.Round = 0 279 pv.LastSignState.Step = 0 280 pv.LastSignState.Signature = sig 281 pv.LastSignState.SignBytes = nil 282 pv.Save() 283 } 284 285 // String returns a string representation of the FilePV. 286 func (pv *FilePV) String() string { 287 return fmt.Sprintf( 288 "PrivValidator{%v LH:%v, LR:%v, LS:%v}", 289 pv.GetAddress(), 290 pv.LastSignState.Height, 291 pv.LastSignState.Round, 292 pv.LastSignState.Step, 293 ) 294 } 295 296 //------------------------------------------------------------------------------------ 297 298 // signVote checks if the vote is good to sign and sets the vote signature. 299 // It may need to set the timestamp as well if the vote is otherwise the same as 300 // a previously signed vote (ie. we crashed after signing but before the vote hit the WAL). 301 func (pv *FilePV) signVote(chainID string, vote *tmproto.Vote) error { 302 height, round, step := vote.Height, vote.Round, voteToStep(vote) 303 304 lss := pv.LastSignState 305 306 _, err := lss.CheckHRS(height, round, step) 307 if err != nil { 308 return err 309 } 310 311 signBytes := types.VoteSignBytes(chainID, vote) 312 313 // It passed the checks. Sign the vote 314 sig, err := pv.Key.PrivKey.Sign(signBytes) 315 if err != nil { 316 return err 317 } 318 pv.saveSigned(height, round, step, signBytes, sig) 319 vote.Signature = sig 320 return nil 321 } 322 323 // signProposal checks if the proposal is good to sign and sets the proposal signature. 324 // It may need to set the timestamp as well if the proposal is otherwise the same as 325 // a previously signed proposal ie. we crashed after signing but before the proposal hit the WAL). 326 func (pv *FilePV) signProposal(chainID string, proposal *tmproto.Proposal) error { 327 height, round, step := proposal.Height, proposal.Round, stepPropose 328 329 lss := pv.LastSignState 330 331 _, err := lss.CheckHRS(height, round, step) 332 if err != nil { 333 return err 334 } 335 336 signBytes := types.ProposalSignBytes(chainID, proposal) 337 338 // It passed the checks. Sign the proposal 339 sig, err := pv.Key.PrivKey.Sign(signBytes) 340 if err != nil { 341 return err 342 } 343 pv.saveSigned(height, round, step, signBytes, sig) 344 proposal.Signature = sig 345 return nil 346 } 347 348 // Persist height/round/step and signature 349 func (pv *FilePV) saveSigned(height int64, round int32, step int8, 350 signBytes []byte, sig []byte) { 351 352 pv.LastSignState.Height = height 353 pv.LastSignState.Round = round 354 pv.LastSignState.Step = step 355 pv.LastSignState.Signature = sig 356 pv.LastSignState.SignBytes = signBytes 357 pv.LastSignState.Save() 358 }