diff --git a/consensus/leader.go b/consensus/leader.go index 8f3fd3d59a..d97bb17e1b 100644 --- a/consensus/leader.go +++ b/consensus/leader.go @@ -93,6 +93,19 @@ func (consensus *Consensus) announce(block *types.Block) { consensus.switchPhase("Announce", FBFTPrepare) } +func (consensus *Consensus) checkFirstReceivedSignature(signerCount int64, phase quorum.Phase) (bool, bool) { + hasMultiBlsKeys := len(consensus.priKey) > 0 + if hasMultiBlsKeys { + var myPubkeys []bls.SerializedPublicKey + for _, key := range consensus.priKey { + myPubkeys = append(myPubkeys, key.Pub.Bytes) + } + mySignsCount := consensus.decider.GetBallotsCount(phase, myPubkeys) + return true, signerCount == mySignsCount + } + return false, false +} + // this method is called for each validator sent their vote message func (consensus *Consensus) onPrepare(recvMsg *FBFTMessage) { // TODO(audit): make FBFT lookup using map instead of looping through all items. @@ -121,14 +134,21 @@ func (consensus *Consensus) onPrepare(recvMsg *FBFTMessage) { } } - if consensus.decider.IsQuorumAchieved(quorum.Prepare) { + signerCount := consensus.decider.SignersCount(quorum.Prepare) + + // check if it is first received signatures + // it may multi bls key validators can achieve quorum on first signature + hasMultiBlsKeys, isFirstReceivedSignature := consensus.checkFirstReceivedSignature(signerCount, quorum.Prepare) + + quorumPreExisting := consensus.decider.IsQuorumAchieved(quorum.Prepare) + //// Read - End + + if quorumPreExisting { // already have enough signatures consensus.getLogger().Debug(). Interface("validatorPubKeys", recvMsg.SenderPubkeys). Msg("[OnPrepare] Received Additional Prepare Message") - return } - signerCount := consensus.decider.SignersCount(quorum.Prepare) //// Read - End consensus.UpdateLeaderMetrics(float64(signerCount), float64(consensus.getBlockNum())) @@ -183,7 +203,11 @@ func (consensus *Consensus) onPrepare(recvMsg *FBFTMessage) { //// Write - End //// Read - Start - if consensus.decider.IsQuorumAchieved(quorum.Prepare) { + quorumFromInitialSignature := hasMultiBlsKeys && isFirstReceivedSignature && quorumPreExisting + quorumPostNewSignatures := consensus.decider.IsQuorumAchieved(quorum.Prepare) + quorumFromNewSignatures := !quorumPreExisting && quorumPostNewSignatures + + if quorumFromInitialSignature || quorumFromNewSignatures { // NOTE Let it handle its own logs if err := consensus.didReachPrepareQuorum(); err != nil { return @@ -216,6 +240,11 @@ func (consensus *Consensus) onCommit(recvMsg *FBFTMessage) { quorumWasMet := consensus.decider.IsQuorumAchieved(quorum.Commit) signerCount := consensus.decider.SignersCount(quorum.Commit) + + // check if it is first received commit + // it may multi bls key validators can achieve quorum on first commit + hasMultiBlsKeys, isFirstReceivedSignature := consensus.checkFirstReceivedSignature(signerCount, quorum.Commit) + //// Read - End // Verify the signature on commitPayload is correct @@ -290,7 +319,10 @@ func (consensus *Consensus) onCommit(recvMsg *FBFTMessage) { quorumIsMet := consensus.decider.IsQuorumAchieved(quorum.Commit) //// Read - End - if !quorumWasMet && quorumIsMet { + quorumAchievedByFirstCommit := hasMultiBlsKeys && isFirstReceivedSignature && quorumWasMet + quorumAchievedByThisCommit := !quorumWasMet && quorumIsMet + + if quorumAchievedByFirstCommit || quorumAchievedByThisCommit { logger.Info().Msg("[OnCommit] 2/3 Enough commits received") consensus.fBFTLog.MarkBlockVerified(blockObj) diff --git a/consensus/quorum/quorum.go b/consensus/quorum/quorum.go index 888b2c3511..e0feee4ec9 100644 --- a/consensus/quorum/quorum.go +++ b/consensus/quorum/quorum.go @@ -101,6 +101,7 @@ type SignatoryTracker interface { type SignatureReader interface { SignatoryTracker ReadBallot(p Phase, pubkey bls.SerializedPublicKey) *votepower.Ballot + GetBallotsCount(p Phase, pubkeys []bls.SerializedPublicKey) int64 TwoThirdsSignersCount() int64 // 96 bytes aggregated signature AggregateVotes(p Phase) *bls_core.Sign @@ -428,6 +429,28 @@ func (s *cIdentities) ReadBallot(p Phase, pubkey bls.SerializedPublicKey) *votep return payload } +func (s *cIdentities) GetBallotsCount(p Phase, pubkeys []bls.SerializedPublicKey) int64 { + ballotBox := map[bls.SerializedPublicKey]*votepower.Ballot{} + + switch p { + case Prepare: + ballotBox = s.prepare.BallotBox + case Commit: + ballotBox = s.commit.BallotBox + case ViewChange: + ballotBox = s.viewChange.BallotBox + } + + count := int64(0) + for _, pubkey := range pubkeys { + _, ok := ballotBox[pubkey] + if ok { + count++ + } + } + return count +} + func (s *cIdentities) ReadAllBallots(p Phase) []*votepower.Ballot { m := map[bls.SerializedPublicKey]*votepower.Ballot{} switch p { diff --git a/consensus/quorum/thread_safe_decider.go b/consensus/quorum/thread_safe_decider.go index 2cc877448a..b95ef96675 100644 --- a/consensus/quorum/thread_safe_decider.go +++ b/consensus/quorum/thread_safe_decider.go @@ -104,6 +104,12 @@ func (a threadSafeDeciderImpl) ReadBallot(p Phase, pubkey bls.SerializedPublicKe return a.decider.ReadBallot(p, pubkey) } +func (a threadSafeDeciderImpl) GetBallotsCount(p Phase, pubkeys []bls.SerializedPublicKey) int64 { + a.mu.Lock() + defer a.mu.Unlock() + return a.decider.GetBallotsCount(p, pubkeys) +} + func (a threadSafeDeciderImpl) TwoThirdsSignersCount() int64 { a.mu.Lock() defer a.mu.Unlock()