Skip to content
Open
2 changes: 2 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ BITCOIN_CORE_H = \
llmq/quorums.h \
llmq/signhash.h \
llmq/signing.h \
llmq/net_signing.h \
llmq/signing_shares.h \
llmq/snapshot.h \
llmq/types.h \
Expand Down Expand Up @@ -535,6 +536,7 @@ libbitcoin_node_a_SOURCES = \
llmq/dkgsessionhandler.cpp \
llmq/dkgsessionmgr.cpp \
llmq/ehf_signals.cpp \
llmq/net_signing.cpp \
llmq/options.cpp \
llmq/quorums.cpp \
llmq/signhash.cpp \
Expand Down
1 change: 0 additions & 1 deletion src/evo/mnhftx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#include <llmq/commitment.h>
#include <llmq/quorums.h>
#include <llmq/signhash.h>
#include <llmq/signing.h>
#include <node/blockstorage.h>

#include <chain.h>
Expand Down
3 changes: 2 additions & 1 deletion src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
#include <llmq/context.h>
#include <llmq/dkgsessionmgr.h>
#include <llmq/options.h>
#include <llmq/signing.h>
#include <llmq/net_signing.h>
#include <masternode/active/context.h>
#include <masternode/active/notificationinterface.h>
#include <masternode/meta.h>
Expand Down Expand Up @@ -2200,6 +2200,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
RegisterValidationInterface(g_active_notification_interface.get());
}
node.peerman->AddExtraHandler(std::make_unique<NetInstantSend>(node.peerman.get(), *node.llmq_ctx->isman, *node.llmq_ctx->qman, chainman.ActiveChainstate()));
node.peerman->AddExtraHandler(std::make_unique<NetSigning>(node.peerman.get(), *node.llmq_ctx->sigman));

// ********************************************************* Step 7d: Setup other Dash services

Expand Down
5 changes: 1 addition & 4 deletions src/llmq/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ LLMQContext::LLMQContext(ChainstateManager& chainman, CDeterministicMNManager& d
qman{std::make_unique<llmq::CQuorumManager>(*bls_worker, chainman.ActiveChainstate(), dmnman, *qdkgsman, evo_db,
*quorum_block_processor, *qsnapman, mn_activeman, mn_sync, sporkman,
db_params)},
sigman{std::make_unique<llmq::CSigningManager>(chainman.ActiveChainstate(), *qman, db_params)},
sigman{std::make_unique<llmq::CSigningManager>(*qman, db_params)},
clhandler{std::make_unique<llmq::CChainLocksHandler>(chainman.ActiveChainstate(), *qman, sporkman, mempool, mn_sync)},
isman{std::make_unique<llmq::CInstantSendManager>(*clhandler, chainman.ActiveChainstate(), *sigman, sporkman,
mempool, mn_sync, db_params)}
Expand All @@ -44,19 +44,16 @@ LLMQContext::~LLMQContext() {
}

void LLMQContext::Interrupt() {
sigman->InterruptWorkerThread();
}

void LLMQContext::Start(PeerManager& peerman)
{
qman->Start();
sigman->StartWorkerThread(peerman);
clhandler->Start(*isman);
}

void LLMQContext::Stop()
{
clhandler->Stop();
sigman->StopWorkerThread();
qman->Stop();
}
149 changes: 149 additions & 0 deletions src/llmq/net_signing.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// Copyright (c) 2025 The Dash Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <llmq/net_signing.h>

#include <llmq/signhash.h>
#include <llmq/signing.h>

#include <bls/bls_batchverifier.h>
#include <cxxtimer.hpp>
#include <logging.h>
#include <streams.h>
#include <util/thread.h>
#include <validationinterface.h>

#include <unordered_map>

void NetSigning::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv)
{
if (msg_type != NetMsgType::QSIGREC) return;

auto recoveredSig = std::make_shared<llmq::CRecoveredSig>();
vRecv >> *recoveredSig;

WITH_LOCK(cs_main, m_peer_manager->PeerEraseObjectRequest(pfrom.GetId(),
CInv{MSG_QUORUM_RECOVERED_SIG, recoveredSig->GetHash()}));

if (!Params().GetLLMQ(recoveredSig->getLlmqType()).has_value()) {
m_peer_manager->PeerMisbehaving(pfrom.GetId(), 100);
return;
}

m_sig_manager.VerifyAndProcessRecoveredSig(pfrom.GetId(), std::move(recoveredSig));
}

void NetSigning::Start()
{
// can't start new thread if we have one running already
if (workThread.joinable()) {
assert(false);
}

workThread = std::thread(&util::TraceThread, "recsigs", [this] { WorkThreadMain(); });
}

void NetSigning::Stop()
{
// make sure to call InterruptWorkerThread() first
if (!workInterrupt) {
assert(false);
}

if (workThread.joinable()) {
workThread.join();
}
}

void NetSigning::ProcessRecoveredSig(std::shared_ptr<const llmq::CRecoveredSig> recoveredSig)
{
if (!m_sig_manager.ProcessRecoveredSig(recoveredSig)) return;

auto listeners = m_sig_manager.GetListeners();
for (auto& l : listeners) {
m_peer_manager->PeerPostProcessMessage(l->HandleNewRecoveredSig(*recoveredSig));
}

GetMainSignals().NotifyRecoveredSig(recoveredSig, recoveredSig->GetHash().ToString());
}

bool NetSigning::ProcessPendingRecoveredSigs()
{
Uint256HashMap<std::shared_ptr<const llmq::CRecoveredSig>> pending{m_sig_manager.FetchPendingReconstructed()};

for (const auto& p : pending) {
ProcessRecoveredSig(p.second);
}

std::unordered_map<NodeId, std::list<std::shared_ptr<const llmq::CRecoveredSig>>> recSigsByNode;
std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CBLSPublicKey, StaticSaltedHasher> pubkeys;

const size_t nMaxBatchSize{32};
bool more_work = m_sig_manager.CollectPendingRecoveredSigsToVerify(nMaxBatchSize, recSigsByNode, pubkeys);
if (recSigsByNode.empty()) {
return false;
}

// It's ok to perform insecure batched verification here as we verify against the quorum public keys, which are not
// craftable by individual entities, making the rogue public key attack impossible
CBLSBatchVerifier<NodeId, uint256> batchVerifier(false, false);

size_t verifyCount = 0;
for (const auto& [nodeId, v] : recSigsByNode) {
for (const auto& recSig : v) {
// we didn't verify the lazy signature until now
if (!recSig->sig.Get().IsValid()) {
batchVerifier.badSources.emplace(nodeId);
break;
}

const auto& pubkey = pubkeys.at(std::make_pair(recSig->getLlmqType(), recSig->getQuorumHash()));
batchVerifier.PushMessage(nodeId, recSig->GetHash(), recSig->buildSignHash().Get(), recSig->sig.Get(), pubkey);
verifyCount++;
}
}

cxxtimer::Timer verifyTimer(true);
batchVerifier.Verify();
verifyTimer.stop();

LogPrint(BCLog::LLMQ, "NetSigning::%s -- verified recovered sig(s). count=%d, vt=%d, nodes=%d\n", __func__,
verifyCount, verifyTimer.count(), recSigsByNode.size());

Uint256HashSet processed;
for (const auto& [nodeId, v] : recSigsByNode) {
if (batchVerifier.badSources.count(nodeId)) {
LogPrint(BCLog::LLMQ, "NetSigning::%s -- invalid recSig from other node, banning peer=%d\n", __func__, nodeId);
m_peer_manager->PeerMisbehaving(nodeId, 100);
continue;
}

for (const auto& recSig : v) {
if (!processed.emplace(recSig->GetHash()).second) {
continue;
}

ProcessRecoveredSig(recSig);
}
}

return more_work;
}

void NetSigning::WorkThreadMain()
{
while (!workInterrupt) {
bool fMoreWork = ProcessPendingRecoveredSigs();

constexpr auto CLEANUP_INTERVAL{5000ms};
if (cleanupThrottler.TryCleanup(CLEANUP_INTERVAL)) {
m_sig_manager.Cleanup();
}

// TODO Wakeup when pending signing is needed?
if (!fMoreWork && !workInterrupt.sleep_for(std::chrono::milliseconds(100))) {
return;
}
}
}
47 changes: 47 additions & 0 deletions src/llmq/net_signing.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) 2025 The Dash Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_LLMQ_NET_SIGNING_H
#define BITCOIN_LLMQ_NET_SIGNING_H

#include <net_processing.h>

#include <util/threadinterrupt.h>
#include <util/time.h>

#include <thread>

namespace llmq {
class CSigningManager;
} // namespace llmq

class NetSigning final : public NetHandler
{
public:
NetSigning(PeerManagerInternal* peer_manager, llmq::CSigningManager& sig_manager) :
NetHandler(peer_manager),
m_sig_manager(sig_manager)
{
workInterrupt.reset();
}
void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv) override;

[[nodiscard]] bool ProcessPendingRecoveredSigs();
void ProcessRecoveredSig(std::shared_ptr<const llmq::CRecoveredSig> recoveredSig);

void Start() override;
void Stop() override;
void Interrupt() override { workInterrupt(); };

void WorkThreadMain();

private:
llmq::CSigningManager& m_sig_manager;

CleanupThrottler<NodeClock> cleanupThrottler;
std::thread workThread;
CThreadInterrupt workInterrupt;
};

#endif // BITCOIN_LLMQ_NET_SIGNING_H
Loading
Loading