mirror of
https://github.com/dashpay/dash.git
synced 2024-12-26 04:22:55 +01:00
Merge #14571: [tests] Test that nodes respond to getdata with notfound
fa78a2fc67 [tests] Test that nodes respond to getdata with notfound (MarcoFalke) Pull request description: If a node has not announced a tx at all, then it should respond to getdata messages for that tx with notfound, to avoid leaking tx origination privacy. In the future this could be adjusted such that a node responds with notfound when a tx has not been announced to us, but that seems to be a more involved change. See e.g. https://github.com/jnewbery/bitcoin/commits/pr14220.1 Tree-SHA512: 6244afa5bd5d8fec9b89dfc02c9958bc370195145a0f3715f33200d6cf73a376c94193d44bf4523867196e6591c53ede8f9b6a77cb296b48c114a117b8c8b1fa
This commit is contained in:
parent
edf0552c0c
commit
6e0fdc7321
57
test/functional/p2p_leak_tx.py
Executable file
57
test/functional/p2p_leak_tx.py
Executable file
@ -0,0 +1,57 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# Copyright (c) 2017-2018 The Bitcoin Core developers
|
||||||
|
# Distributed under the MIT software license, see the accompanying
|
||||||
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
"""Test that we don't leak txs to inbound peers that we haven't yet announced to"""
|
||||||
|
|
||||||
|
from test_framework.messages import msg_getdata, CInv
|
||||||
|
from test_framework.mininode import P2PDataStore
|
||||||
|
from test_framework.test_framework import BitcoinTestFramework
|
||||||
|
from test_framework.util import (
|
||||||
|
assert_equal,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class P2PNode(P2PDataStore):
|
||||||
|
def on_inv(self, msg):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class P2PLeakTxTest(BitcoinTestFramework):
|
||||||
|
def set_test_params(self):
|
||||||
|
self.num_nodes = 1
|
||||||
|
|
||||||
|
def skip_test_if_missing_module(self):
|
||||||
|
self.skip_if_no_wallet()
|
||||||
|
|
||||||
|
def run_test(self):
|
||||||
|
gen_node = self.nodes[0] # The block and tx generating node
|
||||||
|
gen_node.generate(1)
|
||||||
|
|
||||||
|
inbound_peer = self.nodes[0].add_p2p_connection(P2PNode()) # An "attacking" inbound peer
|
||||||
|
|
||||||
|
MAX_REPEATS = 100
|
||||||
|
self.log.info("Running test up to {} times.".format(MAX_REPEATS))
|
||||||
|
for i in range(MAX_REPEATS):
|
||||||
|
self.log.info('Run repeat {}'.format(i + 1))
|
||||||
|
txid = gen_node.sendtoaddress(gen_node.getnewaddress(), 0.01)
|
||||||
|
|
||||||
|
want_tx = msg_getdata()
|
||||||
|
want_tx.inv.append(CInv(t=1, h=int(txid, 16)))
|
||||||
|
inbound_peer.last_message.pop('notfound', None)
|
||||||
|
inbound_peer.send_message(want_tx)
|
||||||
|
inbound_peer.sync_with_ping()
|
||||||
|
|
||||||
|
if inbound_peer.last_message.get('notfound'):
|
||||||
|
self.log.debug('tx {} was not yet announced to us.'.format(txid))
|
||||||
|
self.log.debug("node has responded with a notfound message. End test.")
|
||||||
|
assert_equal(inbound_peer.last_message['notfound'].vec[0].hash, int(txid, 16))
|
||||||
|
inbound_peer.last_message.pop('notfound')
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
self.log.debug('tx {} was already announced to us. Try test again.'.format(txid))
|
||||||
|
assert int(txid, 16) in [inv.hash for inv in inbound_peer.last_message['inv'].inv]
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
P2PLeakTxTest().main()
|
@ -1397,6 +1397,23 @@ class msg_mempool():
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "msg_mempool()"
|
return "msg_mempool()"
|
||||||
|
|
||||||
|
class msg_notfound:
|
||||||
|
__slots__ = ("vec", )
|
||||||
|
command = b"notfound"
|
||||||
|
|
||||||
|
def __init__(self, vec=None):
|
||||||
|
self.vec = vec or []
|
||||||
|
|
||||||
|
def deserialize(self, f):
|
||||||
|
self.vec = deser_vector(f, CInv)
|
||||||
|
|
||||||
|
def serialize(self):
|
||||||
|
return ser_vector(self.vec)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "msg_notfound(vec=%s)" % (repr(self.vec))
|
||||||
|
|
||||||
|
|
||||||
class msg_sendheaders():
|
class msg_sendheaders():
|
||||||
command = b"sendheaders"
|
command = b"sendheaders"
|
||||||
|
|
||||||
|
@ -23,7 +23,42 @@ import sys
|
|||||||
import time
|
import time
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
from test_framework.messages import CBlockHeader, MIN_VERSION_SUPPORTED, msg_addr, msg_addrv2, msg_block, msg_blocktxn, msg_clsig, msg_cmpctblock, msg_getaddr, msg_getblocks, msg_getblocktxn, msg_getdata, msg_getheaders, msg_getmnlistd, msg_headers, msg_inv, msg_islock, msg_mempool, msg_mnlistdiff, msg_ping, msg_pong, msg_qdata, msg_qgetdata, msg_reject, msg_sendaddrv2, msg_sendcmpct, msg_sendheaders, msg_tx, msg_verack, msg_version, MY_SUBVERSION, NODE_NETWORK, sha256
|
from test_framework.messages import (
|
||||||
|
CBlockHeader,
|
||||||
|
MIN_VERSION_SUPPORTED,
|
||||||
|
msg_addr,
|
||||||
|
msg_addrv2,
|
||||||
|
msg_block,
|
||||||
|
msg_blocktxn,
|
||||||
|
msg_clsig,
|
||||||
|
msg_cmpctblock,
|
||||||
|
msg_getaddr,
|
||||||
|
msg_getblocks,
|
||||||
|
msg_getblocktxn,
|
||||||
|
msg_getdata,
|
||||||
|
msg_getheaders,
|
||||||
|
msg_getmnlistd,
|
||||||
|
msg_headers,
|
||||||
|
msg_inv,
|
||||||
|
msg_islock,
|
||||||
|
msg_mempool,
|
||||||
|
msg_mnlistdiff,
|
||||||
|
msg_notfound,
|
||||||
|
msg_ping,
|
||||||
|
msg_pong,
|
||||||
|
msg_qdata,
|
||||||
|
msg_qgetdata,
|
||||||
|
msg_reject,
|
||||||
|
msg_sendaddrv2,
|
||||||
|
msg_sendcmpct,
|
||||||
|
msg_sendheaders,
|
||||||
|
msg_tx,
|
||||||
|
msg_verack,
|
||||||
|
msg_version,
|
||||||
|
MY_SUBVERSION,
|
||||||
|
NODE_NETWORK,
|
||||||
|
sha256,
|
||||||
|
)
|
||||||
from test_framework.util import wait_until
|
from test_framework.util import wait_until
|
||||||
|
|
||||||
MSG_TX = 1
|
MSG_TX = 1
|
||||||
@ -62,7 +97,7 @@ MESSAGEMAP = {
|
|||||||
b"govsync": None,
|
b"govsync": None,
|
||||||
b"islock": msg_islock,
|
b"islock": msg_islock,
|
||||||
b"mnlistdiff": msg_mnlistdiff,
|
b"mnlistdiff": msg_mnlistdiff,
|
||||||
b"notfound": None,
|
b"notfound": msg_notfound,
|
||||||
b"qfcommit": None,
|
b"qfcommit": None,
|
||||||
b"qsendrecsigs": None,
|
b"qsendrecsigs": None,
|
||||||
b"qgetdata": msg_qgetdata,
|
b"qgetdata": msg_qgetdata,
|
||||||
@ -338,6 +373,7 @@ class P2PInterface(P2PConnection):
|
|||||||
def on_getheaders(self, message): pass
|
def on_getheaders(self, message): pass
|
||||||
def on_headers(self, message): pass
|
def on_headers(self, message): pass
|
||||||
def on_mempool(self, message): pass
|
def on_mempool(self, message): pass
|
||||||
|
def on_notfound(self, message): pass
|
||||||
def on_pong(self, message): pass
|
def on_pong(self, message): pass
|
||||||
def on_reject(self, message): pass
|
def on_reject(self, message): pass
|
||||||
def on_sendaddrv2(self, message): pass
|
def on_sendaddrv2(self, message): pass
|
||||||
|
@ -154,6 +154,7 @@ BASE_SCRIPTS = [
|
|||||||
'feature_versionbits_warning.py',
|
'feature_versionbits_warning.py',
|
||||||
'rpc_preciousblock.py',
|
'rpc_preciousblock.py',
|
||||||
'wallet_importprunedfunds.py',
|
'wallet_importprunedfunds.py',
|
||||||
|
'p2p_leak_tx.py',
|
||||||
'rpc_signmessage.py',
|
'rpc_signmessage.py',
|
||||||
'feature_nulldummy.py',
|
'feature_nulldummy.py',
|
||||||
'mempool_accept.py',
|
'mempool_accept.py',
|
||||||
|
Loading…
Reference in New Issue
Block a user