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
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. acm "github.com/tendermint/tendermint/account"
  9. "github.com/tendermint/tendermint/wire"
  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 := wire.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 := wire.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. }