bitbucket.org/number571/tendermint@v0.8.14/types/validation.go (about)

     1  package types
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"bitbucket.org/number571/tendermint/crypto/batch"
     8  	"bitbucket.org/number571/tendermint/crypto/tmhash"
     9  	tmmath "bitbucket.org/number571/tendermint/libs/math"
    10  )
    11  
    12  const batchVerifyThreshold = 2
    13  
    14  func shouldBatchVerify(vals *ValidatorSet, commit *Commit) bool {
    15  	return len(commit.Signatures) >= batchVerifyThreshold && batch.SupportsBatchVerifier(vals.GetProposer().PubKey)
    16  }
    17  
    18  // VerifyCommit verifies +2/3 of the set had signed the given commit.
    19  //
    20  // It checks all the signatures! While it's safe to exit as soon as we have
    21  // 2/3+ signatures, doing so would impact incentivization logic in the ABCI
    22  // application that depends on the LastCommitInfo sent in BeginBlock, which
    23  // includes which validators signed. For instance, Gaia incentivizes proposers
    24  // with a bonus for including more than +2/3 of the signatures.
    25  func VerifyCommit(chainID string, vals *ValidatorSet, blockID BlockID,
    26  	height int64, commit *Commit) error {
    27  	// run a basic validation of the arguments
    28  	if err := verifyBasicValsAndCommit(vals, commit, height, blockID); err != nil {
    29  		return err
    30  	}
    31  
    32  	// calculate voting power needed. Note that total voting power is capped to
    33  	// 1/8th of max int64 so this operation should never overflow
    34  	votingPowerNeeded := vals.TotalVotingPower() * 2 / 3
    35  
    36  	// ignore all absent signatures
    37  	ignore := func(c CommitSig) bool { return c.Absent() }
    38  
    39  	// only count the signatures that are for the block
    40  	count := func(c CommitSig) bool { return c.ForBlock() }
    41  
    42  	// attempt to batch verify
    43  	if shouldBatchVerify(vals, commit) {
    44  		return verifyCommitBatch(chainID, vals, commit,
    45  			votingPowerNeeded, ignore, count, true, true)
    46  	}
    47  
    48  	// if verification failed or is not supported then fallback to single verification
    49  	return verifyCommitSingle(chainID, vals, commit, votingPowerNeeded,
    50  		ignore, count, true, true)
    51  }
    52  
    53  // LIGHT CLIENT VERIFICATION METHODS
    54  
    55  // VerifyCommitLight verifies +2/3 of the set had signed the given commit.
    56  //
    57  // This method is primarily used by the light client and does not check all the
    58  // signatures.
    59  func VerifyCommitLight(chainID string, vals *ValidatorSet, blockID BlockID,
    60  	height int64, commit *Commit) error {
    61  	// run a basic validation of the arguments
    62  	if err := verifyBasicValsAndCommit(vals, commit, height, blockID); err != nil {
    63  		return err
    64  	}
    65  
    66  	// calculate voting power needed
    67  	votingPowerNeeded := vals.TotalVotingPower() * 2 / 3
    68  
    69  	// ignore all commit signatures that are not for the block
    70  	ignore := func(c CommitSig) bool { return !c.ForBlock() }
    71  
    72  	// count all the remaining signatures
    73  	count := func(c CommitSig) bool { return true }
    74  
    75  	// attempt to batch verify
    76  	if shouldBatchVerify(vals, commit) {
    77  		return verifyCommitBatch(chainID, vals, commit,
    78  			votingPowerNeeded, ignore, count, false, true)
    79  	}
    80  
    81  	// if verification failed or is not supported then fallback to single verification
    82  	return verifyCommitSingle(chainID, vals, commit, votingPowerNeeded,
    83  		ignore, count, false, true)
    84  }
    85  
    86  // VerifyCommitLightTrusting verifies that trustLevel of the validator set signed
    87  // this commit.
    88  //
    89  // NOTE the given validators do not necessarily correspond to the validator set
    90  // for this commit, but there may be some intersection.
    91  //
    92  // This method is primarily used by the light client and does not check all the
    93  // signatures.
    94  func VerifyCommitLightTrusting(chainID string, vals *ValidatorSet, commit *Commit, trustLevel tmmath.Fraction) error {
    95  	// sanity checks
    96  	if vals == nil {
    97  		return errors.New("nil validator set")
    98  	}
    99  	if trustLevel.Denominator == 0 {
   100  		return errors.New("trustLevel has zero Denominator")
   101  	}
   102  	if commit == nil {
   103  		return errors.New("nil commit")
   104  	}
   105  
   106  	// safely calculate voting power needed.
   107  	totalVotingPowerMulByNumerator, overflow := safeMul(vals.TotalVotingPower(), int64(trustLevel.Numerator))
   108  	if overflow {
   109  		return errors.New("int64 overflow while calculating voting power needed. please provide smaller trustLevel numerator")
   110  	}
   111  	votingPowerNeeded := totalVotingPowerMulByNumerator / int64(trustLevel.Denominator)
   112  
   113  	// ignore all commit signatures that are not for the block
   114  	ignore := func(c CommitSig) bool { return !c.ForBlock() }
   115  
   116  	// count all the remaining signatures
   117  	count := func(c CommitSig) bool { return true }
   118  
   119  	// attempt to batch verify commit. As the validator set doesn't necessarily
   120  	// correspond with the validator set that signed the block we need to look
   121  	// up by address rather than index.
   122  	if shouldBatchVerify(vals, commit) {
   123  		return verifyCommitBatch(chainID, vals, commit,
   124  			votingPowerNeeded, ignore, count, false, false)
   125  	}
   126  
   127  	// attempt with single verification
   128  	return verifyCommitSingle(chainID, vals, commit, votingPowerNeeded,
   129  		ignore, count, false, false)
   130  }
   131  
   132  // ValidateHash returns an error if the hash is not empty, but its
   133  // size != tmhash.Size.
   134  func ValidateHash(h []byte) error {
   135  	if len(h) > 0 && len(h) != tmhash.Size {
   136  		return fmt.Errorf("expected size to be %d bytes, got %d bytes",
   137  			tmhash.Size,
   138  			len(h),
   139  		)
   140  	}
   141  	return nil
   142  }
   143  
   144  // Batch verification
   145  
   146  // verifyCommitBatch batch verifies commits.  This routine is equivalent
   147  // to verifyCommitSingle in behavior, just faster iff every signature in the
   148  // batch is valid.
   149  //
   150  // Note: The caller is responsible for checking to see if this routine is
   151  // usable via `shouldVerifyBatch(vals, commit)`.
   152  func verifyCommitBatch(
   153  	chainID string,
   154  	vals *ValidatorSet,
   155  	commit *Commit,
   156  	votingPowerNeeded int64,
   157  	ignoreSig func(CommitSig) bool,
   158  	countSig func(CommitSig) bool,
   159  	countAllSignatures bool,
   160  	lookUpByIndex bool,
   161  ) error {
   162  	var (
   163  		val                *Validator
   164  		valIdx             int32
   165  		seenVals                 = make(map[int32]int, len(commit.Signatures))
   166  		batchSigIdxs             = make([]int, 0, len(commit.Signatures))
   167  		talliedVotingPower int64 = 0
   168  	)
   169  	// attempt to create a batch verifier
   170  	bv, ok := batch.CreateBatchVerifier(vals.GetProposer().PubKey)
   171  	// re-check if batch verification is supported
   172  	if !ok || len(commit.Signatures) < batchVerifyThreshold {
   173  		// This should *NEVER* happen.
   174  		return fmt.Errorf("unsupported signature algorithm or insufficient signatures for batch verification")
   175  	}
   176  
   177  	for idx, commitSig := range commit.Signatures {
   178  		// skip over signatures that should be ignored
   179  		if ignoreSig(commitSig) {
   180  			continue
   181  		}
   182  
   183  		// If the vals and commit have a 1-to-1 correspondance we can retrieve
   184  		// them by index else we need to retrieve them by address
   185  		if lookUpByIndex {
   186  			val = vals.Validators[idx]
   187  		} else {
   188  			valIdx, val = vals.GetByAddress(commitSig.ValidatorAddress)
   189  
   190  			// if the signature doesn't belong to anyone in the validator set
   191  			// then we just skip over it
   192  			if val == nil {
   193  				continue
   194  			}
   195  
   196  			// because we are getting validators by address we need to make sure
   197  			// that the same validator doesn't commit twice
   198  			if firstIndex, ok := seenVals[valIdx]; ok {
   199  				secondIndex := idx
   200  				return fmt.Errorf("double vote from %v (%d and %d)", val, firstIndex, secondIndex)
   201  			}
   202  			seenVals[valIdx] = idx
   203  		}
   204  
   205  		// Validate signature.
   206  		voteSignBytes := commit.VoteSignBytes(chainID, int32(idx))
   207  
   208  		// add the key, sig and message to the verifier
   209  		if err := bv.Add(val.PubKey, voteSignBytes, commitSig.Signature); err != nil {
   210  			return err
   211  		}
   212  		batchSigIdxs = append(batchSigIdxs, idx)
   213  
   214  		// If this signature counts then add the voting power of the validator
   215  		// to the tally
   216  		if countSig(commitSig) {
   217  			talliedVotingPower += val.VotingPower
   218  		}
   219  
   220  		// if we don't need to verify all signatures and already have sufficient
   221  		// voting power we can break from batching and verify all the signatures
   222  		if !countAllSignatures && talliedVotingPower > votingPowerNeeded {
   223  			break
   224  		}
   225  	}
   226  
   227  	// ensure that we have batched together enough signatures to exceed the
   228  	// voting power needed else there is no need to even verify
   229  	if got, needed := talliedVotingPower, votingPowerNeeded; got <= needed {
   230  		return ErrNotEnoughVotingPowerSigned{Got: got, Needed: needed}
   231  	}
   232  
   233  	// attempt to verify the batch.
   234  	ok, validSigs := bv.Verify()
   235  	if ok {
   236  		// success
   237  		return nil
   238  	}
   239  
   240  	// one or more of the signatures is invalid, find and return the first
   241  	// invalid signature.
   242  	for i, ok := range validSigs {
   243  		if !ok {
   244  			// go back from the batch index to the commit.Signatures index
   245  			idx := batchSigIdxs[i]
   246  			sig := commit.Signatures[idx]
   247  			return fmt.Errorf("wrong signature (#%d): %X", idx, sig)
   248  		}
   249  	}
   250  
   251  	// execution reaching here is a bug, and one of the following has
   252  	// happened:
   253  	//  * non-zero tallied voting power, empty batch (impossible?)
   254  	//  * bv.Verify() returned `false, []bool{true, ..., true}` (BUG)
   255  	return fmt.Errorf("BUG: batch verification failed with no invalid signatures")
   256  }
   257  
   258  // Single Verification
   259  
   260  // verifyCommitSingle single verifies commits.
   261  // If a key does not support batch verification, or batch verification fails this will be used
   262  // This method is used to check all the signatures included in a commit.
   263  // It is used in consensus for validating a block LastCommit.
   264  // CONTRACT: both commit and validator set should have passed validate basic
   265  func verifyCommitSingle(
   266  	chainID string,
   267  	vals *ValidatorSet,
   268  	commit *Commit,
   269  	votingPowerNeeded int64,
   270  	ignoreSig func(CommitSig) bool,
   271  	countSig func(CommitSig) bool,
   272  	countAllSignatures bool,
   273  	lookUpByIndex bool,
   274  ) error {
   275  	var (
   276  		val                *Validator
   277  		valIdx             int32
   278  		seenVals                 = make(map[int32]int, len(commit.Signatures))
   279  		talliedVotingPower int64 = 0
   280  		voteSignBytes      []byte
   281  	)
   282  	for idx, commitSig := range commit.Signatures {
   283  		if ignoreSig(commitSig) {
   284  			continue
   285  		}
   286  
   287  		// If the vals and commit have a 1-to-1 correspondance we can retrieve
   288  		// them by index else we need to retrieve them by address
   289  		if lookUpByIndex {
   290  			val = vals.Validators[idx]
   291  		} else {
   292  			valIdx, val = vals.GetByAddress(commitSig.ValidatorAddress)
   293  
   294  			// if the signature doesn't belong to anyone in the validator set
   295  			// then we just skip over it
   296  			if val == nil {
   297  				continue
   298  			}
   299  
   300  			// because we are getting validators by address we need to make sure
   301  			// that the same validator doesn't commit twice
   302  			if firstIndex, ok := seenVals[valIdx]; ok {
   303  				secondIndex := idx
   304  				return fmt.Errorf("double vote from %v (%d and %d)", val, firstIndex, secondIndex)
   305  			}
   306  			seenVals[valIdx] = idx
   307  		}
   308  
   309  		voteSignBytes = commit.VoteSignBytes(chainID, int32(idx))
   310  
   311  		if !val.PubKey.VerifySignature(voteSignBytes, commitSig.Signature) {
   312  			return fmt.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature)
   313  		}
   314  
   315  		// If this signature counts then add the voting power of the validator
   316  		// to the tally
   317  		if countSig(commitSig) {
   318  			talliedVotingPower += val.VotingPower
   319  		}
   320  
   321  		// check if we have enough signatures and can thus exit early
   322  		if !countAllSignatures && talliedVotingPower > votingPowerNeeded {
   323  			return nil
   324  		}
   325  	}
   326  
   327  	if got, needed := talliedVotingPower, votingPowerNeeded; got <= needed {
   328  		return ErrNotEnoughVotingPowerSigned{Got: got, Needed: needed}
   329  	}
   330  
   331  	return nil
   332  }
   333  
   334  func verifyBasicValsAndCommit(vals *ValidatorSet, commit *Commit, height int64, blockID BlockID) error {
   335  	if vals == nil {
   336  		return errors.New("nil validator set")
   337  	}
   338  
   339  	if commit == nil {
   340  		return errors.New("nil commit")
   341  	}
   342  
   343  	if vals.Size() != len(commit.Signatures) {
   344  		return NewErrInvalidCommitSignatures(vals.Size(), len(commit.Signatures))
   345  	}
   346  
   347  	// Validate Height and BlockID.
   348  	if height != commit.Height {
   349  		return NewErrInvalidCommitHeight(height, commit.Height)
   350  	}
   351  	if !blockID.Equals(commit.BlockID) {
   352  		return fmt.Errorf("invalid commit -- wrong block ID: want %v, got %v",
   353  			blockID, commit.BlockID)
   354  	}
   355  
   356  	return nil
   357  }