@@ -708,8 +708,6 @@ bool CInstantSendManager::PreVerifyInstantSendLock(NodeId nodeId, const llmq::CI
708708
709709bool CInstantSendManager::ProcessPendingInstantSendLocks ()
710710{
711- auto llmqType = Params ().GetConsensus ().llmqForInstantSend ;
712-
713711 decltype (pendingInstantSendLocks) pend;
714712
715713 {
@@ -731,6 +729,44 @@ bool CInstantSendManager::ProcessPendingInstantSendLocks()
731729 tipHeight = chainActive.Height ();
732730 }
733731
732+ auto llmqType = Params ().GetConsensus ().llmqForInstantSend ;
733+
734+ // Every time a new quorum enters the active set, an older one is removed. This means that between two blocks, the
735+ // active set can be different, leading to different selection of the signing quorum. When we detect such rotation
736+ // of the active set, we must re-check invalid sigs against the previous active set and only ban nodes when this also
737+ // fails.
738+ auto quorums1 = quorumSigningManager->GetActiveQuorumSet (llmqType, tipHeight);
739+ auto quorums2 = quorumSigningManager->GetActiveQuorumSet (llmqType, tipHeight - 1 );
740+ bool quorumsRotated = quorums1 != quorums2;
741+
742+ if (quorumsRotated) {
743+ // first check against the current active set and don't ban
744+ auto badISLocks = ProcessPendingInstantSendLocks (tipHeight, pend, false );
745+ if (!badISLocks.empty ()) {
746+ LogPrintf (" CInstantSendManager::%s -- detected LLMQ active set rotation, redoing verification on old active set\n " , __func__);
747+
748+ // filter out valid IS locks from "pend"
749+ for (auto it = pend.begin (); it != pend.end (); ) {
750+ if (!badISLocks.count (it->first )) {
751+ it = pend.erase (it);
752+ } else {
753+ ++it;
754+ }
755+ }
756+ // now check against the previous active set and perform banning if this fails
757+ ProcessPendingInstantSendLocks (tipHeight - 1 , pend, true );
758+ }
759+ } else {
760+ ProcessPendingInstantSendLocks (tipHeight, pend, true );
761+ }
762+
763+ return true ;
764+ }
765+
766+ std::unordered_set<uint256> CInstantSendManager::ProcessPendingInstantSendLocks (int signHeight, const std::unordered_map<uint256, std::pair<NodeId, CInstantSendLock>>& pend, bool ban)
767+ {
768+ auto llmqType = Params ().GetConsensus ().llmqForInstantSend ;
769+
734770 CBLSBatchVerifier<NodeId, uint256> batchVerifier (false , true , 8 );
735771 std::unordered_map<uint256, std::pair<CQuorumCPtr, CRecoveredSig>> recSigs;
736772
@@ -755,10 +791,10 @@ bool CInstantSendManager::ProcessPendingInstantSendLocks()
755791 continue ;
756792 }
757793
758- auto quorum = quorumSigningManager->SelectQuorumForSigning (llmqType, tipHeight , id);
794+ auto quorum = quorumSigningManager->SelectQuorumForSigning (llmqType, signHeight , id);
759795 if (!quorum) {
760796 // should not happen, but if one fails to select, all others will also fail to select
761- return false ;
797+ return {} ;
762798 }
763799 uint256 signHash = CLLMQUtils::BuildSignHash (llmqType, quorum->qc .quorumHash , id, islock.txid );
764800 batchVerifier.PushMessage (nodeId, hash, signHash, islock.sig .Get (), quorum->qc .quorumPublicKey );
@@ -781,7 +817,9 @@ bool CInstantSendManager::ProcessPendingInstantSendLocks()
781817
782818 batchVerifier.Verify ();
783819
784- if (!batchVerifier.badSources .empty ()) {
820+ std::unordered_set<uint256> badISLocks;
821+
822+ if (ban && !batchVerifier.badSources .empty ()) {
785823 LOCK (cs_main);
786824 for (auto & nodeId : batchVerifier.badSources ) {
787825 // Let's not be too harsh, as the peer might simply be unlucky and might have sent us an old lock which
@@ -797,6 +835,7 @@ bool CInstantSendManager::ProcessPendingInstantSendLocks()
797835 if (batchVerifier.badMessages .count (hash)) {
798836 LogPrintf (" CInstantSendManager::%s -- txid=%s, islock=%s: invalid sig in islock, peer=%d\n " , __func__,
799837 islock.txid .ToString (), hash.ToString (), nodeId);
838+ badISLocks.emplace (hash);
800839 continue ;
801840 }
802841
@@ -817,7 +856,7 @@ bool CInstantSendManager::ProcessPendingInstantSendLocks()
817856 }
818857 }
819858
820- return true ;
859+ return badISLocks ;
821860}
822861
823862void CInstantSendManager::ProcessInstantSendLock (NodeId from, const uint256& hash, const CInstantSendLock& islock)
0 commit comments