2019-05-16 18:44:54 +02:00
#!/usr/bin/env python3
2020-12-31 18:50:11 +01:00
# Copyright (c) 2019-2020 The Bitcoin Core developers
2019-05-16 18:44:54 +02:00
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
2024-03-28 04:21:36 +01:00
""" Test p2p blocksonly mode & block-relay-only connections. """
2019-05-16 18:44:54 +02:00
2024-03-28 04:21:36 +01:00
import time
2024-08-25 18:46:04 +02:00
from test_framework . messages import msg_tx , msg_inv , CInv , MSG_TX
2024-03-28 04:21:36 +01:00
from test_framework . p2p import P2PInterface , P2PTxInvStore
2019-05-16 18:44:54 +02:00
from test_framework . test_framework import BitcoinTestFramework
from test_framework . util import assert_equal
2021-05-06 16:52:45 +02:00
from test_framework . wallet import MiniWallet
2019-05-16 18:44:54 +02:00
class P2PBlocksOnly ( BitcoinTestFramework ) :
def set_test_params ( self ) :
self . num_nodes = 1
self . extra_args = [ [ " -blocksonly " ] ]
def run_test ( self ) :
2021-05-06 16:52:45 +02:00
self . miniwallet = MiniWallet ( self . nodes [ 0 ] )
# Add enough mature utxos to the wallet, so that all txs spend confirmed coins
2024-08-25 18:46:04 +02:00
self . miniwallet . rescan_utxos ( )
2021-05-06 16:52:45 +02:00
2024-03-28 04:21:36 +01:00
self . blocksonly_mode_tests ( )
self . blocks_relay_conn_tests ( )
def blocksonly_mode_tests ( self ) :
self . log . info ( " Tests with node running in -blocksonly mode " )
2019-05-16 18:44:54 +02:00
assert_equal ( self . nodes [ 0 ] . getnetworkinfo ( ) [ ' localrelay ' ] , False )
2024-03-28 04:21:36 +01:00
self . nodes [ 0 ] . add_p2p_connection ( P2PInterface ( ) )
tx , txid , tx_hex = self . check_p2p_tx_violation ( )
2022-06-19 08:02:28 +02:00
2024-08-25 18:46:04 +02:00
self . log . info ( ' Check that tx invs also violate the protocol ' )
self . nodes [ 0 ] . add_p2p_connection ( P2PInterface ( ) )
with self . nodes [ 0 ] . assert_debug_log ( [ ' tx (0000000000000000000000000000000000000000000000000000000000001234) inv sent in violation of protocol, disconnecting peer ' ] ) :
self . nodes [ 0 ] . p2ps [ 0 ] . send_message ( msg_inv ( [ CInv ( t = MSG_TX , h = 0x1234 ) ] ) )
self . nodes [ 0 ] . p2ps [ 0 ] . wait_for_disconnect ( )
del self . nodes [ 0 ] . p2ps [ 0 ]
2019-05-16 18:44:54 +02:00
self . log . info ( ' Check that txs from rpc are not rejected and relayed to other peers ' )
2024-03-28 04:21:36 +01:00
tx_relay_peer = self . nodes [ 0 ] . add_p2p_connection ( P2PInterface ( ) )
2019-05-16 18:44:54 +02:00
assert_equal ( self . nodes [ 0 ] . getpeerinfo ( ) [ 0 ] [ ' relaytxes ' ] , True )
2024-03-28 04:21:36 +01:00
assert_equal ( self . nodes [ 0 ] . testmempoolaccept ( [ tx_hex ] ) [ 0 ] [ ' allowed ' ] , True )
2024-08-25 18:46:04 +02:00
with self . nodes [ 0 ] . assert_debug_log ( [ ' received getdata for: tx {} peer ' . format ( txid ) ] ) :
2024-03-28 04:21:36 +01:00
self . nodes [ 0 ] . sendrawtransaction ( tx_hex )
2019-05-16 18:44:54 +02:00
self . bump_mocktime ( 60 )
2020-09-25 14:18:21 +02:00
tx_relay_peer . wait_for_tx ( txid )
2019-05-16 18:44:54 +02:00
assert_equal ( self . nodes [ 0 ] . getmempoolinfo ( ) [ ' size ' ] , 1 )
2020-09-22 22:44:02 +02:00
self . log . info ( " Restarting node 0 with relay permission and blocksonly " )
2020-12-28 22:40:33 +01:00
self . restart_node ( 0 , [ " -persistmempool=0 " , " -whitelist=relay@127.0.0.1 " , " -blocksonly " ] )
2020-07-11 10:22:44 +02:00
assert_equal ( self . nodes [ 0 ] . getrawmempool ( ) , [ ] )
2020-05-21 14:58:57 +02:00
first_peer = self . nodes [ 0 ] . add_p2p_connection ( P2PInterface ( ) )
second_peer = self . nodes [ 0 ] . add_p2p_connection ( P2PInterface ( ) )
peer_1_info = self . nodes [ 0 ] . getpeerinfo ( ) [ 0 ]
2020-09-22 22:44:02 +02:00
assert_equal ( peer_1_info [ ' permissions ' ] , [ ' relay ' ] )
2020-05-21 14:58:57 +02:00
peer_2_info = self . nodes [ 0 ] . getpeerinfo ( ) [ 1 ]
2020-09-22 22:44:02 +02:00
assert_equal ( peer_2_info [ ' permissions ' ] , [ ' relay ' ] )
2024-03-28 04:21:36 +01:00
assert_equal ( self . nodes [ 0 ] . testmempoolaccept ( [ tx_hex ] ) [ 0 ] [ ' allowed ' ] , True )
2020-05-21 14:58:57 +02:00
2020-09-22 22:44:02 +02:00
self . log . info ( ' Check that the tx from first_peer with relay-permission is relayed to others (ie.second_peer) ' )
2020-05-21 14:58:57 +02:00
with self . nodes [ 0 ] . assert_debug_log ( [ " received getdata " ] ) :
2020-09-22 22:44:02 +02:00
# Note that normally, first_peer would never send us transactions since we're a blocksonly node.
# By activating blocksonly, we explicitly tell our peers that they should not send us transactions,
# and Bitcoin Core respects that choice and will not send transactions.
# But if, for some reason, first_peer decides to relay transactions to us anyway, we should relay them to
# second_peer since we gave relay permission to first_peer.
# See https://github.com/bitcoin/bitcoin/issues/19943 for details.
2024-03-28 04:21:36 +01:00
first_peer . send_message ( msg_tx ( tx ) )
2020-09-22 22:44:02 +02:00
self . log . info ( ' Check that the peer with relay-permission is still connected after sending the transaction ' )
2020-05-21 14:58:57 +02:00
assert_equal ( first_peer . is_connected , True )
2020-09-22 22:44:02 +02:00
self . bump_mocktime ( 60 )
2020-05-21 14:58:57 +02:00
second_peer . wait_for_tx ( txid )
assert_equal ( self . nodes [ 0 ] . getmempoolinfo ( ) [ ' size ' ] , 1 )
2020-09-22 22:44:02 +02:00
self . log . info ( " Relay-permission peer ' s transaction is accepted and relayed " )
2020-07-11 10:22:44 +02:00
2024-03-28 04:21:36 +01:00
self . nodes [ 0 ] . disconnect_p2ps ( )
2024-10-01 21:25:52 +02:00
self . generate ( self . nodes [ 0 ] , 1 )
2024-03-28 04:21:36 +01:00
def blocks_relay_conn_tests ( self ) :
self . log . info ( ' Tests with node in normal mode with block-relay-only connections ' )
self . restart_node ( 0 , [ " -noblocksonly " ] ) # disables blocks only mode
assert_equal ( self . nodes [ 0 ] . getnetworkinfo ( ) [ ' localrelay ' ] , True )
# Ensure we disconnect if a block-relay-only connection sends us a transaction
self . nodes [ 0 ] . add_outbound_p2p_connection ( P2PInterface ( ) , p2p_idx = 0 , connection_type = " block-relay-only " )
assert_equal ( self . nodes [ 0 ] . getpeerinfo ( ) [ 0 ] [ ' relaytxes ' ] , False )
2024-08-25 18:46:04 +02:00
_ , txid , tx_hex = self . check_p2p_tx_violation ( )
2024-03-28 04:21:36 +01:00
self . log . info ( " Check that txs from RPC are not sent to blockrelay connection " )
conn = self . nodes [ 0 ] . add_outbound_p2p_connection ( P2PTxInvStore ( ) , p2p_idx = 1 , connection_type = " block-relay-only " )
self . nodes [ 0 ] . sendrawtransaction ( tx_hex )
2024-04-25 13:52:57 +02:00
# Bump time forward to ensure m_next_inv_send_time timer pops
2024-03-28 04:21:36 +01:00
self . nodes [ 0 ] . setmocktime ( int ( time . time ( ) ) + 60 )
2021-04-28 08:14:22 +02:00
conn . sync_send_with_ping ( )
2024-03-28 04:21:36 +01:00
assert ( int ( txid , 16 ) not in conn . get_invs ( ) )
2024-08-25 18:46:04 +02:00
def check_p2p_tx_violation ( self ) :
2024-03-28 04:21:36 +01:00
self . log . info ( ' Check that txs from P2P are rejected and result in disconnect ' )
2024-08-25 18:46:04 +02:00
spendtx = self . miniwallet . create_self_transfer ( from_node = self . nodes [ 0 ] )
2024-03-28 04:21:36 +01:00
with self . nodes [ 0 ] . assert_debug_log ( [ ' tx sent in violation of protocol peer=0 ' ] ) :
2021-05-06 16:52:45 +02:00
self . nodes [ 0 ] . p2ps [ 0 ] . send_message ( msg_tx ( spendtx [ ' tx ' ] ) )
2024-03-28 04:21:36 +01:00
self . nodes [ 0 ] . p2ps [ 0 ] . wait_for_disconnect ( )
assert_equal ( self . nodes [ 0 ] . getmempoolinfo ( ) [ ' size ' ] , 0 )
# Remove the disconnected peer
del self . nodes [ 0 ] . p2ps [ 0 ]
2021-05-06 16:52:45 +02:00
return spendtx [ ' tx ' ] , spendtx [ ' txid ' ] , spendtx [ ' hex ' ]
2024-03-28 04:21:36 +01:00
2019-05-16 18:44:54 +02:00
if __name__ == ' __main__ ' :
P2PBlocksOnly ( ) . main ( )