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.

185 lines
5.1 KiB

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