@@ -727,8 +727,6 @@ bool CInstantSendManager::PreVerifyInstantSendLock(NodeId nodeId, const llmq::CI
727727
728728bool CInstantSendManager::ProcessPendingInstantSendLocks ()
729729{
730- auto llmqType = Params ().GetConsensus ().llmqForInstantSend ;
731-
732730 decltype (pendingInstantSendLocks) pend;
733731
734732 {
@@ -750,6 +748,44 @@ bool CInstantSendManager::ProcessPendingInstantSendLocks()
750748 tipHeight = chainActive.Height ();
751749 }
752750
751+ auto llmqType = Params ().GetConsensus ().llmqForInstantSend ;
752+
753+ // Every time a new quorum enters the active set, an older one is removed. This means that between two blocks, the
754+ // active set can be different, leading to different selection of the signing quorum. When we detect such rotation
755+ // of the active set, we must re-check invalid sigs against the previous active set and only ban nodes when this also
756+ // fails.
757+ auto quorums1 = quorumSigningManager->GetActiveQuorumSet (llmqType, tipHeight);
758+ auto quorums2 = quorumSigningManager->GetActiveQuorumSet (llmqType, tipHeight - 1 );
759+ bool quorumsRotated = quorums1 != quorums2;
760+
761+ if (quorumsRotated) {
762+ // first check against the current active set and don't ban
763+ auto badISLocks = ProcessPendingInstantSendLocks (tipHeight, pend, false );
764+ if (!badISLocks.empty ()) {
765+ LogPrintf (" CInstantSendManager::%s -- detected LLMQ active set rotation, redoing verification on old active set\n " , __func__);
766+
767+ // filter out valid IS locks from "pend"
768+ for (auto it = pend.begin (); it != pend.end (); ) {
769+ if (!badISLocks.count (it->first )) {
770+ it = pend.erase (it);
771+ } else {
772+ ++it;
773+ }
774+ }
775+ // now check against the previous active set and perform banning if this fails
776+ ProcessPendingInstantSendLocks (tipHeight - 1 , pend, true );
777+ }
778+ } else {
779+ ProcessPendingInstantSendLocks (tipHeight, pend, true );
780+ }
781+
782+ return true ;
783+ }
784+
785+ std::unordered_set<uint256> CInstantSendManager::ProcessPendingInstantSendLocks (int signHeight, const std::unordered_map<uint256, std::pair<NodeId, CInstantSendLock>>& pend, bool ban)
786+ {
787+ auto llmqType = Params ().GetConsensus ().llmqForInstantSend ;
788+
753789 CBLSBatchVerifier<NodeId, uint256> batchVerifier (false , true , 8 );
754790 std::unordered_map<uint256, std::pair<CQuorumCPtr, CRecoveredSig>> recSigs;
755791
@@ -774,10 +810,10 @@ bool CInstantSendManager::ProcessPendingInstantSendLocks()
774810 continue ;
775811 }
776812
777- auto quorum = quorumSigningManager->SelectQuorumForSigning (llmqType, tipHeight , id);
813+ auto quorum = quorumSigningManager->SelectQuorumForSigning (llmqType, signHeight , id);
778814 if (!quorum) {
779815 // should not happen, but if one fails to select, all others will also fail to select
780- return false ;
816+ return {} ;
781817 }
782818 uint256 signHash = CLLMQUtils::BuildSignHash (llmqType, quorum->qc .quorumHash , id, islock.txid );
783819 batchVerifier.PushMessage (nodeId, hash, signHash, islock.sig .Get (), quorum->qc .quorumPublicKey );
@@ -800,7 +836,9 @@ bool CInstantSendManager::ProcessPendingInstantSendLocks()
800836
801837 batchVerifier.Verify ();
802838
803- if (!batchVerifier.badSources .empty ()) {
839+ std::unordered_set<uint256> badISLocks;
840+
841+ if (ban && !batchVerifier.badSources .empty ()) {
804842 LOCK (cs_main);
805843 for (auto & nodeId : batchVerifier.badSources ) {
806844 // Let's not be too harsh, as the peer might simply be unlucky and might have sent us an old lock which
@@ -816,6 +854,7 @@ bool CInstantSendManager::ProcessPendingInstantSendLocks()
816854 if (batchVerifier.badMessages .count (hash)) {
817855 LogPrintf (" CInstantSendManager::%s -- txid=%s, islock=%s: invalid sig in islock, peer=%d\n " , __func__,
818856 islock.txid .ToString (), hash.ToString (), nodeId);
857+ badISLocks.emplace (hash);
819858 continue ;
820859 }
821860
@@ -836,7 +875,7 @@ bool CInstantSendManager::ProcessPendingInstantSendLocks()
836875 }
837876 }
838877
839- return true ;
878+ return badISLocks ;
840879}
841880
842881void CInstantSendManager::ProcessInstantSendLock (NodeId from, const uint256& hash, const CInstantSendLock& islock)
0 commit comments