Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 28 additions & 10 deletions src/llmq/dkgsessionhandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,32 @@ void CDKGPendingMessages::PushPendingMessage(NodeId from, std::shared_ptr<CDataS
{
LOCK(cs_messages);

if (messagesPerNode[from] >= maxMessagesPerNode) {
// Check duplicates before charging the per-node quota so a peer that
// resends the same hash cannot exhaust its budget with dupes.
if (seenMessages.count(hash) != 0) {
LogPrint(BCLog::LLMQ_DKG, "CDKGPendingMessages::%s -- already seen %s, peer=%d\n", __func__, hash.ToString(), from);
return;
}

const auto node_it = messagesPerNode.find(from);
if (node_it != messagesPerNode.end() && node_it->second >= maxMessagesPerNode) {
// TODO ban?
LogPrint(BCLog::LLMQ_DKG, "CDKGPendingMessages::%s -- too many messages, peer=%d\n", __func__, from);
return;
}
messagesPerNode[from]++;

if (!seenMessages.emplace(hash).second) {
LogPrint(BCLog::LLMQ_DKG, "CDKGPendingMessages::%s -- already seen %s, peer=%d\n", __func__, hash.ToString(), from);
const bool is_remote = from != -1;
if ((is_remote && pendingRemoteMessageCount >= maxPendingRemoteMessages) ||
pendingMessages.size() >= maxPendingMessages) {
LogPrint(BCLog::LLMQ_DKG, "CDKGPendingMessages::%s -- pending queue full, peer=%d\n", __func__, from);
return;
}
messagesPerNode[from]++;
if (is_remote) {
pendingRemoteMessageCount++;
}

seenMessages.emplace(hash);
pendingMessages.emplace_back(std::make_pair(from, std::move(pm)));
}

Expand All @@ -50,27 +64,31 @@ std::list<CDKGPendingMessages::BinaryMessage> CDKGPendingMessages::PopPendingMes

std::list<BinaryMessage> ret;
while (!pendingMessages.empty() && ret.size() < maxCount) {
if (pendingMessages.front().first != -1) {
pendingRemoteMessageCount--;
}
ret.emplace_back(std::move(pendingMessages.front()));
pendingMessages.pop_front();
}

return ret;
}

bool CDKGPendingMessages::HasSeen(const uint256& hash) const
{
LOCK(cs_messages);
return seenMessages.count(hash) != 0;
}

void CDKGPendingMessages::Clear()
{
LOCK(cs_messages);
pendingMessages.clear();
pendingRemoteMessageCount = 0;
messagesPerNode.clear();
seenMessages.clear();
}

bool CDKGPendingMessages::HasSeen(const uint256& hash) const
{
LOCK(cs_messages);
return seenMessages.count(hash) != 0;
}

void CDKGSessionHandler::ClearPendingMessages()
{
pendingContributions.Clear();
Expand Down
44 changes: 14 additions & 30 deletions src/llmq/dkgsessionhandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
#include <list>
#include <map>
#include <memory>
#include <optional>
#include <string_view>
#include <vector>

class CDataStream;
Expand Down Expand Up @@ -47,7 +45,7 @@ enum class QuorumPhase {
* main handler thread, we push them into a CDKGPendingMessages object and later pop+deserialize them in the DKG phase
* handler thread.
*
* Each message type has it's own instance of this class.
* Each message type has its own instance of this class.
*/
class CDKGPendingMessages
{
Expand All @@ -56,52 +54,38 @@ class CDKGPendingMessages

private:
const size_t maxMessagesPerNode;
const size_t maxPendingRemoteMessages;
const size_t maxPendingMessages;
mutable Mutex cs_messages;
std::list<BinaryMessage> pendingMessages GUARDED_BY(cs_messages);
size_t pendingRemoteMessageCount GUARDED_BY(cs_messages){0};
std::map<NodeId, size_t> messagesPerNode GUARDED_BY(cs_messages);
Uint256HashSet seenMessages GUARDED_BY(cs_messages);

public:
explicit CDKGPendingMessages(size_t _maxMessagesPerNode) :
maxMessagesPerNode(_maxMessagesPerNode) {};
maxMessagesPerNode(_maxMessagesPerNode),
// Let two peers use their full quota while keeping reconnect-generated
// NodeIds from growing the queue without bound.
maxPendingRemoteMessages(_maxMessagesPerNode * 2),
// Reserve one slot for the message produced by this node during the
// matching phase.
maxPendingMessages(maxPendingRemoteMessages + 1)
{
}

/**
* Enqueue a serialized DKG message under @p from with content hash @p hash.
* Caller is responsible for hashing the payload and (for real peers)
* routing the erase-request to PeerManager. Drops the message silently on
* per-node capacity overflow or duplicate hash.
* per-node or queue-wide capacity overflow, or duplicate hash.
*/
void PushPendingMessage(NodeId from, std::shared_ptr<CDataStream> pm, const uint256& hash)
EXCLUSIVE_LOCKS_REQUIRED(!cs_messages);

std::list<BinaryMessage> PopPendingMessages(size_t maxCount) EXCLUSIVE_LOCKS_REQUIRED(!cs_messages);
bool HasSeen(const uint256& hash) const EXCLUSIVE_LOCKS_REQUIRED(!cs_messages);
void Clear() EXCLUSIVE_LOCKS_REQUIRED(!cs_messages);

// Might return nullptr messages, which indicates that deserialization failed for some reason
template <typename Message>
std::vector<std::pair<NodeId, std::shared_ptr<Message>>> PopAndDeserializeMessages(size_t maxCount)
EXCLUSIVE_LOCKS_REQUIRED(!cs_messages)
{
auto binaryMessages = PopPendingMessages(maxCount);
if (binaryMessages.empty()) {
return {};
}

std::vector<std::pair<NodeId, std::shared_ptr<Message>>> ret;
ret.reserve(binaryMessages.size());
for (const auto& bm : binaryMessages) {
auto msg = std::make_shared<Message>();
try {
*bm.second >> *msg;
} catch (...) {
msg = nullptr;
}
ret.emplace_back(std::make_pair(bm.first, std::move(msg)));
}

return ret;
}
};

/**
Expand Down
Loading
Loading