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

151 lines
5.3 KiB

package consensus
import (
"bytes"
"sort"
"github.com/ethereum/go-ethereum/common"
bls_core "github.com/woop-chain/bls/ffi/go/bls"
"github.com/woop-chain/woop/consensus/quorum"
"github.com/woop-chain/woop/crypto/bls"
"github.com/woop-chain/woop/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) {
addrSet := map[common.Address]struct{}{}
for _, pubKey2 := range recvMsg.SenderPubkeys {
if alreadyCastBallot := consensus.decider.ReadBallot(
quorum.Commit, pubKey2.Bytes,
); alreadyCastBallot != nil {
for _, pubKey1 := range alreadyCastBallot.SignerPubKeys {
if bytes.Compare(pubKey2.Bytes[:], pubKey1[:]) == 0 {
for _, blk := range consensus.fBFTLog.GetBlocksByNumber(recvMsg.BlockNum) {
firstSignedHeader := blk.Header()
areHeightsEqual := firstSignedHeader.Number().Uint64() == recvMsg.BlockNum
areViewIDsEqual := firstSignedHeader.ViewID().Uint64() == recvMsg.ViewID
areHeadersEqual := firstSignedHeader.Hash() == recvMsg.BlockHash
// If signer already firstSignedHeader, 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_core.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.Blockchain().CurrentHeader()
committee, err := consensus.Blockchain().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
}
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(pubKey2.Bytes)
if err != nil {
consensus.getLogger().Err(err).Str("msg", recvMsg.String()).
Msg("could not find address for bls key")
return true
}
if _, ok := addrSet[*addr]; ok {
// Address already slashed
break
}
leaderAddr, err := subComm.AddressForBLSKey(consensus.LeaderPubKey.Bytes)
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) {
secondKeys := make([]bls.SerializedPublicKey, len(recvMsg.SenderPubkeys))
for i, pubKey := range recvMsg.SenderPubkeys {
secondKeys[i] = pubKey.Bytes
}
evid := slash.Evidence{
ConflictingVotes: slash.ConflictingVotes{
FirstVote: slash.Vote{
SignerPubKeys: alreadyCastBallot.SignerPubKeys,
BlockHeaderHash: alreadyCastBallot.BlockHeaderHash,
Signature: alreadyCastBallot.Signature,
},
SecondVote: slash.Vote{
SignerPubKeys: secondKeys,
BlockHeaderHash: recvMsg.BlockHash,
Signature: common.Hex2Bytes(doubleSign.SerializeToHexStr()),
}},
Moment: slash.Moment{
Epoch: curHeader.Epoch(),
ShardID: consensus.ShardID,
Height: recvMsg.BlockNum,
ViewID: recvMsg.ViewID,
},
Offender: *addr,
}
sort.SliceStable(evid.ConflictingVotes.FirstVote.SignerPubKeys, func(i, j int) bool {
return bytes.Compare(
evid.ConflictingVotes.FirstVote.SignerPubKeys[i][:],
evid.ConflictingVotes.FirstVote.SignerPubKeys[j][:]) < 0
})
sort.SliceStable(evid.ConflictingVotes.SecondVote.SignerPubKeys, func(i, j int) bool {
return bytes.Compare(
evid.ConflictingVotes.SecondVote.SignerPubKeys[i][:],
evid.ConflictingVotes.SecondVote.SignerPubKeys[j][:]) < 0
})
proof := slash.Record{
Evidence: evid,
Reporter: reporter,
}
consensus.SlashChan <- proof
}(*leaderAddr)
addrSet[*addr] = struct{}{}
break
}
}
}
}
}
}
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
}