github.com/noirx94/tendermintmp@v0.0.1/test/maverick/node/privval.go (about)

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