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.

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