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/view_change_construct.go

516 lines
17 KiB

package consensus
import (
"bytes"
"encoding/binary"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
"github.com/woop-chain/woop/consensus/quorum"
"github.com/woop-chain/woop/core/types"
bls_core "github.com/woop-chain/bls/ffi/go/bls"
msg_pb "github.com/woop-chain/woop/api/proto/message"
"github.com/woop-chain/woop/crypto/bls"
bls_cosi "github.com/woop-chain/woop/crypto/bls"
"github.com/woop-chain/woop/internal/chain"
"github.com/woop-chain/woop/internal/utils"
"github.com/woop-chain/woop/multibls"
"github.com/pkg/errors"
"github.com/rs/zerolog"
)
const (
// ValidPayloadLength is the valid length for viewchange payload
ValidPayloadLength = 32 + bls.BLSSignatureSizeInBytes
)
// viewChange encapsulate all the view change related data structure and functions
type viewChange struct {
// Commits collected from view change
// for each viewID, we need keep track of corresponding sigs and bitmap
// until one of the viewID has enough votes (>=2f+1)
// after one of viewID has enough votes, we can reset and clean the map
// honest nodes will never double votes on different viewID
// bhpSigs: blockHashPreparedSigs is the signature on m1 type message
bhpSigs map[uint64]map[string]*bls_core.Sign
// nilSigs: there is no prepared message when view change,
// it's signature on m2 type (i.e. nil) messages
nilSigs map[uint64]map[string]*bls_core.Sign
viewIDSigs map[uint64]map[string]*bls_core.Sign
bhpBitmap map[uint64]*bls_cosi.Mask
nilBitmap map[uint64]*bls_cosi.Mask
viewIDBitmap map[uint64]*bls_cosi.Mask
newViewMsg map[uint64]map[string]uint64
m1Payload []byte // message payload for type m1 := |vcBlockHash|prepared_agg_sigs|prepared_bitmap|, new leader only need one
viewChangeDuration time.Duration
}
// newViewChange returns a new viewChange object
func newViewChange() *viewChange {
vc := viewChange{}
vc.Reset()
return &vc
}
// AddViewIDKeyIfNotExist ..
func (vc *viewChange) AddViewIDKeyIfNotExist(viewID uint64, members multibls.PublicKeys) {
if _, ok := vc.bhpSigs[viewID]; !ok {
vc.bhpSigs[viewID] = map[string]*bls_core.Sign{}
}
if _, ok := vc.nilSigs[viewID]; !ok {
vc.nilSigs[viewID] = map[string]*bls_core.Sign{}
}
if _, ok := vc.viewIDSigs[viewID]; !ok {
vc.viewIDSigs[viewID] = map[string]*bls_core.Sign{}
}
if _, ok := vc.bhpBitmap[viewID]; !ok {
bhpBitmap := bls_cosi.NewMask(members)
vc.bhpBitmap[viewID] = bhpBitmap
}
if _, ok := vc.nilBitmap[viewID]; !ok {
nilBitmap := bls_cosi.NewMask(members)
vc.nilBitmap[viewID] = nilBitmap
}
if _, ok := vc.viewIDBitmap[viewID]; !ok {
viewIDBitmap := bls_cosi.NewMask(members)
vc.viewIDBitmap[viewID] = viewIDBitmap
}
}
// Reset resets the state for viewChange.
func (vc *viewChange) Reset() {
vc.m1Payload = []byte{}
vc.bhpSigs = map[uint64]map[string]*bls_core.Sign{}
vc.nilSigs = map[uint64]map[string]*bls_core.Sign{}
vc.viewIDSigs = map[uint64]map[string]*bls_core.Sign{}
vc.bhpBitmap = map[uint64]*bls_cosi.Mask{}
vc.nilBitmap = map[uint64]*bls_cosi.Mask{}
vc.viewIDBitmap = map[uint64]*bls_cosi.Mask{}
vc.newViewMsg = map[uint64]map[string]uint64{}
}
// GetPreparedBlock returns the prepared block or nil if not found
func (vc *viewChange) GetPreparedBlock(fbftlog *FBFTLog) ([]byte, []byte) {
if vc.isM1PayloadEmpty() {
return nil, nil
}
blockHash := [32]byte{}
// First 32 bytes of m1 payload is the correct block hash
copy(blockHash[:], vc.GetM1Payload())
if !fbftlog.IsBlockVerified(blockHash) {
return nil, nil
}
if block := fbftlog.GetBlockByHash(blockHash); block != nil {
encodedBlock, err := rlp.EncodeToBytes(block)
if err != nil || len(encodedBlock) == 0 {
return nil, nil
}
return vc.m1Payload, encodedBlock
}
return nil, nil
}
// GetM2Bitmap returns the nilBitmap as M2Bitmap
func (vc *viewChange) GetM2Bitmap(viewID uint64) ([]byte, []byte) {
sig2arr := []*bls_core.Sign{}
for _, sig := range vc.nilSigs[viewID] {
sig2arr = append(sig2arr, sig)
}
if len(sig2arr) > 0 {
m2Sig := bls_cosi.AggregateSig(sig2arr)
vc.getLogger().Info().Int("len", len(sig2arr)).Msg("[GetM2Bitmap] M2 (NIL) type signatures")
return m2Sig.Serialize(), vc.nilBitmap[viewID].Bitmap
}
vc.getLogger().Info().Uint64("viewID", viewID).Msg("[GetM2Bitmap] No M2 (NIL) type signatures")
return nil, nil
}
// GetM3Bitmap returns the viewIDBitmap as M3Bitmap
func (vc *viewChange) GetM3Bitmap(viewID uint64) ([]byte, []byte) {
sig3arr := []*bls_core.Sign{}
for _, sig := range vc.viewIDSigs[viewID] {
sig3arr = append(sig3arr, sig)
}
// even we check here for safty, m3 type signatures must >= 2f+1
if len(sig3arr) > 0 {
m3Sig := bls_cosi.AggregateSig(sig3arr)
vc.getLogger().Info().Int("len", len(sig3arr)).Msg("[GetM3Bitmap] M3 (ViewID) type signatures")
return m3Sig.Serialize(), vc.viewIDBitmap[viewID].Bitmap
}
vc.getLogger().Info().Uint64("viewID", viewID).Msg("[GetM3Bitmap] No M3 (ViewID) type signatures")
return nil, nil
}
// VerifyNewViewMsg returns true if the new view message is valid
func (vc *viewChange) VerifyNewViewMsg(recvMsg *FBFTMessage, verifyBlock func(block *types.Block) error) (*types.Block, error) {
if recvMsg.M3AggSig == nil || recvMsg.M3Bitmap == nil {
return nil, errors.New("[VerifyNewViewMsg] M3AggSig or M3Bitmap is nil")
}
senderKey := recvMsg.SenderPubkeys[0]
senderKeyStr := senderKey.Bytes.Hex()
// first time received the new view message
_, ok := vc.newViewMsg[recvMsg.ViewID]
if !ok {
newViewMap := map[string]uint64{}
vc.newViewMsg[recvMsg.ViewID] = newViewMap
}
_, ok = vc.newViewMsg[recvMsg.ViewID][senderKeyStr]
if ok {
vc.getLogger().Warn().
Uint64("MsgBlockNum", recvMsg.BlockNum).
Uint64("MyBlockNum", vc.newViewMsg[recvMsg.ViewID][senderKeyStr]).
Msg("[VerifyNewViewMsg] redundant NewView Message")
}
vc.newViewMsg[recvMsg.ViewID][senderKeyStr] = recvMsg.BlockNum
m3Sig := recvMsg.M3AggSig
m3Mask := recvMsg.M3Bitmap
viewIDBytes := make([]byte, 8)
binary.LittleEndian.PutUint64(viewIDBytes, recvMsg.ViewID)
if !m3Sig.VerifyHash(m3Mask.AggregatePublic, viewIDBytes) {
vc.getLogger().Warn().
Bytes("viewIDBytes", viewIDBytes).
Interface("AggregatePublic", m3Mask.AggregatePublic).
Msg("m3Sig.VerifyHash Failed")
return nil, errors.New("[VerifyNewViewMsg] Unable to Verify Aggregated Signature of M3 (ViewID) payload")
}
m2Mask := recvMsg.M2Bitmap
if recvMsg.M2AggSig != nil {
m2Sig := recvMsg.M2AggSig
if !m2Sig.VerifyHash(m2Mask.AggregatePublic, NIL) {
vc.getLogger().Warn().
Bytes("NIL", NIL).
Interface("AggregatePublic", m2Mask.AggregatePublic).
Msg("m2Sig.VerifyHash Failed")
return nil, errors.New("[VerifyNewViewMsg] Unable to Verify Aggregated Signature of M2 (NIL) payload")
}
}
// check when M3 sigs > M2 sigs, then M1 (recvMsg.Payload) should not be empty
preparedBlock := &types.Block{}
if len(recvMsg.Payload) >= ValidPayloadLength && len(recvMsg.Block) != 0 {
if err := rlp.DecodeBytes(recvMsg.Block, preparedBlock); err != nil {
return nil, errors.New("[VerifyNewViewMsg] Unparseable prepared block data")
}
blockHash := recvMsg.Payload[:32]
preparedBlockHash := preparedBlock.Hash()
if !bytes.Equal(preparedBlockHash[:], blockHash) {
return nil, errors.New("[VerifyNewViewMsg] Prepared block hash doesn't match msg block hash")
}
if err := verifyBlock(preparedBlock); err != nil {
return nil, err
}
return preparedBlock, nil
}
return nil, nil
}
var (
errDupM1 = errors.New("received M1 (prepared) message already")
errDupM2 = errors.New("received M2 (NIL) message already")
errDupM3 = errors.New("received M3 (ViewID) message already")
errVerifyM1 = errors.New("failed to verfiy signature for M1 message")
errVerifyM2 = errors.New("failed to verfiy signature for M2 message")
errM1Payload = errors.New("failed to verify multi signature for M1 prepared payload")
errNoQuorum = errors.New("no quorum on M1 (prepared) payload")
errIncorrectSender = errors.New("multiple senders not allowed")
)
// ProcessViewChangeMsg process the view change message after verification
func (vc *viewChange) ProcessViewChangeMsg(
fbftlog *FBFTLog,
decider quorum.Decider,
recvMsg *FBFTMessage,
verifyBlock func(block *types.Block) error,
) error {
preparedBlock := &types.Block{}
if !recvMsg.HasSingleSender() {
return errIncorrectSender
}
senderKey := recvMsg.SenderPubkeys[0]
senderKeyStr := senderKey.Bytes.Hex()
// check and add viewID (m3 type) message signature
if _, ok := vc.viewIDSigs[recvMsg.ViewID][senderKeyStr]; ok {
return errDupM3
}
if len(recvMsg.Payload) >= ValidPayloadLength && len(recvMsg.Block) != 0 {
if err := rlp.DecodeBytes(recvMsg.Block, preparedBlock); err != nil {
return err
}
if err := verifyBlock(preparedBlock); err != nil {
return err
}
_, ok := vc.bhpSigs[recvMsg.ViewID][senderKeyStr]
if ok {
return errDupM1
}
if !recvMsg.ViewchangeSig.VerifyHash(senderKey.Object, recvMsg.Payload) {
return errVerifyM1
}
blockHash := recvMsg.Payload[:32]
aggSig, mask, err := chain.ReadSignatureBitmapByPublicKeys(recvMsg.Payload[32:], decider.Participants())
if err != nil {
return err
}
if !decider.IsQuorumAchievedByMask(mask) {
return errNoQuorum
}
if !aggSig.VerifyHash(mask.AggregatePublic, blockHash[:]) {
return errM1Payload
}
vc.getLogger().Info().Uint64("viewID", recvMsg.ViewID).
Str("validatorPubKey", senderKeyStr).
Msg("[ProcessViewChangeMsg] Add M1 (prepared) type message")
if _, ok := vc.bhpSigs[recvMsg.ViewID]; !ok {
vc.bhpSigs[recvMsg.ViewID] = map[string]*bls_core.Sign{}
}
vc.bhpSigs[recvMsg.ViewID][senderKeyStr] = recvMsg.ViewchangeSig
if _, ok := vc.bhpBitmap[recvMsg.ViewID]; !ok {
bhpBitmap := bls_cosi.NewMask(decider.Participants())
vc.bhpBitmap[recvMsg.ViewID] = bhpBitmap
}
vc.bhpBitmap[recvMsg.ViewID].SetKey(senderKey.Bytes, true) // Set the bitmap indicating that this validator signed.
vc.getLogger().Info().Uint64("viewID", recvMsg.ViewID).
Str("validatorPubKey", senderKeyStr).
Msg("[ProcessViewChangeMsg] Add M3 (ViewID) type message")
if _, ok := vc.viewIDSigs[recvMsg.ViewID]; !ok {
vc.viewIDSigs[recvMsg.ViewID] = map[string]*bls_core.Sign{}
}
vc.viewIDSigs[recvMsg.ViewID][senderKeyStr] = recvMsg.ViewidSig
if _, ok := vc.viewIDBitmap[recvMsg.ViewID]; !ok {
viewIDBitmap := bls_cosi.NewMask(decider.Participants())
vc.viewIDBitmap[recvMsg.ViewID] = viewIDBitmap
}
// Set the bitmap indicating that this validator signed.
vc.viewIDBitmap[recvMsg.ViewID].SetKey(senderKey.Bytes, true)
if vc.isM1PayloadEmpty() {
vc.m1Payload = append(recvMsg.Payload[:0:0], recvMsg.Payload...)
// create prepared message for new leader
preparedMsg := FBFTMessage{
MessageType: msg_pb.MessageType_PREPARED,
ViewID: recvMsg.ViewID,
BlockNum: recvMsg.BlockNum,
}
preparedMsg.BlockHash = common.Hash{}
copy(preparedMsg.BlockHash[:], recvMsg.Payload[:32])
preparedMsg.Payload = make([]byte, len(recvMsg.Payload)-32)
copy(preparedMsg.Payload[:], recvMsg.Payload[32:])
preparedMsg.SenderPubkeys = []*bls.PublicKeyWrapper{recvMsg.LeaderPubkey}
vc.getLogger().Info().Msg("[ProcessViewChangeMsg] New Leader Prepared Message Added")
fbftlog.AddVerifiedMessage(&preparedMsg)
fbftlog.AddBlock(preparedBlock)
}
return nil
}
_, ok := vc.nilSigs[recvMsg.ViewID][senderKeyStr]
if ok {
return errDupM2
}
if !recvMsg.ViewchangeSig.VerifyHash(senderKey.Object, NIL) {
return errVerifyM2
}
vc.getLogger().Info().Uint64("viewID", recvMsg.ViewID).
Str("validatorPubKey", senderKeyStr).
Msg("[ProcessViewChangeMsg] Add M2 (NIL) type message")
if _, ok := vc.nilSigs[recvMsg.ViewID]; !ok {
vc.nilSigs[recvMsg.ViewID] = map[string]*bls_core.Sign{}
}
vc.nilSigs[recvMsg.ViewID][senderKeyStr] = recvMsg.ViewchangeSig
if _, ok := vc.nilBitmap[recvMsg.ViewID]; !ok {
nilBitmap := bls_cosi.NewMask(decider.Participants())
vc.nilBitmap[recvMsg.ViewID] = nilBitmap
}
vc.nilBitmap[recvMsg.ViewID].SetKey(senderKey.Bytes, true) // Set the bitmap indicating that this validator signed.
vc.getLogger().Info().Uint64("viewID", recvMsg.ViewID).
Str("validatorPubKey", senderKeyStr).
Msg("[ProcessViewChangeMsg] Add M3 (ViewID) type message")
if _, ok := vc.viewIDSigs[recvMsg.ViewID]; !ok {
vc.viewIDSigs[recvMsg.ViewID] = map[string]*bls_core.Sign{}
}
vc.viewIDSigs[recvMsg.ViewID][senderKeyStr] = recvMsg.ViewidSig
// Set the bitmap indicating that this validator signed.
if _, ok := vc.viewIDBitmap[recvMsg.ViewID]; !ok {
viewIDBitmap := bls_cosi.NewMask(decider.Participants())
vc.viewIDBitmap[recvMsg.ViewID] = viewIDBitmap
}
vc.viewIDBitmap[recvMsg.ViewID].SetKey(senderKey.Bytes, true)
return nil
}
// InitPayload do it once when validator received view change message
func (vc *viewChange) InitPayload(
fbftlog *FBFTLog,
viewID uint64,
blockNum uint64,
privKeys multibls.PrivateKeys,
members multibls.PublicKeys,
verifyBlock func(block *types.Block) error,
) error {
// m1 or m2 init once per viewID/key.
// m1 and m2 are mutually exclusive.
// If the node has valid prepared block, it will add m1 signature.
// If not, the node will add m2 signature.
// Honest node should have only one kind of m1/m2 signature.
inited := false
for _, key := range privKeys {
_, ok1 := vc.bhpSigs[viewID][key.Pub.Bytes.Hex()]
_, ok2 := vc.nilSigs[viewID][key.Pub.Bytes.Hex()]
if ok1 || ok2 {
inited = true
break
}
}
// add my own M1/M2 type message signature and bitmap
if !inited {
preparedMsgs := fbftlog.GetMessagesByTypeSeq(msg_pb.MessageType_PREPARED, blockNum)
preparedMsg := fbftlog.FindMessageByMaxViewID(preparedMsgs)
hasBlock := false
if preparedMsg != nil {
if preparedBlock := fbftlog.GetBlockByHash(preparedMsg.BlockHash); preparedBlock != nil {
if err := verifyBlock(preparedBlock); err == nil {
vc.getLogger().Info().Uint64("viewID", viewID).Uint64("blockNum", blockNum).Int("size", binary.Size(preparedBlock)).Msg("[InitPayload] add my M1 (prepared) type messaage")
msgToSign := append(preparedMsg.BlockHash[:], preparedMsg.Payload...)
for _, key := range privKeys {
// update the dictionary key if the viewID is first time received
if _, ok := vc.bhpBitmap[viewID]; !ok {
bhpBitmap := bls_cosi.NewMask(members)
vc.bhpBitmap[viewID] = bhpBitmap
}
if err := vc.bhpBitmap[viewID].SetKey(key.Pub.Bytes, true); err != nil {
vc.getLogger().Warn().Str("key", key.Pub.Bytes.Hex()).Msg("[InitPayload] bhpBitmap setkey failed")
continue
}
if _, ok := vc.bhpSigs[viewID]; !ok {
vc.bhpSigs[viewID] = map[string]*bls_core.Sign{}
}
vc.bhpSigs[viewID][key.Pub.Bytes.Hex()] = key.Pri.SignHash(msgToSign)
}
hasBlock = true
// if m1Payload is empty, we just add one
if vc.isM1PayloadEmpty() {
vc.m1Payload = append(preparedMsg.BlockHash[:], preparedMsg.Payload...)
}
}
}
}
if !hasBlock {
vc.getLogger().Info().Uint64("viewID", viewID).Uint64("blockNum", blockNum).Msg("[InitPayload] add my M2 (NIL) type messaage")
for _, key := range privKeys {
if _, ok := vc.nilBitmap[viewID]; !ok {
nilBitmap := bls_cosi.NewMask(members)
vc.nilBitmap[viewID] = nilBitmap
}
if err := vc.nilBitmap[viewID].SetKey(key.Pub.Bytes, true); err != nil {
vc.getLogger().Warn().Err(err).
Str("key", key.Pub.Bytes.Hex()).Msg("[InitPayload] nilBitmap setkey failed")
continue
}
if _, ok := vc.nilSigs[viewID]; !ok {
vc.nilSigs[viewID] = map[string]*bls_core.Sign{}
}
vc.nilSigs[viewID][key.Pub.Bytes.Hex()] = key.Pri.SignHash(NIL)
}
}
}
inited = false
for _, key := range privKeys {
_, ok3 := vc.viewIDSigs[viewID][key.Pub.Bytes.Hex()]
if ok3 {
inited = true
break
}
}
// add my own M3 type message signature and bitmap
if !inited {
viewIDBytes := make([]byte, 8)
binary.LittleEndian.PutUint64(viewIDBytes, viewID)
vc.getLogger().Info().Uint64("viewID", viewID).Uint64("blockNum", blockNum).Msg("[InitPayload] add my M3 (ViewID) type message")
for _, key := range privKeys {
if _, ok := vc.viewIDBitmap[viewID]; !ok {
viewIDBitmap := bls_cosi.NewMask(members)
vc.viewIDBitmap[viewID] = viewIDBitmap
}
if err := vc.viewIDBitmap[viewID].SetKey(key.Pub.Bytes, true); err != nil {
vc.getLogger().Warn().Err(err).
Str("key", key.Pub.Bytes.Hex()).Msg("[InitPayload] viewIDBitmap setkey failed")
continue
}
if _, ok := vc.viewIDSigs[viewID]; !ok {
vc.viewIDSigs[viewID] = map[string]*bls_core.Sign{}
}
vc.viewIDSigs[viewID][key.Pub.Bytes.Hex()] = key.Pri.SignHash(viewIDBytes)
}
}
return nil
}
// isM1PayloadEmpty returns true if m1Payload is not set
// this is an unlocked internal function call
func (vc *viewChange) isM1PayloadEmpty() bool {
return len(vc.m1Payload) == 0
}
// IsM1PayloadEmpty returns true if m1Payload is not set
func (vc *viewChange) IsM1PayloadEmpty() bool {
return vc.isM1PayloadEmpty()
}
// GetViewIDBitmap returns the viewIDBitmap
func (vc *viewChange) GetViewIDBitmap(viewID uint64) *bls_cosi.Mask {
return vc.viewIDBitmap[viewID]
}
// GetM1Payload returns the m1Payload.
func (vc *viewChange) GetM1Payload() []byte {
return vc.m1Payload
}
// getLogger returns logger for view change contexts added
func (vc *viewChange) getLogger() *zerolog.Logger {
logger := utils.Logger().With().
Str("context", "ViewChange").
Logger()
return &logger
}