From 0c3ba643246299b77d3b2e98b5511e9f63d1663a Mon Sep 17 00:00:00 2001 From: pasta Date: Wed, 11 Nov 2026 21:30:44 -0600 Subject: [PATCH] fix(llmq): process our own DKG messages ahead of queued peer messages A node's own contribution, complaint, justification and premature commitment went to the back of the pending queue, so they were only verified and relayed after every peer message already waiting. Verifying a premature commitment of a 400-member quorum takes a noticeable fraction of a second, so on a busy or slow node a backlog could hold its own commitment back for much of a short commit phase. Put messages this node produced at the front of the queue. Co-Authored-By: Claude Fable 5.2 --- src/llmq/dkgsessionhandler.cpp | 7 ++++++- src/llmq/dkgsessionhandler.h | 3 ++- src/test/llmq_dkg_tests.cpp | 17 +++++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/llmq/dkgsessionhandler.cpp b/src/llmq/dkgsessionhandler.cpp index 267a5bc372a4..04ad6bf8816d 100644 --- a/src/llmq/dkgsessionhandler.cpp +++ b/src/llmq/dkgsessionhandler.cpp @@ -52,7 +52,12 @@ void CDKGPendingMessages::PushPendingMessage(NodeId from, const uint256& sender_ count++; seenMessages.emplace(hash); - pendingMessages.emplace_back(from, std::move(pm)); + if (from == -1) { + // Our own message must not wait behind a backlog of peer messages before it is relayed + pendingMessages.emplace_front(from, std::move(pm)); + } else { + pendingMessages.emplace_back(from, std::move(pm)); + } } std::list CDKGPendingMessages::PopPendingMessages(size_t maxCount) diff --git a/src/llmq/dkgsessionhandler.h b/src/llmq/dkgsessionhandler.h index f52bbc81c416..eb3d560343fe 100644 --- a/src/llmq/dkgsessionhandler.h +++ b/src/llmq/dkgsessionhandler.h @@ -71,7 +71,8 @@ class CDKGPendingMessages * Enqueue a serialized DKG message under @p from with content hash @p hash. * @p sender_protx keys the per-proTx quota: the sender's MNAuth-verified * proTxHash for remote messages, or this node's own for messages it - * produced itself (@p from == -1). Drops the message silently on quota + * produced itself (@p from == -1), which go to the front of the queue. + * Drops the message silently on quota * overflow or duplicate hash; quota-dropped messages are not marked seen, * so another peer with budget can re-deliver them. Caller is responsible * for hashing the payload and (for real peers) routing the erase-request diff --git a/src/test/llmq_dkg_tests.cpp b/src/test/llmq_dkg_tests.cpp index 6595cf457a02..d2de8564211d 100644 --- a/src/test/llmq_dkg_tests.cpp +++ b/src/test/llmq_dkg_tests.cpp @@ -60,6 +60,23 @@ BOOST_AUTO_TEST_CASE(pending_messages_own_messages_share_quota_path) BOOST_CHECK_EQUAL(pending.PopPendingMessages(3).size(), 2U); } +BOOST_AUTO_TEST_CASE(pending_messages_own_message_is_processed_first) +{ + using namespace llmq; + + CDKGPendingMessages pending{/*_maxMessagesPerProTx=*/2}; + pending.PushPendingMessage(/*from=*/1, MakeTestHash(0xa1), MakeDKGMessage(), MakeTestHash(1)); + pending.PushPendingMessage(/*from=*/2, MakeTestHash(0xa2), MakeDKGMessage(), MakeTestHash(2)); + pending.PushPendingMessage(/*from=*/-1, MakeTestHash(0xee), MakeDKGMessage(), MakeTestHash(3)); + + const auto msgs = pending.PopPendingMessages(3); + BOOST_REQUIRE_EQUAL(msgs.size(), 3U); + auto it = msgs.begin(); + BOOST_CHECK_EQUAL((it++)->first, -1); + BOOST_CHECK_EQUAL((it++)->first, 1); + BOOST_CHECK_EQUAL((it++)->first, 2); +} + BOOST_AUTO_TEST_CASE(pending_messages_quota_survives_reconnect) { using namespace llmq;