Skip to content

Commit 7f609f6

Browse files
committed
Merge #19731: net, rpc: expose nLastBlockTime/nLastTXTime as last block/last_transaction in getpeerinfo
5da9621 doc: release note for getpeerinfo last_block/last_transaction (Jon Atack) cfef5a2 test: rpc_net.py logging and test naming improvements (Jon Atack) 21c57ba test: getpeerinfo last_block and last_transaction tests (Jon Atack) 8a560a7 rpc: expose nLastBlockTime/TXTime as getpeerinfo last_block/transaction (Jon Atack) 02fbe3a net: add nLastBlockTime/TXTime to CNodeStats, CNode::copyStats (Jon Atack) Pull request description: This PR adds inbound peer eviction criteria `nLastBlockTime` and `nLastTXTime` to `CNodeStats` and `CNode::copyStats`, which then allows exposing them in the next commit as `last_transaction` and `last_block` Unix Epoch Time fields in RPC `getpeerinfo`. This may be useful for writing missing eviction tests. I'd also like to add `lasttx` and `lastblk` columns to the `-netinfo` dashboard as described in bitcoin/bitcoin#19643 (comment). Relevant discussion at the p2p irc meeting http://www.erisian.com.au/bitcoin-core-dev/log-2020-08-11.html#l-549: ```text <jonatack> i was specifically trying to observe and figure out how to test bitcoin/bitcoin#19500 <jonatack> which made me realise that i didn't know what was going on with my peer conns in enough detail <jonatack> i'm running bitcoin locally with nLastBlockTime and nLastTXTime added to getpeerinfo for my peer connections dashboard <jonatack> sipa: is there a good reason why that (eviction criteria) data is not exposed through getpeerinfo currently? <sipa> jonatack: nope; i suspect just nobody ever added it <jonatack> sipa: thanks. will propose. ``` The last commit is optional, but I think it would be good to have logging in `rpc_net.py`. ACKs for top commit: jnewbery: Code review ACK 5da9621 theStack: Code Review ACK 5da9621 darosior: ACK 5da9621 Tree-SHA512: 2db164bc979c014837a676e890869a128beb7cf40114853239e7280f57e768bcb43bff6c1ea76a61556212135281863b5290b50ff9d24fce16c5b89b55d4cd70
2 parents 4fefd80 + 5da9621 commit 7f609f6

File tree

5 files changed

+52
-17
lines changed

5 files changed

+52
-17
lines changed

doc/release-notes-19731.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Updated RPCs
2+
------------
3+
4+
- The `getpeerinfo` RPC now has additional `last_block` and `last_transaction`
5+
fields that return the UNIX epoch time of the last block and the last valid
6+
transaction received from each peer. (#19731)

src/net.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,8 @@ void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap)
530530
}
531531
X(nLastSend);
532532
X(nLastRecv);
533+
X(nLastTXTime);
534+
X(nLastBlockTime);
533535
X(nTimeConnected);
534536
X(nTimeOffset);
535537
stats.addrName = GetAddrName();

src/net.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,8 @@ class CNodeStats
619619
bool fRelayTxes;
620620
int64_t nLastSend;
621621
int64_t nLastRecv;
622+
int64_t nLastTXTime;
623+
int64_t nLastBlockTime;
622624
int64_t nTimeConnected;
623625
int64_t nTimeOffset;
624626
std::string addrName;

src/rpc/net.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
100100
{RPCResult::Type::BOOL, "relaytxes", "Whether peer has asked us to relay transactions to it"},
101101
{RPCResult::Type::NUM_TIME, "lastsend", "The " + UNIX_EPOCH_TIME + " of the last send"},
102102
{RPCResult::Type::NUM_TIME, "lastrecv", "The " + UNIX_EPOCH_TIME + " of the last receive"},
103+
{RPCResult::Type::NUM_TIME, "last_transaction", "The " + UNIX_EPOCH_TIME + " of the last valid transaction received from this peer"},
104+
{RPCResult::Type::NUM_TIME, "last_block", "The " + UNIX_EPOCH_TIME + " of the last block received from this peer"},
103105
{RPCResult::Type::NUM, "bytessent", "The total bytes sent"},
104106
{RPCResult::Type::NUM, "bytesrecv", "The total bytes received"},
105107
{RPCResult::Type::NUM_TIME, "conntime", "The " + UNIX_EPOCH_TIME + " of the connection"},
@@ -169,6 +171,8 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
169171
obj.pushKV("relaytxes", stats.fRelayTxes);
170172
obj.pushKV("lastsend", stats.nLastSend);
171173
obj.pushKV("lastrecv", stats.nLastRecv);
174+
obj.pushKV("last_transaction", stats.nLastTXTime);
175+
obj.pushKV("last_block", stats.nLastBlockTime);
172176
obj.pushKV("bytessent", stats.nSendBytes);
173177
obj.pushKV("bytesrecv", stats.nRecvBytes);
174178
obj.pushKV("conntime", stats.nTimeConnected);

test/functional/rpc_net.py

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@
88
"""
99

1010
from decimal import Decimal
11+
from itertools import product
12+
import time
1113

1214
from test_framework.test_framework import BitcoinTestFramework
1315
from test_framework.util import (
16+
assert_approx,
1417
assert_equal,
1518
assert_greater_than_or_equal,
1619
assert_greater_than,
@@ -48,25 +51,27 @@ def set_test_params(self):
4851
self.supports_cli = False
4952

5053
def run_test(self):
51-
self.log.info('Get out of IBD for the minfeefilter test')
52-
self.nodes[0].generate(1)
53-
self.log.info('Connect nodes both way')
54+
# Get out of IBD for the minfeefilter and getpeerinfo tests.
55+
self.nodes[0].generate(101)
56+
# Connect nodes both ways.
5457
connect_nodes(self.nodes[0], 1)
5558
connect_nodes(self.nodes[1], 0)
5659

57-
self._test_connection_count()
58-
self._test_getnettotals()
59-
self._test_getnetworkinfo()
60-
self._test_getaddednodeinfo()
61-
self._test_getpeerinfo()
60+
self.test_connection_count()
61+
self.test_getpeerinfo()
62+
self.test_getnettotals()
63+
self.test_getnetworkinfo()
64+
self.test_getaddednodeinfo()
6265
self.test_service_flags()
63-
self._test_getnodeaddresses()
66+
self.test_getnodeaddresses()
6467

65-
def _test_connection_count(self):
66-
# connect_nodes connects each node to the other
68+
def test_connection_count(self):
69+
self.log.info("Test getconnectioncount")
70+
# After using `connect_nodes` to connect nodes 0 and 1 to each other.
6771
assert_equal(self.nodes[0].getconnectioncount(), 2)
6872

69-
def _test_getnettotals(self):
73+
def test_getnettotals(self):
74+
self.log.info("Test getnettotals")
7075
# getnettotals totalbytesrecv and totalbytessent should be
7176
# consistent with getpeerinfo. Since the RPC calls are not atomic,
7277
# and messages might have been recvd or sent between RPC calls, call
@@ -96,7 +101,8 @@ def _test_getnettotals(self):
96101
assert_greater_than_or_equal(after['bytesrecv_per_msg'].get('pong', 0), before['bytesrecv_per_msg'].get('pong', 0) + 32)
97102
assert_greater_than_or_equal(after['bytessent_per_msg'].get('ping', 0), before['bytessent_per_msg'].get('ping', 0) + 32)
98103

99-
def _test_getnetworkinfo(self):
104+
def test_getnetworkinfo(self):
105+
self.log.info("Test getnetworkinfo")
100106
assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], True)
101107
assert_equal(self.nodes[0].getnetworkinfo()['connections'], 2)
102108

@@ -108,7 +114,7 @@ def _test_getnetworkinfo(self):
108114

109115
with self.nodes[0].assert_debug_log(expected_msgs=['SetNetworkActive: true\n']):
110116
self.nodes[0].setnetworkactive(state=True)
111-
self.log.info('Connect nodes both way')
117+
# Connect nodes both ways.
112118
connect_nodes(self.nodes[0], 1)
113119
connect_nodes(self.nodes[1], 0)
114120

@@ -120,7 +126,8 @@ def _test_getnetworkinfo(self):
120126
for info in network_info:
121127
assert_net_servicesnames(int(info["localservices"], 0x10), info["localservicesnames"])
122128

123-
def _test_getaddednodeinfo(self):
129+
def test_getaddednodeinfo(self):
130+
self.log.info("Test getaddednodeinfo")
124131
assert_equal(self.nodes[0].getaddednodeinfo(), [])
125132
# add a node (node2) to node0
126133
ip_port = "127.0.0.1:{}".format(p2p_port(2))
@@ -139,8 +146,20 @@ def _test_getaddednodeinfo(self):
139146
# check that a non-existent node returns an error
140147
assert_raises_rpc_error(-24, "Node has not been added", self.nodes[0].getaddednodeinfo, '1.1.1.1')
141148

142-
def _test_getpeerinfo(self):
149+
def test_getpeerinfo(self):
150+
self.log.info("Test getpeerinfo")
151+
# Create a few getpeerinfo last_block/last_transaction values.
152+
if self.is_wallet_compiled():
153+
self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1)
154+
self.nodes[1].generate(1)
155+
self.sync_all()
156+
time_now = int(time.time())
143157
peer_info = [x.getpeerinfo() for x in self.nodes]
158+
# Verify last_block and last_transaction keys/values.
159+
for node, peer, field in product(range(self.num_nodes), range(2), ['last_block', 'last_transaction']):
160+
assert field in peer_info[node][peer].keys()
161+
if peer_info[node][peer][field] != 0:
162+
assert_approx(peer_info[node][peer][field], time_now, vspan=60)
144163
# check both sides of bidirectional connection between nodes
145164
# the address bound to on one side will be the source address for the other node
146165
assert_equal(peer_info[0][0]['addrbind'], peer_info[1][0]['addr'])
@@ -152,11 +171,13 @@ def _test_getpeerinfo(self):
152171
assert_net_servicesnames(int(info[0]["services"], 0x10), info[0]["servicesnames"])
153172

154173
def test_service_flags(self):
174+
self.log.info("Test service flags")
155175
self.nodes[0].add_p2p_connection(P2PInterface(), services=(1 << 4) | (1 << 63))
156176
assert_equal(['UNKNOWN[2^4]', 'UNKNOWN[2^63]'], self.nodes[0].getpeerinfo()[-1]['servicesnames'])
157177
self.nodes[0].disconnect_p2ps()
158178

159-
def _test_getnodeaddresses(self):
179+
def test_getnodeaddresses(self):
180+
self.log.info("Test getnodeaddresses")
160181
self.nodes[0].add_p2p_connection(P2PInterface())
161182

162183
# Add some addresses to the Address Manager over RPC. Due to the way

0 commit comments

Comments
 (0)