This adds 4 ban options to the peers tables context menu (1h, 24h, 7d, 365d). If there are active bans, a table with banned subnets will be shown.
Sync between the rpc bans and the ui bans are guaranteed over a new uiInterface signal.
Screenshots:

This adds 4 ban options to the peers tables context menu (1h, 24h, 7d, 365d). If there are active bans, a table with banned subnets will be shown.
Sync between the rpc bans and the ui bans are guaranteed over a new uiInterface signal.
Screenshots:

527 | @@ -527,6 +528,7 @@ UniValue setban(const UniValue& params, bool fHelp) 528 | throw JSONRPCError(RPC_MISC_ERROR, "Error: Unban failed"); 529 | } 530 | 531 | + uiInterface.BannedListChanged();
I'm not sure if the locking of a signal emit is done within boost::signal of if this needs explicit locking of uiInterface?
118 | + case Address: 119 | + return QString::fromStdString(rec->subnet.ToString()); 120 | + case Bantime: 121 | + //show time in users local timezone, not 64bit compatible! 122 | + //TODO find a way to support 64bit timestamps 123 | + boost::posix_time::ptime pt1 = boost::posix_time::from_time_t(rec->bantil);
i recognized that boost::posix_time::from_time_t is not 64bit capable. Maybe someone has a idea how to fix this.
What about QDateTime::fromTime_t()?
QDateTime::fromTime_t(rec->bantil).toString() didn't test it...
I think this won't work either. The docs are saying: QDateTime QDateTime::fromTime_t(uint seconds) (uint!).
Found a fix! Pushed.
31 | + Qt::SortOrder sortOrder; 32 | + 33 | + /** Pull a full list of banned nodes from CNode into our cache */ 34 | + void refreshBanlist() 35 | + { 36 | + std::map<CSubNet, int64_t> banMap;
I was thinking about a typedef for this, what do you mean?
typedefs are useful but can also make reading the code more complicate. In this case (simple map) i don't see a usecase for a typedef.
Why would it make it more complicated? Worse comes to worse, you comment it, which is not really needed anyways.
39 | + cachedBanlist.clear(); 40 | +#if QT_VERSION >= 0x040700 41 | + cachedBanlist.reserve(banMap.size()); 42 | +#endif 43 | + std::map<CSubNet, int64_t>::iterator iter; 44 | + for (iter = banMap.begin(); iter != banMap.end(); ++iter) {
Could this be a Qt foreach? Would also be easy to convert to C++11 then :)?
47 | + banEntry.bantil = iter->second; 48 | + cachedBanlist.append(banEntry); 49 | + } 50 | + } 51 | + 52 | + int size()
Could this be int size() const?
56 | + 57 | + CCombinedBan *index(int idx) 58 | + { 59 | + if(idx >= 0 && idx < cachedBanlist.size()) { 60 | + return &cachedBanlist[idx]; 61 | + } else {
That else can be removed. I see you copied most of the code here from peertablemodel.cpp where all these suggestions would also fit and perhaps we should clean it up, to keep the codebase close together?
Not sure if this else can be removed. What if the QTableView requests a row at index which is not in the cache QList. Obviously it should not happen because the table size is driven by the size of the cache QList. But I prefer keeping this.
Not sure if you're right here, when the if clause isn't true we always run into the else (as we have a return in the if we won't reach the return 0, if the clause is false). I'd rather remove it...
126 | + ss << pt2; 127 | + return QString::fromStdString(ss.str()); 128 | + } 129 | + } else if (role == Qt::TextAlignmentRole) { 130 | + if (index.column() == Bantime) 131 | + return (int)(Qt::AlignRight | Qt::AlignVCenter);
Is int the right type here? I tried (QVariant) which also works.
162 | + 163 | + if (data) 164 | + { 165 | + return createIndex(row, column, data); 166 | + } 167 | + else
Same as above unneeded else. If we don't have data we can return just QModelIndex(). I think it's easier to read without else.
16 | + 17 | +QT_BEGIN_NAMESPACE 18 | +class QTimer; 19 | +QT_END_NAMESPACE 20 | + 21 | +struct CCombinedBan {
We have a struct here and a std::map for similar stuff... is there a way to unify these 2?
I think they should be separated and represent cores and ui form of keeping the data.
6 | + 7 | +#include "clientmodel.h" 8 | +#include "guiconstants.h" 9 | +#include "guiutil.h" 10 | + 11 | +#include "net.h"
This is included in the header already.
312 | @@ -311,23 +313,66 @@ void RPCConsole::setClientModel(ClientModel *model) 313 | ui->peerWidget->setColumnWidth(PeerTableModel::Address, ADDRESS_COLUMN_WIDTH); 314 | ui->peerWidget->setColumnWidth(PeerTableModel::Subversion, SUBVERSION_COLUMN_WIDTH); 315 | ui->peerWidget->setColumnWidth(PeerTableModel::Ping, PING_COLUMN_WIDTH); 316 | + ui->peerWidget->horizontalHeader()->setStretchLastSection(true); 317 | 318 | // create context menu actions 319 | - QAction* disconnectAction = new QAction(tr("&Disconnect Node"), this); 320 | + QAction* disconnectAction = new QAction(tr("&Disconnect Node"), this); 321 | + QAction* banAction1h = new QAction(tr("&Ban Node for 1 hour"), this);
You are repeating some translation text, perhaps there is an efficient way to reduce that?
split into two strings to reduce translation effort.
757 | + std::string addr; 758 | + int port = 0; 759 | + SplitHostPort(nStr, port, addr); 760 | + 761 | + CNode::Ban(CNetAddr(addr), bantime); 762 | + bannedNode->CloseSocketDisconnect();
Seems you reintroduced that over fDisconnect flag?
This was a serious overlook. Fixed. Thanks.
787 | @@ -699,3 +788,10 @@ void RPCConsole::clearSelectedNode() 788 | ui->detailWidget->hide(); 789 | ui->peerHeading->setText(tr("Select a peer to view detailed information.")); 790 | } 791 | + 792 | +void RPCConsole::showOrHideBanTableIfRequired() 793 | +{ 794 | + bool visible = clientModel->getBanTableModel()->shouldShow();
This could also use a NULL pointer check for clientModel IMHO.
Nullpointer check added.
Great pull IMHO, sorry for hitting you hard with that bunch of comments...
Thanks @Diapolo for the review. Appricate detailed feedback. Will fix all your points (especially the miss transaction to fDisconnect!).
Prebuilt binaries: https://builds.jonasschnelli.ch/pulls/6315/
122 | + date = date.addSecs(rec->bantil); 123 | + return date.toString(Qt::SystemLocaleLongDate); 124 | + } 125 | + } else if (role == Qt::TextAlignmentRole) { 126 | + if (index.column() == Bantime) 127 | + return (int)(Qt::AlignRight | Qt::AlignVCenter);
Still the question: Is int the right type here? I tried (QVariant) which also works and is more the Qt style?
Right. This is also the same as we have it in PeersTableModel, it works, but i agree that it should be QVariant (as the method definition says).
239 | @@ -226,12 +240,19 @@ static void NotifyAlertChanged(ClientModel *clientmodel, const uint256 &hash, Ch 240 | Q_ARG(int, status)); 241 | } 242 | 243 | +static void BannedListChanged(ClientModel *clientmodel) 244 | +{ 245 | + qDebug() << "BannedListChanged";
You could also use something like:
qDebug() << QString("%1: Requesting update for peer banlist").arg(__func__); I intend to creat a pull to make use of that format so we have the debug logging include always the correct function name :).
7 | @@ -8,6 +8,7 @@ 8 | #include "clientmodel.h" 9 | #include "guiutil.h" 10 | #include "peertablemodel.h" 11 | +#include "bantablemodel.h"
Not even a nit, but a please: Can you add #include "bantablemodel.h" above clientmodel :)?
336 | // context menu signals 337 | - connect(ui->peerWidget, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showMenu(const QPoint&))); 338 | + connect(ui->peerWidget, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showPeersTableContextMenu(const QPoint&))); 339 | connect(disconnectAction, SIGNAL(triggered()), this, SLOT(disconnectSelectedNode())); 340 | 341 | + //add a signal mapping to allow a dynamic argument
Nit: These comments are missing a whitespace at the front.
379 | @@ -335,6 +380,9 @@ void RPCConsole::setClientModel(ClientModel *model) 380 | ui->startupTime->setText(model->formatClientStartupTime()); 381 | 382 | ui->networkName->setText(QString::fromStdString(Params().NetworkIDString())); 383 | + 384 | + connect(model, SIGNAL(banListChanged()), this, SLOT(showOrHideBanTableIfRequired()));
I'd rather group these near the banlist stuff or add some small comment, before this is getting confusing here?
Currently integrating this into my build to test, will report back :).
Observations:
Edit:
There is also an issue with removing a peer from banned peers list when banned until is over:

The peer should be removed from the list, which is not working currently.
listbanned also still shows them: [ { "address": "127.0.0.1/255.255.255.255", "banned_untill": 1434891295 }, { "address": "nkf5e6b7pl4jfd4a.onion/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "banned_untill": 1434891291 }, { "address": "t6xj6wilh4ytvcs7.onion/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "banned_untill": 1434891400 } ]
@Diapolo: for autocleaning then ban list see PR #6310
I think you point 1) is not a problem. The peer is still selected (gray) when you interact with the bantable. Point 2) and 3) are cosmetics and should not hold this PR back. Point 4) agreed.
Banning tor nodes needs testing (best first over RPC) and maybe needs more implementation. Would be good if you could see what's missing there.
Tested also with Tor nodes. Works fine. @Diapolo: what issues did you encounter with tor nodes?
@jonasschnelli The problem are incoming Tor nodes with 127.0.0.1 blocking one connected node blocks any other incoming 127.0.0.1 node. Other connected 127.0.0.1 are not affected.
Also I'd suggest splitting of the autocleaning pull from the disk-store-pull as autocleaning is uncontroversial and also needed by this one.
I also disagree on ignoring UI or UX issues for now as often such "small" or "minor" things are just forgotton or I pick them up. I'd vote for making this pull good and not just "working okay" :).
Fixed @Diapolos nits.
Can the netmask be changed to the more modern (eg) /24 /128 etc format? Also prefer to have banned peers not take up space in the peer tab... somehow.
Updated.
Added a commit that uses CIDR notation (like /32) when printing (ToString()) CSubNet.
UI and RPC looks now like...

@jonasschnelli Thanks for your hard work on this, still there is a second round of nits open I made and also still the UX problems, the autoupdate (I have comments in the other pull also, so I'm fine if that get's some love or merged soon :)) and 127.0.0.1 handling for incoming Tor.
@Diapolo: what nits are open? Font size, selecting rows, column sized are fixed. Autocleanup is not in this PR and it will stay in #6310 because it makes most sense to clean data before writing to disk. It's not a bug if your ban list keeps a entry which has expired. Feel free to write a patch/commit for the incoming tor node banning (separate bans by port). I won't cover it in this PR.
@jonasschnelli Last time I saw quite some uncommented stuff in the code, even if minor. I need to resync from your repo and take another look then.
Added @Diapolos polishing commit on top.
I've got another pull in the pipe covering UX issues, but I need to digg in deeper...
@jonasschnelli Fixed the CSubNet::ToString function for subnets that can't be represented as /x, please pick the top commit from https://github.com/laanwj/bitcoin/tree/2015_06_cideriad
Added @laanwj commit on top to support non CIDR compatible netmasks.
@jonasschnelli I'm asking myself if this is worth adding the core signal BannedListChanged it makes it more complex and we don't need it. We pull the list / refresh the list via timer that calls void BanTableModel::refresh() anyway. Are you okay when I rework the signal handling to be easier?
@Diapolo: i extra added the signal to avoid a timer. For the peers list, a timer makes sense because we show recv/sent-bytes counters. The interval is 250ms (!). Poll updating through timers is a bad design IMO. Sometimes unavoidable.
Adding another timer (or use the same one) for the ban list would mean unnecessarily using CPU ticks.
Well placed signals/hocks allow a software to be extendable without creating unnecessary class coupling. I think we should not be to cautious we adding new signals.
Take a look at https://github.com/jonasschnelli/bitcoin/pull/5 IMHO this is not that bad and much easier flow-wise. We also don't have any core signals for the normal peer list. @laanwj What do you think?
Perhaps a check could be added to verify we only pull a fresh list if the old one changed, that would reduce CPU ticks.
@Diapolo: i had a look at the PR and would prefer the current solution. Your solution would make generating a relatively static ban list every 250ms which need to go over a short locks of cs_setBanned.
But don't have a strong opinion on that.
I'll try to rethink how this can be further improved, but code-wise it's much easier to understand. I respect your arguments for the core signals, but it feels like too much for this.
@Diapolo: can you point out the drawbacks of adding new signal to the main signals?
I had the impression the code-flow is overly complex, also because of updateBanlist in clientmodel, which you manually call from inside the rpcconsole. Also we have that timer code but it was disabled. The other way could be to remove the timer code and leave the core signal stuff, but also simplify clientmodel usage.
Good point. Added a commit that removes the unused timer code in bantablemodel.cpp | .h.
The signaling approach over ClientModel is totally fine IMO. I would even say the complexity of the code is much higher with a timer than with a simple signal.
See https://github.com/jonasschnelli/bitcoin/pull/6, which should further simplify the code a little :). I rebased the pull to current master, this is just the new commit: https://github.com/Diapolo/bitcoin/commit/025954cb2fe1cfbbd14a5b0e86984197c5c7caa3
This is progressing nicely, hope we can get some consensus on my last 3 commits in https://github.com/jonasschnelli/bitcoin/pull/6 and then get this merged :).
@Diapolo: Please stop improving this PR within this PR. It's contra-productive. I can't follow all your change requests and it might crushes down this PR. Some of your things are really cool (like the sorting). But you mix everything together and that makes it hard to read and distinct between useful and not useful changes.
Let stabilize this PR with searching and solving real problems. Once – and if – this is merge, you can PR your optimizations.
Too bad... as I'm solving "real" problems ;). I won't add anything new here I promise, but it would be cool to get the things I have opened in.
Added three cleanup/optimization PR from @Diapolo on top. Now it would be good to stabilize this PR.
322 | - contextMenu = new QMenu(); 323 | - contextMenu->addAction(disconnectAction); 324 | - 325 | - // context menu signals 326 | - connect(ui->peerWidget, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showMenu(const QPoint&))); 327 | + QAction* banAction1h = new QAction(tr("&Ban Node for") + " " + tr("&1 hour"), this);
I rechecked the code and during a translation for my build I came to this.
The & is used as a shortcut when pressing ALT IMHO, so this will produce 2 shortcuts for every single menu entry. I'd suggest just removing them, because it will be hard to make them unique and still usable.
Edit: I can't even access them on Windows... perhaps they are not wanted in context menus? What about Mac @jonasschnelli can you use these?
Ping
Would be great if you can rebase this, as the banlist.dat change was merged :). Looking forward to now get this in.
Rebased and fix @Diapolos nits. Also made the ban table heading text non-selectable.
Added another commit from @Diapolo that stores the banlist.dat file after banning/unbanning over QT.
ACK
Can this be rebased and get some love :)? Would be great to get this in soon...
This PR doesn't need rebase and already has plenty love. Ready for merge IMO.
Here are some binaries if somebody want to test without ability to build: https://builds.jonasschnelli.ch/pulls/6315/
@Diapolo: you where right, needed adaption to the post QT_NO_KEYWORDS code. Rebased and fixed.
ut ACK - ready to merge once conflicts fixed via rebase
Rebased (trivial).
Happy to see this moving again, I had the impression our GUI is dead...
add ban option for peer context menu (1h, 24h, 7d, 1y).
- some code cleanups
- fix date formatting
- reduce header includes
Only use CIDR notation if the netmask can be represented as such.
- remove banListChanged signal from client model
- directly call clientModel->getBanTableModel()->refresh() without the way
over clientModel->updateBanlist()
- also fix clearing peer detail window, when selecting (clicking)
peers in the ban list
- add missing NULL pointer checks
- add better comments and reorder some code in rpcconsole.cpp
- remove unneeded leftovers in bantable.cpp
- update bantable column sizes to prevent cutting of banned until
- 1 (h)our
- 1 (d)ay
- 1 (w)eek
- 1 (y)ear
- this matches RPC call behaviour
Rebased. Travis is happy now.
Concept ACK
Why does 7f90ea78cb68c60408df85d5c653257dbc9160fe remove the DumpBanlist calls?
Thank @luke-jr for the precise review. I think this sneaked in over a rebase because this PR started before the banlist was persisted to disk.
Just added a commit that re-implenents the DumpBanlist() calls in case of banning/unbanning over QT.
Note I did not do a precise review for this (yet?), just noticed those disappear from the previous version. ;)