Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
d9beeec
qt: switch RPCConsole and leftover MasternodeList code to feed
kwvg Feb 18, 2026
960666b
qt: switch ProposalList to consume masternode list feed
kwvg Feb 21, 2026
b524eef
qt: precompute mappings to avoid expensive searches
kwvg Feb 18, 2026
dfec7d8
fix(qt): align headers in debug window's information tab
kwvg Jan 26, 2026
0a64bab
refactor(qt): move debug window stats to separate information widget
kwvg Feb 20, 2026
918cba1
refactor(qt): move Dash-specific reporting to network widget
kwvg Feb 20, 2026
6bb3e6b
chore(qt): update header and label descriptions based on capability
kwvg Feb 19, 2026
d1f61f5
refactor: drop now-unused cached masternode list routine
kwvg Feb 21, 2026
ba406f5
interfaces: introduce UI signal `NotifyInstantSendChanged`
kwvg Feb 16, 2026
654724d
qt: register instantsend information as a feed, replace polling approach
kwvg Feb 19, 2026
ab8d6d2
qt: register chainlocks information as a feed, treat UI notif as trigger
kwvg Feb 19, 2026
a4e9fbc
qt: report chainlock time to maintain parity with block fields
kwvg Feb 18, 2026
b9a14cc
qt: show more instantsend counters (pending, waiting, unprotected) in UI
kwvg Feb 21, 2026
26ed211
feat(qt): report credit pool statistics in network widget
kwvg Feb 21, 2026
12dc7fe
feat(qt): report quorum statistics in network widget
kwvg Feb 22, 2026
dff26d6
refactor(qt): use horizontal layout with vertical grids, reorder data
kwvg Feb 21, 2026
8a721c2
refactor(qt): move "Chainlocks" outside hlayout due to value width
kwvg Feb 19, 2026
7839115
feat(qt): add tooltip for quorum statistics with rotation, expiry, age
kwvg Feb 20, 2026
f85a459
refactor(qt): move debug log action from General widget to File menu
kwvg Feb 23, 2026
831e0a7
governance: add unique voter count reporting, expose through interface
kwvg Feb 20, 2026
3fabce1
feat(qt): report proposal information
kwvg Feb 20, 2026
cb8dc0a
feat(qt): add wallet-specific section to proposal widget
kwvg Feb 20, 2026
fe63b27
feat(qt): add proposal info button to proposal list
kwvg Feb 23, 2026
5f96aec
feat(qt): add interactive DonutChart widget
kwvg Feb 20, 2026
6bcf12e
feat(qt): add budget donut chart to proposal info view
kwvg Feb 20, 2026
5961ed6
feat(qt): add lifecycle and vote-aware sorting in proposal list
kwvg Feb 20, 2026
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
20 changes: 18 additions & 2 deletions src/Makefile.qt.include
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,17 @@ QT_FORMS_UI = \
qt/forms/descriptiondialog.ui \
qt/forms/editaddressdialog.ui \
qt/forms/helpmessagedialog.ui \
qt/forms/informationwidget.ui \
qt/forms/intro.ui \
qt/forms/masternodelist.ui \
qt/forms/mnemonicverificationdialog.ui \
qt/forms/modaloverlay.ui \
qt/forms/networkwidget.ui \
qt/forms/openuridialog.ui \
qt/forms/optionsdialog.ui \
qt/forms/overviewpage.ui \
qt/forms/proposalcreate.ui \
qt/forms/proposalinfo.ui \
qt/forms/proposallist.ui \
qt/forms/proposalresume.ui \
qt/forms/psbtoperationsdialog.ui \
Expand Down Expand Up @@ -60,8 +63,10 @@ QT_MOC_CPP = \
qt/moc_createwalletdialog.cpp \
qt/moc_csvmodelwriter.cpp \
qt/moc_descriptiondialog.cpp \
qt/moc_donutchart.cpp \
qt/moc_editaddressdialog.cpp \
qt/moc_guiutil.cpp \
qt/moc_informationwidget.cpp \
qt/moc_initexecutor.cpp \
qt/moc_intro.cpp \
qt/moc_macdockiconhandler.cpp \
Expand All @@ -70,6 +75,7 @@ QT_MOC_CPP = \
qt/moc_masternodemodel.cpp \
qt/moc_mnemonicverificationdialog.cpp \
qt/moc_modaloverlay.cpp \
qt/moc_networkwidget.cpp \
qt/moc_notificator.cpp \
qt/moc_openuridialog.cpp \
qt/moc_optionsdialog.cpp \
Expand All @@ -79,6 +85,7 @@ QT_MOC_CPP = \
qt/moc_peertablemodel.cpp \
qt/moc_peertablesortproxy.cpp \
qt/moc_proposalcreate.cpp \
qt/moc_proposalinfo.cpp \
qt/moc_proposallist.cpp \
qt/moc_proposalmodel.cpp \
qt/moc_proposalresume.cpp \
Expand Down Expand Up @@ -141,10 +148,12 @@ BITCOIN_QT_H = \
qt/createwalletdialog.h \
qt/csvmodelwriter.h \
qt/descriptiondialog.h \
qt/donutchart.h \
qt/editaddressdialog.h \
qt/guiconstants.h \
qt/guiutil_font.h \
qt/guiutil.h \
qt/informationwidget.h \
qt/initexecutor.h \
qt/intro.h \
qt/macdockiconhandler.h \
Expand All @@ -155,6 +164,7 @@ BITCOIN_QT_H = \
qt/mnemonicverificationdialog.h \
qt/modaloverlay.h \
qt/networkstyle.h \
qt/networkwidget.h \
qt/notificator.h \
qt/openuridialog.h \
qt/optionsdialog.h \
Expand All @@ -164,6 +174,7 @@ BITCOIN_QT_H = \
qt/peertablemodel.h \
qt/peertablesortproxy.h \
qt/proposalcreate.h \
qt/proposalinfo.h \
qt/proposallist.h \
qt/proposalmodel.h \
qt/proposalresume.h \
Expand All @@ -189,6 +200,7 @@ BITCOIN_QT_H = \
qt/transactionrecord.h \
qt/transactiontablemodel.h \
qt/transactionview.h \
qt/util.h \
qt/utilitydialog.h \
qt/walletcontroller.h \
qt/walletframe.h \
Expand Down Expand Up @@ -244,25 +256,29 @@ BITCOIN_QT_BASE_CPP = \
qt/bantablemodel.cpp \
qt/bitcoin.cpp \
qt/bitcoinaddressvalidator.cpp \
qt/masternodemodel.cpp \
qt/proposalmodel.cpp \
qt/bitcoinamountfield.cpp \
qt/bitcoingui.cpp \
qt/bitcoinunits.cpp \
qt/clientfeeds.cpp \
qt/clientmodel.cpp \
qt/csvmodelwriter.cpp \
qt/donutchart.cpp \
qt/guiutil.cpp \
qt/guiutil_font.cpp \
qt/informationwidget.cpp \
qt/initexecutor.cpp \
qt/intro.cpp \
qt/masternodemodel.cpp \
qt/modaloverlay.cpp \
qt/networkstyle.cpp \
qt/networkwidget.cpp \
qt/notificator.cpp \
qt/optionsdialog.cpp \
qt/optionsmodel.cpp \
qt/peertablemodel.cpp \
qt/peertablesortproxy.cpp \
qt/proposalinfo.cpp \
qt/proposalmodel.cpp \
qt/qvalidatedlineedit.cpp \
qt/qvaluecombobox.cpp \
qt/rpcconsole.cpp \
Expand Down
21 changes: 21 additions & 0 deletions src/governance/object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,27 @@ int CGovernanceObject::GetAbstainCount(const CDeterministicMNList& tip_mn_list,
return CountMatchingVotes(tip_mn_list, eVoteSignalIn, VOTE_OUTCOME_ABSTAIN);
}

CGovernanceObject::UniqueVoterCount CGovernanceObject::GetUniqueVoterCount(const CDeterministicMNList& tip_mn_list, vote_signal_enum_t eVoteSignalIn) const
{
LOCK(cs);
UniqueVoterCount result;
for (const auto& [outpoint, recVote] : mapCurrentMNVotes) {
if (recVote.mapInstances.count(eVoteSignalIn) == 0) {
continue;
}
auto dmn = tip_mn_list.GetMNByCollateral(outpoint);
if (!dmn) {
continue;
}
if (dmn->nType == MnType::Evo) {
++result.m_evo;
} else {
++result.m_regular;
}
}
return result;
}

bool CGovernanceObject::GetCurrentMNVotes(const COutPoint& mnCollateralOutpoint, vote_rec_t& voteRecord) const
{
LOCK(cs);
Expand Down
7 changes: 7 additions & 0 deletions src/governance/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,13 @@ class CGovernanceObject
int GetAbstainCount(const CDeterministicMNList& tip_mn_list, vote_signal_enum_t eVoteSignalIn) const
EXCLUSIVE_LOCKS_REQUIRED(!cs);

struct UniqueVoterCount {
uint16_t m_regular{0};
uint16_t m_evo{0};
};
UniqueVoterCount GetUniqueVoterCount(const CDeterministicMNList& tip_mn_list, vote_signal_enum_t eVoteSignalIn) const
EXCLUSIVE_LOCKS_REQUIRED(!cs);

bool GetCurrentMNVotes(const COutPoint& mnCollateralOutpoint, vote_rec_t& voteRecord) const
EXCLUSIVE_LOCKS_REQUIRED(!cs);

Expand Down
2 changes: 2 additions & 0 deletions src/instantsend/net_instantsend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <instantsend/net_instantsend.h>

#include <bls/bls_batchverifier.h>
#include <node/interface_ui.h>
#include <cxxtimer.hpp>
#include <instantsend/instantsend.h>
#include <llmq/commitment.h>
Expand Down Expand Up @@ -307,6 +308,7 @@ void NetInstantSend::ProcessPendingISLocks(std::vector<instantsend::PendingISLoc
// Now check against the previous active set and perform banning if this fails
ProcessPendingInstantSendLocks(llmq_params, dkgInterval, /*ban=*/true, still_pending);
}
uiInterface.NotifyInstantSendChanged();
}

void NetInstantSend::WorkThreadMain()
Expand Down
15 changes: 9 additions & 6 deletions src/interfaces/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,19 +115,13 @@ class MnList
virtual uint256 getBlockHash() const = 0;

virtual void forEachMN(bool only_valid, std::function<void(const MnEntryCPtr&)> cb) const = 0;
virtual MnEntryCPtr getMN(const uint256& hash) const = 0;
virtual MnEntryCPtr getMNByService(const CService& service) const = 0;
virtual MnEntryCPtr getValidMN(const uint256& hash) const = 0;
virtual std::vector<MnEntryCPtr> getProjectedMNPayees(const CBlockIndex* pindex) const = 0;

virtual void copyContextTo(MnList& mn_list) const = 0;
virtual void setContext(node::NodeContext* context) = 0;
};

using MnListPtr = std::shared_ptr<MnList>;

MnListPtr MakeMNList(const CDeterministicMNList& mn_list);

//! Interface for the src/evo part of a dash node (dashd process).
class EVO
{
Expand All @@ -149,6 +143,11 @@ class GOV
int32_t m_yes{0};
};
virtual Votes getObjVotes(const CGovernanceObject& obj, vote_signal_enum_t vote_signal) = 0;
struct UniqueVoters {
uint16_t m_regular{0};
uint16_t m_evo{0};
};
virtual UniqueVoters getObjUniqueVoters(const CGovernanceObject& obj, vote_signal_enum_t vote_signal) = 0;
virtual bool getObjLocalValidity(const CGovernanceObject& obj, std::string& error, bool check_collateral) = 0;
virtual bool existsObj(const uint256& hash) = 0;
virtual bool isEnabled() = 0;
Expand Down Expand Up @@ -517,6 +516,10 @@ class Node
std::function<void(SynchronizationState, interfaces::BlockTip tip, double verification_progress)>;
virtual std::unique_ptr<Handler> handleNotifyHeaderTip(NotifyHeaderTipFn fn) = 0;

//! Register handler for InstantSend data messages.
using NotifyInstantSendChangedFn = std::function<void()>;
virtual std::unique_ptr<Handler> handleNotifyInstantSendChanged(NotifyInstantSendChangedFn fn) = 0;

//! Register handler for governance data messages.
using NotifyGovernanceChangedFn = std::function<void()>;
virtual std::unique_ptr<Handler> handleNotifyGovernanceChanged(NotifyGovernanceChangedFn fn) = 0;
Expand Down
3 changes: 3 additions & 0 deletions src/node/interface_ui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ struct UISignals {
boost::signals2::signal<CClientUIInterface::NotifyChainLockSig> NotifyChainLock;
boost::signals2::signal<CClientUIInterface::NotifyHeaderTipSig> NotifyHeaderTip;
boost::signals2::signal<CClientUIInterface::NotifyGovernanceChangedSig> NotifyGovernanceChanged;
boost::signals2::signal<CClientUIInterface::NotifyInstantSendChangedSig> NotifyInstantSendChanged;
boost::signals2::signal<CClientUIInterface::NotifyMasternodeListChangedSig> NotifyMasternodeListChanged;
boost::signals2::signal<CClientUIInterface::NotifyAdditionalDataSyncProgressChangedSig> NotifyAdditionalDataSyncProgressChanged;
boost::signals2::signal<CClientUIInterface::BannedListChangedSig> BannedListChanged;
Expand All @@ -48,6 +49,7 @@ ADD_SIGNALS_IMPL_WRAPPER(NotifyBlockTip);
ADD_SIGNALS_IMPL_WRAPPER(NotifyChainLock);
ADD_SIGNALS_IMPL_WRAPPER(NotifyHeaderTip);
ADD_SIGNALS_IMPL_WRAPPER(NotifyGovernanceChanged);
ADD_SIGNALS_IMPL_WRAPPER(NotifyInstantSendChanged);
ADD_SIGNALS_IMPL_WRAPPER(NotifyMasternodeListChanged);
ADD_SIGNALS_IMPL_WRAPPER(NotifyAdditionalDataSyncProgressChanged);
ADD_SIGNALS_IMPL_WRAPPER(BannedListChanged);
Expand All @@ -64,6 +66,7 @@ void CClientUIInterface::NotifyBlockTip(SynchronizationState s, const CBlockInde
void CClientUIInterface::NotifyChainLock(const std::string& bestChainLockHash, int bestChainLockHeight) { return g_ui_signals.NotifyChainLock(bestChainLockHash, bestChainLockHeight); }
void CClientUIInterface::NotifyHeaderTip(SynchronizationState s, const CBlockIndex* i) { return g_ui_signals.NotifyHeaderTip(s, i); }
void CClientUIInterface::NotifyGovernanceChanged() { return g_ui_signals.NotifyGovernanceChanged(); }
void CClientUIInterface::NotifyInstantSendChanged() { return g_ui_signals.NotifyInstantSendChanged(); }
void CClientUIInterface::NotifyMasternodeListChanged(const CDeterministicMNList& list, const CBlockIndex* i) { return g_ui_signals.NotifyMasternodeListChanged(list, i); }
void CClientUIInterface::NotifyAdditionalDataSyncProgressChanged(double nSyncProgress) { return g_ui_signals.NotifyAdditionalDataSyncProgressChanged(nSyncProgress); }
void CClientUIInterface::BannedListChanged() { return g_ui_signals.BannedListChanged(); }
Expand Down
3 changes: 3 additions & 0 deletions src/node/interface_ui.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ class CClientUIInterface
/** Masternode list has changed */
ADD_SIGNALS_DECL_WRAPPER(NotifyMasternodeListChanged, void, const CDeterministicMNList&, const CBlockIndex*);

/** InstantSend data changed */
ADD_SIGNALS_DECL_WRAPPER(NotifyInstantSendChanged, void);

/** Governance data changed */
ADD_SIGNALS_DECL_WRAPPER(NotifyGovernanceChanged, void);

Expand Down
39 changes: 18 additions & 21 deletions src/node/interfaces.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,21 +164,6 @@ class MnListImpl : public MnList
cb(std::make_shared<const MnEntryImpl>(dmn));
});
}
MnEntryCPtr getMN(const uint256& hash) const override
{
const auto dmn{m_list.GetMN(hash)};
return dmn ? std::make_shared<const MnEntryImpl>(dmn) : nullptr;
}
MnEntryCPtr getMNByService(const CService& service) const override
{
const auto dmn{m_list.GetMNByService(service)};
return dmn ? std::make_shared<const MnEntryImpl>(dmn) : nullptr;
}
MnEntryCPtr getValidMN(const uint256& hash) const override
{
const auto dmn{m_list.GetValidMN(hash)};
return dmn ? std::make_shared<const MnEntryImpl>(dmn) : nullptr;
}
std::vector<MnEntryCPtr> getProjectedMNPayees(const CBlockIndex* pindex) const override
{
std::vector<MnEntryCPtr> ret;
Expand All @@ -188,11 +173,6 @@ class MnListImpl : public MnList
return ret;
}

void copyContextTo(MnList& mn_list) const override
{
if (!m_context) return;
mn_list.setContext(m_context);
}
void setContext(NodeContext* context) override
{
m_context = context;
Expand Down Expand Up @@ -264,6 +244,20 @@ class GOVImpl : public GOV
}
return ret;
}
UniqueVoters getObjUniqueVoters(const CGovernanceObject& obj, vote_signal_enum_t vote_signal) override
{
if (context().govman != nullptr && context().dmnman != nullptr) {
const auto& tip_mn_list{context().dmnman->GetListAtChainTip()};
if (auto govobj{context().govman->FindGovernanceObject(obj.GetHash())}) {
const auto count = govobj->GetUniqueVoterCount(tip_mn_list, vote_signal);
return {.m_regular = count.m_regular, .m_evo = count.m_evo};
} else {
const auto count = obj.GetUniqueVoterCount(tip_mn_list, vote_signal);
return {.m_regular = count.m_regular, .m_evo = count.m_evo};
}
}
return {0, 0};
}
bool existsObj(const uint256& hash) override
{
if (context().govman != nullptr) {
Expand Down Expand Up @@ -1023,6 +1017,10 @@ class NodeImpl : public Node
/* verification progress is unused when a header was received */ 0);
}));
}
std::unique_ptr<Handler> handleNotifyInstantSendChanged(NotifyInstantSendChangedFn fn) override
{
return MakeHandler(::uiInterface.NotifyInstantSendChanged_connect(fn));
}
std::unique_ptr<Handler> handleNotifyGovernanceChanged(NotifyGovernanceChangedFn fn) override
{
return MakeHandler(::uiInterface.NotifyGovernanceChanged_connect(fn));
Expand Down Expand Up @@ -1503,5 +1501,4 @@ class ChainImpl : public Chain
namespace interfaces {
std::unique_ptr<Node> MakeNode(node::NodeContext& context) { return std::make_unique<node::NodeImpl>(context); }
std::unique_ptr<Chain> MakeChain(node::NodeContext& node) { return std::make_unique<node::ChainImpl>(node); }
MnListPtr MakeMNList(const CDeterministicMNList& mn_list) { return std::make_shared<node::MnListImpl>(mn_list); }
} // namespace interfaces
Loading
Loading