Related to: #18428
When -blocksonly is turned on, a node would still relay transactions from whitelisted peers. This funcitonality has not been tested.
2@@ -3,7 +3,7 @@
3 # Distributed under the MIT software license, see the accompanying
4 # file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 """Test p2p blocksonly"""
6-
7+from pprint import pprint
94+ pprint(self.nodes[0].p2p.last_message.get('tx'))
95+
96+ with self.nodes[0].assert_debug_log(['received getdata for: tx {} peer=0'.format(txid1)]):
97+ self.nodes[0].sendrawtransaction(sigtx1)
98+ self.nodes[0].p2p.wait_for_tx(txid1)
99+ pprint(self.nodes[0].p2p.wait_for_tx(txid1))
I am getting this error on this line:
File "/Users/gwang/Documents/bitcoin/test/functional/test_framework/util.py", line 234, in wait_until
raise AssertionError("Predicate {} not true after {} seconds".format(predicate_source, timeout))
AssertionError: Predicate ''''
def test_function():
assert self.is_connected
if not self.last_message.get('tx'):
return False
return self.last_message['tx'].tx.rehash() == txid
''' not true after 60 seconds
I’m a bit confused about why this transaction isn’t working, since I constructed it very similarly to the first transaction in this test file. I am still learning the way that this test setup works, and I might have been missing something
54@@ -55,8 +55,51 @@ def run_test(self):
55 with self.nodes[0].assert_debug_log(['received getdata for: tx {} peer=1'.format(txid)]):
56 self.nodes[0].sendrawtransaction(sigtx)
57 self.nodes[0].p2p.wait_for_tx(txid)
58+ pprint(self.nodes[0].p2p.wait_for_tx(txid))
59 assert_equal(self.nodes[0].getmempoolinfo()['size'], 1)
60
61+ self.log.info("Restarting node 0 with whitelist permission and blocksonly")
62+ self.stop_node(0)
63+ self.start_node(0, ["-whitelist=127.0.0.1", "-whitelistforcerelay", "-blocksonly"])
["-whitelist=127.0.0.1", "-whitelistrelay=0"]
, or ["-whitelist=127.0.0.1", "-whitelistrelay=0"],
, ['-whitelist=noban@127.0.0.1']
, etc. Understanding the nuances between them is a bit challenging after I have done quite a bit of digging. Could someone point me to some resource for this?
I think if you add nopersistmempool, it will drop the tx from the mempool and you can reuse it for this test without having to construct it again. In fact the below construction looks similar to the one above, so the resulting tx might be the same, thus leading to test failures.
0 self.start_node(0, ["-whitelist=127.0.0.1", "-whitelistforcerelay", "-blocksonly", "-nopersistmempool"])
63+ self.start_node(0, ["-whitelist=127.0.0.1", "-whitelistforcerelay", "-blocksonly"])
64+ self.nodes[0].add_p2p_connection(P2PInterface())
65+ self.log.info('Check that txs from whitelisted peers are not rejected and relayed to others')
66+ peerInfo = self.nodes[0].getpeerinfo()[0]
67+ assert_equal(peerInfo['whitelisted'], True)
68+ assert_equal(peerInfo['permissions'], ['noban', 'forcerelay', 'relay', 'mempool']) #checkpermission use check permission from p2p_permission
noban
is necessary to check here
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
Reviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.
211@@ -212,6 +212,7 @@ def wait_for_rpc_connection(self):
212 poll_per_s = 4
213 for _ in range(poll_per_s * self.rpc_timeout):
214 if self.process.poll() is not None:
215+ print('process poll is not NONE', self.process.poll()) #this outputs me 1
On startup failures or general test failures, you can combine the logs to see the issue:
0$ ./test/functional/combine_logs.py -c /tmp/bitcoin_func_test_wnpgbmh_ | tail
1 File "/home/marco/workspace/btc_bitcoin_core/test/functional/test_framework/test_framework.py", line 419, in start_node
2 node.wait_for_rpc_connection()
3 File "/home/marco/workspace/btc_bitcoin_core/test/functional/test_framework/test_node.py", line 217, in wait_for_rpc_connection
4 'bitcoind exited with status {} during initialization'.format(self.process.returncode)))
5 test_framework.test_node.FailedToStartError: [node 0] bitcoind exited with status 1 during initialization
6 test 2020-04-08T00:11:49.629000Z TestFramework (DEBUG): Closing down network thread
7 test 2020-04-08T00:11:49.680000Z TestFramework (INFO): Stopping nodes
8 test 2020-04-08T00:11:49.681000Z TestFramework.node0 (DEBUG): Stopping node
9
10 node0 stderr Error: Command line contains unexpected token 'persistmempool=0', see bitcoind -h for a list of options.
See also https://github.com/bitcoin/bitcoin/tree/master/test#test-logging
I just ran the test with
0$ ./test/functional/p2p_blocksonly.py --tracerpc -l DEBUG
54@@ -55,8 +55,28 @@ def run_test(self):
55 with self.nodes[0].assert_debug_log(['received getdata for: tx {} peer=1'.format(txid)]):
56 self.nodes[0].sendrawtransaction(sigtx)
57 self.nodes[0].p2p.wait_for_tx(txid)
58+ pprint(self.nodes[0].p2p.wait_for_tx(txid))
59 assert_equal(self.nodes[0].getmempoolinfo()['size'], 1)
60
61+ self.log.info("Restarting node 0 with whitelist permission and blocksonly")
62+ self.restart_node(0, ["persistmempool=0", "-whitelist=127.0.0.1", "-whitelistforcerelay", "-blocksonly"])
0 self.restart_node(0, ["-persistmempool=0", "-whitelist=127.0.0.1", "-whitelistforcerelay", "-blocksonly"])
71+ pprint(self.nodes[0].p2p.last_message)
72+ pprint(self.nodes[0].p2p.last_message.get('tx'))
73+
74+ assert_equal(self.nodes[0].p2p.is_connected, True)
75+ with self.nodes[0].assert_debug_log(['received getdata for: tx {} peer=0'.format(txid)]):
76+ self.nodes[0].sendrawtransaction(sigtx)
You will need at least three (mini)nodes if you want to check what you claim to be wanting to check. “Check that txs from whitelisted peers are not rejected and relayed to others”
whitlisted peer — tx —> Bitcoin Core —- tx —–> another peer
0 another_peer = self.nodes[0].add_p2p_connection(P2PInterface())
1 ... etc
self.nodes[0].p2p.wait_for_tx(txid)
kept timing out(line 77)…but little did I realized that this tx was originated by self.nodes[0].p2p
hence it wouldn’t hear this transaction again?? And instead, another_peer.wait_for_tx(txid)
would be the correct thing to check for..?
Also, this has been unclear to me: line 69 suggests to me that it is nodes[0]’s peer who sends out the transaction, which should be received by node[0]. We want to test whether node[0] would accept and relay this tx. This is aligned with purpose of the test.
However, on line 24, 33, and line 76, it seems to be that node[0] is the one who initiates the transaction, zB self.nodes[0].sendrawtransaction(sigtx)
. As a relay node (of the peer node[0].p2p), why is node[0] creating, signing, and transaction then?
And instead, another_peer.wait_for_tx(txid) would be the correct thing to check for..?
Correct
self.nodes[0]
is the Bitcoin Core node “in the middle”. I suggest not using self.nodes[0].p2p at all and instead assigning the peers proper names. You did that with first_peer
, second_peer
.
first_peer
and second_peer
are not Bitcoin Core nodes, but python mininodes (mock nodes).
When you say self.nodes[0].sendrawtransacttion
, I believe you really meant first_peer.send_message(msg_tx(FromHex(CTransaction(), sigtx)))
?
self.nodes[0].sendrawtransaction(sigtx)
so I kind of mimicked that line 😂 Does this mean that line 56 should be fixed too?
That is a different test “Check that txs from rpc are not rejected”, here in this line you are testing “Check that the tx from whitelisted peers …”
So those two lines should differ between the test cases
71+
72+ self.log.info('Check that the whitelisted peer is still connected after sending the transaction')
73+ assert_equal(first_peer.is_connected, True)
74+
75+ self.log.info('Check that the tx from whitelisted first_peer is relayed to others (ie.second_peer)')
76+ second_peer.wait_for_tx(txid)
73+ assert_equal(first_peer.is_connected, True)
74+
75+ self.log.info('Check that the tx from whitelisted first_peer is relayed to others (ie.second_peer)')
76+ second_peer.wait_for_tx(txid)
77+ assert_equal(self.nodes[0].getmempoolinfo()['size'], 1)
78+ self.log.info("Whitelisted peer's transaction is accepted and relayed")
It is optional. If you want you can check that an exact code path was hit that also contained a debug statement.
In this case it would be:
0with self.nodes[0].assert_debug_log(["Force relaying tx {} from whitelisted peer=0".format(txid)]):
1 first_peer.send(...
2 ...
To check you are hitting the right branch in src/net_processing.cpp
, where it say “Force relaying tx %s …”
2@@ -3,7 +3,6 @@
3 # Distributed under the MIT software license, see the accompanying
4 # file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 """Test p2p blocksonly"""
6-
Travis seems to be passing, but there is a linter error. Potentially pep8. Maybe it is this missing line?
You can try to format the python code with some kind of formatter or run the ./test/lint/lint-python.sh
and fix the issues up manually.
From travis:
0test/functional/p2p_blocksonly.py:68:83: W291 trailing whitespace
1
2test/functional/p2p_blocksonly.py:74:1: W293 blank line contains whitespace
3
4^---- failure generated from test/lint/lint-python.sh
5
6
7This diff appears to have added new lines with trailing whitespace.
8
9The following changes were suspected:
10
11diff --git a/test/functional/p2p_blocksonly.py b/test/functional/p2p_blocksonly.py
12
13@@ -59,0 +59,20 @@ class P2PBlocksOnly(BitcoinTestFramework):
14
15+ assert_equal(self.nodes[0].testmempoolaccept([sigtx])[0]['allowed'], True)
16
17+
18
19^---- failure generated from test/lint/lint-whitespace.sh
75+
76+ self.log.info('Check that the whitelisted peer is still connected after sending the transaction')
77+ assert_equal(first_peer.is_connected, True)
78+
79+ self.log.info('Check that the tx from whitelisted first_peer is relayed to others (ie.second_peer)')
80+ with self.nodes[0].assert_debug_log(["received getdata"]):
I wasn’t able assert that if (pfrom->HasPermission(PF_FORCERELAY)) {…} is hit, even though my peer’s permissions list includes “forcerelay”, which should correspond to PF_FORCERELAY…
From here: “The getdata message should usually only be used to request data from a node which previously advertised it had that data by sending an inv
message.” “It [inv
] can be sent unsolicited to announce new transactions or blocks” …this makes me think that perhaps self.nodes[0] announces the transaction once it receives it from first_peer, sending out inv
to second_peer, and second_peer sends getdata
to self.nodes[0]
0 with self.nodes[0].assert_debug_log(["received getdata", "Force relaying tx {} from whitelisted peer={}".format(txid, 0)]):
You can assert that the if-branch is hit by reading the log statement that is written to the debug log when this if-branch is executed.
I am still not sure why I am getting error trying to assert that this if branch is hit.
From else if (permission == "forcerelay") NetPermissions::AddFlag(flags, PF_FORCERELAY);
I konw that we can assert that PF_FORCERELAY flag is set with “forcerelay”. However, when I tried to assert this, I got this following error that suggest that this branch isn’t:
AssertionError: [node 0] Expected messages “[‘received getdata’, ‘Force relaying tx af34fc5ff9ea8babbd4083fbb79ffd2ad5aff1d6def803c07ca5aeed880bd60f from whitelisted peer=0’]” does not partially match log:
And the other place where we tested whether this if-branch is hit is in p2p_permissions.py: and the major difference between the two setups is this one uses P2PDataStore, mine uses P2PInterface:
p2p_rebroadcast_wallet = self.nodes[1].add_p2p_connection(P2PDataStore())
Also, this test was able to assert Force relaying ...
beacuse this broadcast happened between two full-nodes, not between full node and mini-node (like the case here). However, I did try substituing one full node out with a mini node, and this didn’t change anything and test still passed.
2534@@ -2535,8 +2535,9 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
2535
2536 if (msg_type == NetMsgType::TX) {
2537 // Stop processing the transaction early if
2538- // We are in blocks only mode and peer is either not whitelisted or whitelistrelay is off
2539- // or if this peer is supposed to be a block-relay-only peer
2540+ // 1) We are in blocks only mode and peer is either not whitelisted or the peer is whitelisted
0 // 1) We are in blocks only mode and the peer has no relay permission
I think this can be said slightly more concise, but still correct
2534@@ -2535,8 +2535,9 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
2535
2536 if (msg_type == NetMsgType::TX) {
2537 // Stop processing the transaction early if
2538- // We are in blocks only mode and peer is either not whitelisted or whitelistrelay is off
2539- // or if this peer is supposed to be a block-relay-only peer
2540+ // 1) We are in blocks only mode and the peer has no relay permission
2541+ // but "-whitelistrelay" is off, or