lbry-sdk/tests/integration/blockchain/test_network.py

216 lines
9.8 KiB
Python
Raw Normal View History

2019-02-12 22:50:43 +01:00
import asyncio
import lbry
2019-06-03 21:47:08 +02:00
from unittest.mock import Mock
2018-10-17 19:32:45 +02:00
2020-01-03 08:40:37 +01:00
from lbry.wallet.network import Network
from lbry.wallet.orchstr8 import Conductor
from lbry.wallet.orchstr8.node import SPVNode
from lbry.wallet.rpc import RPCSession
2021-01-21 22:20:01 +01:00
from lbry.wallet.server.udp import StatusServer
2019-12-31 21:30:13 +01:00
from lbry.testcase import IntegrationTestCase, AsyncioTestCase
2018-10-11 05:07:38 +02:00
2019-06-26 08:41:35 +02:00
class NetworkTests(IntegrationTestCase):
async def test_remote_height_updated_automagically(self):
initial_height = self.ledger.network.remote_height
await self.blockchain.generate(1)
await self.ledger.network.on_header.first
self.assertEqual(self.ledger.network.remote_height, initial_height + 1)
async def test_server_features(self):
2019-10-08 17:01:38 +02:00
self.assertDictEqual({
2019-09-17 21:30:27 +02:00
'genesis_hash': self.conductor.spv_node.coin_class.GENESIS_HASH,
'hash_function': 'sha256',
'hosts': {},
2020-01-06 17:33:07 +01:00
'protocol_max': '0.99.0',
'protocol_min': '0.54.0',
2019-09-17 21:30:27 +02:00
'pruning': None,
'description': '',
'payment_address': '',
'donation_address': '',
'daily_fee': '0',
2020-03-04 02:26:07 +01:00
'server_version': lbry.__version__,
'trending_algorithm': 'zscore',
}, await self.ledger.network.get_server_features())
2021-01-16 22:27:42 +01:00
# await self.conductor.spv_node.stop()
payment_address, donation_address = await self.account.get_addresses(limit=2)
2021-01-16 22:27:42 +01:00
self.conductor.spv_node.server.env.payment_address = payment_address
self.conductor.spv_node.server.env.donation_address = donation_address
self.conductor.spv_node.server.env.description = 'Fastest server in the west.'
self.conductor.spv_node.server.env.daily_fee = '42'
from lbry.wallet.server.session import LBRYElectrumX
LBRYElectrumX.set_server_features(self.conductor.spv_node.server.env)
# await self.ledger.network.on_connected.first
2019-10-08 17:01:38 +02:00
self.assertDictEqual({
2019-09-17 21:30:27 +02:00
'genesis_hash': self.conductor.spv_node.coin_class.GENESIS_HASH,
'hash_function': 'sha256',
'hosts': {},
2020-01-06 17:33:07 +01:00
'protocol_max': '0.99.0',
'protocol_min': '0.54.0',
2019-09-17 21:30:27 +02:00
'pruning': None,
'description': 'Fastest server in the west.',
'payment_address': payment_address,
'donation_address': donation_address,
'daily_fee': '42',
2020-03-04 02:26:07 +01:00
'server_version': lbry.__version__,
'trending_algorithm': 'zscore',
}, await self.ledger.network.get_server_features())
2019-06-26 08:41:35 +02:00
2020-03-30 23:02:08 +02:00
class ReconnectTests(IntegrationTestCase):
2018-10-11 05:07:38 +02:00
async def test_multiple_servers(self):
# we have a secondary node that connects later, so
node2 = SPVNode(self.conductor.spv_module, node_number=2)
await node2.start(self.blockchain)
2021-01-21 22:20:01 +01:00
self.ledger.network.config['default_servers'].append((node2.hostname, node2.port))
self.ledger.network.config['default_servers'].reverse()
self.assertEqual(50002, self.ledger.network.client.server[1])
await self.ledger.stop()
await self.ledger.start()
self.assertTrue(self.ledger.network.is_connected)
2021-01-21 22:20:01 +01:00
self.assertEqual(50003, self.ledger.network.client.server[1])
await node2.stop(True)
self.assertFalse(self.ledger.network.is_connected)
await self.ledger.resolve([], ['derp'])
self.assertEqual(50002, self.ledger.network.client.server[1])
2019-10-09 02:45:47 +02:00
async def test_direct_sync(self):
await self.ledger.stop()
2019-10-09 03:17:09 +02:00
initial_height = self.ledger.local_height_including_downloaded_height
2019-10-09 02:45:47 +02:00
await self.blockchain.generate(100)
2020-01-14 02:48:07 +01:00
while self.conductor.spv_node.server.session_mgr.notified_height < initial_height + 99: # off by 1
2019-10-09 02:45:47 +02:00
await asyncio.sleep(0.1)
2019-10-09 03:17:09 +02:00
self.assertEqual(initial_height, self.ledger.local_height_including_downloaded_height)
2020-03-21 08:32:03 +01:00
await self.ledger.headers.open()
await self.ledger.network.start()
await self.ledger.network.on_connected.first
await self.ledger.initial_headers_sync()
self.assertEqual(initial_height + 100, self.ledger.local_height_including_downloaded_height)
2019-10-09 02:45:47 +02:00
2018-10-11 05:07:38 +02:00
async def test_connection_drop_still_receives_events_after_reconnected(self):
2018-10-15 06:45:21 +02:00
address1 = await self.account.receiving.get_or_create_usable_address()
2019-08-06 07:17:39 +02:00
# disconnect and send a new tx, should reconnect and get it
2021-01-21 22:20:01 +01:00
self.ledger.network.client.transport.close()
2019-08-06 07:17:39 +02:00
self.assertFalse(self.ledger.network.is_connected)
2021-01-21 22:20:01 +01:00
await self.ledger.resolve([], 'derp')
2018-10-11 05:07:38 +02:00
sendtxid = await self.blockchain.send_to_address(address1, 1.1337)
2021-01-21 22:20:01 +01:00
# await self.ledger.resolve([], 'derp')
# self.assertTrue(self.ledger.network.is_connected)
await asyncio.wait_for(self.on_transaction_id(sendtxid), 10.0) # mempool
2018-10-11 05:07:38 +02:00
await self.blockchain.generate(1)
await self.on_transaction_id(sendtxid) # confirmed
2019-08-12 23:16:53 +02:00
self.assertLess(self.ledger.network.client.response_time, 1) # response time properly set lower, we are fine
2018-10-11 05:07:38 +02:00
2018-11-19 05:17:39 +01:00
await self.assertBalance(self.account, '1.1337')
2018-10-11 05:07:38 +02:00
# is it real? are we rich!? let me see this tx...
d = self.ledger.network.get_transaction(sendtxid)
# what's that smoke on my ethernet cable? oh no!
master_client = self.ledger.network.client
2018-10-17 19:32:45 +02:00
self.ledger.network.client.connection_lost(Exception())
2019-08-12 23:16:53 +02:00
with self.assertRaises(asyncio.TimeoutError):
await d
self.assertIsNone(master_client.response_time) # response time unknown as it failed
2018-10-11 05:07:38 +02:00
# rich but offline? no way, no water, let's retry
with self.assertRaisesRegex(ConnectionError, 'connection is not available'):
2018-10-15 06:45:21 +02:00
await self.ledger.network.get_transaction(sendtxid)
2018-10-11 05:07:38 +02:00
# * goes to pick some water outside... * time passes by and another donation comes in
sendtxid = await self.blockchain.send_to_address(address1, 42)
await self.blockchain.generate(1)
2019-10-02 20:04:30 +02:00
# (this is just so the test doesn't hang forever if it doesn't reconnect)
2019-08-12 18:32:20 +02:00
if not self.ledger.network.is_connected:
2021-01-21 22:20:01 +01:00
await asyncio.wait_for(self.ledger.network.on_connected.first, timeout=10.0)
2018-10-11 05:07:38 +02:00
# omg, the burned cable still works! torba is fire proof!
2018-10-15 06:45:21 +02:00
await self.ledger.network.get_transaction(sendtxid)
2019-02-12 22:50:43 +01:00
async def test_timeout_then_reconnect(self):
2019-08-06 07:17:39 +02:00
# tests that it connects back after some failed attempts
2019-06-04 07:34:58 +02:00
await self.conductor.spv_node.stop()
self.assertFalse(self.ledger.network.is_connected)
2019-08-06 07:17:39 +02:00
await asyncio.sleep(0.2) # let it retry and fail once
2019-06-04 07:34:58 +02:00
await self.conductor.spv_node.start(self.conductor.blockchain_node)
await self.ledger.network.on_connected.first
2019-06-03 10:21:57 +02:00
self.assertTrue(self.ledger.network.is_connected)
2021-01-21 22:20:01 +01:00
# async def test_online_but_still_unavailable(self):
# # Edge case. See issue #2445 for context
# self.assertIsNotNone(self.ledger.network.session_pool.fastest_session)
# for session in self.ledger.network.session_pool.sessions:
# session.response_time = None
# self.assertIsNone(self.ledger.network.session_pool.fastest_session)
2019-06-03 21:47:08 +02:00
class UDPServerFailDiscoveryTest(AsyncioTestCase):
async def test_wallet_connects_despite_lack_of_udp(self):
conductor = Conductor()
conductor.spv_node.udp_port = '0'
await conductor.start_blockchain()
self.addCleanup(conductor.stop_blockchain)
await conductor.start_spv()
self.addCleanup(conductor.stop_spv)
self.assertFalse(conductor.spv_node.server.bp.status_server.is_running)
await asyncio.wait_for(conductor.start_wallet(), timeout=5)
self.addCleanup(conductor.stop_wallet)
2021-03-12 18:35:39 +01:00
self.assertTrue(conductor.wallet_node.ledger.network.is_connected)
2019-06-03 21:47:08 +02:00
class ServerPickingTestCase(AsyncioTestCase):
2021-01-21 22:20:01 +01:00
async def _make_udp_server(self, port):
s = StatusServer()
await s.start(0, b'\x00' * 32, '127.0.0.1', port)
self.addCleanup(s.stop)
async def _make_fake_server(self, latency=1.0, port=1):
2019-06-03 21:03:20 +02:00
# local fake server with artificial latency
class FakeSession(RPCSession):
async def handle_request(self, request):
await asyncio.sleep(latency)
if request.method == 'server.version':
return tuple(request.args)
return {'height': 1}
server = await self.loop.create_server(lambda: FakeSession(), host='127.0.0.1', port=port)
self.addCleanup(server.close)
2021-01-21 22:20:01 +01:00
await self._make_udp_server(port)
return '127.0.0.1', port
2019-07-13 00:54:04 +02:00
async def _make_bad_server(self, port=42420):
async def echo(reader, writer):
while True:
writer.write(await reader.read())
server = await asyncio.start_server(echo, host='127.0.0.1', port=port)
2019-06-03 21:03:20 +02:00
self.addCleanup(server.close)
2021-01-21 22:20:01 +01:00
await self._make_udp_server(port)
2019-07-13 00:54:04 +02:00
return '127.0.0.1', port
2019-06-03 21:03:20 +02:00
2021-01-21 22:20:01 +01:00
async def _test_pick_fastest(self):
2019-06-03 21:47:08 +02:00
ledger = Mock(config={
'default_servers': [
# fast but unhealthy, should be discarded
await self._make_bad_server(),
('localhost', 1),
('example.that.doesnt.resolve', 9000),
2019-08-06 07:17:39 +02:00
await self._make_fake_server(latency=1.0, port=1340),
await self._make_fake_server(latency=0.1, port=1337),
await self._make_fake_server(latency=0.4, port=1339),
2019-06-03 21:47:08 +02:00
],
'connect_timeout': 3
2019-06-03 21:47:08 +02:00
})
2019-06-03 21:03:20 +02:00
2020-01-03 08:40:37 +01:00
network = Network(ledger)
self.addCleanup(network.stop)
2021-01-21 22:20:01 +01:00
await network.start()
await asyncio.wait_for(network.on_connected.first, timeout=10)
2019-06-03 21:03:20 +02:00
self.assertTrue(network.is_connected)
2019-10-08 17:01:38 +02:00
self.assertTupleEqual(network.client.server, ('127.0.0.1', 1337))
2019-08-06 07:17:39 +02:00
self.assertTrue(all([not session.is_closing() for session in network.session_pool.available_sessions]))
# ensure we are connected to all of them after a while
await asyncio.sleep(1)
self.assertEqual(len(list(network.session_pool.available_sessions)), 3)