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.
216 lines
7.3 KiB
216 lines
7.3 KiB
package consensus
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
|
|
protobuf "github.com/golang/protobuf/proto"
|
|
libbls "github.com/harmony-one/bls/ffi/go/bls"
|
|
msg_pb "github.com/harmony-one/harmony/api/proto/message"
|
|
"github.com/harmony-one/harmony/core/types"
|
|
"github.com/harmony-one/harmony/crypto/bls"
|
|
"github.com/harmony-one/harmony/crypto/hash"
|
|
"github.com/harmony-one/harmony/internal/chain"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// MaxBlockNumDiff limits the received block number to only 100 further from the current block number
|
|
const MaxBlockNumDiff = 100
|
|
|
|
// verifyMessageSig verify the signature of the message are valid from the signer's public key.
|
|
func verifyMessageSig(signerPubKey *libbls.PublicKey, message *msg_pb.Message) error {
|
|
signature := message.Signature
|
|
message.Signature = nil
|
|
messageBytes, err := protobuf.Marshal(message)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
msgSig := libbls.Sign{}
|
|
err = msgSig.Deserialize(signature)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
msgHash := hash.Keccak256(messageBytes)
|
|
if !msgSig.VerifyHash(signerPubKey, msgHash[:]) {
|
|
return errors.New("failed to verify the signature")
|
|
}
|
|
message.Signature = signature
|
|
return nil
|
|
}
|
|
|
|
func (consensus *Consensus) senderKeySanityChecks(msg *msg_pb.Message, senderKey *bls.SerializedPublicKey) bool {
|
|
pubkey, err := bls.BytesToBLSPublicKey(senderKey[:])
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
if err := verifyMessageSig(pubkey, msg); err != nil {
|
|
consensus.getLogger().Error().Err(err).Msgf(
|
|
"[%s] Failed to verify sender's signature",
|
|
msg.GetType().String(),
|
|
)
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (consensus *Consensus) isRightBlockNumAndViewID(recvMsg *FBFTMessage,
|
|
) bool {
|
|
if recvMsg.ViewID != consensus.GetCurBlockViewID() || recvMsg.BlockNum != consensus.blockNum {
|
|
consensus.getLogger().Debug().
|
|
Uint64("MsgViewID", recvMsg.ViewID).
|
|
Uint64("MsgBlockNum", recvMsg.BlockNum).
|
|
Uint64("blockNum", consensus.blockNum).
|
|
Str("ValidatorPubKey", recvMsg.SenderPubkey.Bytes.Hex()).
|
|
Msg("BlockNum/viewID not match")
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (consensus *Consensus) onAnnounceSanityChecks(recvMsg *FBFTMessage) bool {
|
|
logMsgs := consensus.FBFTLog.GetMessagesByTypeSeqView(
|
|
msg_pb.MessageType_ANNOUNCE, recvMsg.BlockNum, recvMsg.ViewID,
|
|
)
|
|
if len(logMsgs) > 0 {
|
|
if logMsgs[0].BlockHash != recvMsg.BlockHash &&
|
|
bytes.Equal(logMsgs[0].SenderPubkey.Bytes[:], recvMsg.SenderPubkey.Bytes[:]) {
|
|
consensus.getLogger().Debug().
|
|
Str("logMsgSenderKey", logMsgs[0].SenderPubkey.Bytes.Hex()).
|
|
Str("logMsgBlockHash", logMsgs[0].BlockHash.Hex()).
|
|
Str("recvMsg.SenderPubkey", recvMsg.SenderPubkey.Bytes.Hex()).
|
|
Uint64("recvMsg.BlockNum", recvMsg.BlockNum).
|
|
Uint64("recvMsg.ViewID", recvMsg.ViewID).
|
|
Str("recvMsgBlockHash", recvMsg.BlockHash.Hex()).
|
|
Str("LeaderKey", consensus.LeaderPubKey.Bytes.Hex()).
|
|
Msg("[OnAnnounce] Leader is malicious")
|
|
if consensus.IsViewChangingMode() {
|
|
consensus.getLogger().Debug().Msg(
|
|
"[OnAnnounce] Already in ViewChanging mode, conflicing announce, doing noop",
|
|
)
|
|
} else {
|
|
consensus.startViewChange(consensus.GetCurBlockViewID() + 1)
|
|
}
|
|
}
|
|
consensus.getLogger().Debug().
|
|
Str("leaderKey", consensus.LeaderPubKey.Bytes.Hex()).
|
|
Msg("[OnAnnounce] Announce message received again")
|
|
}
|
|
return consensus.isRightBlockNumCheck(recvMsg)
|
|
}
|
|
|
|
func (consensus *Consensus) isRightBlockNumCheck(recvMsg *FBFTMessage) bool {
|
|
if recvMsg.BlockNum < consensus.blockNum {
|
|
consensus.getLogger().Debug().
|
|
Uint64("MsgBlockNum", recvMsg.BlockNum).
|
|
Msg("Wrong BlockNum Received, ignoring!")
|
|
return false
|
|
} else if recvMsg.BlockNum-consensus.blockNum > MaxBlockNumDiff {
|
|
consensus.getLogger().Debug().
|
|
Uint64("MsgBlockNum", recvMsg.BlockNum).
|
|
Uint64("MaxBlockNumDiff", MaxBlockNumDiff).
|
|
Msg("Received blockNum that is MaxBlockNumDiff further from the current blockNum!")
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (consensus *Consensus) onPreparedSanityChecks(
|
|
blockObj *types.Block, recvMsg *FBFTMessage,
|
|
) bool {
|
|
if blockObj.NumberU64() != recvMsg.BlockNum ||
|
|
recvMsg.BlockNum < consensus.blockNum {
|
|
consensus.getLogger().Warn().
|
|
Uint64("MsgBlockNum", recvMsg.BlockNum).
|
|
Uint64("blockNum", blockObj.NumberU64()).
|
|
Msg("[OnPrepared] BlockNum not match")
|
|
return false
|
|
}
|
|
if blockObj.Header().Hash() != recvMsg.BlockHash {
|
|
consensus.getLogger().Warn().
|
|
Uint64("MsgBlockNum", recvMsg.BlockNum).
|
|
Hex("MsgBlockHash", recvMsg.BlockHash[:]).
|
|
Str("blockObjHash", blockObj.Header().Hash().Hex()).
|
|
Msg("[OnPrepared] BlockHash not match")
|
|
return false
|
|
}
|
|
if consensus.current.Mode() == Normal {
|
|
err := chain.Engine.VerifyHeader(consensus.ChainReader, blockObj.Header(), true)
|
|
if err != nil {
|
|
consensus.getLogger().Error().
|
|
Err(err).
|
|
Str("inChain", consensus.ChainReader.CurrentHeader().Number().String()).
|
|
Str("MsgBlockNum", blockObj.Header().Number().String()).
|
|
Msg("[OnPrepared] Block header is not verified successfully")
|
|
return false
|
|
}
|
|
if consensus.BlockVerifier == nil {
|
|
// do nothing
|
|
} else if err := consensus.BlockVerifier(blockObj); err != nil {
|
|
consensus.getLogger().Error().Err(err).Msg("[OnPrepared] Block verification failed")
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// TODO: leo: move the sanity check to p2p message validation
|
|
func (consensus *Consensus) onViewChangeSanityCheck(recvMsg *FBFTMessage) bool {
|
|
// TODO: if difference is only one, new leader can still propose the same committed block to avoid another view change
|
|
// TODO: new leader catchup without ignore view change message
|
|
|
|
consensus.getLogger().Info().
|
|
Uint64("MsgBlockNum", recvMsg.BlockNum).
|
|
Uint64("MyViewChangingID", consensus.GetViewChangingID()).
|
|
Uint64("MsgViewChangingID", recvMsg.ViewID).
|
|
Str("SendPubKey", recvMsg.SenderPubkey.Bytes.Hex()).
|
|
Msg("[onViewChangeSanityCheck]")
|
|
|
|
if consensus.blockNum > recvMsg.BlockNum {
|
|
consensus.getLogger().Debug().
|
|
Msg("[onViewChange] Message BlockNum Is Low")
|
|
return false
|
|
}
|
|
if consensus.blockNum < recvMsg.BlockNum {
|
|
consensus.getLogger().Warn().
|
|
Msg("[onViewChangeSanityCheck] MsgBlockNum is different from my BlockNumber")
|
|
return false
|
|
}
|
|
if consensus.IsViewChangingMode() &&
|
|
consensus.GetCurBlockViewID() > recvMsg.ViewID {
|
|
consensus.getLogger().Warn().
|
|
Msg("[onViewChangeSanityCheck] ViewChanging ID Is Low")
|
|
return false
|
|
}
|
|
if recvMsg.ViewID-consensus.GetViewChangingID() > MaxViewIDDiff {
|
|
consensus.getLogger().Debug().
|
|
Msg("[onViewChangeSanityCheck] Received viewID that is MaxViewIDDiff (100) further from the current viewID!")
|
|
return false
|
|
}
|
|
senderKey := recvMsg.SenderPubkey
|
|
|
|
viewIDHash := make([]byte, 8)
|
|
binary.LittleEndian.PutUint64(viewIDHash, recvMsg.ViewID)
|
|
if !recvMsg.ViewidSig.VerifyHash(senderKey.Object, viewIDHash) {
|
|
consensus.getLogger().Warn().
|
|
Uint64("MsgViewID", recvMsg.ViewID).
|
|
Msg("[onViewChangeSanityCheck] Failed to Verify viewID Signature")
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// TODO: leo: move the sanity check to p2p message validation
|
|
func (consensus *Consensus) onNewViewSanityCheck(recvMsg *FBFTMessage) bool {
|
|
if recvMsg.ViewID < consensus.GetCurBlockViewID() {
|
|
consensus.getLogger().Warn().
|
|
Uint64("LastSuccessfulConsensusViewID", consensus.GetCurBlockViewID()).
|
|
Uint64("MsgViewChangingID", recvMsg.ViewID).
|
|
Msg("[onNewView] ViewID should be larger than the viewID of the last successful consensus")
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|