This change adds a new row to the Node Window (debugwindow.ui) under the Network section which shows the LocalAddresses.
fixes #564
I’ve got this from the linter:
0he locale dependent function std::to_string(...) appears to be used:
1src/qt/rpcconsole.cpp: localAddresses += item.first.ToString() + ":" + std::to_string(item.second.nPort) + ", ";
2
3Unnecessary locale depedence can cause bugs that are very tricky to isolate and fix. Please avoid using locale dependent functions if possible.
Not sure how I should convert from int to string correctly. Any advice?
259+ <widget class="QLabel" name="localAddresses">
260+ <property name="cursor">
261+ <cursorShape>IBeamCursor</cursorShape>
262+ </property>
263+ <property name="text">
264+ <string>N/A</string>
notr="true"
property
948@@ -949,6 +949,7 @@ void RPCConsole::message(int category, const QString &message, bool html)
949
950 void RPCConsole::updateNetworkState()
951 {
952+ // numberOfConnections
966+ for (const std::pair<const CNetAddr, LocalServiceInfo> &item : mapLocalHost)
967+ localAddresses += item.first.ToString() + ":" + std::to_string(item.second.nPort) + ", ";
968+ if (localAddresses.length() > 2)
969+ localAddresses[localAddresses.length()-2] = 0;
970+ else
971+ localAddresses = "None";
Would suggest to write it differently:
0std::string localAddresses = "";
1LOCK(g_maplocalhost_mutex);
2for (auto it = mapLocalHost.begin(); it != mapLocalHost.end();) {
3 localAddresses += it->first.ToString() + ":" + std::to_string(it->second.nPort);
4 if (++it != mapLocalHost.end()) localAddresses += ", ";
5}
6if (localAddresses.empty()) localAddresses = "None";
This is a review on an earlier state of the PR, the following would be the appropriate diff to express your idea:
0diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
1index 386b9373c..a249d7895 100644
2--- a/src/qt/rpcconsole.cpp
3+++ b/src/qt/rpcconsole.cpp
4@@ -964,13 +964,12 @@ void RPCConsole::updateNetworkState()
5 // localAddressess
6 QString local_addresses;
7 LOCK(g_maplocalhost_mutex);
8- for (const std::pair<const CNetAddr, LocalServiceInfo> &item : mapLocalHost)
9- local_addresses += QString::fromStdString(item.first.ToString()) + ":" + QString::number(item.second.nPort) + ", ";
10- if (local_addresses.length() > 2)
11- local_addresses[local_addresses.length()-2] = 0;
12- else
13- // Signals to the user that they do not have any local addresses.
14- local_addresses = tr("None");
15+ for (auto it = mapLocalHost.begin(); it != mapLocalHost.end();) {
16+ local_addresses += QString::fromStdString(it->first.ToString()) + ":" + QString::number(it->second.nPort) + ", ";
17+ if (++it != mapLocalHost.end()) local_addresses += ", ";
18+ }
19+ //: Signals to the user that they do not have any local addresses.
20+ if (local_addresses.isEmpty()) local_addresses = tr("None");
21 ui->localAddresses->setText(local_addresses);
22 }
mapLocalHost
access in the backend, which is Qt/GUI independent. So cannot use QString from there. Need to craft a plain std::string, then convert it to QString once it passes through the model class or directly in the widget.
Dear @furszy thanks for the valuable input. Applied most of them except this encapsulation part. I understand your rational and I think it’s a better design. But since the number of connections are also calculated here, I did the same with Local Address. Since I’m leaving for a 10 day off the grid trip, I submitted a new commit (squashed) with these chagnes:
Thanks @jarolrod for the inputs and fixes and reviews.
Dear @furszy thanks for the valuable input. Applied most of them except this encapsulation part. I understand your rational and I think it’s a better design. But since the number of connections are also calculated here, I did the same with Local Address.
The connections aren’t calculated here (on the widget, as you are doing for the external addresses). It’s encapsulated inside the connections manager. The flow is the following one: widget -> model -> node -> conman.
updateNetworkState
calls clientModel->getNumConnections
which calls the interface node.getNodeCount
, which calls the connection manager.
Let’s try to respect the different layers, encapsulating access where is possible, to get cleaner code to work with. (If you need a hand with it, feel very welcome to ping me on IRC)
std::map<CNetAddr, LocalServiceInfo>
to keep it flexible enough for different use cases; for example in this specific usecase (showing IPs in GUI), i was able to do check for “IsI2P” on CNetAddrs.
958@@ -958,6 +959,18 @@ void RPCConsole::updateNetworkState()
959 }
960
961 ui->numberOfConnections->setText(connections);
962+
963+ // localAddressess
964+ std::string localAddresses="";
item.first.ToString()
-> QString::fromStdString(item.first.ToString())
localAddresses
-> local_addresses
to keep in line with our coding style
958@@ -958,6 +959,18 @@ void RPCConsole::updateNetworkState()
959 }
960
961 ui->numberOfConnections->setText(connections);
962+
963+ // localAddressess
964+ std::string localAddresses="";
965+ LOCK(g_maplocalhost_mutex);
966+ for (const std::pair<const CNetAddr, LocalServiceInfo> &item : mapLocalHost)
967+ localAddresses += item.first.ToString() + ":" + std::to_string(item.second.nPort) + ", ";
to fix the linter issue arrising from std::to_string(item.second.nPort)
, and keeping in mind that local_addresses
is now a QString:
0 local_addresses += QString::fromStdString(item.first.ToString()) + ":" + QString::number(item.second.nPort) + ", ";
QString::number
fixes the linter issue, from Qt Docs: “The formatting always uses QLocale::C, i.e., English/UnitedStates”
concept ack
full diff:
0diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
1index 01989b5a0..080f9b979 100644
2--- a/src/qt/rpcconsole.cpp
3+++ b/src/qt/rpcconsole.cpp
4@@ -961,15 +961,16 @@ void RPCConsole::updateNetworkState()
5 ui->numberOfConnections->setText(connections);
6
7 // localAddressess
8- std::string localAddresses="";
9+ QString local_addresses;
10 LOCK(g_maplocalhost_mutex);
11 for (const std::pair<const CNetAddr, LocalServiceInfo> &item : mapLocalHost)
12- localAddresses += item.first.ToString() + ":" + std::to_string(item.second.nPort) + ", ";
13- if (localAddresses.length() > 2)
14- localAddresses[localAddresses.length()-2] = 0;
15+ local_addresses += QString::fromStdString(item.first.ToString()) + ":" + QString::number(item.second.nPort) + ", ";
16+ if (local_addresses.length() > 2)
17+ local_addresses[local_addresses.length()-2] = 0;
18 else
19- localAddresses = "None";
20- ui->localAddresses->setText(QString::fromStdString(localAddresses));
21+ //: Signals to the user that they do not have any local addresses.
22+ local_addresses = tr("None");
23+ ui->localAddresses->setText(local_addresses);
24
25 }
48@@ -49,6 +49,7 @@
49
50 #include <chrono>
51
52+
Aside from the code comments, there is an important topic to mention:
Let’s not access fields from the net layer in a GUI widget/view, the sources architecture is (should be) widget -> model -> interface -> backend.
Better to create the proper flow to access the information that you are looking for: rpcconsole -> clientModel -> node interface -> net layer.
In this way, the field access is encapsulated, layers responsibilities are respected and the same function can be used, and tested, from other user facing end points like the RPC server equally.
248@@ -249,6 +249,29 @@
249 </widget>
250 </item>
251 <item row="9" column="0">
252+ <widget class="QLabel" name="label_14">
nit:
0 <widget class="QLabel" name="labelLocalAddresses">
thanks for the checks & ACK @shaavan . I kept the label_14 in accordance in most of other fields which had the same pattern.
regarding the listening port, you can test with something like (for testnet):
0testnet=1
1blocksonly=1
2[test]
3#prune=1000 # this has no effect during syncing
4externalip=100.200.100.10
5rpcbind=127.0.0.1
6rpcallowip=0.0.0.0/0
7rpcuser=user
8rpcpassword=pass
as your bitcoin.conf to force a listening IP.
Concept ACK
Ubuntu 22.04
with Qt version 5.15.3
.Screenshot:
Currently, I am figuring out how to add local addresses to check if they are displayed correctly. It would be awesome if someone could point towards documentation on how to do so.
ACK c47f01bf25e1353b51c24b9b31260a539a394b1a
Continuing on my previous review:
I was able to add local addresses successfully and tested that those were displayed perfectly on the GUI under the Local Address option.
Method of testing:
I added the following options in the bitcoin.conf
file.
0[signet]
1externalip=100.200.100.11
2externalip=100.200.100.10
3externalip=100.200.100.12
The IP addresses, along with the port number, appeared in the GUI.
Screenshot:
Note: The IP:port
appears in the sorted order irrespective of the order provided in the bitcoin.conf
file. This behavior is the same as the -netinfo
RPC command.
Screenshot:
Thanks, @jadijadi, for the help on adding the listening ports!
248@@ -249,6 +249,29 @@
249 </widget>
250 </item>
251 <item row="9" column="0">
252+ <widget class="QLabel" name="label_14">
253+ <property name="text">
254+ <string>Local Address</string>
we could add a translator comment here by adding in the extracomment
field:
0 <string extracomment="nice translator comment">Local Address</string>
Code Review ACK c47f01bf25e1353b51c24b9b31260a539a394b1a
Some nits
0qt/rpcconsole.cpp: In member function ‘void RPCConsole::updateNetworkState()’:
1qt/rpcconsole.cpp:965:61: error: ‘const class CNetAddr’ has no member named ‘ToString’; did you mean ‘ToStringAddr’?
2 local_addresses += QString::fromStdString(it->first.ToString()) + ":" + QString::number(it->second.nPort);
3 ^~~~~~~~
4 ToStringAddr
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
See the guideline for information on the review process.
Type | Reviewers |
---|---|
ACK | pablomartin4btc, furszy |
Concept ACK | RandyMcMillan, kristapsk |
Stale ACK | shaavan, jarolrod |
If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.
No conflicts as of last run.
Concept ACK
Will test on macos x86/Arm64 today
957@@ -958,6 +958,17 @@ void RPCConsole::updateNetworkState()
958 }
959
960 ui->numberOfConnections->setText(connections);
961+
962+ QString local_addresses;
963+ LOCK(g_maplocalhost_mutex);
964+ for (auto it = mapLocalHost.begin(); it != mapLocalHost.end();) {
965+ local_addresses += QString::fromStdString(it->first.ToString()) + ":" + QString::number(it->second.nPort);
966+ if (++it != mapLocalHost.end()) local_addresses += ", ";
it != mapLocalHost.begin()
at the start of it?
Concept ACK and tested c47f01bf25e1353b51c24b9b31260a539a394b1a
Thanks @furszy for emphasizing the importance of a good code design and architectural integrity. These principles are crucial for the long-term maintainability and scalability of our project. I totally agree with his code change recommendation in terms of the flow/ pattern to follow.
In terms of the UI itself, I’d use the plural form “Local Addresses”, as @jarolrod also pointed it out, to be consistent with the getnetworkinfo
rpc command output.
Concept ACK, kinda works.
Minor nit noticed - could avoid adding “:0” as a port for I2P addresses.
This PR still have a couple of unaddressed comments: #626 (comment) and #626 (comment).
@jadijadi Are you still working on this?
Sorry for forgetting this… will check and comment soon
Concept ACK, kinda works.
Minor nit noticed - could avoid adding “:0” as a port for I2P addresses.
thanks for the suggestion, did this.
Another thing - list is currently comma separated, without wrapping. Wouldn’t it be too long if somebody has IPv4, IPv6, CJDNS, Tor and I2P addresses at the same time?
Thanks for this point, it is fixed and the widget worswraps and extends when needed.
This PR still have a couple of unaddressed comments: #626 (comment) and #626 (comment).
Dear @hebasto , addressed all issues, updated the code & replied to queries. Thanks for the follow up.
167@@ -168,6 +168,9 @@ class Node
168 //! Get num blocks.
169 virtual int getNumBlocks() = 0;
170
171+ //! Get num blocks.
172+ virtual std::map<CNetAddr, LocalServiceInfo> getLocalAddresses() = 0;
280@@ -281,6 +281,13 @@ class NodeImpl : public Node
281 }
282 return false;
283 }
284+ std::map<CNetAddr, LocalServiceInfo> getLocalAddresses() override
285+ {
286+ std::map<CNetAddr, LocalServiceInfo> empty;
287+
288+ return m_context->connman ? m_context->connman->getLocalAddresses() : empty;
0return m_context->connman ? m_context->connman->getLocalAddresses() : {};
Thanks for all the support and patient. Applying them. The {}
was also my first try but I got:
error: initializer list cannot be used on the right hand side of operator ':'
Now I changed it to
0 if (m_context->connman)
1 return m_context->connman->getLocalAddresses();
2 else
3 return {};
to prevent that ugly empty
variable of mine.
982+ if (it != mapHosts.begin()) local_addresses += ", ";
983+ if (it->first.IsI2P())
984+ local_addresses += QString::fromStdString(it->first.ToStringAddr());
985+ else
986+ local_addresses += QString::fromStdString(it->first.ToStringAddr()) + ":" + QString::number(it->second.nPort);
987+ }
Maybe?
0QString local_addresses;
1std::map<CNetAddr, LocalServiceInfo> hosts = clientModel->getLocalAddresses();
2for (const auto& [addr, info] : hosts) {
3 local_addresses += QString::fromStdString(it->first.ToStringAddr());
4 if (!addr.IsPrivacyNet()) local_addresses += ":" + QString::number(it->second.nPort);
5 local_addresses += ", ";
6}
7local_addresses.chop(2); // remove last ", "
This is more beautiful and understandable. Replaced the it->first
& it->second
with addr
& info
:
0 for (const auto& [addr, info] : hosts) {
1 local_addresses += QString::fromStdString(addr.ToStringAddr());
2 if (!addr.IsPrivacyNet()) local_addresses += ":" + QString::number(info.nPort);
3 local_addresses += ", ";
4 }
5 local_addresses.chop(2); // remove last ", "
167@@ -168,6 +168,9 @@ class Node
168 //! Get num blocks.
169 virtual int getNumBlocks() = 0;
170
171+ //! Get local addresses.
172+ virtual std::map<CNetAddr, LocalServiceInfo> getLocalAddresses() = 0;
nit
0 virtual std::map<CNetAddr, LocalServiceInfo> getNetLocalAddresses() = 0;
974@@ -975,6 +975,18 @@ void RPCConsole::updateNetworkState()
975 }
976
977 ui->numberOfConnections->setText(connections);
978+
979+ QString local_addresses;
980+ std::map<CNetAddr, LocalServiceInfo> hosts = clientModel->getLocalAddresses();
981+ for (const auto& [addr, info] : hosts) {
982+ local_addresses += QString::fromStdString(addr.ToStringAddr());
983+ if (!addr.IsPrivacyNet()) local_addresses += ":" + QString::number(info.nPort);
Also, please split the interfaces changes into a different commit than the GUI changes.
did that. cleaner and atomic. thanks.
did that. cleaner and atomic. thanks.
Hmm, it’s not that way in your new push tho
did that. cleaner and atomic. thanks.
Hmm, it’s not that way in your new push tho
Ouch… pushed. thanks.
Somehow you introduced a typo in the commit message >_<
Oh.. sorry. fixed and added a spellchecker.
12@@ -13,6 +13,9 @@
13 #include <sync.h>
14 #include <uint256.h>
15
16+#include <net.h>
Could remove the need for adding this dependency here:
0diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h
1--- a/src/qt/clientmodel.h (revision ea576b65592c23895f80c9116f881ff063253ff8)
2+++ b/src/qt/clientmodel.h (date 1722363555212)
3@@ -13,7 +13,6 @@
4 #include <sync.h>
5 #include <uint256.h>
6
7-#include <net.h>
8 #include <netaddress.h>
9
10 class BanTableModel;
11@@ -22,6 +21,7 @@
12 class PeerTableModel;
13 class PeerTableSortProxy;
14 enum class SynchronizationState;
15+struct LocalServiceInfo;
16
17 namespace interfaces {
18 class Handler;
This will avoid including the backend net.h
file in all the gui files that are already including clientmodel.h
.
Ideally, net.h
structs/classes shouldn’t be accesible by the GUI widgets. But no prob, it seems we are already including it in rpcconsole.h
too.
utACK
Maybe add a QLabel hint describing what “local addresses” are for GUI users? (and/or use another term for them).
utACK
Maybe add a QLabel hint describing what “local addresses” are for GUI users? (and/or use another term for them).
Any suggestion? English is not my first language. Something like “Addresses where Bitcoin Core binds to”? On DataDir we have the tooltip on the value column. I’ll also keep it like that.
Maybe add a QLabel hint describing what “local addresses” are for GUI users? (and/or use another term for them).
Any suggestion? English is not my first language. Something like “Addresses where Bitcoin Core binds to”? On DataDir we have the tooltip on the value column. I’ll also keep it like that.
Perhaps any of these (or a combination):
Each local address broadcast by this node.
IP addresses of your local machine or network interface that the node uses to connect to the Bitcoin network
Network addresses that your Bitcoin node is currently using to communicate with other nodes.
- Network addresses that your Bitcoin node is currently using to communicate with other nodes.
Thanks @pablomartin4btc . Added this as the tooltip & thanks for the tACK
Contributes to #564 by providing an interface for mapLocalHost
through net -> node interface -> clientModel. Later this value can be
read by GUI to show the local addresses.
Adds a new row to the Node Window (debugwindow.ui)
under the Network section which shows the LocalAddresses.
fixes #564
re-ACK 189c987386a0da132d7ef076cdf539f9eb75fc3c
This has been fixed since my last review:
You changed the net.h stuff in the second commit, after adding it in the first