The core protocol of WoopChain
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
woop/consensus/double_sign.go

141 lines
4.7 KiB

package consensus
import (
"math/big"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/harmony-one/bls/ffi/go/bls"
"github.com/harmony-one/harmony/consensus/quorum"
"github.com/harmony-one/harmony/consensus/votepower"
"github.com/harmony-one/harmony/shard"
"github.com/harmony-one/harmony/staking/slash"
)
// Check for double sign and if any, send it out to beacon chain for slashing.
// Returns true when it is a double-sign or there is error, otherwise, false.
func (consensus *Consensus) checkDoubleSign(recvMsg *FBFTMessage) bool {
if consensus.couldThisBeADoubleSigner(recvMsg) {
if alreadyCastBallot := consensus.Decider.ReadBallot(
quorum.Commit, recvMsg.SenderPubkey,
); alreadyCastBallot != nil {
firstPubKey := bls.PublicKey{}
alreadyCastBallot.SignerPubKey.ToLibBLSPublicKey(&firstPubKey)
if recvMsg.SenderPubkey.IsEqual(&firstPubKey) {
for _, blk := range consensus.FBFTLog.GetBlocksByNumber(recvMsg.BlockNum) {
firstSignedBlock := blk.Header()
areHeightsEqual := firstSignedBlock.Number().Uint64() == recvMsg.BlockNum
areViewIDsEqual := firstSignedBlock.ViewID().Uint64() == recvMsg.ViewID
areHeadersEqual := firstSignedBlock.Hash() == recvMsg.BlockHash
// If signer already firstSignedBlock, and the block height is the same
// and the viewID is the same, then we need to verify the block
// hash, and if block hash is different, then that is a clear
// case of double signing
if areHeightsEqual && areViewIDsEqual && !areHeadersEqual {
var doubleSign bls.Sign
if err := doubleSign.Deserialize(recvMsg.Payload); err != nil {
consensus.getLogger().Err(err).Str("msg", recvMsg.String()).
Msg("could not deserialize potential double signer")
return true
}
curHeader := consensus.ChainReader.CurrentHeader()
committee, err := consensus.ChainReader.ReadShardState(curHeader.Epoch())
if err != nil {
consensus.getLogger().Err(err).
Uint32("shard", consensus.ShardID).
Uint64("epoch", curHeader.Epoch().Uint64()).
Msg("could not read shard state")
return true
}
offender := shard.FromLibBLSPublicKeyUnsafe(recvMsg.SenderPubkey)
if offender == nil {
consensus.getLogger().Error().
Str("msg", recvMsg.String()).
Msg("could not get shard key from sender's key")
return true
}
subComm, err := committee.FindCommitteeByID(
consensus.ShardID,
)
if err != nil {
consensus.getLogger().Err(err).
Str("msg", recvMsg.String()).
Msg("could not find subcommittee for bls key")
return true
}
addr, err := subComm.AddressForBLSKey(*offender)
if err != nil {
consensus.getLogger().Err(err).Str("msg", recvMsg.String()).
Msg("could not find address for bls key")
return true
}
now := big.NewInt(time.Now().UnixNano())
leaderShardKey := shard.FromLibBLSPublicKeyUnsafe(consensus.LeaderPubKey)
if leaderShardKey == nil {
consensus.getLogger().Error().
Str("msg", recvMsg.String()).
Msg("could not get shard key from leader's key")
return true
}
leaderAddr, err := subComm.AddressForBLSKey(*leaderShardKey)
if err != nil {
consensus.getLogger().Err(err).Str("msg", recvMsg.String()).
Msg("could not find address for leader bls key")
return true
}
go func(reporter common.Address) {
evid := slash.Evidence{
ConflictingBallots: slash.ConflictingBallots{
AlreadyCastBallot: *alreadyCastBallot,
DoubleSignedBallot: votepower.Ballot{
SignerPubKey: *offender,
BlockHeaderHash: recvMsg.BlockHash,
Signature: common.Hex2Bytes(doubleSign.SerializeToHexStr()),
Height: recvMsg.BlockNum,
ViewID: recvMsg.ViewID,
}},
Moment: slash.Moment{
Epoch: curHeader.Epoch(),
ShardID: consensus.ShardID,
TimeUnixNano: now,
},
}
proof := slash.Record{
Evidence: evid,
Reporter: reporter,
Offender: *addr,
}
consensus.SlashChan <- proof
}(*leaderAddr)
return true
}
}
}
}
return true
}
return false
}
func (consensus *Consensus) couldThisBeADoubleSigner(
recvMsg *FBFTMessage,
) bool {
num, hash := consensus.blockNum, recvMsg.BlockHash
suspicious := !consensus.FBFTLog.HasMatchingAnnounce(num, hash) ||
!consensus.FBFTLog.HasMatchingPrepared(num, hash)
if suspicious {
consensus.getLogger().Debug().
Str("message", recvMsg.String()).
Uint64("block-on-consensus", num).
Msg("possible double signer")
return true
}
return false
}