Skip to content

Commit b1362d1

Browse files
committed
Implement retrying of IS lock verification when the LLMQ active set rotates
1 parent 770126a commit b1362d1

File tree

2 files changed

+40
-6
lines changed

2 files changed

+40
-6
lines changed

src/llmq/quorums_instantsend.cpp

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -736,10 +736,41 @@ bool CInstantSendManager::ProcessPendingInstantSendLocks()
736736
tipHeight = chainActive.Height();
737737
}
738738

739-
return ProcessPendingInstantSendLocks(tipHeight, pend);
739+
auto llmqType = Params().GetConsensus().llmqForInstantSend;
740+
741+
// Every time a new quorum enters the active set, an older one is removed. This means that between two blocks, the
742+
// active set can be different, leading to different selection of the signing quorum. When we detect such rotation
743+
// of the active set, we must re-check invalid sigs against the previous active set and only ban nodes when this also
744+
// fails.
745+
auto quorums1 = quorumSigningManager->GetActiveQuorumSet(llmqType, tipHeight);
746+
auto quorums2 = quorumSigningManager->GetActiveQuorumSet(llmqType, tipHeight - 1);
747+
bool quorumsRotated = quorums1 != quorums2;
748+
749+
if (quorumsRotated) {
750+
// first check against the current active set and don't ban
751+
auto badISLocks = ProcessPendingInstantSendLocks(tipHeight, pend, false);
752+
if (!badISLocks.empty()) {
753+
LogPrintf("CInstantSendManager::%s -- detected LLMQ active set rotation, redoing verification on old active set\n", __func__);
754+
755+
// filter out valid IS locks from "pend"
756+
for (auto it = pend.begin(); it != pend.end(); ) {
757+
if (!badISLocks.count(it->first)) {
758+
it = pend.erase(it);
759+
} else {
760+
++it;
761+
}
762+
}
763+
// now check against the previous active set and perform banning if this fails
764+
ProcessPendingInstantSendLocks(tipHeight - 1, pend, true);
765+
}
766+
} else {
767+
ProcessPendingInstantSendLocks(tipHeight, pend, true);
768+
}
769+
770+
return true;
740771
}
741772

742-
bool CInstantSendManager::ProcessPendingInstantSendLocks(int signHeight, const std::unordered_map<uint256, std::pair<NodeId, CInstantSendLock>>& pend)
773+
std::unordered_set<uint256> CInstantSendManager::ProcessPendingInstantSendLocks(int signHeight, const std::unordered_map<uint256, std::pair<NodeId, CInstantSendLock>>& pend, bool ban)
743774
{
744775
auto llmqType = Params().GetConsensus().llmqForInstantSend;
745776

@@ -770,7 +801,7 @@ bool CInstantSendManager::ProcessPendingInstantSendLocks(int signHeight, const s
770801
auto quorum = quorumSigningManager->SelectQuorumForSigning(llmqType, signHeight, id);
771802
if (!quorum) {
772803
// should not happen, but if one fails to select, all others will also fail to select
773-
return false;
804+
return {};
774805
}
775806
uint256 signHash = CLLMQUtils::BuildSignHash(llmqType, quorum->qc.quorumHash, id, islock.txid);
776807
batchVerifier.PushMessage(nodeId, hash, signHash, islock.sig.Get(), quorum->qc.quorumPublicKey);
@@ -793,7 +824,9 @@ bool CInstantSendManager::ProcessPendingInstantSendLocks(int signHeight, const s
793824

794825
batchVerifier.Verify();
795826

796-
if (!batchVerifier.badSources.empty()) {
827+
std::unordered_set<uint256> badISLocks;
828+
829+
if (ban && !batchVerifier.badSources.empty()) {
797830
LOCK(cs_main);
798831
for (auto& nodeId : batchVerifier.badSources) {
799832
// Let's not be too harsh, as the peer might simply be unlucky and might have sent us an old lock which
@@ -809,6 +842,7 @@ bool CInstantSendManager::ProcessPendingInstantSendLocks(int signHeight, const s
809842
if (batchVerifier.badMessages.count(hash)) {
810843
LogPrintf("CInstantSendManager::%s -- txid=%s, islock=%s: invalid sig in islock, peer=%d\n", __func__,
811844
islock.txid.ToString(), hash.ToString(), nodeId);
845+
badISLocks.emplace(hash);
812846
continue;
813847
}
814848

@@ -829,7 +863,7 @@ bool CInstantSendManager::ProcessPendingInstantSendLocks(int signHeight, const s
829863
}
830864
}
831865

832-
return true;
866+
return badISLocks;
833867
}
834868

835869
void CInstantSendManager::ProcessInstantSendLock(NodeId from, const uint256& hash, const CInstantSendLock& islock)

src/llmq/quorums_instantsend.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ class CInstantSendManager : public CRecoveredSigsListener
138138
void ProcessMessageInstantSendLock(CNode* pfrom, const CInstantSendLock& islock, CConnman& connman);
139139
bool PreVerifyInstantSendLock(NodeId nodeId, const CInstantSendLock& islock, bool& retBan);
140140
bool ProcessPendingInstantSendLocks();
141-
bool ProcessPendingInstantSendLocks(int signHeight, const std::unordered_map<uint256, std::pair<NodeId, CInstantSendLock>>& pend);
141+
std::unordered_set<uint256> ProcessPendingInstantSendLocks(int signHeight, const std::unordered_map<uint256, std::pair<NodeId, CInstantSendLock>>& pend, bool ban);
142142
void ProcessInstantSendLock(NodeId from, const uint256& hash, const CInstantSendLock& islock);
143143
void UpdateWalletTransaction(const CTransactionRef& tx, const CInstantSendLock& islock);
144144

0 commit comments

Comments
 (0)