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.

420 lines
13 KiB

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