github.com/okex/exchain@v1.8.0/libs/tendermint/privval/file.go (about) 1 package privval 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "io/ioutil" 8 "time" 9 10 "github.com/okex/exchain/libs/tendermint/crypto" 11 "github.com/okex/exchain/libs/tendermint/crypto/ed25519" 12 tmbytes "github.com/okex/exchain/libs/tendermint/libs/bytes" 13 tmos "github.com/okex/exchain/libs/tendermint/libs/os" 14 "github.com/okex/exchain/libs/tendermint/libs/tempfile" 15 "github.com/okex/exchain/libs/tendermint/types" 16 tmtime "github.com/okex/exchain/libs/tendermint/types/time" 17 ) 18 19 // TODO: type ? 20 const ( 21 stepNone int8 = 0 // Used to distinguish the initial state 22 stepPropose int8 = 1 23 stepPrevote int8 = 2 24 stepPrecommit int8 = 3 25 ) 26 27 // A vote is either stepPrevote or stepPrecommit. 28 func voteToStep(vote *types.Vote) int8 { 29 switch vote.Type { 30 case types.PrevoteType: 31 return stepPrevote 32 case types.PrecommitType: 33 return stepPrecommit 34 default: 35 panic(fmt.Sprintf("Unknown vote type: %v", vote.Type)) 36 } 37 } 38 39 //------------------------------------------------------------------------------- 40 41 // FilePVKey stores the immutable part of PrivValidator. 42 type FilePVKey struct { 43 Address types.Address `json:"address"` 44 PubKey crypto.PubKey `json:"pub_key"` 45 PrivKey crypto.PrivKey `json:"priv_key"` 46 47 filePath string 48 } 49 50 // Save persists the FilePVKey to its filePath. 51 func (pvKey FilePVKey) Save() { 52 outFile := pvKey.filePath 53 if outFile == "" { 54 panic("cannot save PrivValidator key: filePath not set") 55 } 56 57 jsonBytes, err := cdc.MarshalJSONIndent(pvKey, "", " ") 58 if err != nil { 59 panic(err) 60 } 61 err = tempfile.WriteFileAtomic(outFile, jsonBytes, 0600) 62 if err != nil { 63 panic(err) 64 } 65 66 } 67 68 //------------------------------------------------------------------------------- 69 70 // FilePVLastSignState stores the mutable part of PrivValidator. 71 type FilePVLastSignState struct { 72 Height int64 `json:"height"` 73 Round int `json:"round"` 74 Step int8 `json:"step"` 75 Signature []byte `json:"signature,omitempty"` 76 SignBytes tmbytes.HexBytes `json:"signbytes,omitempty"` 77 78 filePath string 79 } 80 81 // CheckHRS checks the given height, round, step (HRS) against that of the 82 // FilePVLastSignState. It returns an error if the arguments constitute a regression, 83 // or if they match but the SignBytes are empty. 84 // The returned boolean indicates whether the last Signature should be reused - 85 // it returns true if the HRS matches the arguments and the SignBytes are not empty (indicating 86 // we have already signed for this HRS, and can reuse the existing signature). 87 // It panics if the HRS matches the arguments, there's a SignBytes, but no Signature. 88 func (lss *FilePVLastSignState) CheckHRS(height int64, round int, step int8, avc bool) (bool, error) { 89 90 if lss.Height > height { 91 return false, fmt.Errorf("height regression. Got %v, last height %v", height, lss.Height) 92 } 93 94 if lss.Height == height { 95 if lss.Round > round { 96 return false, fmt.Errorf("round regression at height %v. Got %v, last round %v", height, round, lss.Round) 97 } 98 99 if lss.Round == round { 100 if avc { 101 return false, nil 102 } 103 if lss.Step > step { 104 return false, fmt.Errorf( 105 "step regression at height %v round %v. Got %v, last step %v", 106 height, 107 round, 108 step, 109 lss.Step, 110 ) 111 } else if lss.Step == step { 112 if lss.SignBytes != nil { 113 if lss.Signature == nil { 114 panic("pv: Signature is nil but SignBytes is not!") 115 } 116 return true, nil 117 } 118 return false, errors.New("no SignBytes found") 119 } 120 } 121 } 122 return false, nil 123 } 124 125 // Save persists the FilePvLastSignState to its filePath. 126 func (lss *FilePVLastSignState) Save() { 127 outFile := lss.filePath 128 if outFile == "" { 129 panic("cannot save FilePVLastSignState: filePath not set") 130 } 131 jsonBytes, err := cdc.MarshalJSONIndent(lss, "", " ") 132 if err != nil { 133 panic(err) 134 } 135 err = tempfile.WriteFileAtomic(outFile, jsonBytes, 0600) 136 if err != nil { 137 panic(err) 138 } 139 } 140 141 //------------------------------------------------------------------------------- 142 143 // FilePV implements PrivValidator using data persisted to disk 144 // to prevent double signing. 145 // NOTE: the directories containing pv.Key.filePath and pv.LastSignState.filePath must already exist. 146 // It includes the LastSignature and LastSignBytes so we don't lose the signature 147 // if the process crashes after signing but before the resulting consensus message is processed. 148 type FilePV struct { 149 Key FilePVKey 150 LastSignState FilePVLastSignState 151 } 152 153 // GenFilePV generates a new validator with randomly generated private key 154 // and sets the filePaths, but does not call Save(). 155 func GenFilePV(keyFilePath, stateFilePath string) *FilePV { 156 privKey := ed25519.GenPrivKey() 157 158 return &FilePV{ 159 Key: FilePVKey{ 160 Address: privKey.PubKey().Address(), 161 PubKey: privKey.PubKey(), 162 PrivKey: privKey, 163 filePath: keyFilePath, 164 }, 165 LastSignState: FilePVLastSignState{ 166 Step: stepNone, 167 filePath: stateFilePath, 168 }, 169 } 170 } 171 172 // LoadFilePV loads a FilePV from the filePaths. The FilePV handles double 173 // signing prevention by persisting data to the stateFilePath. If either file path 174 // does not exist, the program will exit. 175 func LoadFilePV(keyFilePath, stateFilePath string) *FilePV { 176 return loadFilePV(keyFilePath, stateFilePath, true) 177 } 178 179 // LoadFilePVEmptyState loads a FilePV from the given keyFilePath, with an empty LastSignState. 180 // If the keyFilePath does not exist, the program will exit. 181 func LoadFilePVEmptyState(keyFilePath, stateFilePath string) *FilePV { 182 return loadFilePV(keyFilePath, stateFilePath, false) 183 } 184 185 // If loadState is true, we load from the stateFilePath. Otherwise, we use an empty LastSignState. 186 func loadFilePV(keyFilePath, stateFilePath string, loadState bool) *FilePV { 187 keyJSONBytes, err := ioutil.ReadFile(keyFilePath) 188 if err != nil { 189 tmos.Exit(err.Error()) 190 } 191 pvKey := FilePVKey{} 192 err = cdc.UnmarshalJSON(keyJSONBytes, &pvKey) 193 if err != nil { 194 tmos.Exit(fmt.Sprintf("Error reading PrivValidator key from %v: %v\n", keyFilePath, err)) 195 } 196 197 // overwrite pubkey and address for convenience 198 pvKey.PubKey = pvKey.PrivKey.PubKey() 199 pvKey.Address = pvKey.PubKey.Address() 200 pvKey.filePath = keyFilePath 201 202 pvState := FilePVLastSignState{} 203 if loadState { 204 stateJSONBytes, err := ioutil.ReadFile(stateFilePath) 205 if err != nil { 206 tmos.Exit(err.Error()) 207 } 208 err = cdc.UnmarshalJSON(stateJSONBytes, &pvState) 209 if err != nil { 210 tmos.Exit(fmt.Sprintf("Error reading PrivValidator state from %v: %v\n", stateFilePath, err)) 211 } 212 } 213 214 pvState.filePath = stateFilePath 215 216 return &FilePV{ 217 Key: pvKey, 218 LastSignState: pvState, 219 } 220 } 221 222 // LoadOrGenFilePV loads a FilePV from the given filePaths 223 // or else generates a new one and saves it to the filePaths. 224 func LoadOrGenFilePV(keyFilePath, stateFilePath string) *FilePV { 225 var pv *FilePV 226 if tmos.FileExists(keyFilePath) { 227 pv = LoadFilePV(keyFilePath, stateFilePath) 228 } else { 229 pv = GenFilePV(keyFilePath, stateFilePath) 230 pv.Save() 231 } 232 return pv 233 } 234 235 // GetAddress returns the address of the validator. 236 // Implements PrivValidator. 237 func (pv *FilePV) GetAddress() types.Address { 238 return pv.Key.Address 239 } 240 241 // GetPubKey returns the public key of the validator. 242 // Implements PrivValidator. 243 func (pv *FilePV) GetPubKey() (crypto.PubKey, error) { 244 return pv.Key.PubKey, nil 245 } 246 247 // SignVote signs a canonical representation of the vote, along with the 248 // chainID. Implements PrivValidator. 249 func (pv *FilePV) SignVote(chainID string, vote *types.Vote) error { 250 if err := pv.signVote(chainID, vote); err != nil { 251 return fmt.Errorf("error signing vote: %v", err) 252 } 253 return nil 254 } 255 256 // SignProposal signs a canonical representation of the proposal, along with 257 // the chainID. Implements PrivValidator. 258 func (pv *FilePV) SignProposal(chainID string, proposal *types.Proposal) error { 259 if err := pv.signProposal(chainID, proposal); err != nil { 260 return fmt.Errorf("error signing proposal: %v", err) 261 } 262 return nil 263 } 264 265 // SignBytes signs some bytes. Implements PrivValidator. 266 func (pv *FilePV) SignBytes(bz []byte) ([]byte, error) { 267 return pv.Key.PrivKey.Sign(bz) 268 } 269 270 // Save persists the FilePV to disk. 271 func (pv *FilePV) Save() { 272 pv.Key.Save() 273 pv.LastSignState.Save() 274 } 275 276 // Reset resets all fields in the FilePV. 277 // NOTE: Unsafe! 278 func (pv *FilePV) Reset() { 279 var sig []byte 280 pv.LastSignState.Height = 0 281 pv.LastSignState.Round = 0 282 pv.LastSignState.Step = 0 283 pv.LastSignState.Signature = sig 284 pv.LastSignState.SignBytes = nil 285 pv.Save() 286 } 287 288 // String returns a string representation of the FilePV. 289 func (pv *FilePV) String() string { 290 return fmt.Sprintf( 291 "PrivValidator{%v LH:%v, LR:%v, LS:%v}", 292 pv.GetAddress(), 293 pv.LastSignState.Height, 294 pv.LastSignState.Round, 295 pv.LastSignState.Step, 296 ) 297 } 298 299 //------------------------------------------------------------------------------------ 300 301 // signVote checks if the vote is good to sign and sets the vote signature. 302 // It may need to set the timestamp as well if the vote is otherwise the same as 303 // a previously signed vote (ie. we crashed after signing but before the vote hit the WAL). 304 func (pv *FilePV) signVote(chainID string, vote *types.Vote) error { 305 height, round, step := vote.Height, vote.Round, voteToStep(vote) 306 307 lss := pv.LastSignState 308 309 sameHRS, err := lss.CheckHRS(height, round, step, vote.HasVC) 310 if err != nil { 311 return err 312 } 313 314 signBytes := vote.SignBytes(chainID) 315 316 // We might crash before writing to the wal, 317 // causing us to try to re-sign for the same HRS. 318 // If signbytes are the same, use the last signature. 319 // If they only differ by timestamp, use last timestamp and signature 320 // Otherwise, return error 321 if sameHRS { 322 if bytes.Equal(signBytes, lss.SignBytes) { 323 vote.Signature = lss.Signature 324 } else if timestamp, ok := checkVotesOnlyDifferByTimestamp(lss.Height, lss.SignBytes, signBytes); ok { 325 vote.Timestamp = timestamp 326 vote.Signature = lss.Signature 327 } else { 328 err = fmt.Errorf("conflicting data") 329 } 330 return err 331 } 332 333 // It passed the checks. Sign the vote 334 sig, err := pv.Key.PrivKey.Sign(signBytes) 335 if err != nil { 336 return err 337 } 338 pv.saveSigned(height, round, step, signBytes, sig) 339 vote.Signature = sig 340 return nil 341 } 342 343 // signProposal checks if the proposal is good to sign and sets the proposal signature. 344 // It may need to set the timestamp as well if the proposal is otherwise the same as 345 // a previously signed proposal ie. we crashed after signing but before the proposal hit the WAL). 346 func (pv *FilePV) signProposal(chainID string, proposal *types.Proposal) error { 347 height, round, step := proposal.Height, proposal.Round, stepPropose 348 349 lss := pv.LastSignState 350 351 sameHRS, err := lss.CheckHRS(height, round, step, proposal.HasVC) 352 if err != nil { 353 return err 354 } 355 356 signBytes := proposal.SignBytes(chainID) 357 358 // We might crash before writing to the wal, 359 // causing us to try to re-sign for the same HRS. 360 // If signbytes are the same, use the last signature. 361 // If they only differ by timestamp, use last timestamp and signature 362 // Otherwise, return error 363 if sameHRS { 364 if bytes.Equal(signBytes, lss.SignBytes) { 365 proposal.Signature = lss.Signature 366 } else if timestamp, ok := checkProposalsOnlyDifferByTimestamp(lss.SignBytes, signBytes); ok { 367 proposal.Timestamp = timestamp 368 proposal.Signature = lss.Signature 369 } else { 370 err = fmt.Errorf("conflicting data") 371 } 372 return err 373 } 374 375 // It passed the checks. Sign the proposal 376 sig, err := pv.Key.PrivKey.Sign(signBytes) 377 if err != nil { 378 return err 379 } 380 pv.saveSigned(height, round, step, signBytes, sig) 381 proposal.Signature = sig 382 return nil 383 } 384 385 // Persist height/round/step and signature 386 func (pv *FilePV) saveSigned(height int64, round int, step int8, 387 signBytes []byte, sig []byte) { 388 389 pv.LastSignState.Height = height 390 pv.LastSignState.Round = round 391 pv.LastSignState.Step = step 392 pv.LastSignState.Signature = sig 393 pv.LastSignState.SignBytes = signBytes 394 pv.LastSignState.Save() 395 } 396 397 //----------------------------------------------------------------------------------------- 398 399 // returns the timestamp from the lastSignBytes. 400 // returns true if the only difference in the votes is their timestamp. 401 //func checkVotesOnlyDifferByTimestamp(lastSignBytes, newSignBytes []byte) (time.Time, bool) { 402 // var lastVote, newVote types.CanonicalVote 403 // if err := cdc.UnmarshalBinaryLengthPrefixed(lastSignBytes, &lastVote); err != nil { 404 // panic(fmt.Sprintf("LastSignBytes cannot be unmarshalled into vote: %v", err)) 405 // } 406 // if err := cdc.UnmarshalBinaryLengthPrefixed(newSignBytes, &newVote); err != nil { 407 // panic(fmt.Sprintf("signBytes cannot be unmarshalled into vote: %v", err)) 408 // } 409 // 410 // lastTime := lastVote.Timestamp 411 // 412 // // set the times to the same value and check equality 413 // now := tmtime.Now() 414 // lastVote.Timestamp = now 415 // newVote.Timestamp = now 416 // lastVoteBytes, _ := cdc.MarshalJSON(lastVote) 417 // newVoteBytes, _ := cdc.MarshalJSON(newVote) 418 // 419 // return lastTime, bytes.Equal(newVoteBytes, lastVoteBytes) 420 //} 421 422 func checkVotesOnlyDifferByTimestamp(h int64, lastSignBytes, newSignBytes []byte) (time.Time, bool) { 423 if types.HigherThanVenus1(h) { 424 return ibcCheckVotesOnlyDifferByTimestamp(lastSignBytes, newSignBytes) 425 } 426 return originCheckVotesOnlyDifferByTimestamp(lastSignBytes, newSignBytes) 427 } 428 429 // returns the timestamp from the lastSignBytes. 430 // returns true if the only difference in the proposals is their timestamp 431 func checkProposalsOnlyDifferByTimestamp(lastSignBytes, newSignBytes []byte) (time.Time, bool) { 432 var lastProposal, newProposal types.CanonicalProposal 433 if err := cdc.UnmarshalBinaryLengthPrefixed(lastSignBytes, &lastProposal); err != nil { 434 panic(fmt.Sprintf("LastSignBytes cannot be unmarshalled into proposal: %v", err)) 435 } 436 if err := cdc.UnmarshalBinaryLengthPrefixed(newSignBytes, &newProposal); err != nil { 437 panic(fmt.Sprintf("signBytes cannot be unmarshalled into proposal: %v", err)) 438 } 439 440 lastTime := lastProposal.Timestamp 441 // set the times to the same value and check equality 442 now := tmtime.Now() 443 lastProposal.Timestamp = now 444 newProposal.Timestamp = now 445 lastProposalBytes, _ := cdc.MarshalBinaryLengthPrefixed(lastProposal) 446 newProposalBytes, _ := cdc.MarshalBinaryLengthPrefixed(newProposal) 447 448 return lastTime, bytes.Equal(newProposalBytes, lastProposalBytes) 449 }