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.

184 lines
4.9 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
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/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 := 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) Save() {
  78. privVal.mtx.Lock()
  79. defer privVal.mtx.Unlock()
  80. privVal.save()
  81. }
  82. func (privVal *PrivValidator) save() {
  83. jsonBytes := binary.JSONBytes(privVal)
  84. err := ioutil.WriteFile(privVal.filename, jsonBytes, 0700)
  85. if err != nil {
  86. panic(err)
  87. }
  88. }
  89. // TODO: test
  90. func (privVal *PrivValidator) SignVote(vote *blk.Vote) error {
  91. privVal.mtx.Lock()
  92. defer privVal.mtx.Unlock()
  93. // If height regression, panic
  94. if privVal.LastHeight > vote.Height {
  95. return errors.New("Height regression in SignVote")
  96. }
  97. // More cases for when the height matches
  98. if privVal.LastHeight == vote.Height {
  99. // If attempting any sign after commit, panic
  100. if privVal.LastStep == stepCommit {
  101. return errors.New("SignVote on matching height after a commit")
  102. }
  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(vote)
  119. return nil
  120. }
  121. func (privVal *PrivValidator) SignVoteUnsafe(vote *blk.Vote) {
  122. vote.Signature = privVal.PrivKey.Sign(account.SignBytes(vote)).(account.SignatureEd25519)
  123. }
  124. func (privVal *PrivValidator) SignProposal(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(account.SignBytes(proposal)).(account.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(rebondTx *blk.RebondTx) error {
  143. privVal.mtx.Lock()
  144. defer privVal.mtx.Unlock()
  145. if privVal.LastHeight < rebondTx.Height {
  146. // Persist height/round/step
  147. privVal.LastHeight = rebondTx.Height
  148. privVal.LastRound = math.MaxUint64 // We can't do anything else for this rebondTx.Height.
  149. privVal.LastStep = math.MaxUint8
  150. privVal.save()
  151. // Sign
  152. rebondTx.Signature = privVal.PrivKey.Sign(account.SignBytes(rebondTx)).(account.SignatureEd25519)
  153. return nil
  154. } else {
  155. return errors.New(fmt.Sprintf("Attempt of duplicate signing of rebondTx: Height %v", rebondTx.Height))
  156. }
  157. }
  158. func (privVal *PrivValidator) String() string {
  159. return fmt.Sprintf("PrivValidator{%X LH:%v, LR:%v, LS:%v}", privVal.Address, privVal.LastHeight, privVal.LastRound, privVal.LastStep)
  160. }