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.

358 lines
10 KiB

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