You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

187 lines
5.3 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. package state
  2. import (
  3. "errors"
  4. "fmt"
  5. "io/ioutil"
  6. "math"
  7. "sync"
  8. "github.com/tendermint/tendermint/account"
  9. "github.com/tendermint/tendermint/binary"
  10. . "github.com/tendermint/tendermint/common"
  11. . "github.com/tendermint/tendermint/consensus/types"
  12. "github.com/tendermint/tendermint/types"
  13. "github.com/tendermint/tendermint/Godeps/_workspace/src/github.com/tendermint/ed25519"
  14. )
  15. const (
  16. stepNone = 0 // Used to distinguish the initial state
  17. stepPropose = 1
  18. stepPrevote = 2
  19. stepPrecommit = 3
  20. )
  21. func voteToStep(vote *types.Vote) int8 {
  22. switch vote.Type {
  23. case types.VoteTypePrevote:
  24. return stepPrevote
  25. case types.VoteTypePrecommit:
  26. return stepPrecommit
  27. default:
  28. // SANITY CHECK (binary decoding should catch bad vote types
  29. // before they get here (right?!)
  30. panic("Unknown vote type")
  31. }
  32. }
  33. type PrivValidator struct {
  34. Address []byte `json:"address"`
  35. PubKey account.PubKeyEd25519 `json:"pub_key"`
  36. PrivKey account.PrivKeyEd25519 `json:"priv_key"`
  37. LastHeight int `json:"last_height"`
  38. LastRound int `json:"last_round"`
  39. LastStep int8 `json:"last_step"`
  40. // For persistence.
  41. // Overloaded for testing.
  42. filePath string
  43. mtx sync.Mutex
  44. }
  45. // Generates a new validator with private key.
  46. func GenPrivValidator() *PrivValidator {
  47. privKeyBytes := new([64]byte)
  48. copy(privKeyBytes[:32], CRandBytes(32))
  49. pubKeyBytes := ed25519.MakePublicKey(privKeyBytes)
  50. pubKey := account.PubKeyEd25519(pubKeyBytes[:])
  51. privKey := account.PrivKeyEd25519(privKeyBytes[:])
  52. return &PrivValidator{
  53. Address: pubKey.Address(),
  54. PubKey: pubKey,
  55. PrivKey: privKey,
  56. LastHeight: 0,
  57. LastRound: 0,
  58. LastStep: stepNone,
  59. filePath: "",
  60. }
  61. }
  62. func LoadPrivValidator(filePath string) *PrivValidator {
  63. privValJSONBytes, err := ioutil.ReadFile(filePath)
  64. if err != nil {
  65. Exit(err.Error())
  66. }
  67. privVal := binary.ReadJSON(&PrivValidator{}, privValJSONBytes, &err).(*PrivValidator)
  68. if err != nil {
  69. Exit(Fmt("Error reading PrivValidator from %v: %v\n", filePath, err))
  70. }
  71. privVal.filePath = filePath
  72. return privVal
  73. }
  74. func (privVal *PrivValidator) SetFile(filePath string) {
  75. privVal.mtx.Lock()
  76. defer privVal.mtx.Unlock()
  77. privVal.filePath = filePath
  78. }
  79. func (privVal *PrivValidator) Save() {
  80. privVal.mtx.Lock()
  81. defer privVal.mtx.Unlock()
  82. privVal.save()
  83. }
  84. func (privVal *PrivValidator) save() {
  85. if privVal.filePath == "" {
  86. // SANITY CHECK
  87. panic("Cannot save PrivValidator: filePath not set")
  88. }
  89. jsonBytes := binary.JSONBytes(privVal)
  90. err := WriteFileAtomic(privVal.filePath, jsonBytes)
  91. if err != nil {
  92. // `@; BOOM!!!
  93. panic(err)
  94. }
  95. }
  96. func (privVal *PrivValidator) SignVote(chainID string, vote *types.Vote) error {
  97. privVal.mtx.Lock()
  98. defer privVal.mtx.Unlock()
  99. // If height regression, panic
  100. if privVal.LastHeight > vote.Height {
  101. return errors.New("Height regression in SignVote")
  102. }
  103. // More cases for when the height matches
  104. if privVal.LastHeight == vote.Height {
  105. // If round regression, panic
  106. if privVal.LastRound > vote.Round {
  107. return errors.New("Round regression in SignVote")
  108. }
  109. // If step regression, panic
  110. if privVal.LastRound == vote.Round && privVal.LastStep > voteToStep(vote) {
  111. return errors.New("Step regression in SignVote")
  112. }
  113. }
  114. // Persist height/round/step
  115. privVal.LastHeight = vote.Height
  116. privVal.LastRound = vote.Round
  117. privVal.LastStep = voteToStep(vote)
  118. privVal.save()
  119. // Sign
  120. privVal.SignVoteUnsafe(chainID, vote)
  121. return nil
  122. }
  123. func (privVal *PrivValidator) SignVoteUnsafe(chainID string, vote *types.Vote) {
  124. vote.Signature = privVal.PrivKey.Sign(account.SignBytes(chainID, vote)).(account.SignatureEd25519)
  125. }
  126. func (privVal *PrivValidator) SignProposal(chainID string, proposal *Proposal) error {
  127. privVal.mtx.Lock()
  128. defer privVal.mtx.Unlock()
  129. if privVal.LastHeight < proposal.Height ||
  130. privVal.LastHeight == proposal.Height && privVal.LastRound < proposal.Round ||
  131. privVal.LastHeight == 0 && privVal.LastRound == 0 && privVal.LastStep == stepNone {
  132. // Persist height/round/step
  133. privVal.LastHeight = proposal.Height
  134. privVal.LastRound = proposal.Round
  135. privVal.LastStep = stepPropose
  136. privVal.save()
  137. // Sign
  138. proposal.Signature = privVal.PrivKey.Sign(account.SignBytes(chainID, proposal)).(account.SignatureEd25519)
  139. return nil
  140. } else {
  141. return errors.New(fmt.Sprintf("Attempt of duplicate signing of proposal: Height %v, Round %v", proposal.Height, proposal.Round))
  142. }
  143. }
  144. func (privVal *PrivValidator) SignRebondTx(chainID string, rebondTx *types.RebondTx) error {
  145. privVal.mtx.Lock()
  146. defer privVal.mtx.Unlock()
  147. if privVal.LastHeight < rebondTx.Height {
  148. // Persist height/round/step
  149. // Prevent doing anything else for this rebondTx.Height.
  150. privVal.LastHeight = rebondTx.Height
  151. privVal.LastRound = math.MaxInt32 // MaxInt64 overflows on 32bit architectures.
  152. privVal.LastStep = math.MaxInt8
  153. privVal.save()
  154. // Sign
  155. rebondTx.Signature = privVal.PrivKey.Sign(account.SignBytes(chainID, rebondTx)).(account.SignatureEd25519)
  156. return nil
  157. } else {
  158. return errors.New(fmt.Sprintf("Attempt of duplicate signing of rebondTx: Height %v", rebondTx.Height))
  159. }
  160. }
  161. func (privVal *PrivValidator) String() string {
  162. return fmt.Sprintf("PrivValidator{%X LH:%v, LR:%v, LS:%v}", privVal.Address, privVal.LastHeight, privVal.LastRound, privVal.LastStep)
  163. }