parent
c280606310
commit
eb54d32e2c
@ -0,0 +1,119 @@ |
|||||||
|
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.
|
||||||
|
func (consensus *Consensus) checkDoubleSign(recvMsg *FBFTMessage) { |
||||||
|
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 |
||||||
|
} |
||||||
|
|
||||||
|
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 |
||||||
|
} |
||||||
|
offender := *shard.FromLibBLSPublicKeyUnsafe(recvMsg.SenderPubkey) |
||||||
|
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 |
||||||
|
} |
||||||
|
|
||||||
|
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 |
||||||
|
} |
||||||
|
|
||||||
|
now := big.NewInt(time.Now().UnixNano()) |
||||||
|
|
||||||
|
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 |
||||||
|
}(consensus.SelfAddresses[consensus.LeaderPubKey.SerializeToHexStr()]) |
||||||
|
return |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (consensus *Consensus) couldThisBeADoubleSigner( |
||||||
|
recvMsg *FBFTMessage, |
||||||
|
) bool { |
||||||
|
num, hash, now := consensus.blockNum, recvMsg.BlockHash, consensus.blockNum |
||||||
|
suspicious := !consensus.FBFTLog.HasMatchingAnnounce(num, hash) || |
||||||
|
!consensus.FBFTLog.HasMatchingPrepared(num, hash) |
||||||
|
if suspicious { |
||||||
|
consensus.getLogger().Debug(). |
||||||
|
Str("message", recvMsg.String()). |
||||||
|
Uint64("block-on-consensus", now). |
||||||
|
Msg("possible double signer") |
||||||
|
return true |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
Loading…
Reference in new issue