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.

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