@@ -124,6 +124,17 @@ func ValidateVoteExtensions(ctx sdk.Context, reqHeight int64, extVoteInfo []abci
124124 continue
125125 }
126126
127+ // ensure proposer-supplied power matches canonical power
128+ // This is not used for tallying, but a mismatch indicates potential tampering.
129+ if vote .Validator .Power != validator .VotingPower {
130+ return fmt .Errorf (
131+ "mismatching voting power in ValidateVoteExtension for validator %s: got %d from ExtendedCommitInfo, expected %d" ,
132+ valAddrStr ,
133+ vote .Validator .Power ,
134+ validator .VotingPower ,
135+ )
136+ }
137+
127138 cmtPubKey , err := getValidatorPublicKey (validator )
128139 if err != nil {
129140 return err
@@ -252,6 +263,17 @@ func FilterVoteExtensions(ctx sdk.Context, reqHeight int64, extVoteInfo []abciTy
252263 continue
253264 }
254265
266+ // ensure proposer-supplied power matches canonical power
267+ if vote .Validator .Power != validator .VotingPower {
268+ logger .Warn (
269+ "mismatching voting power in FilterVoteExtension" ,
270+ "validator" , valAddrStr ,
271+ "receivedVotingPower" , vote .Validator .Power ,
272+ "expectedVotingPower" , validator .VotingPower ,
273+ )
274+ continue
275+ }
276+
255277 cmtPubKey , err := getValidatorPublicKey (validator )
256278 if err != nil {
257279 return nil , err
@@ -326,10 +348,13 @@ func FilterVoteExtensions(ctx sdk.Context, reqHeight int64, extVoteInfo []abciTy
326348
327349// tallyVotes tallies the votes received for the side tx
328350// It returns the lists of txs which got >2/3+ YES, NO and UNSPECIFIED votes respectively
329- func tallyVotes (extVoteInfo []abciTypes.ExtendedVoteInfo , logger log.Logger , totalVotingPower int64 , currentHeight int64 ) ([][]byte , [][]byte , [][]byte , error ) {
351+ func tallyVotes (extVoteInfo []abciTypes.ExtendedVoteInfo , logger log.Logger , validatorSet * stakeTypes. ValidatorSet , currentHeight int64 ) ([][]byte , [][]byte , [][]byte , error ) {
330352 logger .Debug ("Tallying votes" )
331353
332- voteByTxHash , err := aggregateVotes (extVoteInfo , currentHeight , logger )
354+ // Always derive total voting power from the canonical validator set
355+ totalVotingPower := validatorSet .GetTotalVotingPower ()
356+
357+ voteByTxHash , err := aggregateVotes (extVoteInfo , validatorSet , currentHeight , logger )
333358 if err != nil {
334359 return nil , nil , nil , err
335360 }
@@ -386,11 +411,13 @@ func tallyVotes(extVoteInfo []abciTypes.ExtendedVoteInfo, logger log.Logger, tot
386411}
387412
388413// aggregateVotes collates votes received for side txs
389- func aggregateVotes (extVoteInfo []abciTypes.ExtendedVoteInfo , currentHeight int64 , logger log.Logger ) (map [string ]map [sidetxs.Vote ]int64 , error ) {
414+ func aggregateVotes (extVoteInfo []abciTypes.ExtendedVoteInfo , validatorSet * stakeTypes. ValidatorSet , currentHeight int64 , logger log.Logger ) (map [string ]map [sidetxs.Vote ]int64 , error ) {
390415 voteByTxHash := make (map [string ]map [sidetxs.Vote ]int64 ) // track votes for a side tx
391416 validatorToTxMap := make (map [string ]map [string ]struct {}) // ensure a validator doesn't procure conflicting votes for a side tx
392417 var blockHash []byte // store the block hash to make sure all votes are for the same block
393418
419+ ac := address.HexCodec {}
420+
394421 for _ , vote := range extVoteInfo {
395422 // make sure the BlockIdFlag is valid
396423 if ! isBlockIdFlagValid (vote .BlockIdFlag ) {
@@ -402,8 +429,7 @@ func aggregateVotes(extVoteInfo []abciTypes.ExtendedVoteInfo, currentHeight int6
402429 }
403430
404431 ve := new (sidetxs.VoteExtension )
405- err := ve .Unmarshal (vote .VoteExtension )
406- if err != nil {
432+ if err := ve .Unmarshal (vote .VoteExtension ); err != nil {
407433 return nil , err
408434 }
409435
@@ -416,7 +442,6 @@ func aggregateVotes(extVoteInfo []abciTypes.ExtendedVoteInfo, currentHeight int6
416442 // store the block hash from the first vote
417443 blockHash = ve .BlockHash
418444 } else {
419- ac := address.HexCodec {}
420445 valAddr , err := ac .BytesToString (vote .Validator .Address )
421446 if err != nil {
422447 return nil , err
@@ -431,11 +456,20 @@ func aggregateVotes(extVoteInfo []abciTypes.ExtendedVoteInfo, currentHeight int6
431456 }
432457 }
433458
434- addr , err := address . NewHexCodec () .BytesToString (vote .Validator .Address )
459+ addr , err := ac .BytesToString (vote .Validator .Address )
435460 if err != nil {
436461 return nil , err
437462 }
438463
464+ // Look up canonical validator and voting power (don't trust the power carried in ExtendedCommitInfo)
465+ _ , validator := validatorSet .GetByAddress (addr )
466+ if validator == nil {
467+ logger .Error (
468+ "validator from vote extension not found in canonical validator set" , "validator" , addr , "height" , currentHeight )
469+ continue
470+ }
471+ canonicalPower := validator .VotingPower
472+
439473 if validatorToTxMap [addr ] != nil {
440474 return nil , fmt .Errorf ("duplicate vote received from %s" , addr )
441475 }
@@ -459,7 +493,8 @@ func aggregateVotes(extVoteInfo []abciTypes.ExtendedVoteInfo, currentHeight int6
459493 voteByTxHash [txHashStr ] = make (map [sidetxs.Vote ]int64 )
460494 }
461495
462- voteByTxHash [txHashStr ][res.Result ] += vote .Validator .Power
496+ // use canonical power from the validator set
497+ voteByTxHash [txHashStr ][res.Result ] += canonicalPower
463498
464499 // validator's vote received; mark it to avoid duplicated votes
465500 validatorToTxMap [addr ][txHashStr ] = struct {}{}
0 commit comments