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.

190 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 := ioutil.WriteFile(privVal.filename, jsonBytes, 0700)
  90. if err != nil {
  91. panic(err)
  92. }
  93. }
  94. // TODO: test
  95. func (privVal *PrivValidator) SignVote(vote *types.Vote) error {
  96. privVal.mtx.Lock()
  97. defer privVal.mtx.Unlock()
  98. // If height regression, panic
  99. if privVal.LastHeight > vote.Height {
  100. return errors.New("Height regression in SignVote")
  101. }
  102. // More cases for when the height matches
  103. if privVal.LastHeight == vote.Height {
  104. // If attempting any sign after commit, panic
  105. if privVal.LastStep == stepCommit {
  106. return errors.New("SignVote on matching height after a commit")
  107. }
  108. // If round regression, panic
  109. if privVal.LastRound > vote.Round {
  110. return errors.New("Round regression in SignVote")
  111. }
  112. // If step regression, panic
  113. if privVal.LastRound == vote.Round && privVal.LastStep > voteToStep(vote) {
  114. return errors.New("Step regression in SignVote")
  115. }
  116. }
  117. // Persist height/round/step
  118. privVal.LastHeight = vote.Height
  119. privVal.LastRound = vote.Round
  120. privVal.LastStep = voteToStep(vote)
  121. privVal.save()
  122. // Sign
  123. privVal.SignVoteUnsafe(vote)
  124. return nil
  125. }
  126. func (privVal *PrivValidator) SignVoteUnsafe(vote *types.Vote) {
  127. vote.Signature = privVal.PrivKey.Sign(account.SignBytes(vote)).(account.SignatureEd25519)
  128. }
  129. func (privVal *PrivValidator) SignProposal(proposal *Proposal) error {
  130. privVal.mtx.Lock()
  131. defer privVal.mtx.Unlock()
  132. if privVal.LastHeight < proposal.Height ||
  133. privVal.LastHeight == proposal.Height && privVal.LastRound < proposal.Round ||
  134. privVal.LastHeight == 0 && privVal.LastRound == 0 && privVal.LastStep == stepNone {
  135. // Persist height/round/step
  136. privVal.LastHeight = proposal.Height
  137. privVal.LastRound = proposal.Round
  138. privVal.LastStep = stepPropose
  139. privVal.save()
  140. // Sign
  141. proposal.Signature = privVal.PrivKey.Sign(account.SignBytes(proposal)).(account.SignatureEd25519)
  142. return nil
  143. } else {
  144. return errors.New(fmt.Sprintf("Attempt of duplicate signing of proposal: Height %v, Round %v", proposal.Height, proposal.Round))
  145. }
  146. }
  147. func (privVal *PrivValidator) SignRebondTx(rebondTx *types.RebondTx) error {
  148. privVal.mtx.Lock()
  149. defer privVal.mtx.Unlock()
  150. if privVal.LastHeight < rebondTx.Height {
  151. // Persist height/round/step
  152. privVal.LastHeight = rebondTx.Height
  153. privVal.LastRound = math.MaxUint64 // We can't do anything else for this rebondTx.Height.
  154. privVal.LastStep = math.MaxUint8
  155. privVal.save()
  156. // Sign
  157. rebondTx.Signature = privVal.PrivKey.Sign(account.SignBytes(rebondTx)).(account.SignatureEd25519)
  158. return nil
  159. } else {
  160. return errors.New(fmt.Sprintf("Attempt of duplicate signing of rebondTx: Height %v", rebondTx.Height))
  161. }
  162. }
  163. func (privVal *PrivValidator) String() string {
  164. return fmt.Sprintf("PrivValidator{%X LH:%v, LR:%v, LS:%v}", privVal.Address, privVal.LastHeight, privVal.LastRound, privVal.LastStep)
  165. }