Skip to content

Commit

Permalink
hotfix: Select BlockHash that has achieved consensus (#475)
Browse files Browse the repository at this point in the history
  • Loading branch information
vitsalis authored Feb 15, 2024
2 parents 20136e7 + 5c32350 commit 7a9ae9a
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 63 deletions.
2 changes: 1 addition & 1 deletion proto/babylon/checkpointing/v1/bls_key.proto
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ message VoteExtension {
// validator_address is the address of the validator
string validator_address = 2;
// block_hash is the hash of the block that the vote extension is signed over
bytes block_hash = 3;
bytes block_hash = 3 [ (gogoproto.customtype) = "BlockHash" ];
// epoch_num is the epoch number of the vote extension
uint64 epoch_num = 4;
// height is the height of the vote extension
Expand Down
2 changes: 1 addition & 1 deletion testutil/datagen/vote_ext.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func GenRandomVoteExtension(
sig := GenRandomBlsMultiSig(r)
ve := checkpointingtypes.VoteExtension{
Signer: genesisKeys[i].ValidatorAddress,
BlockHash: blockHash,
BlockHash: &blockHash,
EpochNum: epochNum,
Height: height,
BlsSig: &sig,
Expand Down
2 changes: 1 addition & 1 deletion testutil/helper/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func (h *Helper) getExtendedVotesFromValSet(epochNum, height uint64, blockHash c
sig := bls12381.Sign(sk, signBytes)
ve := checkpointingtypes.VoteExtension{
Signer: genesisKeys[i].ValidatorAddress,
BlockHash: blockHash,
BlockHash: &blockHash,
EpochNum: epochNum,
Height: height,
BlsSig: &sig,
Expand Down
52 changes: 46 additions & 6 deletions x/checkpointing/proposal.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package checkpointing

import (
"encoding/hex"
"fmt"
"slices"

Expand Down Expand Up @@ -180,15 +181,54 @@ func (h *ProposalHandler) getValidBlsSigs(ctx sdk.Context, extendedVotes []abci.
return validBLSSigs
}

// findLastBlockHash finds the last block hash from the first vote extension
// this is a workaround that the last block hash can't be obtained from the context
// this is also safe as the BLS sig is already verified by VerifyVoteExtension
// findLastBlockHash iterates over all vote extensions and finds the block hash
// that consensus has agreed upon.
// We need to iterate over all block hashes to find the one that has achieved consensus
// as CometBFT does not verify vote extensions once it has achieved >2/3 voting power in a block.
// Contract: This function should only be called for Vote Extensions
// that have been included in a previous block.
func (h *ProposalHandler) findLastBlockHash(extendedVotes []abci.ExtendedVoteInfo) ([]byte, error) {
var ve ckpttypes.VoteExtension
if err := ve.Unmarshal(extendedVotes[0].VoteExtension); err != nil {
// Mapping between block hashes and voting power committed to them
blockHashes := make(map[string]int64, 0)
// Iterate over vote extensions and if they have a valid structure
// increase the voting power of the block hash they commit to
for _, vote := range extendedVotes {
var ve ckpttypes.VoteExtension
if err := ve.Unmarshal(vote.VoteExtension); err != nil {
continue
}
// Encode the block hash using hex
blockHashes[hex.EncodeToString(ve.BlockHash.MustMarshal())] += vote.Validator.Power
}
var (
maxPower int64 = 0
totalPower int64 = 0
resBlockHash string
)
// Find the block hash that has the maximum voting power committed to it
for blockHash, power := range blockHashes {
if power > maxPower {
resBlockHash = blockHash
maxPower = power
}
totalPower += power
}
if len(resBlockHash) == 0 {
return nil, fmt.Errorf("could not find the block hash")
}
// Verify that the voting power committed to the found block hash is
// more than 2/3 of the total voting power.
if requiredVP := ((totalPower * 2) / 3) + 1; maxPower < requiredVP {
return nil, fmt.Errorf(
"insufficient cumulative voting power received to verify vote extensions; got: %d, expected: >=%d",
maxPower, requiredVP,
)
}
decoded, err := hex.DecodeString(resBlockHash)
if err != nil {
return nil, err
}
return ve.ToBLSSig().BlockHash.MustMarshal(), nil
return decoded, nil
}

// ProcessProposal examines the checkpoint in the injected tx of the proposal
Expand Down
102 changes: 51 additions & 51 deletions x/checkpointing/types/bls_key.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions x/checkpointing/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,10 +202,9 @@ func BytesToCkptWithMeta(cdc codec.BinaryCodec, bz []byte) (*RawCheckpointWithMe
}

func (ve *VoteExtension) ToBLSSig() *BlsSig {
blockHash := BlockHash(ve.BlockHash)
return &BlsSig{
EpochNum: ve.EpochNum,
BlockHash: &blockHash,
BlockHash: ve.BlockHash,
BlsSig: ve.BlsSig,
SignerAddress: ve.Signer,
ValidatorAddress: ve.ValidatorAddress,
Expand Down
7 changes: 6 additions & 1 deletion x/checkpointing/vote_ext.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,16 @@ func (h *VoteExtensionHandler) ExtendVote() sdk.ExtendVoteHandler {
epoch.EpochNumber, req.Height)
}

var bhash ckpttypes.BlockHash
if err := bhash.Unmarshal(req.Hash); err != nil {
return emptyRes, fmt.Errorf("invalid CometBFT hash")
}

// 3. build vote extension
ve := &ckpttypes.VoteExtension{
Signer: signer.String(),
ValidatorAddress: k.GetValidatorAddress().String(),
BlockHash: req.Hash,
BlockHash: &bhash,
EpochNum: epoch.EpochNumber,
Height: uint64(req.Height),
BlsSig: &blsSig,
Expand Down

0 comments on commit 7a9ae9a

Please sign in to comment.