2018-11-03 23:50:34 +01:00
|
|
|
import logging
|
2019-02-12 22:50:43 +01:00
|
|
|
import asyncio
|
2019-06-03 21:47:08 +02:00
|
|
|
from unittest.mock import Mock
|
2018-10-17 19:32:45 +02:00
|
|
|
|
2019-06-03 21:03:20 +02:00
|
|
|
from torba.client.basenetwork import BaseNetwork
|
2019-06-03 10:21:57 +02:00
|
|
|
from torba.rpc import RPCSession
|
2019-06-03 21:47:08 +02:00
|
|
|
from torba.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)
|
|
|
|
|
|
|
|
|
2018-10-11 05:07:38 +02:00
|
|
|
class ReconnectTests(IntegrationTestCase):
|
|
|
|
|
2018-11-04 06:55:50 +01:00
|
|
|
VERBOSITY = logging.WARN
|
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
|
2018-10-17 19:32:45 +02:00
|
|
|
self.ledger.network.client.connection_lost(Exception())
|
2019-08-06 07:17:39 +02:00
|
|
|
self.assertFalse(self.ledger.network.is_connected)
|
2018-10-11 05:07:38 +02:00
|
|
|
sendtxid = await self.blockchain.send_to_address(address1, 1.1337)
|
2019-08-06 07:17:39 +02:00
|
|
|
await asyncio.wait_for(self.on_transaction_id(sendtxid), 1.0) # mempool
|
2018-10-11 05:07:38 +02:00
|
|
|
await self.blockchain.generate(1)
|
|
|
|
await self.on_transaction_id(sendtxid) # confirmed
|
|
|
|
|
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!
|
2018-10-17 19:32:45 +02:00
|
|
|
self.ledger.network.client.connection_lost(Exception())
|
2019-08-06 07:54:11 +02:00
|
|
|
with self.assertRaises((asyncio.TimeoutError, asyncio.CancelledError)):
|
2018-10-15 06:45:21 +02:00
|
|
|
await d
|
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)
|
|
|
|
# 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)
|
|
|
|
|
2019-06-03 21:47:08 +02:00
|
|
|
|
|
|
|
class ServerPickingTestCase(AsyncioTestCase):
|
2019-07-20 01:03:18 +02:00
|
|
|
|
|
|
|
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
|
2019-07-20 01:03:18 +02:00
|
|
|
class FakeSession(RPCSession):
|
|
|
|
async def handle_request(self, request):
|
|
|
|
await asyncio.sleep(latency)
|
|
|
|
return {"height": 1}
|
|
|
|
server = await self.loop.create_server(lambda: FakeSession(), host='127.0.0.1', port=port)
|
|
|
|
self.addCleanup(server.close)
|
|
|
|
return '127.0.0.1', port
|
2019-07-13 00:54:04 +02:00
|
|
|
|
2019-07-20 01:03:18 +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)
|
2019-07-13 00:54:04 +02:00
|
|
|
return '127.0.0.1', port
|
2019-06-03 21:03:20 +02:00
|
|
|
|
|
|
|
async def test_pick_fastest(self):
|
2019-06-03 21:47:08 +02:00
|
|
|
ledger = Mock(config={
|
|
|
|
'default_servers': [
|
2019-07-20 01:03:18 +02:00
|
|
|
# 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
|
|
|
],
|
2019-07-20 01:03:18 +02:00
|
|
|
'connect_timeout': 3
|
2019-06-03 21:47:08 +02:00
|
|
|
})
|
2019-06-03 21:03:20 +02:00
|
|
|
|
2019-06-03 21:47:08 +02:00
|
|
|
network = BaseNetwork(ledger)
|
2019-06-03 21:10:41 +02:00
|
|
|
self.addCleanup(network.stop)
|
2019-06-03 21:03:20 +02:00
|
|
|
asyncio.ensure_future(network.start())
|
2019-08-06 07:17:39 +02:00
|
|
|
await asyncio.wait_for(network.on_connected.first, timeout=1)
|
2019-06-03 21:03:20 +02:00
|
|
|
self.assertTrue(network.is_connected)
|
2019-06-03 21:47:08 +02:00
|
|
|
self.assertEqual(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(network.session_pool.available_sessions), 3)
|