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.

361 lines
11 KiB

  1. package privval
  2. import (
  3. "bytes"
  4. "errors"
  5. "fmt"
  6. "io/ioutil"
  7. "sync"
  8. "time"
  9. "github.com/tendermint/tendermint/crypto"
  10. "github.com/tendermint/tendermint/crypto/ed25519"
  11. cmn "github.com/tendermint/tendermint/libs/common"
  12. "github.com/tendermint/tendermint/types"
  13. tmtime "github.com/tendermint/tendermint/types/time"
  14. )
  15. // TODO: type ?
  16. const (
  17. stepNone int8 = 0 // Used to distinguish the initial state
  18. stepPropose int8 = 1
  19. stepPrevote int8 = 2
  20. stepPrecommit int8 = 3
  21. )
  22. func voteToStep(vote *types.Vote) int8 {
  23. switch vote.Type {
  24. case types.VoteTypePrevote:
  25. return stepPrevote
  26. case types.VoteTypePrecommit:
  27. return stepPrecommit
  28. default:
  29. cmn.PanicSanity("Unknown vote type")
  30. return 0
  31. }
  32. }
  33. // FilePV implements PrivValidator using data persisted to disk
  34. // to prevent double signing.
  35. // NOTE: the directory containing the pv.filePath must already exist.
  36. type FilePV struct {
  37. Address types.Address `json:"address"`
  38. PubKey crypto.PubKey `json:"pub_key"`
  39. LastHeight int64 `json:"last_height"`
  40. LastRound int `json:"last_round"`
  41. LastStep int8 `json:"last_step"`
  42. LastSignature []byte `json:"last_signature,omitempty"` // so we dont lose signatures XXX Why would we lose signatures?
  43. LastSignBytes cmn.HexBytes `json:"last_signbytes,omitempty"` // so we dont lose signatures XXX Why would we lose signatures?
  44. PrivKey crypto.PrivKey `json:"priv_key"`
  45. // For persistence.
  46. // Overloaded for testing.
  47. filePath string
  48. mtx sync.Mutex
  49. }
  50. // GetAddress returns the address of the validator.
  51. // Implements PrivValidator.
  52. func (pv *FilePV) GetAddress() types.Address {
  53. return pv.Address
  54. }
  55. // GetPubKey returns the public key of the validator.
  56. // Implements PrivValidator.
  57. func (pv *FilePV) GetPubKey() crypto.PubKey {
  58. return pv.PubKey
  59. }
  60. // GenFilePV generates a new validator with randomly generated private key
  61. // and sets the filePath, but does not call Save().
  62. func GenFilePV(filePath string) *FilePV {
  63. privKey := ed25519.GenPrivKey()
  64. return &FilePV{
  65. Address: privKey.PubKey().Address(),
  66. PubKey: privKey.PubKey(),
  67. PrivKey: privKey,
  68. LastStep: stepNone,
  69. filePath: filePath,
  70. }
  71. }
  72. // LoadFilePV loads a FilePV from the filePath. The FilePV handles double
  73. // signing prevention by persisting data to the filePath. If the filePath does
  74. // not exist, the FilePV must be created manually and saved.
  75. func LoadFilePV(filePath string) *FilePV {
  76. pvJSONBytes, err := ioutil.ReadFile(filePath)
  77. if err != nil {
  78. cmn.Exit(err.Error())
  79. }
  80. pv := &FilePV{}
  81. err = cdc.UnmarshalJSON(pvJSONBytes, &pv)
  82. if err != nil {
  83. cmn.Exit(fmt.Sprintf("Error reading PrivValidator from %v: %v\n", filePath, err))
  84. }
  85. // overwrite pubkey and address for convenience
  86. pv.PubKey = pv.PrivKey.PubKey()
  87. pv.Address = pv.PubKey.Address()
  88. pv.filePath = filePath
  89. return pv
  90. }
  91. // LoadOrGenFilePV loads a FilePV from the given filePath
  92. // or else generates a new one and saves it to the filePath.
  93. func LoadOrGenFilePV(filePath string) *FilePV {
  94. var pv *FilePV
  95. if cmn.FileExists(filePath) {
  96. pv = LoadFilePV(filePath)
  97. } else {
  98. pv = GenFilePV(filePath)
  99. pv.Save()
  100. }
  101. return pv
  102. }
  103. // Save persists the FilePV to disk.
  104. func (pv *FilePV) Save() {
  105. pv.mtx.Lock()
  106. defer pv.mtx.Unlock()
  107. pv.save()
  108. }
  109. func (pv *FilePV) save() {
  110. outFile := pv.filePath
  111. if outFile == "" {
  112. panic("Cannot save PrivValidator: filePath not set")
  113. }
  114. jsonBytes, err := cdc.MarshalJSONIndent(pv, "", " ")
  115. if err != nil {
  116. panic(err)
  117. }
  118. err = cmn.WriteFileAtomic(outFile, jsonBytes, 0600)
  119. if err != nil {
  120. panic(err)
  121. }
  122. }
  123. // Reset resets all fields in the FilePV.
  124. // NOTE: Unsafe!
  125. func (pv *FilePV) Reset() {
  126. var sig []byte
  127. pv.LastHeight = 0
  128. pv.LastRound = 0
  129. pv.LastStep = 0
  130. pv.LastSignature = sig
  131. pv.LastSignBytes = nil
  132. pv.Save()
  133. }
  134. // SignVote signs a canonical representation of the vote, along with the
  135. // chainID. Implements PrivValidator.
  136. func (pv *FilePV) SignVote(chainID string, vote *types.Vote) error {
  137. pv.mtx.Lock()
  138. defer pv.mtx.Unlock()
  139. if err := pv.signVote(chainID, vote); err != nil {
  140. return fmt.Errorf("Error signing vote: %v", err)
  141. }
  142. return nil
  143. }
  144. // SignProposal signs a canonical representation of the proposal, along with
  145. // the chainID. Implements PrivValidator.
  146. func (pv *FilePV) SignProposal(chainID string, proposal *types.Proposal) error {
  147. pv.mtx.Lock()
  148. defer pv.mtx.Unlock()
  149. if err := pv.signProposal(chainID, proposal); err != nil {
  150. return fmt.Errorf("Error signing proposal: %v", err)
  151. }
  152. return nil
  153. }
  154. // returns error if HRS regression or no LastSignBytes. returns true if HRS is unchanged
  155. func (pv *FilePV) checkHRS(height int64, round int, step int8) (bool, error) {
  156. if pv.LastHeight > height {
  157. return false, errors.New("Height regression")
  158. }
  159. if pv.LastHeight == height {
  160. if pv.LastRound > round {
  161. return false, errors.New("Round regression")
  162. }
  163. if pv.LastRound == round {
  164. if pv.LastStep > step {
  165. return false, errors.New("Step regression")
  166. } else if pv.LastStep == step {
  167. if pv.LastSignBytes != nil {
  168. if pv.LastSignature == nil {
  169. panic("pv: LastSignature is nil but LastSignBytes is not!")
  170. }
  171. return true, nil
  172. }
  173. return false, errors.New("No LastSignature found")
  174. }
  175. }
  176. }
  177. return false, nil
  178. }
  179. // signVote checks if the vote is good to sign and sets the vote signature.
  180. // It may need to set the timestamp as well if the vote is otherwise the same as
  181. // a previously signed vote (ie. we crashed after signing but before the vote hit the WAL).
  182. func (pv *FilePV) signVote(chainID string, vote *types.Vote) error {
  183. height, round, step := vote.Height, vote.Round, voteToStep(vote)
  184. signBytes := vote.SignBytes(chainID)
  185. sameHRS, err := pv.checkHRS(height, round, step)
  186. if err != nil {
  187. return err
  188. }
  189. // We might crash before writing to the wal,
  190. // causing us to try to re-sign for the same HRS.
  191. // If signbytes are the same, use the last signature.
  192. // If they only differ by timestamp, use last timestamp and signature
  193. // Otherwise, return error
  194. if sameHRS {
  195. if bytes.Equal(signBytes, pv.LastSignBytes) {
  196. vote.Signature = pv.LastSignature
  197. } else if timestamp, ok := checkVotesOnlyDifferByTimestamp(pv.LastSignBytes, signBytes); ok {
  198. vote.Timestamp = timestamp
  199. vote.Signature = pv.LastSignature
  200. } else {
  201. err = fmt.Errorf("Conflicting data")
  202. }
  203. return err
  204. }
  205. // It passed the checks. Sign the vote
  206. sig, err := pv.PrivKey.Sign(signBytes)
  207. if err != nil {
  208. return err
  209. }
  210. pv.saveSigned(height, round, step, signBytes, sig)
  211. vote.Signature = sig
  212. return nil
  213. }
  214. // signProposal checks if the proposal is good to sign and sets the proposal signature.
  215. // It may need to set the timestamp as well if the proposal is otherwise the same as
  216. // a previously signed proposal ie. we crashed after signing but before the proposal hit the WAL).
  217. func (pv *FilePV) signProposal(chainID string, proposal *types.Proposal) error {
  218. height, round, step := proposal.Height, proposal.Round, stepPropose
  219. signBytes := proposal.SignBytes(chainID)
  220. sameHRS, err := pv.checkHRS(height, round, step)
  221. if err != nil {
  222. return err
  223. }
  224. // We might crash before writing to the wal,
  225. // causing us to try to re-sign for the same HRS.
  226. // If signbytes are the same, use the last signature.
  227. // If they only differ by timestamp, use last timestamp and signature
  228. // Otherwise, return error
  229. if sameHRS {
  230. if bytes.Equal(signBytes, pv.LastSignBytes) {
  231. proposal.Signature = pv.LastSignature
  232. } else if timestamp, ok := checkProposalsOnlyDifferByTimestamp(pv.LastSignBytes, signBytes); ok {
  233. proposal.Timestamp = timestamp
  234. proposal.Signature = pv.LastSignature
  235. } else {
  236. err = fmt.Errorf("Conflicting data")
  237. }
  238. return err
  239. }
  240. // It passed the checks. Sign the proposal
  241. sig, err := pv.PrivKey.Sign(signBytes)
  242. if err != nil {
  243. return err
  244. }
  245. pv.saveSigned(height, round, step, signBytes, sig)
  246. proposal.Signature = sig
  247. return nil
  248. }
  249. // Persist height/round/step and signature
  250. func (pv *FilePV) saveSigned(height int64, round int, step int8,
  251. signBytes []byte, sig []byte) {
  252. pv.LastHeight = height
  253. pv.LastRound = round
  254. pv.LastStep = step
  255. pv.LastSignature = sig
  256. pv.LastSignBytes = signBytes
  257. pv.save()
  258. }
  259. // SignHeartbeat signs a canonical representation of the heartbeat, along with the chainID.
  260. // Implements PrivValidator.
  261. func (pv *FilePV) SignHeartbeat(chainID string, heartbeat *types.Heartbeat) error {
  262. pv.mtx.Lock()
  263. defer pv.mtx.Unlock()
  264. sig, err := pv.PrivKey.Sign(heartbeat.SignBytes(chainID))
  265. if err != nil {
  266. return err
  267. }
  268. heartbeat.Signature = sig
  269. return nil
  270. }
  271. // String returns a string representation of the FilePV.
  272. func (pv *FilePV) String() string {
  273. return fmt.Sprintf("PrivValidator{%v LH:%v, LR:%v, LS:%v}", pv.GetAddress(), pv.LastHeight, pv.LastRound, pv.LastStep)
  274. }
  275. //-------------------------------------
  276. // returns the timestamp from the lastSignBytes.
  277. // returns true if the only difference in the votes is their timestamp.
  278. func checkVotesOnlyDifferByTimestamp(lastSignBytes, newSignBytes []byte) (time.Time, bool) {
  279. var lastVote, newVote types.CanonicalJSONVote
  280. if err := cdc.UnmarshalJSON(lastSignBytes, &lastVote); err != nil {
  281. panic(fmt.Sprintf("LastSignBytes cannot be unmarshalled into vote: %v", err))
  282. }
  283. if err := cdc.UnmarshalJSON(newSignBytes, &newVote); err != nil {
  284. panic(fmt.Sprintf("signBytes cannot be unmarshalled into vote: %v", err))
  285. }
  286. lastTime, err := time.Parse(types.TimeFormat, lastVote.Timestamp)
  287. if err != nil {
  288. panic(err)
  289. }
  290. // set the times to the same value and check equality
  291. now := types.CanonicalTime(tmtime.Now())
  292. lastVote.Timestamp = now
  293. newVote.Timestamp = now
  294. lastVoteBytes, _ := cdc.MarshalJSON(lastVote)
  295. newVoteBytes, _ := cdc.MarshalJSON(newVote)
  296. return lastTime, bytes.Equal(newVoteBytes, lastVoteBytes)
  297. }
  298. // returns the timestamp from the lastSignBytes.
  299. // returns true if the only difference in the proposals is their timestamp
  300. func checkProposalsOnlyDifferByTimestamp(lastSignBytes, newSignBytes []byte) (time.Time, bool) {
  301. var lastProposal, newProposal types.CanonicalJSONProposal
  302. if err := cdc.UnmarshalJSON(lastSignBytes, &lastProposal); err != nil {
  303. panic(fmt.Sprintf("LastSignBytes cannot be unmarshalled into proposal: %v", err))
  304. }
  305. if err := cdc.UnmarshalJSON(newSignBytes, &newProposal); err != nil {
  306. panic(fmt.Sprintf("signBytes cannot be unmarshalled into proposal: %v", err))
  307. }
  308. lastTime, err := time.Parse(types.TimeFormat, lastProposal.Timestamp)
  309. if err != nil {
  310. panic(err)
  311. }
  312. // set the times to the same value and check equality
  313. now := types.CanonicalTime(tmtime.Now())
  314. lastProposal.Timestamp = now
  315. newProposal.Timestamp = now
  316. lastProposalBytes, _ := cdc.MarshalJSON(lastProposal)
  317. newProposalBytes, _ := cdc.MarshalJSON(newProposal)
  318. return lastTime, bytes.Equal(newProposalBytes, lastProposalBytes)
  319. }