Merge pull request #6315
7aac6db
[QT] dump banlist to disk in case of ban/unban over QT (Jonas Schnelli)7f90ea7
[QA] adabt QT_NO_KEYWORDS for QT ban implementation (Jonas Schnelli)07f70b2
[QA] fix netbase tests because of new CSubNet::ToString() output (Jonas Schnelli)4ed0510
[Qt] call DumpBanlist() when baning unbaning nodes (Philip Kaufmann)be89292
[Qt] reenabling hotkeys for ban context menu, use different words (Jonas Schnelli)b1189cf
[Qt] adapt QT ban option to banlist.dat changes (Jonas Schnelli)65abe91
[Qt] add sorting for bantable (Philip Kaufmann)51654de
[Qt] bantable polish (Philip Kaufmann)cdd72cd
[Qt] simplify ban list signal handling (Philip Kaufmann)43c1f5b
[Qt] remove unused timer-code from banlistmodel.cpp (Jonas Schnelli)e2b8028
net: Fix CIDR notation in ToString() (Wladimir J. van der Laan)9e521c1
[Qt] polish ban table (Philip Kaufmann)607809f
net: use CIDR notation in CSubNet::ToString() (Jonas Schnelli)53caec6
[Qt] bantable overhaul (Jonas Schnelli)f0bcbc4
[Qt] bantable fix timestamp 64bit issue (Jonas Schnelli)6135309
[Qt] banlist, UI optimizing and better signal handling (Jonas Schnelli)770ca79
[Qt] add context menu with unban option to ban table (Jonas Schnelli)5f42132
[Qt] add ui signal for banlist changes (Jonas Schnelli)ad204df
[Qt] add banlist table below peers table (Jonas Schnelli)50f0908
[Qt] add ban functions to peers window (Jonas Schnelli)
This commit is contained in:
commit
e59d2a80f9
@ -55,7 +55,7 @@ class NodeHandlingTest (BitcoinTestFramework):
|
|||||||
self.nodes[2].setban("192.168.0.1", "add", 1) #ban for 1 seconds
|
self.nodes[2].setban("192.168.0.1", "add", 1) #ban for 1 seconds
|
||||||
self.nodes[2].setban("2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/19", "add", 1000) #ban for 1000 seconds
|
self.nodes[2].setban("2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/19", "add", 1000) #ban for 1000 seconds
|
||||||
listBeforeShutdown = self.nodes[2].listbanned();
|
listBeforeShutdown = self.nodes[2].listbanned();
|
||||||
assert_equal("192.168.0.1/255.255.255.255", listBeforeShutdown[2]['address']) #must be here
|
assert_equal("192.168.0.1/32", listBeforeShutdown[2]['address']) #must be here
|
||||||
time.sleep(2) #make 100% sure we expired 192.168.0.1 node time
|
time.sleep(2) #make 100% sure we expired 192.168.0.1 node time
|
||||||
|
|
||||||
#stop node
|
#stop node
|
||||||
@ -63,9 +63,9 @@ class NodeHandlingTest (BitcoinTestFramework):
|
|||||||
|
|
||||||
self.nodes[2] = start_node(2, self.options.tmpdir)
|
self.nodes[2] = start_node(2, self.options.tmpdir)
|
||||||
listAfterShutdown = self.nodes[2].listbanned();
|
listAfterShutdown = self.nodes[2].listbanned();
|
||||||
assert_equal("127.0.0.0/255.255.255.0", listAfterShutdown[0]['address'])
|
assert_equal("127.0.0.0/24", listAfterShutdown[0]['address'])
|
||||||
assert_equal("127.0.0.0/255.255.255.255", listAfterShutdown[1]['address'])
|
assert_equal("127.0.0.0/32", listAfterShutdown[1]['address'])
|
||||||
assert_equal("2001:4000::/ffff:e000:0:0:0:0:0:0", listAfterShutdown[2]['address'])
|
assert_equal("/19" in listAfterShutdown[2]['address'], True)
|
||||||
|
|
||||||
###########################
|
###########################
|
||||||
# RPC disconnectnode test #
|
# RPC disconnectnode test #
|
||||||
|
@ -97,6 +97,7 @@ QT_MOC_CPP = \
|
|||||||
qt/moc_addressbookpage.cpp \
|
qt/moc_addressbookpage.cpp \
|
||||||
qt/moc_addresstablemodel.cpp \
|
qt/moc_addresstablemodel.cpp \
|
||||||
qt/moc_askpassphrasedialog.cpp \
|
qt/moc_askpassphrasedialog.cpp \
|
||||||
|
qt/moc_bantablemodel.cpp \
|
||||||
qt/moc_bitcoinaddressvalidator.cpp \
|
qt/moc_bitcoinaddressvalidator.cpp \
|
||||||
qt/moc_bitcoinamountfield.cpp \
|
qt/moc_bitcoinamountfield.cpp \
|
||||||
qt/moc_bitcoingui.cpp \
|
qt/moc_bitcoingui.cpp \
|
||||||
@ -162,6 +163,7 @@ BITCOIN_QT_H = \
|
|||||||
qt/addressbookpage.h \
|
qt/addressbookpage.h \
|
||||||
qt/addresstablemodel.h \
|
qt/addresstablemodel.h \
|
||||||
qt/askpassphrasedialog.h \
|
qt/askpassphrasedialog.h \
|
||||||
|
qt/bantablemodel.h \
|
||||||
qt/bitcoinaddressvalidator.h \
|
qt/bitcoinaddressvalidator.h \
|
||||||
qt/bitcoinamountfield.h \
|
qt/bitcoinamountfield.h \
|
||||||
qt/bitcoingui.h \
|
qt/bitcoingui.h \
|
||||||
@ -260,6 +262,7 @@ RES_ICONS = \
|
|||||||
qt/res/icons/verify.png
|
qt/res/icons/verify.png
|
||||||
|
|
||||||
BITCOIN_QT_CPP = \
|
BITCOIN_QT_CPP = \
|
||||||
|
qt/bantablemodel.cpp \
|
||||||
qt/bitcoinaddressvalidator.cpp \
|
qt/bitcoinaddressvalidator.cpp \
|
||||||
qt/bitcoinamountfield.cpp \
|
qt/bitcoinamountfield.cpp \
|
||||||
qt/bitcoingui.cpp \
|
qt/bitcoingui.cpp \
|
||||||
|
@ -1311,17 +1311,57 @@ bool CSubNet::Match(const CNetAddr &addr) const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int NetmaskBits(uint8_t x)
|
||||||
|
{
|
||||||
|
switch(x) {
|
||||||
|
case 0x00: return 0; break;
|
||||||
|
case 0x80: return 1; break;
|
||||||
|
case 0xc0: return 2; break;
|
||||||
|
case 0xe0: return 3; break;
|
||||||
|
case 0xf0: return 4; break;
|
||||||
|
case 0xf8: return 5; break;
|
||||||
|
case 0xfc: return 6; break;
|
||||||
|
case 0xfe: return 7; break;
|
||||||
|
case 0xff: return 8; break;
|
||||||
|
default: return -1; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::string CSubNet::ToString() const
|
std::string CSubNet::ToString() const
|
||||||
{
|
{
|
||||||
|
/* Parse binary 1{n}0{N-n} to see if mask can be represented as /n */
|
||||||
|
int cidr = 0;
|
||||||
|
bool valid_cidr = true;
|
||||||
|
int n = network.IsIPv4() ? 12 : 0;
|
||||||
|
for (; n < 16 && netmask[n] == 0xff; ++n)
|
||||||
|
cidr += 8;
|
||||||
|
if (n < 16) {
|
||||||
|
int bits = NetmaskBits(netmask[n]);
|
||||||
|
if (bits < 0)
|
||||||
|
valid_cidr = false;
|
||||||
|
else
|
||||||
|
cidr += bits;
|
||||||
|
++n;
|
||||||
|
}
|
||||||
|
for (; n < 16 && valid_cidr; ++n)
|
||||||
|
if (netmask[n] != 0x00)
|
||||||
|
valid_cidr = false;
|
||||||
|
|
||||||
|
/* Format output */
|
||||||
std::string strNetmask;
|
std::string strNetmask;
|
||||||
if (network.IsIPv4())
|
if (valid_cidr) {
|
||||||
strNetmask = strprintf("%u.%u.%u.%u", netmask[12], netmask[13], netmask[14], netmask[15]);
|
strNetmask = strprintf("%u", cidr);
|
||||||
else
|
} else {
|
||||||
strNetmask = strprintf("%x:%x:%x:%x:%x:%x:%x:%x",
|
if (network.IsIPv4())
|
||||||
netmask[0] << 8 | netmask[1], netmask[2] << 8 | netmask[3],
|
strNetmask = strprintf("%u.%u.%u.%u", netmask[12], netmask[13], netmask[14], netmask[15]);
|
||||||
netmask[4] << 8 | netmask[5], netmask[6] << 8 | netmask[7],
|
else
|
||||||
netmask[8] << 8 | netmask[9], netmask[10] << 8 | netmask[11],
|
strNetmask = strprintf("%x:%x:%x:%x:%x:%x:%x:%x",
|
||||||
netmask[12] << 8 | netmask[13], netmask[14] << 8 | netmask[15]);
|
netmask[0] << 8 | netmask[1], netmask[2] << 8 | netmask[3],
|
||||||
|
netmask[4] << 8 | netmask[5], netmask[6] << 8 | netmask[7],
|
||||||
|
netmask[8] << 8 | netmask[9], netmask[10] << 8 | netmask[11],
|
||||||
|
netmask[12] << 8 | netmask[13], netmask[14] << 8 | netmask[15]);
|
||||||
|
}
|
||||||
|
|
||||||
return network.ToString() + "/" + strNetmask;
|
return network.ToString() + "/" + strNetmask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
181
src/qt/bantablemodel.cpp
Normal file
181
src/qt/bantablemodel.cpp
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
// Copyright (c) 2011-2015 The Bitcoin Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include "bantablemodel.h"
|
||||||
|
|
||||||
|
#include "clientmodel.h"
|
||||||
|
#include "guiconstants.h"
|
||||||
|
#include "guiutil.h"
|
||||||
|
|
||||||
|
#include "sync.h"
|
||||||
|
#include "utiltime.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
|
bool BannedNodeLessThan::operator()(const CCombinedBan& left, const CCombinedBan& right) const
|
||||||
|
{
|
||||||
|
const CCombinedBan* pLeft = &left;
|
||||||
|
const CCombinedBan* pRight = &right;
|
||||||
|
|
||||||
|
if (order == Qt::DescendingOrder)
|
||||||
|
std::swap(pLeft, pRight);
|
||||||
|
|
||||||
|
switch(column)
|
||||||
|
{
|
||||||
|
case BanTableModel::Address:
|
||||||
|
return pLeft->subnet.ToString().compare(pRight->subnet.ToString()) < 0;
|
||||||
|
case BanTableModel::Bantime:
|
||||||
|
return pLeft->banEntry.nBanUntil < pRight->banEntry.nBanUntil;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// private implementation
|
||||||
|
class BanTablePriv
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/** Local cache of peer information */
|
||||||
|
QList<CCombinedBan> cachedBanlist;
|
||||||
|
/** Column to sort nodes by */
|
||||||
|
int sortColumn;
|
||||||
|
/** Order (ascending or descending) to sort nodes by */
|
||||||
|
Qt::SortOrder sortOrder;
|
||||||
|
|
||||||
|
/** Pull a full list of banned nodes from CNode into our cache */
|
||||||
|
void refreshBanlist()
|
||||||
|
{
|
||||||
|
banmap_t banMap;
|
||||||
|
CNode::GetBanned(banMap);
|
||||||
|
|
||||||
|
cachedBanlist.clear();
|
||||||
|
#if QT_VERSION >= 0x040700
|
||||||
|
cachedBanlist.reserve(banMap.size());
|
||||||
|
#endif
|
||||||
|
for (banmap_t::iterator it = banMap.begin(); it != banMap.end(); it++)
|
||||||
|
{
|
||||||
|
CCombinedBan banEntry;
|
||||||
|
banEntry.subnet = (*it).first;
|
||||||
|
banEntry.banEntry = (*it).second;
|
||||||
|
cachedBanlist.append(banEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sortColumn >= 0)
|
||||||
|
// sort cachedBanlist (use stable sort to prevent rows jumping around unneceesarily)
|
||||||
|
qStableSort(cachedBanlist.begin(), cachedBanlist.end(), BannedNodeLessThan(sortColumn, sortOrder));
|
||||||
|
}
|
||||||
|
|
||||||
|
int size() const
|
||||||
|
{
|
||||||
|
return cachedBanlist.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
CCombinedBan *index(int idx)
|
||||||
|
{
|
||||||
|
if (idx >= 0 && idx < cachedBanlist.size())
|
||||||
|
return &cachedBanlist[idx];
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BanTableModel::BanTableModel(ClientModel *parent) :
|
||||||
|
QAbstractTableModel(parent),
|
||||||
|
clientModel(parent)
|
||||||
|
{
|
||||||
|
columns << tr("IP/Netmask") << tr("Banned Until");
|
||||||
|
priv = new BanTablePriv();
|
||||||
|
// default to unsorted
|
||||||
|
priv->sortColumn = -1;
|
||||||
|
|
||||||
|
// load initial data
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
int BanTableModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(parent);
|
||||||
|
return priv->size();
|
||||||
|
}
|
||||||
|
|
||||||
|
int BanTableModel::columnCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(parent);
|
||||||
|
return columns.length();;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant BanTableModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if(!index.isValid())
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
CCombinedBan *rec = static_cast<CCombinedBan*>(index.internalPointer());
|
||||||
|
|
||||||
|
if (role == Qt::DisplayRole) {
|
||||||
|
switch(index.column())
|
||||||
|
{
|
||||||
|
case Address:
|
||||||
|
return QString::fromStdString(rec->subnet.ToString());
|
||||||
|
case Bantime:
|
||||||
|
QDateTime date = QDateTime::fromMSecsSinceEpoch(0);
|
||||||
|
date = date.addSecs(rec->banEntry.nBanUntil);
|
||||||
|
return date.toString(Qt::SystemLocaleLongDate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant BanTableModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||||
|
{
|
||||||
|
if(orientation == Qt::Horizontal)
|
||||||
|
{
|
||||||
|
if(role == Qt::DisplayRole && section < columns.size())
|
||||||
|
{
|
||||||
|
return columns[section];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::ItemFlags BanTableModel::flags(const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
if(!index.isValid())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex BanTableModel::index(int row, int column, const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(parent);
|
||||||
|
CCombinedBan *data = priv->index(row);
|
||||||
|
|
||||||
|
if (data)
|
||||||
|
return createIndex(row, column, data);
|
||||||
|
return QModelIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BanTableModel::refresh()
|
||||||
|
{
|
||||||
|
Q_EMIT layoutAboutToBeChanged();
|
||||||
|
priv->refreshBanlist();
|
||||||
|
Q_EMIT layoutChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BanTableModel::sort(int column, Qt::SortOrder order)
|
||||||
|
{
|
||||||
|
priv->sortColumn = column;
|
||||||
|
priv->sortOrder = order;
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BanTableModel::shouldShow()
|
||||||
|
{
|
||||||
|
if (priv->size() > 0)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
72
src/qt/bantablemodel.h
Normal file
72
src/qt/bantablemodel.h
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// Copyright (c) 2011-2013 The Bitcoin Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#ifndef BITCOIN_QT_BANTABLEMODEL_H
|
||||||
|
#define BITCOIN_QT_BANTABLEMODEL_H
|
||||||
|
|
||||||
|
#include "net.h"
|
||||||
|
|
||||||
|
#include <QAbstractTableModel>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
class ClientModel;
|
||||||
|
class BanTablePriv;
|
||||||
|
|
||||||
|
struct CCombinedBan {
|
||||||
|
CSubNet subnet;
|
||||||
|
CBanEntry banEntry;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BannedNodeLessThan
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BannedNodeLessThan(int nColumn, Qt::SortOrder fOrder) :
|
||||||
|
column(nColumn), order(fOrder) {}
|
||||||
|
bool operator()(const CCombinedBan& left, const CCombinedBan& right) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int column;
|
||||||
|
Qt::SortOrder order;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
Qt model providing information about connected peers, similar to the
|
||||||
|
"getpeerinfo" RPC call. Used by the rpc console UI.
|
||||||
|
*/
|
||||||
|
class BanTableModel : public QAbstractTableModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit BanTableModel(ClientModel *parent = 0);
|
||||||
|
void startAutoRefresh();
|
||||||
|
void stopAutoRefresh();
|
||||||
|
|
||||||
|
enum ColumnIndex {
|
||||||
|
Address = 0,
|
||||||
|
Bantime = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @name Methods overridden from QAbstractTableModel
|
||||||
|
@{*/
|
||||||
|
int rowCount(const QModelIndex &parent) const;
|
||||||
|
int columnCount(const QModelIndex &parent) const;
|
||||||
|
QVariant data(const QModelIndex &index, int role) const;
|
||||||
|
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
|
||||||
|
QModelIndex index(int row, int column, const QModelIndex &parent) const;
|
||||||
|
Qt::ItemFlags flags(const QModelIndex &index) const;
|
||||||
|
void sort(int column, Qt::SortOrder order);
|
||||||
|
bool shouldShow();
|
||||||
|
/*@}*/
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void refresh();
|
||||||
|
|
||||||
|
private:
|
||||||
|
ClientModel *clientModel;
|
||||||
|
QStringList columns;
|
||||||
|
BanTablePriv *priv;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // BITCOIN_QT_BANTABLEMODEL_H
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include "clientmodel.h"
|
#include "clientmodel.h"
|
||||||
|
|
||||||
|
#include "bantablemodel.h"
|
||||||
#include "guiconstants.h"
|
#include "guiconstants.h"
|
||||||
#include "peertablemodel.h"
|
#include "peertablemodel.h"
|
||||||
|
|
||||||
@ -26,6 +27,7 @@ ClientModel::ClientModel(OptionsModel *optionsModel, QObject *parent) :
|
|||||||
QObject(parent),
|
QObject(parent),
|
||||||
optionsModel(optionsModel),
|
optionsModel(optionsModel),
|
||||||
peerTableModel(0),
|
peerTableModel(0),
|
||||||
|
banTableModel(0),
|
||||||
cachedNumBlocks(0),
|
cachedNumBlocks(0),
|
||||||
cachedBlockDate(QDateTime()),
|
cachedBlockDate(QDateTime()),
|
||||||
cachedReindexing(0),
|
cachedReindexing(0),
|
||||||
@ -33,6 +35,7 @@ ClientModel::ClientModel(OptionsModel *optionsModel, QObject *parent) :
|
|||||||
pollTimer(0)
|
pollTimer(0)
|
||||||
{
|
{
|
||||||
peerTableModel = new PeerTableModel(this);
|
peerTableModel = new PeerTableModel(this);
|
||||||
|
banTableModel = new BanTableModel(this);
|
||||||
pollTimer = new QTimer(this);
|
pollTimer = new QTimer(this);
|
||||||
connect(pollTimer, SIGNAL(timeout()), this, SLOT(updateTimer()));
|
connect(pollTimer, SIGNAL(timeout()), this, SLOT(updateTimer()));
|
||||||
pollTimer->start(MODEL_UPDATE_DELAY);
|
pollTimer->start(MODEL_UPDATE_DELAY);
|
||||||
@ -176,6 +179,11 @@ PeerTableModel *ClientModel::getPeerTableModel()
|
|||||||
return peerTableModel;
|
return peerTableModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BanTableModel *ClientModel::getBanTableModel()
|
||||||
|
{
|
||||||
|
return banTableModel;
|
||||||
|
}
|
||||||
|
|
||||||
QString ClientModel::formatFullVersion() const
|
QString ClientModel::formatFullVersion() const
|
||||||
{
|
{
|
||||||
return QString::fromStdString(FormatFullVersion());
|
return QString::fromStdString(FormatFullVersion());
|
||||||
@ -206,6 +214,11 @@ QString ClientModel::formatClientStartupTime() const
|
|||||||
return QDateTime::fromTime_t(nClientStartupTime).toString();
|
return QDateTime::fromTime_t(nClientStartupTime).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClientModel::updateBanlist()
|
||||||
|
{
|
||||||
|
banTableModel->refresh();
|
||||||
|
}
|
||||||
|
|
||||||
// Handlers for core signals
|
// Handlers for core signals
|
||||||
static void ShowProgress(ClientModel *clientmodel, const std::string &title, int nProgress)
|
static void ShowProgress(ClientModel *clientmodel, const std::string &title, int nProgress)
|
||||||
{
|
{
|
||||||
@ -230,12 +243,19 @@ static void NotifyAlertChanged(ClientModel *clientmodel, const uint256 &hash, Ch
|
|||||||
Q_ARG(int, status));
|
Q_ARG(int, status));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void BannedListChanged(ClientModel *clientmodel)
|
||||||
|
{
|
||||||
|
qDebug() << QString("%1: Requesting update for peer banlist").arg(__func__);
|
||||||
|
QMetaObject::invokeMethod(clientmodel, "updateBanlist", Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
void ClientModel::subscribeToCoreSignals()
|
void ClientModel::subscribeToCoreSignals()
|
||||||
{
|
{
|
||||||
// Connect signals to client
|
// Connect signals to client
|
||||||
uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2));
|
uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2));
|
||||||
uiInterface.NotifyNumConnectionsChanged.connect(boost::bind(NotifyNumConnectionsChanged, this, _1));
|
uiInterface.NotifyNumConnectionsChanged.connect(boost::bind(NotifyNumConnectionsChanged, this, _1));
|
||||||
uiInterface.NotifyAlertChanged.connect(boost::bind(NotifyAlertChanged, this, _1, _2));
|
uiInterface.NotifyAlertChanged.connect(boost::bind(NotifyAlertChanged, this, _1, _2));
|
||||||
|
uiInterface.BannedListChanged.connect(boost::bind(BannedListChanged, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientModel::unsubscribeFromCoreSignals()
|
void ClientModel::unsubscribeFromCoreSignals()
|
||||||
@ -244,4 +264,5 @@ void ClientModel::unsubscribeFromCoreSignals()
|
|||||||
uiInterface.ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2));
|
uiInterface.ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2));
|
||||||
uiInterface.NotifyNumConnectionsChanged.disconnect(boost::bind(NotifyNumConnectionsChanged, this, _1));
|
uiInterface.NotifyNumConnectionsChanged.disconnect(boost::bind(NotifyNumConnectionsChanged, this, _1));
|
||||||
uiInterface.NotifyAlertChanged.disconnect(boost::bind(NotifyAlertChanged, this, _1, _2));
|
uiInterface.NotifyAlertChanged.disconnect(boost::bind(NotifyAlertChanged, this, _1, _2));
|
||||||
|
uiInterface.BannedListChanged.disconnect(boost::bind(BannedListChanged, this));
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
|
||||||
class AddressTableModel;
|
class AddressTableModel;
|
||||||
|
class BanTableModel;
|
||||||
class OptionsModel;
|
class OptionsModel;
|
||||||
class PeerTableModel;
|
class PeerTableModel;
|
||||||
class TransactionTableModel;
|
class TransactionTableModel;
|
||||||
@ -44,6 +45,7 @@ public:
|
|||||||
|
|
||||||
OptionsModel *getOptionsModel();
|
OptionsModel *getOptionsModel();
|
||||||
PeerTableModel *getPeerTableModel();
|
PeerTableModel *getPeerTableModel();
|
||||||
|
BanTableModel *getBanTableModel();
|
||||||
|
|
||||||
//! Return number of connections, default is in- and outbound (total)
|
//! Return number of connections, default is in- and outbound (total)
|
||||||
int getNumConnections(unsigned int flags = CONNECTIONS_ALL) const;
|
int getNumConnections(unsigned int flags = CONNECTIONS_ALL) const;
|
||||||
@ -72,6 +74,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
OptionsModel *optionsModel;
|
OptionsModel *optionsModel;
|
||||||
PeerTableModel *peerTableModel;
|
PeerTableModel *peerTableModel;
|
||||||
|
BanTableModel *banTableModel;
|
||||||
|
|
||||||
int cachedNumBlocks;
|
int cachedNumBlocks;
|
||||||
QDateTime cachedBlockDate;
|
QDateTime cachedBlockDate;
|
||||||
@ -99,6 +102,7 @@ public Q_SLOTS:
|
|||||||
void updateTimer();
|
void updateTimer();
|
||||||
void updateNumConnections(int numConnections);
|
void updateNumConnections(int numConnections);
|
||||||
void updateAlert(const QString &hash, int status);
|
void updateAlert(const QString &hash, int status);
|
||||||
|
void updateBanlist();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // BITCOIN_QT_CLIENTMODEL_H
|
#endif // BITCOIN_QT_CLIENTMODEL_H
|
||||||
|
@ -713,17 +713,85 @@
|
|||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QGridLayout" name="gridLayout_2">
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
<item row="0" column="0" rowspan="2">
|
<item row="0" column="0" rowspan="2">
|
||||||
<widget class="QTableView" name="peerWidget">
|
<layout class="QVBoxLayout" name="verticalLayout_101">
|
||||||
<property name="horizontalScrollBarPolicy">
|
<property name="spacing">
|
||||||
<enum>Qt::ScrollBarAsNeeded</enum>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="sortingEnabled">
|
<item>
|
||||||
<bool>true</bool>
|
<widget class="QTableView" name="peerWidget">
|
||||||
</property>
|
<property name="horizontalScrollBarPolicy">
|
||||||
<attribute name="horizontalHeaderHighlightSections">
|
<enum>Qt::ScrollBarAsNeeded</enum>
|
||||||
<bool>false</bool>
|
</property>
|
||||||
</attribute>
|
<property name="sortingEnabled">
|
||||||
</widget>
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<attribute name="horizontalHeaderHighlightSections">
|
||||||
|
<bool>false</bool>
|
||||||
|
</attribute>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="banHeading">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>300</width>
|
||||||
|
<height>32</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>16777215</width>
|
||||||
|
<height>32</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>12</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="cursor">
|
||||||
|
<cursorShape>IBeamCursor</cursorShape>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Banned peers</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="textInteractionFlags">
|
||||||
|
<set>Qt::NoTextInteraction</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QTableView" name="banlistWidget">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="horizontalScrollBarPolicy">
|
||||||
|
<enum>Qt::ScrollBarAsNeeded</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sortingEnabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<attribute name="horizontalHeaderHighlightSections">
|
||||||
|
<bool>false</bool>
|
||||||
|
</attribute>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="0" column="1">
|
||||||
<widget class="QLabel" name="peerHeading">
|
<widget class="QLabel" name="peerHeading">
|
||||||
|
@ -5,9 +5,11 @@
|
|||||||
#include "rpcconsole.h"
|
#include "rpcconsole.h"
|
||||||
#include "ui_rpcconsole.h"
|
#include "ui_rpcconsole.h"
|
||||||
|
|
||||||
|
#include "bantablemodel.h"
|
||||||
#include "clientmodel.h"
|
#include "clientmodel.h"
|
||||||
#include "guiutil.h"
|
#include "guiutil.h"
|
||||||
#include "platformstyle.h"
|
#include "platformstyle.h"
|
||||||
|
#include "bantablemodel.h"
|
||||||
|
|
||||||
#include "chainparams.h"
|
#include "chainparams.h"
|
||||||
#include "rpcserver.h"
|
#include "rpcserver.h"
|
||||||
@ -25,6 +27,7 @@
|
|||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QScrollBar>
|
#include <QScrollBar>
|
||||||
|
#include <QSignalMapper>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QTime>
|
#include <QTime>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
@ -240,8 +243,9 @@ RPCConsole::RPCConsole(const PlatformStyle *platformStyle, QWidget *parent) :
|
|||||||
clientModel(0),
|
clientModel(0),
|
||||||
historyPtr(0),
|
historyPtr(0),
|
||||||
cachedNodeid(-1),
|
cachedNodeid(-1),
|
||||||
contextMenu(0),
|
platformStyle(platformStyle),
|
||||||
platformStyle(platformStyle)
|
peersTableContextMenu(0),
|
||||||
|
banTableContextMenu(0)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
GUIUtil::restoreWindowGeometry("nRPCConsoleWindow", this->size(), this);
|
GUIUtil::restoreWindowGeometry("nRPCConsoleWindow", this->size(), this);
|
||||||
@ -328,8 +332,7 @@ void RPCConsole::setClientModel(ClientModel *model)
|
|||||||
{
|
{
|
||||||
clientModel = model;
|
clientModel = model;
|
||||||
ui->trafficGraph->setClientModel(model);
|
ui->trafficGraph->setClientModel(model);
|
||||||
if(model)
|
if (model && clientModel->getPeerTableModel() && clientModel->getBanTableModel()) {
|
||||||
{
|
|
||||||
// Keep up to date with client
|
// Keep up to date with client
|
||||||
setNumConnections(model->getNumConnections());
|
setNumConnections(model->getNumConnections());
|
||||||
connect(model, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int)));
|
connect(model, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int)));
|
||||||
@ -350,23 +353,75 @@ void RPCConsole::setClientModel(ClientModel *model)
|
|||||||
ui->peerWidget->setColumnWidth(PeerTableModel::Address, ADDRESS_COLUMN_WIDTH);
|
ui->peerWidget->setColumnWidth(PeerTableModel::Address, ADDRESS_COLUMN_WIDTH);
|
||||||
ui->peerWidget->setColumnWidth(PeerTableModel::Subversion, SUBVERSION_COLUMN_WIDTH);
|
ui->peerWidget->setColumnWidth(PeerTableModel::Subversion, SUBVERSION_COLUMN_WIDTH);
|
||||||
ui->peerWidget->setColumnWidth(PeerTableModel::Ping, PING_COLUMN_WIDTH);
|
ui->peerWidget->setColumnWidth(PeerTableModel::Ping, PING_COLUMN_WIDTH);
|
||||||
|
ui->peerWidget->horizontalHeader()->setStretchLastSection(true);
|
||||||
|
|
||||||
// create context menu actions
|
// create peer table context menu actions
|
||||||
QAction* disconnectAction = new QAction(tr("&Disconnect Node"), this);
|
QAction* disconnectAction = new QAction(tr("&Disconnect Node"), this);
|
||||||
|
QAction* banAction1h = new QAction(tr("Ban Node for") + " " + tr("1 &hour"), this);
|
||||||
|
QAction* banAction24h = new QAction(tr("Ban Node for") + " " + tr("1 &day"), this);
|
||||||
|
QAction* banAction7d = new QAction(tr("Ban Node for") + " " + tr("1 &week"), this);
|
||||||
|
QAction* banAction365d = new QAction(tr("Ban Node for") + " " + tr("1 &year"), this);
|
||||||
|
|
||||||
// create context menu
|
// create peer table context menu
|
||||||
contextMenu = new QMenu();
|
peersTableContextMenu = new QMenu();
|
||||||
contextMenu->addAction(disconnectAction);
|
peersTableContextMenu->addAction(disconnectAction);
|
||||||
|
peersTableContextMenu->addAction(banAction1h);
|
||||||
|
peersTableContextMenu->addAction(banAction24h);
|
||||||
|
peersTableContextMenu->addAction(banAction7d);
|
||||||
|
peersTableContextMenu->addAction(banAction365d);
|
||||||
|
|
||||||
// context menu signals
|
// Add a signal mapping to allow dynamic context menu arguments.
|
||||||
connect(ui->peerWidget, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showMenu(const QPoint&)));
|
// We need to use int (instead of int64_t), because signal mapper only supports
|
||||||
|
// int or objects, which is okay because max bantime (1 year) is < int_max.
|
||||||
|
QSignalMapper* signalMapper = new QSignalMapper(this);
|
||||||
|
signalMapper->setMapping(banAction1h, 60*60);
|
||||||
|
signalMapper->setMapping(banAction24h, 60*60*24);
|
||||||
|
signalMapper->setMapping(banAction7d, 60*60*24*7);
|
||||||
|
signalMapper->setMapping(banAction365d, 60*60*24*365);
|
||||||
|
connect(banAction1h, SIGNAL(triggered()), signalMapper, SLOT(map()));
|
||||||
|
connect(banAction24h, SIGNAL(triggered()), signalMapper, SLOT(map()));
|
||||||
|
connect(banAction7d, SIGNAL(triggered()), signalMapper, SLOT(map()));
|
||||||
|
connect(banAction365d, SIGNAL(triggered()), signalMapper, SLOT(map()));
|
||||||
|
connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(banSelectedNode(int)));
|
||||||
|
|
||||||
|
// peer table context menu signals
|
||||||
|
connect(ui->peerWidget, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showPeersTableContextMenu(const QPoint&)));
|
||||||
connect(disconnectAction, SIGNAL(triggered()), this, SLOT(disconnectSelectedNode()));
|
connect(disconnectAction, SIGNAL(triggered()), this, SLOT(disconnectSelectedNode()));
|
||||||
|
|
||||||
// connect the peerWidget selection model to our peerSelected() handler
|
// peer table signal handling - update peer details when selecting new node
|
||||||
connect(ui->peerWidget->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
|
connect(ui->peerWidget->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
|
||||||
this, SLOT(peerSelected(const QItemSelection &, const QItemSelection &)));
|
this, SLOT(peerSelected(const QItemSelection &, const QItemSelection &)));
|
||||||
|
// peer table signal handling - update peer details when new nodes are added to the model
|
||||||
connect(model->getPeerTableModel(), SIGNAL(layoutChanged()), this, SLOT(peerLayoutChanged()));
|
connect(model->getPeerTableModel(), SIGNAL(layoutChanged()), this, SLOT(peerLayoutChanged()));
|
||||||
|
|
||||||
|
// set up ban table
|
||||||
|
ui->banlistWidget->setModel(model->getBanTableModel());
|
||||||
|
ui->banlistWidget->verticalHeader()->hide();
|
||||||
|
ui->banlistWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||||
|
ui->banlistWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||||
|
ui->banlistWidget->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||||
|
ui->banlistWidget->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
ui->banlistWidget->setColumnWidth(BanTableModel::Address, BANSUBNET_COLUMN_WIDTH);
|
||||||
|
ui->banlistWidget->setColumnWidth(BanTableModel::Bantime, BANTIME_COLUMN_WIDTH);
|
||||||
|
ui->banlistWidget->horizontalHeader()->setStretchLastSection(true);
|
||||||
|
|
||||||
|
// create ban table context menu action
|
||||||
|
QAction* unbanAction = new QAction(tr("&Unban Node"), this);
|
||||||
|
|
||||||
|
// create ban table context menu
|
||||||
|
banTableContextMenu = new QMenu();
|
||||||
|
banTableContextMenu->addAction(unbanAction);
|
||||||
|
|
||||||
|
// ban table context menu signals
|
||||||
|
connect(ui->banlistWidget, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showBanTableContextMenu(const QPoint&)));
|
||||||
|
connect(unbanAction, SIGNAL(triggered()), this, SLOT(unbanSelectedNode()));
|
||||||
|
|
||||||
|
// ban table signal handling - clear peer details when clicking a peer in the ban table
|
||||||
|
connect(ui->banlistWidget, SIGNAL(clicked(const QModelIndex&)), this, SLOT(clearSelectedNode()));
|
||||||
|
// ban table signal handling - ensure ban table is shown or hidden (if empty)
|
||||||
|
connect(model->getBanTableModel(), SIGNAL(layoutChanged()), this, SLOT(showOrHideBanTableIfRequired()));
|
||||||
|
showOrHideBanTableIfRequired();
|
||||||
|
|
||||||
// Provide initial values
|
// Provide initial values
|
||||||
ui->clientVersion->setText(model->formatFullVersion());
|
ui->clientVersion->setText(model->formatFullVersion());
|
||||||
ui->clientUserAgent->setText(model->formatSubVersion());
|
ui->clientUserAgent->setText(model->formatSubVersion());
|
||||||
@ -576,7 +631,7 @@ void RPCConsole::peerSelected(const QItemSelection &selected, const QItemSelecti
|
|||||||
{
|
{
|
||||||
Q_UNUSED(deselected);
|
Q_UNUSED(deselected);
|
||||||
|
|
||||||
if (!clientModel || selected.indexes().isEmpty())
|
if (!clientModel || !clientModel->getPeerTableModel() || selected.indexes().isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(selected.indexes().first().row());
|
const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(selected.indexes().first().row());
|
||||||
@ -586,7 +641,7 @@ void RPCConsole::peerSelected(const QItemSelection &selected, const QItemSelecti
|
|||||||
|
|
||||||
void RPCConsole::peerLayoutChanged()
|
void RPCConsole::peerLayoutChanged()
|
||||||
{
|
{
|
||||||
if (!clientModel)
|
if (!clientModel || !clientModel->getPeerTableModel())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const CNodeCombinedStats *stats = NULL;
|
const CNodeCombinedStats *stats = NULL;
|
||||||
@ -695,7 +750,7 @@ void RPCConsole::showEvent(QShowEvent *event)
|
|||||||
{
|
{
|
||||||
QWidget::showEvent(event);
|
QWidget::showEvent(event);
|
||||||
|
|
||||||
if (!clientModel)
|
if (!clientModel || !clientModel->getPeerTableModel())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// start PeerTableModel auto refresh
|
// start PeerTableModel auto refresh
|
||||||
@ -706,18 +761,25 @@ void RPCConsole::hideEvent(QHideEvent *event)
|
|||||||
{
|
{
|
||||||
QWidget::hideEvent(event);
|
QWidget::hideEvent(event);
|
||||||
|
|
||||||
if (!clientModel)
|
if (!clientModel || !clientModel->getPeerTableModel())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// stop PeerTableModel auto refresh
|
// stop PeerTableModel auto refresh
|
||||||
clientModel->getPeerTableModel()->stopAutoRefresh();
|
clientModel->getPeerTableModel()->stopAutoRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RPCConsole::showMenu(const QPoint& point)
|
void RPCConsole::showPeersTableContextMenu(const QPoint& point)
|
||||||
{
|
{
|
||||||
QModelIndex index = ui->peerWidget->indexAt(point);
|
QModelIndex index = ui->peerWidget->indexAt(point);
|
||||||
if (index.isValid())
|
if (index.isValid())
|
||||||
contextMenu->exec(QCursor::pos());
|
peersTableContextMenu->exec(QCursor::pos());
|
||||||
|
}
|
||||||
|
|
||||||
|
void RPCConsole::showBanTableContextMenu(const QPoint& point)
|
||||||
|
{
|
||||||
|
QModelIndex index = ui->banlistWidget->indexAt(point);
|
||||||
|
if (index.isValid())
|
||||||
|
banTableContextMenu->exec(QCursor::pos());
|
||||||
}
|
}
|
||||||
|
|
||||||
void RPCConsole::disconnectSelectedNode()
|
void RPCConsole::disconnectSelectedNode()
|
||||||
@ -731,6 +793,46 @@ void RPCConsole::disconnectSelectedNode()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RPCConsole::banSelectedNode(int bantime)
|
||||||
|
{
|
||||||
|
if (!clientModel)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Get currently selected peer address
|
||||||
|
QString strNode = GUIUtil::getEntryData(ui->peerWidget, 0, PeerTableModel::Address);
|
||||||
|
// Find possible nodes, ban it and clear the selected node
|
||||||
|
if (CNode *bannedNode = FindNode(strNode.toStdString())) {
|
||||||
|
std::string nStr = strNode.toStdString();
|
||||||
|
std::string addr;
|
||||||
|
int port = 0;
|
||||||
|
SplitHostPort(nStr, port, addr);
|
||||||
|
|
||||||
|
CNode::Ban(CNetAddr(addr), BanReasonManuallyAdded, bantime);
|
||||||
|
bannedNode->fDisconnect = true;
|
||||||
|
DumpBanlist();
|
||||||
|
|
||||||
|
clearSelectedNode();
|
||||||
|
clientModel->getBanTableModel()->refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RPCConsole::unbanSelectedNode()
|
||||||
|
{
|
||||||
|
if (!clientModel)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Get currently selected ban address
|
||||||
|
QString strNode = GUIUtil::getEntryData(ui->banlistWidget, 0, BanTableModel::Address);
|
||||||
|
CSubNet possibleSubnet(strNode.toStdString());
|
||||||
|
|
||||||
|
if (possibleSubnet.IsValid())
|
||||||
|
{
|
||||||
|
CNode::Unban(possibleSubnet);
|
||||||
|
DumpBanlist();
|
||||||
|
clientModel->getBanTableModel()->refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void RPCConsole::clearSelectedNode()
|
void RPCConsole::clearSelectedNode()
|
||||||
{
|
{
|
||||||
ui->peerWidget->selectionModel()->clearSelection();
|
ui->peerWidget->selectionModel()->clearSelection();
|
||||||
@ -738,3 +840,13 @@ void RPCConsole::clearSelectedNode()
|
|||||||
ui->detailWidget->hide();
|
ui->detailWidget->hide();
|
||||||
ui->peerHeading->setText(tr("Select a peer to view detailed information."));
|
ui->peerHeading->setText(tr("Select a peer to view detailed information."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RPCConsole::showOrHideBanTableIfRequired()
|
||||||
|
{
|
||||||
|
if (!clientModel)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool visible = clientModel->getBanTableModel()->shouldShow();
|
||||||
|
ui->banlistWidget->setVisible(visible);
|
||||||
|
ui->banHeading->setVisible(visible);
|
||||||
|
}
|
@ -61,7 +61,13 @@ private Q_SLOTS:
|
|||||||
void showEvent(QShowEvent *event);
|
void showEvent(QShowEvent *event);
|
||||||
void hideEvent(QHideEvent *event);
|
void hideEvent(QHideEvent *event);
|
||||||
/** Show custom context menu on Peers tab */
|
/** Show custom context menu on Peers tab */
|
||||||
void showMenu(const QPoint& point);
|
void showPeersTableContextMenu(const QPoint& point);
|
||||||
|
/** Show custom context menu on Bans tab */
|
||||||
|
void showBanTableContextMenu(const QPoint& point);
|
||||||
|
/** Hides ban table if no bans are present */
|
||||||
|
void showOrHideBanTableIfRequired();
|
||||||
|
/** clear the selected node */
|
||||||
|
void clearSelectedNode();
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
void clear();
|
void clear();
|
||||||
@ -80,6 +86,10 @@ public Q_SLOTS:
|
|||||||
void peerLayoutChanged();
|
void peerLayoutChanged();
|
||||||
/** Disconnect a selected node on the Peers tab */
|
/** Disconnect a selected node on the Peers tab */
|
||||||
void disconnectSelectedNode();
|
void disconnectSelectedNode();
|
||||||
|
/** Ban a selected node on the Peers tab */
|
||||||
|
void banSelectedNode(int bantime);
|
||||||
|
/** Unban a selected node on the Bans tab */
|
||||||
|
void unbanSelectedNode();
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
// For RPC command executor
|
// For RPC command executor
|
||||||
@ -92,14 +102,15 @@ private:
|
|||||||
void setTrafficGraphRange(int mins);
|
void setTrafficGraphRange(int mins);
|
||||||
/** show detailed information on ui about selected node */
|
/** show detailed information on ui about selected node */
|
||||||
void updateNodeDetail(const CNodeCombinedStats *stats);
|
void updateNodeDetail(const CNodeCombinedStats *stats);
|
||||||
/** clear the selected node */
|
|
||||||
void clearSelectedNode();
|
|
||||||
|
|
||||||
enum ColumnWidths
|
enum ColumnWidths
|
||||||
{
|
{
|
||||||
ADDRESS_COLUMN_WIDTH = 200,
|
ADDRESS_COLUMN_WIDTH = 200,
|
||||||
SUBVERSION_COLUMN_WIDTH = 100,
|
SUBVERSION_COLUMN_WIDTH = 100,
|
||||||
PING_COLUMN_WIDTH = 80
|
PING_COLUMN_WIDTH = 80,
|
||||||
|
BANSUBNET_COLUMN_WIDTH = 200,
|
||||||
|
BANTIME_COLUMN_WIDTH = 250
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ui::RPCConsole *ui;
|
Ui::RPCConsole *ui;
|
||||||
@ -107,9 +118,10 @@ private:
|
|||||||
QStringList history;
|
QStringList history;
|
||||||
int historyPtr;
|
int historyPtr;
|
||||||
NodeId cachedNodeid;
|
NodeId cachedNodeid;
|
||||||
QMenu *contextMenu;
|
|
||||||
const PlatformStyle *platformStyle;
|
const PlatformStyle *platformStyle;
|
||||||
RPCTimerInterface *rpcTimerInterface;
|
RPCTimerInterface *rpcTimerInterface;
|
||||||
|
QMenu *peersTableContextMenu;
|
||||||
|
QMenu *banTableContextMenu;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // BITCOIN_QT_RPCCONSOLE_H
|
#endif // BITCOIN_QT_RPCCONSOLE_H
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "protocol.h"
|
#include "protocol.h"
|
||||||
#include "sync.h"
|
#include "sync.h"
|
||||||
#include "timedata.h"
|
#include "timedata.h"
|
||||||
|
#include "ui_interface.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "utilstrencodings.h"
|
#include "utilstrencodings.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
@ -531,6 +532,8 @@ UniValue setban(const UniValue& params, bool fHelp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
DumpBanlist(); //store banlist to disk
|
DumpBanlist(); //store banlist to disk
|
||||||
|
uiInterface.BannedListChanged();
|
||||||
|
|
||||||
return NullUniValue;
|
return NullUniValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -577,6 +580,7 @@ UniValue clearbanned(const UniValue& params, bool fHelp)
|
|||||||
|
|
||||||
CNode::ClearBanned();
|
CNode::ClearBanned();
|
||||||
DumpBanlist(); //store banlist to disk
|
DumpBanlist(); //store banlist to disk
|
||||||
|
uiInterface.BannedListChanged();
|
||||||
|
|
||||||
return NullUniValue;
|
return NullUniValue;
|
||||||
}
|
}
|
||||||
|
@ -149,12 +149,90 @@ BOOST_AUTO_TEST_CASE(subnet_test)
|
|||||||
BOOST_CHECK(CSubNet(CNetAddr("127.0.0.1")).IsValid());
|
BOOST_CHECK(CSubNet(CNetAddr("127.0.0.1")).IsValid());
|
||||||
BOOST_CHECK(CSubNet(CNetAddr("127.0.0.1")).Match(CNetAddr("127.0.0.1")));
|
BOOST_CHECK(CSubNet(CNetAddr("127.0.0.1")).Match(CNetAddr("127.0.0.1")));
|
||||||
BOOST_CHECK(!CSubNet(CNetAddr("127.0.0.1")).Match(CNetAddr("127.0.0.2")));
|
BOOST_CHECK(!CSubNet(CNetAddr("127.0.0.1")).Match(CNetAddr("127.0.0.2")));
|
||||||
BOOST_CHECK(CSubNet(CNetAddr("127.0.0.1")).ToString() == "127.0.0.1/255.255.255.255");
|
BOOST_CHECK(CSubNet(CNetAddr("127.0.0.1")).ToString() == "127.0.0.1/32");
|
||||||
|
|
||||||
BOOST_CHECK(CSubNet(CNetAddr("1:2:3:4:5:6:7:8")).IsValid());
|
BOOST_CHECK(CSubNet(CNetAddr("1:2:3:4:5:6:7:8")).IsValid());
|
||||||
BOOST_CHECK(CSubNet(CNetAddr("1:2:3:4:5:6:7:8")).Match(CNetAddr("1:2:3:4:5:6:7:8")));
|
BOOST_CHECK(CSubNet(CNetAddr("1:2:3:4:5:6:7:8")).Match(CNetAddr("1:2:3:4:5:6:7:8")));
|
||||||
BOOST_CHECK(!CSubNet(CNetAddr("1:2:3:4:5:6:7:8")).Match(CNetAddr("1:2:3:4:5:6:7:9")));
|
BOOST_CHECK(!CSubNet(CNetAddr("1:2:3:4:5:6:7:8")).Match(CNetAddr("1:2:3:4:5:6:7:9")));
|
||||||
BOOST_CHECK(CSubNet(CNetAddr("1:2:3:4:5:6:7:8")).ToString() == "1:2:3:4:5:6:7:8/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
|
BOOST_CHECK(CSubNet(CNetAddr("1:2:3:4:5:6:7:8")).ToString() == "1:2:3:4:5:6:7:8/128");
|
||||||
|
|
||||||
|
CSubNet subnet = CSubNet("1.2.3.4/255.255.255.255");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.4/32");
|
||||||
|
subnet = CSubNet("1.2.3.4/255.255.255.254");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.4/31");
|
||||||
|
subnet = CSubNet("1.2.3.4/255.255.255.252");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.4/30");
|
||||||
|
subnet = CSubNet("1.2.3.4/255.255.255.248");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.0/29");
|
||||||
|
subnet = CSubNet("1.2.3.4/255.255.255.240");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.0/28");
|
||||||
|
subnet = CSubNet("1.2.3.4/255.255.255.224");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.0/27");
|
||||||
|
subnet = CSubNet("1.2.3.4/255.255.255.192");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.0/26");
|
||||||
|
subnet = CSubNet("1.2.3.4/255.255.255.128");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.0/25");
|
||||||
|
subnet = CSubNet("1.2.3.4/255.255.255.0");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.0/24");
|
||||||
|
subnet = CSubNet("1.2.3.4/255.255.254.0");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.2.0/23");
|
||||||
|
subnet = CSubNet("1.2.3.4/255.255.252.0");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/22");
|
||||||
|
subnet = CSubNet("1.2.3.4/255.255.248.0");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/21");
|
||||||
|
subnet = CSubNet("1.2.3.4/255.255.240.0");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/20");
|
||||||
|
subnet = CSubNet("1.2.3.4/255.255.224.0");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/19");
|
||||||
|
subnet = CSubNet("1.2.3.4/255.255.192.0");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/18");
|
||||||
|
subnet = CSubNet("1.2.3.4/255.255.128.0");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/17");
|
||||||
|
subnet = CSubNet("1.2.3.4/255.255.0.0");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/16");
|
||||||
|
subnet = CSubNet("1.2.3.4/255.254.0.0");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/15");
|
||||||
|
subnet = CSubNet("1.2.3.4/255.252.0.0");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/14");
|
||||||
|
subnet = CSubNet("1.2.3.4/255.248.0.0");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/13");
|
||||||
|
subnet = CSubNet("1.2.3.4/255.240.0.0");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/12");
|
||||||
|
subnet = CSubNet("1.2.3.4/255.224.0.0");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/11");
|
||||||
|
subnet = CSubNet("1.2.3.4/255.192.0.0");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/10");
|
||||||
|
subnet = CSubNet("1.2.3.4/255.128.0.0");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/9");
|
||||||
|
subnet = CSubNet("1.2.3.4/255.0.0.0");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/8");
|
||||||
|
subnet = CSubNet("1.2.3.4/254.0.0.0");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/7");
|
||||||
|
subnet = CSubNet("1.2.3.4/252.0.0.0");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/6");
|
||||||
|
subnet = CSubNet("1.2.3.4/248.0.0.0");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/5");
|
||||||
|
subnet = CSubNet("1.2.3.4/240.0.0.0");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/4");
|
||||||
|
subnet = CSubNet("1.2.3.4/224.0.0.0");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/3");
|
||||||
|
subnet = CSubNet("1.2.3.4/192.0.0.0");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/2");
|
||||||
|
subnet = CSubNet("1.2.3.4/128.0.0.0");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/1");
|
||||||
|
subnet = CSubNet("1.2.3.4/0.0.0.0");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/0");
|
||||||
|
|
||||||
|
subnet = CSubNet("1:2:3:4:5:6:7:8/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "1:2:3:4:5:6:7:8/128");
|
||||||
|
subnet = CSubNet("1:2:3:4:5:6:7:8/ffff:0000:0000:0000:0000:0000:0000:0000");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "1::/16");
|
||||||
|
subnet = CSubNet("1:2:3:4:5:6:7:8/0000:0000:0000:0000:0000:0000:0000:0000");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "::/0");
|
||||||
|
subnet = CSubNet("1.2.3.4/255.255.232.0");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/255.255.232.0");
|
||||||
|
subnet = CSubNet("1:2:3:4:5:6:7:8/ffff:ffff:ffff:fffe:ffff:ffff:ffff:ff0f");
|
||||||
|
BOOST_CHECK_EQUAL(subnet.ToString(), "1:2:3:4:5:6:7:8/ffff:ffff:ffff:fffe:ffff:ffff:ffff:ff0f");
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(netbase_getgroup)
|
BOOST_AUTO_TEST_CASE(netbase_getgroup)
|
||||||
|
@ -235,7 +235,7 @@ BOOST_AUTO_TEST_CASE(rpc_ban)
|
|||||||
UniValue ar = r.get_array();
|
UniValue ar = r.get_array();
|
||||||
UniValue o1 = ar[0].get_obj();
|
UniValue o1 = ar[0].get_obj();
|
||||||
UniValue adr = find_value(o1, "address");
|
UniValue adr = find_value(o1, "address");
|
||||||
BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/255.255.255.255");
|
BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/32");
|
||||||
BOOST_CHECK_NO_THROW(CallRPC(string("setban 127.0.0.0 remove")));;
|
BOOST_CHECK_NO_THROW(CallRPC(string("setban 127.0.0.0 remove")));;
|
||||||
BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned")));
|
BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned")));
|
||||||
ar = r.get_array();
|
ar = r.get_array();
|
||||||
@ -247,7 +247,7 @@ BOOST_AUTO_TEST_CASE(rpc_ban)
|
|||||||
o1 = ar[0].get_obj();
|
o1 = ar[0].get_obj();
|
||||||
adr = find_value(o1, "address");
|
adr = find_value(o1, "address");
|
||||||
UniValue banned_until = find_value(o1, "banned_until");
|
UniValue banned_until = find_value(o1, "banned_until");
|
||||||
BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/255.255.255.0");
|
BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/24");
|
||||||
BOOST_CHECK_EQUAL(banned_until.get_int64(), 1607731200); // absolute time check
|
BOOST_CHECK_EQUAL(banned_until.get_int64(), 1607731200); // absolute time check
|
||||||
|
|
||||||
BOOST_CHECK_NO_THROW(CallRPC(string("clearbanned")));
|
BOOST_CHECK_NO_THROW(CallRPC(string("clearbanned")));
|
||||||
@ -258,7 +258,7 @@ BOOST_AUTO_TEST_CASE(rpc_ban)
|
|||||||
o1 = ar[0].get_obj();
|
o1 = ar[0].get_obj();
|
||||||
adr = find_value(o1, "address");
|
adr = find_value(o1, "address");
|
||||||
banned_until = find_value(o1, "banned_until");
|
banned_until = find_value(o1, "banned_until");
|
||||||
BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/255.255.255.0");
|
BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/24");
|
||||||
int64_t now = GetTime();
|
int64_t now = GetTime();
|
||||||
BOOST_CHECK(banned_until.get_int64() > now);
|
BOOST_CHECK(banned_until.get_int64() > now);
|
||||||
BOOST_CHECK(banned_until.get_int64()-now <= 200);
|
BOOST_CHECK(banned_until.get_int64()-now <= 200);
|
||||||
@ -288,15 +288,15 @@ BOOST_AUTO_TEST_CASE(rpc_ban)
|
|||||||
ar = r.get_array();
|
ar = r.get_array();
|
||||||
o1 = ar[0].get_obj();
|
o1 = ar[0].get_obj();
|
||||||
adr = find_value(o1, "address");
|
adr = find_value(o1, "address");
|
||||||
BOOST_CHECK_EQUAL(adr.get_str(), "fe80::202:b3ff:fe1e:8329/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
|
BOOST_CHECK_EQUAL(adr.get_str(), "fe80::202:b3ff:fe1e:8329/128");
|
||||||
|
|
||||||
BOOST_CHECK_NO_THROW(CallRPC(string("clearbanned")));
|
BOOST_CHECK_NO_THROW(CallRPC(string("clearbanned")));
|
||||||
BOOST_CHECK_NO_THROW(r = CallRPC(string("setban 2001:db8::/30 add")));
|
BOOST_CHECK_NO_THROW(r = CallRPC(string("setban 2001:db8::/ffff:fffc:0:0:0:0:0:0 add")));
|
||||||
BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned")));
|
BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned")));
|
||||||
ar = r.get_array();
|
ar = r.get_array();
|
||||||
o1 = ar[0].get_obj();
|
o1 = ar[0].get_obj();
|
||||||
adr = find_value(o1, "address");
|
adr = find_value(o1, "address");
|
||||||
BOOST_CHECK_EQUAL(adr.get_str(), "2001:db8::/ffff:fffc:0:0:0:0:0:0");
|
BOOST_CHECK_EQUAL(adr.get_str(), "2001:db8::/30");
|
||||||
|
|
||||||
BOOST_CHECK_NO_THROW(CallRPC(string("clearbanned")));
|
BOOST_CHECK_NO_THROW(CallRPC(string("clearbanned")));
|
||||||
BOOST_CHECK_NO_THROW(r = CallRPC(string("setban 2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/128 add")));
|
BOOST_CHECK_NO_THROW(r = CallRPC(string("setban 2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/128 add")));
|
||||||
@ -304,7 +304,7 @@ BOOST_AUTO_TEST_CASE(rpc_ban)
|
|||||||
ar = r.get_array();
|
ar = r.get_array();
|
||||||
o1 = ar[0].get_obj();
|
o1 = ar[0].get_obj();
|
||||||
adr = find_value(o1, "address");
|
adr = find_value(o1, "address");
|
||||||
BOOST_CHECK_EQUAL(adr.get_str(), "2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
|
BOOST_CHECK_EQUAL(adr.get_str(), "2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/128");
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
@ -95,6 +95,9 @@ public:
|
|||||||
|
|
||||||
/** New block has been accepted */
|
/** New block has been accepted */
|
||||||
boost::signals2::signal<void (const uint256& hash)> NotifyBlockTip;
|
boost::signals2::signal<void (const uint256& hash)> NotifyBlockTip;
|
||||||
|
|
||||||
|
/** Banlist did change. */
|
||||||
|
boost::signals2::signal<void (void)> BannedListChanged;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern CClientUIInterface uiInterface;
|
extern CClientUIInterface uiInterface;
|
||||||
|
Loading…
Reference in New Issue
Block a user