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.

183 lines
4.8 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. blk "github.com/tendermint/tendermint/block"
  12. . "github.com/tendermint/tendermint/common"
  13. "github.com/tendermint/tendermint/config"
  14. . "github.com/tendermint/tendermint/consensus/types"
  15. "github.com/tendermint/go-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 *blk.Vote) uint8 {
  25. switch vote.Type {
  26. case blk.VoteTypePrevote:
  27. return stepPrevote
  28. case blk.VoteTypePrecommit:
  29. return stepPrecommit
  30. case blk.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 := CRandBytes(32)
  51. pubKeyBytes := ed25519.MakePubKey(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. filename: config.App.GetString("PrivValidatorFile"),
  62. }
  63. }
  64. func LoadPrivValidator(filename string) *PrivValidator {
  65. privValJSONBytes, err := ioutil.ReadFile(filename)
  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", filename, err))
  72. }
  73. privVal.filename = filename
  74. return privVal
  75. }
  76. func (privVal *PrivValidator) Save() {
  77. privVal.mtx.Lock()
  78. defer privVal.mtx.Unlock()
  79. privVal.save()
  80. }
  81. func (privVal *PrivValidator) save() {
  82. jsonBytes := binary.JSONBytes(privVal)
  83. err := ioutil.WriteFile(privVal.filename, jsonBytes, 0700)
  84. if err != nil {
  85. panic(err)
  86. }
  87. }
  88. // TODO: test
  89. func (privVal *PrivValidator) SignVote(vote *blk.Vote) error {
  90. privVal.mtx.Lock()
  91. defer privVal.mtx.Unlock()
  92. // If height regression, panic
  93. if privVal.LastHeight > vote.Height {
  94. return errors.New("Height regression in SignVote")
  95. }
  96. // More cases for when the height matches
  97. if privVal.LastHeight == vote.Height {
  98. // If attempting any sign after commit, panic
  99. if privVal.LastStep == stepCommit {
  100. return errors.New("SignVote on matching height after a commit")
  101. }
  102. // If round regression, panic
  103. if privVal.LastRound > vote.Round {
  104. return errors.New("Round regression in SignVote")
  105. }
  106. // If step regression, panic
  107. if privVal.LastRound == vote.Round && privVal.LastStep > voteToStep(vote) {
  108. return errors.New("Step regression in SignVote")
  109. }
  110. }
  111. // Persist height/round/step
  112. privVal.LastHeight = vote.Height
  113. privVal.LastRound = vote.Round
  114. privVal.LastStep = voteToStep(vote)
  115. privVal.save()
  116. // Sign
  117. privVal.SignVoteUnsafe(vote)
  118. return nil
  119. }
  120. func (privVal *PrivValidator) SignVoteUnsafe(vote *blk.Vote) {
  121. vote.Signature = privVal.PrivKey.Sign(account.SignBytes(vote)).(account.SignatureEd25519)
  122. }
  123. func (privVal *PrivValidator) SignProposal(proposal *Proposal) error {
  124. privVal.mtx.Lock()
  125. defer privVal.mtx.Unlock()
  126. if privVal.LastHeight < proposal.Height ||
  127. privVal.LastHeight == proposal.Height && privVal.LastRound < proposal.Round ||
  128. privVal.LastHeight == 0 && privVal.LastRound == 0 && privVal.LastStep == stepNone {
  129. // Persist height/round/step
  130. privVal.LastHeight = proposal.Height
  131. privVal.LastRound = proposal.Round
  132. privVal.LastStep = stepPropose
  133. privVal.save()
  134. // Sign
  135. proposal.Signature = privVal.PrivKey.Sign(account.SignBytes(proposal)).(account.SignatureEd25519)
  136. return nil
  137. } else {
  138. return errors.New(fmt.Sprintf("Attempt of duplicate signing of proposal: Height %v, Round %v", proposal.Height, proposal.Round))
  139. }
  140. }
  141. func (privVal *PrivValidator) SignRebondTx(rebondTx *blk.RebondTx) error {
  142. privVal.mtx.Lock()
  143. defer privVal.mtx.Unlock()
  144. if privVal.LastHeight < rebondTx.Height {
  145. // Persist height/round/step
  146. privVal.LastHeight = rebondTx.Height
  147. privVal.LastRound = math.MaxUint64 // We can't do anything else for this rebondTx.Height.
  148. privVal.LastStep = math.MaxUint8
  149. privVal.save()
  150. // Sign
  151. rebondTx.Signature = privVal.PrivKey.Sign(account.SignBytes(rebondTx)).(account.SignatureEd25519)
  152. return nil
  153. } else {
  154. return errors.New(fmt.Sprintf("Attempt of duplicate signing of rebondTx: Height %v", rebondTx.Height))
  155. }
  156. }
  157. func (privVal *PrivValidator) String() string {
  158. return fmt.Sprintf("PrivValidator{%X LH:%v, LR:%v, LS:%v}", privVal.Address, privVal.LastHeight, privVal.LastRound, privVal.LastStep)
  159. }