github.com/okex/exchain@v1.8.0/libs/tendermint/privval/file.go (about)

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