def test_invalid_same_peer_id(self): manager3 = self.create_peer(self.network, peer_id=self.peer_id1) conn = FakeConnection(self.manager1, manager3) conn.run_one_step() # HELLO conn.run_one_step() # PEER-ID self._check_result_only_cmd(conn.peek_tr1_value(), b'ERROR') self.assertTrue(conn.tr1.disconnecting)
def test_block_sync_new_blocks_and_txs(self): self._add_new_blocks(25) self._add_new_transactions(3) self._add_new_blocks(4) self._add_new_transactions(5) manager2 = self.create_peer(self.network) self.assertEqual(manager2.state, manager2.NodeState.READY) conn = FakeConnection(self.manager1, manager2) for _ in range(1000): conn.run_one_step() self.clock.advance(0.1) # dot1 = self.manager1.tx_storage.graphviz(format='pdf') # dot1.render('dot1') # dot2 = manager2.tx_storage.graphviz(format='pdf') # dot2.render('dot2') node_sync = conn.proto1.state.sync_manager self.assertEqual(self.manager1.tx_storage.latest_timestamp, manager2.tx_storage.latest_timestamp) self.assertEqual(node_sync.synced_timestamp, node_sync.peer_timestamp) self.assertTipsEqual(self.manager1, manager2) self.assertConsensusEqual(self.manager1, manager2) self.assertConsensusValid(self.manager1) self.assertConsensusValid(manager2)
def test_match_peer_id(self): network = 'testnet' peer_id1 = PeerId() peer_id2 = PeerId() manager1 = self.create_peer(network, peer_id=peer_id1) manager2 = self.create_peer(network, peer_id=peer_id2) conn = FakeConnection(manager1, manager2) self.assertTrue(conn.proto2.is_state(conn.proto2.PeerState.HELLO)) matcher = NetfilterMatchPeerId(str(peer_id1.id)) context = NetfilterContext(protocol=conn.proto2) self.assertFalse(matcher.match(context)) conn.run_one_step() self.assertTrue(conn.proto2.is_state(conn.proto2.PeerState.PEER_ID)) self.assertFalse(matcher.match(context)) # Success because the connection is ready and proto2 is connected to proto1. conn.run_one_step() conn.run_one_step() self.assertTrue(conn.proto2.is_state(conn.proto2.PeerState.READY)) self.assertTrue(matcher.match(context)) # Fail because proto1 is connected to proto2, and the peer id cannot match. context = NetfilterContext(protocol=conn.proto1) self.assertFalse(matcher.match(context))
def test_invalid_different_network(self): manager3 = self.create_peer(network='mainnet') conn = FakeConnection(self.manager1, manager3) conn.run_one_step() # HELLO self._check_result_only_cmd(conn.peek_tr1_value(), b'ERROR') self.assertTrue(conn.tr1.disconnecting) conn.run_one_step() # ERROR
def test_capabilities(self): network = 'testnet' manager1 = self.create_peer( network, capabilities=[settings.CAPABILITY_WHITELIST]) manager2 = self.create_peer(network, capabilities=[]) conn = FakeConnection(manager1, manager2) # Run the p2p protocol. for _ in range(100): conn.run_one_step(debug=True) self.clock.advance(0.1) # Even if we don't have the capability we must connect because the whitelist url conf is None self.assertEqual(conn._proto1.state.state_name, 'READY') self.assertEqual(conn._proto2.state.state_name, 'READY') manager3 = self.create_peer( network, capabilities=[settings.CAPABILITY_WHITELIST]) manager4 = self.create_peer( network, capabilities=[settings.CAPABILITY_WHITELIST]) conn2 = FakeConnection(manager3, manager4) # Run the p2p protocol. for _ in range(100): conn2.run_one_step(debug=True) self.clock.advance(0.1) self.assertEqual(conn2._proto1.state.state_name, 'READY') self.assertEqual(conn2._proto2.state.state_name, 'READY')
def test_block_sync_only_genesis(self): manager2 = self.create_peer(self.network) self.assertEqual(manager2.state, manager2.NodeState.READY) conn = FakeConnection(self.manager1, manager2) conn.run_one_step() # HELLO conn.run_one_step() # PEER-ID conn.run_one_step() # READY node_sync = conn.proto1.state.sync_manager self.assertEqual(node_sync.synced_timestamp, node_sync.peer_timestamp) self.assertTipsEqual(self.manager1, manager2)
def test_two_connections(self): self.conn.run_one_step() # HELLO self.conn.run_one_step() # PEER-ID self.conn.run_one_step() # READY self.conn.run_one_step() # GET-PEERS self.conn.run_one_step() # GET-TIPS manager3 = self.create_peer(self.network) conn = FakeConnection(self.manager1, manager3) conn.run_one_step() # HELLO conn.run_one_step() # PEER-ID conn.run_one_step() # READY conn.run_one_step() # GET-PEERS self._check_result_only_cmd(self.conn.peek_tr1_value(), b'PEERS') self.conn.run_one_step()
def test_block_sync_many_new_blocks(self): self._add_new_blocks(150) manager2 = self.create_peer(self.network) self.assertEqual(manager2.state, manager2.NodeState.READY) conn = FakeConnection(self.manager1, manager2) while not conn.is_empty(): conn.run_one_step(debug=True) self.clock.advance(0.1) node_sync = conn.proto1.state.sync_manager self.assertEqual(node_sync.synced_timestamp, node_sync.peer_timestamp) self.assertTipsEqual(self.manager1, manager2) self.assertConsensusEqual(self.manager1, manager2) self.assertConsensusValid(self.manager1) self.assertConsensusValid(manager2)
def test_mempool_basic(self): # 10 blocks self._add_new_blocks(2) # N blocks to unlock the reward add_blocks_unlock_reward(self.manager1) # 5 transactions to be confirmed by the next blocks self._add_new_transactions(5) # 2 more blocks self._add_new_blocks(2) # 30 transactions in the mempool self._add_new_transactions(30) debug_pdf = False if debug_pdf: dot1 = GraphvizVisualizer(self.manager1.tx_storage, include_verifications=True, include_funds=True).dot() dot1.render('mempool-test') manager2 = self.create_peer(self.network, enable_sync_v1=True) self.assertEqual(manager2.state, manager2.NodeState.READY) conn = FakeConnection(self.manager1, manager2) for _ in range(1000): if conn.is_empty(): break conn.run_one_step(debug=True) self.clock.advance(1) self.assertConsensusValid(self.manager1) self.assertConsensusValid(manager2) self.assertConsensusEqual(self.manager1, manager2) # 3 genesis # 25 blocks # Unlock reward blocks # 8 txs self.assertEqual(len(manager2.tx_storage.indexes.mempool_tips.get()), 1) self.assertEqual( len(self.manager1.tx_storage.indexes.mempool_tips.get()), 1)
def test_downloader(self): from hathor.p2p.node_sync import NodeSyncTimestamp blocks = self._add_new_blocks(3) manager2 = self.create_peer(self.network) self.assertEqual(manager2.state, manager2.NodeState.READY) conn = FakeConnection(self.manager1, manager2) # Get to PEER-ID state only because when it gets to READY it will automatically sync conn.run_one_step() self.assertTrue(isinstance(conn.proto1.state, PeerIdState)) self.assertTrue(isinstance(conn.proto2.state, PeerIdState)) downloader = conn.proto2.connections._sync_factories[ SyncVersion.V1].downloader node_sync1 = NodeSyncTimestamp(conn.proto1, downloader, reactor=conn.proto1.node.reactor) node_sync1.start() node_sync2 = NodeSyncTimestamp(conn.proto2, downloader, reactor=conn.proto2.node.reactor) node_sync2.start() self.assertTrue(isinstance(conn.proto1.state, PeerIdState)) self.assertTrue(isinstance(conn.proto2.state, PeerIdState)) deferred1 = downloader.get_tx(blocks[0].hash, node_sync1) deferred1.addCallback(node_sync1.on_tx_success) self.assertEqual(len(downloader.pending_transactions), 1) details = downloader.pending_transactions[blocks[0].hash] self.assertEqual(len(details.connections), 1) self.assertEqual(len(downloader.downloading_deque), 1) deferred2 = downloader.get_tx(blocks[0].hash, node_sync2) deferred2.addCallback(node_sync2.on_tx_success) self.assertEqual(len(downloader.pending_transactions), 1) self.assertEqual( len(downloader.pending_transactions[blocks[0].hash].connections), 2) self.assertEqual(len(downloader.downloading_deque), 1) self.assertEqual(deferred1, deferred2) details.downloading_deferred.callback(blocks[0]) self.assertEqual(len(downloader.downloading_deque), 0) self.assertEqual(len(downloader.pending_transactions), 0) # Getting tx already downloaded downloader.get_tx(blocks[0].hash, node_sync1) self.assertEqual(len(downloader.downloading_deque), 0) # Adding fake tx_id to downloading deque downloader.downloading_deque.append('1') # Getting new tx downloader.get_tx(blocks[1].hash, node_sync1) self.assertEqual(len(downloader.pending_transactions), 1) details = downloader.pending_transactions[blocks[1].hash] self.assertEqual(len(details.connections), 1) self.assertEqual(len(downloader.downloading_deque), 2) details.downloading_deferred.callback(blocks[1]) # Still 2 elements because the first one is not downloaded yet self.assertEqual(len(downloader.downloading_deque), 2) # Remove it downloader.downloading_deque.popleft() # And try again downloader.check_downloading_queue() self.assertEqual(len(downloader.downloading_deque), 0)
class BaseHathorSyncMethodsTestCase(unittest.TestCase): __test__ = False def setUp(self): super().setUp() # import sys # from twisted.python import log # log.startLogging(sys.stdout) self.network = 'testnet' self.manager1 = self.create_peer(self.network, unlock_wallet=True) self.manager1.avg_time_between_blocks = 4 self.genesis = self.manager1.tx_storage.get_all_genesis() self.genesis_blocks = [tx for tx in self.genesis if tx.is_block] def _add_new_tx(self, address, value): from hathor.transaction import Transaction from hathor.wallet.base_wallet import WalletOutputInfo outputs = [] outputs.append( WalletOutputInfo(address=decode_address(address), value=int(value), timelock=None)) tx = self.manager1.wallet.prepare_transaction_compute_inputs( Transaction, outputs, self.manager1.tx_storage) tx.timestamp = int(self.clock.seconds()) tx.storage = self.manager1.tx_storage tx.weight = 10 tx.parents = self.manager1.get_new_tx_parents() tx.resolve() tx.verify() self.manager1.propagate_tx(tx) self.clock.advance(10) return tx def _add_new_transactions(self, num_txs): txs = [] for _ in range(num_txs): address = self.get_address(0) value = random.choice([5, 10, 50, 100, 120]) tx = self._add_new_tx(address, value) txs.append(tx) return txs def _add_new_block(self, propagate=True): block = self.manager1.generate_mining_block() self.assertTrue(block.resolve()) block.verify() self.manager1.on_new_tx(block, propagate_to_peers=propagate) self.clock.advance(10) return block def _add_new_blocks(self, num_blocks, propagate=True): blocks = [] for _ in range(num_blocks): blocks.append(self._add_new_block(propagate=propagate)) return blocks def test_get_blocks_before(self): genesis_block = self.genesis_blocks[0] result = self.manager1.tx_storage.get_blocks_before(genesis_block.hash) self.assertEqual(0, len(result)) genesis_tx = [tx for tx in self.genesis if not tx.is_block][0] with self.assertRaises(TransactionIsNotABlock): self.manager1.tx_storage.get_blocks_before(genesis_tx.hash) blocks = self._add_new_blocks(20) num_blocks = 5 for i, block in enumerate(blocks): result = self.manager1.tx_storage.get_blocks_before( block.hash, num_blocks=num_blocks) expected_result = [genesis_block] + blocks[:i] expected_result = expected_result[-num_blocks:] expected_result = expected_result[::-1] self.assertEqual(result, expected_result) def test_block_sync_only_genesis(self): manager2 = self.create_peer(self.network) self.assertEqual(manager2.state, manager2.NodeState.READY) conn = FakeConnection(self.manager1, manager2) conn.run_one_step() # HELLO conn.run_one_step() # PEER-ID conn.run_one_step() # READY node_sync = conn.proto1.state.sync_manager self.assertEqual(node_sync.synced_timestamp, node_sync.peer_timestamp) self.assertTipsEqual(self.manager1, manager2) def test_block_sync_new_blocks(self): self._add_new_blocks(15) manager2 = self.create_peer(self.network) self.assertEqual(manager2.state, manager2.NodeState.READY) conn = FakeConnection(self.manager1, manager2) for _ in range(10000): if conn.is_empty(): break conn.run_one_step(debug=True) self.clock.advance(0.1) node_sync = conn.proto1.state.sync_manager self.assertEqual(node_sync.synced_timestamp, node_sync.peer_timestamp) self.assertTipsEqual(self.manager1, manager2) self.assertConsensusEqual(self.manager1, manager2) self.assertConsensusValid(self.manager1) self.assertConsensusValid(manager2) def test_block_sync_many_new_blocks(self): self._add_new_blocks(150) manager2 = self.create_peer(self.network) self.assertEqual(manager2.state, manager2.NodeState.READY) conn = FakeConnection(self.manager1, manager2) while not conn.is_empty(): conn.run_one_step(debug=True) self.clock.advance(0.1) node_sync = conn.proto1.state.sync_manager self.assertEqual(node_sync.synced_timestamp, node_sync.peer_timestamp) self.assertTipsEqual(self.manager1, manager2) self.assertConsensusEqual(self.manager1, manager2) self.assertConsensusValid(self.manager1) self.assertConsensusValid(manager2) def test_block_sync_new_blocks_and_txs(self): self._add_new_blocks(25) self._add_new_transactions(3) self._add_new_blocks(4) self._add_new_transactions(5) manager2 = self.create_peer(self.network) self.assertEqual(manager2.state, manager2.NodeState.READY) conn = FakeConnection(self.manager1, manager2) for _ in range(1000): conn.run_one_step() self.clock.advance(0.1) # dot1 = self.manager1.tx_storage.graphviz(format='pdf') # dot1.render('dot1') # dot2 = manager2.tx_storage.graphviz(format='pdf') # dot2.render('dot2') node_sync = conn.proto1.state.sync_manager self.assertEqual(self.manager1.tx_storage.latest_timestamp, manager2.tx_storage.latest_timestamp) self.assertEqual(node_sync.synced_timestamp, node_sync.peer_timestamp) self.assertTipsEqual(self.manager1, manager2) self.assertConsensusEqual(self.manager1, manager2) self.assertConsensusValid(self.manager1) self.assertConsensusValid(manager2) def test_tx_propagation_nat_peers(self): """ manager1 <- manager2 <- manager3 """ self._add_new_blocks(25) self.manager2 = self.create_peer(self.network) self.conn1 = FakeConnection(self.manager1, self.manager2) for _ in range(1000): if self.conn1.is_empty(): break self.conn1.run_one_step() self.clock.advance(0.1) self.assertTipsEqual(self.manager1, self.manager2) self._add_new_blocks(1) for _ in range(1000): if self.conn1.is_empty(): break self.conn1.run_one_step() self.clock.advance(0.1) self.assertTipsEqual(self.manager1, self.manager2) self.manager3 = self.create_peer(self.network) self.conn2 = FakeConnection(self.manager2, self.manager3) for _ in range(1000): if self.conn1.is_empty() and self.conn2.is_empty(): break self.conn1.run_one_step() self.conn2.run_one_step() self.clock.advance(0.1) self.assertTipsEqual(self.manager1, self.manager2) self.assertTipsEqual(self.manager1, self.manager3) self._add_new_transactions(1) for _ in range(1000): if self.conn1.is_empty() and self.conn2.is_empty(): break self.conn1.run_one_step() self.conn2.run_one_step() self.clock.advance(0.1) self.assertTipsEqual(self.manager1, self.manager2) self.assertTipsEqual(self.manager1, self.manager3) self.assertConsensusEqual(self.manager1, self.manager2) self.assertConsensusEqual(self.manager1, self.manager3) self.assertConsensusValid(self.manager1) self.assertConsensusValid(self.manager2) self.assertConsensusValid(self.manager3)
def test_split_brain(self): debug_pdf = False manager1 = self.create_peer(self.network, unlock_wallet=True) manager1.avg_time_between_blocks = 3 manager2 = self.create_peer(self.network, unlock_wallet=True) manager2.avg_time_between_blocks = 3 for _ in range(10): add_new_block(manager1, advance_clock=1) add_blocks_unlock_reward(manager1) add_new_block(manager2, advance_clock=1) add_blocks_unlock_reward(manager2) self.clock.advance(10) for _ in range(random.randint(3, 10)): add_new_transactions(manager1, random.randint(2, 4), advance_clock=1) add_new_transactions(manager2, random.randint(3, 7), advance_clock=1) add_new_double_spending(manager1) add_new_double_spending(manager2) self.clock.advance(10) self.clock.advance(20) if debug_pdf: dot1 = GraphvizVisualizer(manager1.tx_storage, include_verifications=True).dot() dot1.render('dot1-pre') self.assertTipsNotEqual(manager1, manager2) self.assertConsensusValid(manager1) self.assertConsensusValid(manager2) # input('Press enter to continue...') conn = FakeConnection(manager1, manager2) conn.run_one_step() # HELLO conn.run_one_step() # PEER-ID conn.run_one_step() # READY conn.run_one_step() # GET-PEERS conn.run_one_step() # GET-TIPS conn.run_one_step() # PEERS conn.run_one_step() # TIPS empty_counter = 0 for i in range(2000): if conn.is_empty(): empty_counter += 1 if empty_counter > 10: break else: empty_counter = 0 conn.run_one_step() self.clock.advance(0.2) if debug_pdf: dot1 = GraphvizVisualizer(manager1.tx_storage, include_verifications=True).dot() dot1.render('dot1-post') dot2 = GraphvizVisualizer(manager2.tx_storage, include_verifications=True).dot() dot2.render('dot2-post') node_sync = conn.proto1.state.sync_manager self.assertEqual(node_sync.synced_timestamp, node_sync.peer_timestamp) self.assertTipsEqual(manager1, manager2) self.assertConsensusEqual(manager1, manager2) self.assertConsensusValid(manager1) self.assertConsensusValid(manager2)
def test_invalid_same_peer_id2(self): """ We connect nodes 1-2 and 1-3. Nodes 2 and 3 have the same peer_id. The connections are established simultaneously, so we do not detect a peer id duplication in PEER_ID state, only on READY state. """ # Disable idle timeout before creating any new peer because self.create_peer(...) # runs the main loop. self.conn.disable_idle_timeout() # Create new peer and disable idle timeout. manager3 = self.create_peer(self.network, peer_id=self.peer_id2) conn = FakeConnection(manager3, self.manager1) # Disable idle timeout. conn.disable_idle_timeout() # HELLO self.assertEqual(self.conn.peek_tr1_value().split()[0], b'HELLO') self.assertEqual(self.conn.peek_tr2_value().split()[0], b'HELLO') self.assertEqual(conn.peek_tr1_value().split()[0], b'HELLO') self.assertEqual(conn.peek_tr2_value().split()[0], b'HELLO') self.conn.run_one_step() conn.run_one_step() # PEER-ID self.assertEqual(self.conn.peek_tr1_value().split()[0], b'PEER-ID') self.assertEqual(self.conn.peek_tr2_value().split()[0], b'PEER-ID') self.assertEqual(conn.peek_tr1_value().split()[0], b'PEER-ID') self.assertEqual(conn.peek_tr2_value().split()[0], b'PEER-ID') self.conn.run_one_step() conn.run_one_step() # READY self.assertEqual(self.conn.peek_tr1_value().split()[0], b'READY') self.assertEqual(self.conn.peek_tr2_value().split()[0], b'READY') self.assertEqual(conn.peek_tr1_value().split()[0], b'READY') self.assertEqual(conn.peek_tr2_value().split()[0], b'READY') self.conn.run_one_step() conn.run_one_step() # continue until messages stop self.conn.run_until_complete() conn.run_until_complete() self.run_to_completion() # one of the peers will close the connection. We don't know which on, as it depends # on the peer ids conn1_value = self.conn.peek_tr1_value() + self.conn.peek_tr2_value() conn2_value = conn.peek_tr1_value() + conn.peek_tr2_value() if b'ERROR' in conn1_value: conn_dead = self.conn conn_alive = conn elif b'ERROR' in conn2_value: conn_dead = conn conn_alive = self.conn else: raise Exception('It should never happen.') self._check_result_only_cmd( conn_dead.peek_tr1_value() + conn_dead.peek_tr2_value(), b'ERROR') # at this point, the connection must be closing as the error was detected on READY state self.assertIn( True, [conn_dead.tr1.disconnecting, conn_dead.tr2.disconnecting]) # check connected_peers connected_peers = list( self.manager1.connections.connected_peers.values()) self.assertEquals(1, len(connected_peers)) self.assertIn(connected_peers[0], [conn_alive.proto1, conn_alive.proto2]) # connection is still up self.assertIsConnected(conn_alive)
class BaseHathorProtocolTestCase(unittest.TestCase): __test__ = False def setUp(self): super().setUp() self.network = 'testnet' self.peer_id1 = PeerId() self.peer_id2 = PeerId() self.manager1 = self.create_peer(self.network, peer_id=self.peer_id1) self.manager2 = self.create_peer(self.network, peer_id=self.peer_id2) self.conn = FakeConnection(self.manager1, self.manager2) def assertIsConnected(self, conn=None): if conn is None: conn = self.conn self.assertFalse(conn.tr1.disconnecting) self.assertFalse(conn.tr2.disconnecting) def assertIsNotConnected(self, conn=None): if conn is None: conn = self.conn self.assertTrue(conn.tr1.disconnecting) self.assertTrue(conn.tr2.disconnecting) def _send_cmd(self, proto, cmd, payload=None): if not payload: line = '{}\r\n'.format(cmd) else: line = '{} {}\r\n'.format(cmd, payload) if isinstance(line, str): line = line.encode('utf-8') return proto.dataReceived(line) def _check_result_only_cmd(self, result, expected_cmd): cmd_list = [] for line in result.split(b'\r\n'): cmd, _, _ = line.partition(b' ') cmd_list.append(cmd) self.assertIn(expected_cmd, cmd_list) def _check_cmd_and_value(self, result, expected): result_list = [] for line in result.split(b'\r\n'): cmd, _, data = line.partition(b' ') result_list.append((cmd, data)) self.assertIn(expected, result_list) def test_on_connect(self): self._check_result_only_cmd(self.conn.peek_tr1_value(), b'HELLO') def test_invalid_command(self): self._send_cmd(self.conn.proto1, 'INVALID-CMD') self.conn.proto1.state.handle_error('') self.assertTrue(self.conn.tr1.disconnecting) def test_rate_limit(self): hits = 1 window = 60 self.conn.proto1.ratelimit.set_limit( HathorProtocol.RateLimitKeys.GLOBAL, hits, window) # first will be OK and reach the hits limit per window self.conn.run_one_step() # HELLO # second will fail and be throttled self.conn.run_one_step() # PEER-ID self._check_cmd_and_value( self.conn.peek_tr1_value(), (b'THROTTLE', 'global At most {} hits every {} seconds'.format( hits, window).encode('utf-8')), ) self.conn.proto1.state.handle_throttle(b'') # Test empty disconnect self.conn.proto1.state = None self.conn.proto1.connections = None self.conn.proto1.on_disconnect(Failure(Exception())) def test_invalid_size(self): self.conn.tr1.clear() # Creating big payload big_payload = '[' for x in range(65536): big_payload = '{}{}'.format(big_payload, x) big_payload = '{}]'.format(big_payload) self._send_cmd(self.conn.proto1, 'HELLO', big_payload) self.assertTrue(self.conn.tr1.disconnecting) def test_invalid_payload(self): self.conn.run_one_step() # HELLO self.conn.run_one_step() # PEER-ID self.conn.run_one_step() # READY with self.assertRaises(json.decoder.JSONDecodeError): self._send_cmd(self.conn.proto1, 'PEERS', 'abc') def test_invalid_hello1(self): self.conn.tr1.clear() self._send_cmd(self.conn.proto1, 'HELLO') self._check_result_only_cmd(self.conn.peek_tr1_value(), b'ERROR') self.assertTrue(self.conn.tr1.disconnecting) def test_invalid_hello2(self): self.conn.tr1.clear() self._send_cmd(self.conn.proto1, 'HELLO', 'invalid_payload') self._check_result_only_cmd(self.conn.peek_tr1_value(), b'ERROR') self.assertTrue(self.conn.tr1.disconnecting) def test_invalid_hello3(self): self.conn.tr1.clear() self._send_cmd(self.conn.proto1, 'HELLO', '{}') self._check_result_only_cmd(self.conn.peek_tr1_value(), b'ERROR') self.assertTrue(self.conn.tr1.disconnecting) def test_invalid_hello4(self): self.conn.tr1.clear() self._send_cmd( self.conn.proto1, 'HELLO', '{"app": 0, "remote_address": 1, "network": 2, "genesis_hash": "123", "settings_hash": "456"}' ) self._check_result_only_cmd(self.conn.peek_tr1_value(), b'ERROR') self.assertTrue(self.conn.tr1.disconnecting) def test_invalid_hello5(self): # hello with clocks too far apart self.conn.tr1.clear() data = self.conn.proto2.state._get_hello_data() data['timestamp'] = data[ 'timestamp'] + settings.MAX_FUTURE_TIMESTAMP_ALLOWED / 2 + 1 self._send_cmd(self.conn.proto1, 'HELLO', json.dumps(data)) self._check_result_only_cmd(self.conn.peek_tr1_value(), b'ERROR') self.assertTrue(self.conn.tr1.disconnecting) def test_valid_hello(self): self.conn.run_one_step() # HELLO self._check_result_only_cmd(self.conn.peek_tr1_value(), b'PEER-ID') self._check_result_only_cmd(self.conn.peek_tr2_value(), b'PEER-ID') self.assertFalse(self.conn.tr1.disconnecting) self.assertFalse(self.conn.tr2.disconnecting) @inlineCallbacks def test_invalid_peer_id(self): self.conn.run_one_step() # HELLO self.conn.run_one_step() # PEER-ID self.conn.run_one_step() # READY self.conn.run_one_step() # GET-PEERS self.conn.run_one_step() # GET-TIPS self.conn.run_one_step() # PEERS self.conn.run_one_step() # TIPS invalid_payload = { 'id': '123', 'entrypoints': ['tcp://localhost:1234'] } yield self._send_cmd(self.conn.proto1, 'PEER-ID', json.dumps(invalid_payload)) self._check_result_only_cmd(self.conn.peek_tr1_value(), b'ERROR') self.assertTrue(self.conn.tr1.disconnecting) def test_invalid_same_peer_id(self): manager3 = self.create_peer(self.network, peer_id=self.peer_id1) conn = FakeConnection(self.manager1, manager3) conn.run_one_step() # HELLO conn.run_one_step() # PEER-ID self._check_result_only_cmd(conn.peek_tr1_value(), b'ERROR') self.assertTrue(conn.tr1.disconnecting) def test_invalid_same_peer_id2(self): """ We connect nodes 1-2 and 1-3. Nodes 2 and 3 have the same peer_id. The connections are established simultaneously, so we do not detect a peer id duplication in PEER_ID state, only on READY state. """ # Disable idle timeout before creating any new peer because self.create_peer(...) # runs the main loop. self.conn.disable_idle_timeout() # Create new peer and disable idle timeout. manager3 = self.create_peer(self.network, peer_id=self.peer_id2) conn = FakeConnection(manager3, self.manager1) # Disable idle timeout. conn.disable_idle_timeout() # HELLO self.assertEqual(self.conn.peek_tr1_value().split()[0], b'HELLO') self.assertEqual(self.conn.peek_tr2_value().split()[0], b'HELLO') self.assertEqual(conn.peek_tr1_value().split()[0], b'HELLO') self.assertEqual(conn.peek_tr2_value().split()[0], b'HELLO') self.conn.run_one_step() conn.run_one_step() # PEER-ID self.assertEqual(self.conn.peek_tr1_value().split()[0], b'PEER-ID') self.assertEqual(self.conn.peek_tr2_value().split()[0], b'PEER-ID') self.assertEqual(conn.peek_tr1_value().split()[0], b'PEER-ID') self.assertEqual(conn.peek_tr2_value().split()[0], b'PEER-ID') self.conn.run_one_step() conn.run_one_step() # READY self.assertEqual(self.conn.peek_tr1_value().split()[0], b'READY') self.assertEqual(self.conn.peek_tr2_value().split()[0], b'READY') self.assertEqual(conn.peek_tr1_value().split()[0], b'READY') self.assertEqual(conn.peek_tr2_value().split()[0], b'READY') self.conn.run_one_step() conn.run_one_step() # continue until messages stop self.conn.run_until_complete() conn.run_until_complete() self.run_to_completion() # one of the peers will close the connection. We don't know which on, as it depends # on the peer ids conn1_value = self.conn.peek_tr1_value() + self.conn.peek_tr2_value() conn2_value = conn.peek_tr1_value() + conn.peek_tr2_value() if b'ERROR' in conn1_value: conn_dead = self.conn conn_alive = conn elif b'ERROR' in conn2_value: conn_dead = conn conn_alive = self.conn else: raise Exception('It should never happen.') self._check_result_only_cmd( conn_dead.peek_tr1_value() + conn_dead.peek_tr2_value(), b'ERROR') # at this point, the connection must be closing as the error was detected on READY state self.assertIn( True, [conn_dead.tr1.disconnecting, conn_dead.tr2.disconnecting]) # check connected_peers connected_peers = list( self.manager1.connections.connected_peers.values()) self.assertEquals(1, len(connected_peers)) self.assertIn(connected_peers[0], [conn_alive.proto1, conn_alive.proto2]) # connection is still up self.assertIsConnected(conn_alive) def test_invalid_different_network(self): manager3 = self.create_peer(network='mainnet') conn = FakeConnection(self.manager1, manager3) conn.run_one_step() # HELLO self._check_result_only_cmd(conn.peek_tr1_value(), b'ERROR') self.assertTrue(conn.tr1.disconnecting) conn.run_one_step() # ERROR def test_valid_hello_and_peer_id(self): self._check_result_only_cmd(self.conn.peek_tr1_value(), b'HELLO') self._check_result_only_cmd(self.conn.peek_tr2_value(), b'HELLO') self.conn.run_one_step() # HELLO self._check_result_only_cmd(self.conn.peek_tr1_value(), b'PEER-ID') self._check_result_only_cmd(self.conn.peek_tr2_value(), b'PEER-ID') self.conn.run_one_step() # PEER-ID self._check_result_only_cmd(self.conn.peek_tr1_value(), b'READY') self._check_result_only_cmd(self.conn.peek_tr2_value(), b'READY') self.conn.run_one_step() # READY self._check_result_only_cmd(self.conn.peek_tr1_value(), b'GET-PEERS') self._check_result_only_cmd(self.conn.peek_tr2_value(), b'GET-PEERS') self.conn.run_one_step() # GET-PEERS self._check_result_only_cmd(self.conn.peek_tr1_value(), b'GET-TIPS') self._check_result_only_cmd(self.conn.peek_tr2_value(), b'GET-TIPS') self.conn.run_one_step() # GET-TIPS self.assertIsConnected() self._check_result_only_cmd(self.conn.peek_tr1_value(), b'PEERS') self._check_result_only_cmd(self.conn.peek_tr2_value(), b'PEERS') self.conn.run_one_step() # PEERS self._check_result_only_cmd(self.conn.peek_tr1_value(), b'TIPS') self._check_result_only_cmd(self.conn.peek_tr2_value(), b'TIPS') self.conn.run_one_step() # TIPS self.assertIsConnected() def test_send_ping(self): self.conn.run_one_step() # HELLO self.conn.run_one_step() # PEER-ID self.conn.run_one_step() # READY self.conn.run_one_step() # GET-PEERS self.conn.run_one_step() # GET-TIPS self.conn.run_one_step() # PEERS self.conn.run_one_step() # TIPS self.assertIsConnected() self.clock.advance(5) self.assertEqual(b'PING\r\n', self.conn.peek_tr1_value()) self.assertEqual(b'PING\r\n', self.conn.peek_tr2_value()) self.conn.run_one_step() # PING self.conn.run_one_step() # GET-TIPS self.assertEqual(b'PONG\r\n', self.conn.peek_tr1_value()) self.assertEqual(b'PONG\r\n', self.conn.peek_tr2_value()) while b'PONG\r\n' in self.conn.peek_tr1_value(): self.conn.run_one_step() self.assertEqual(self.clock.seconds(), self.conn.proto1.last_message) def test_send_invalid_unicode(self): # \xff is an invalid unicode. self.conn.proto1.dataReceived(b'\xff\r\n') self.assertTrue(self.conn.tr1.disconnecting) def test_on_disconnect(self): self.assertIn(self.conn.proto1, self.manager1.connections.handshaking_peers) self.conn.disconnect(Failure(Exception('testing'))) self.assertNotIn(self.conn.proto1, self.manager1.connections.handshaking_peers) def test_on_disconnect_after_hello(self): self.conn.run_one_step() # HELLO self.assertIn(self.conn.proto1, self.manager1.connections.handshaking_peers) self.conn.disconnect(Failure(Exception('testing'))) self.assertNotIn(self.conn.proto1, self.manager1.connections.handshaking_peers) def test_on_disconnect_after_peer_id(self): self.conn.run_one_step() # HELLO self.assertIn(self.conn.proto1, self.manager1.connections.handshaking_peers) # No peer id in the peer_storage (known_peers) self.assertNotIn(self.peer_id2.id, self.manager1.connections.peer_storage) # The peer READY now depends on a message exchange from both peers, so we need one more step self.conn.run_one_step() # PEER-ID self.conn.run_one_step() # READY self.assertIn(self.conn.proto1, self.manager1.connections.connected_peers.values()) # Peer id 2 in the peer_storage (known_peers) after connection self.assertIn(self.peer_id2.id, self.manager1.connections.peer_storage) self.assertNotIn(self.conn.proto1, self.manager1.connections.handshaking_peers) self.conn.disconnect(Failure(Exception('testing'))) # Peer id 2 in the peer_storage (known_peers) after disconnection but before looping call self.assertIn(self.peer_id2.id, self.manager1.connections.peer_storage) self.assertNotIn(self.conn.proto1, self.manager1.connections.connected_peers.values()) self.clock.advance(10) # Peer id 2 removed from peer_storage (known_peers) after disconnection and after looping call self.assertNotIn(self.peer_id2.id, self.manager1.connections.peer_storage) def test_two_connections(self): self.conn.run_one_step() # HELLO self.conn.run_one_step() # PEER-ID self.conn.run_one_step() # READY self.conn.run_one_step() # GET-PEERS self.conn.run_one_step() # GET-TIPS manager3 = self.create_peer(self.network) conn = FakeConnection(self.manager1, manager3) conn.run_one_step() # HELLO conn.run_one_step() # PEER-ID conn.run_one_step() # READY conn.run_one_step() # GET-PEERS self._check_result_only_cmd(self.conn.peek_tr1_value(), b'PEERS') self.conn.run_one_step() def test_idle_connection(self): self.clock.advance(settings.PEER_IDLE_TIMEOUT - 10) self.assertIsConnected(self.conn) self.clock.advance(15) self.assertIsNotConnected(self.conn) @inlineCallbacks def test_get_data(self): self.conn.run_one_step() # HELLO self.conn.run_one_step() # PEER-ID self.conn.run_one_step() # READY self.conn.run_one_step() # GET-PEERS self.conn.run_one_step() # GET-TIPS self.conn.run_one_step() # PEERS self.conn.run_one_step() # TIPS self.assertIsConnected() missing_tx = '00000000228dfcd5dec1c9c6263f6430a5b4316bb9e3decb9441a6414bfd8697' yield self._send_cmd(self.conn.proto1, 'GET-DATA', missing_tx) self._check_result_only_cmd(self.conn.peek_tr1_value(), b'NOT-FOUND') self.conn.run_one_step()
class BaseStatusTest(_BaseResourceTest._ResourceTest): __test__ = False def setUp(self): super().setUp() self.web = StubSite(StatusResource(self.manager)) self.manager2 = self.create_peer('testnet') self.conn1 = FakeConnection(self.manager, self.manager2) @inlineCallbacks def test_get(self): response = yield self.web.get("status") data = response.json_value() server_data = data.get('server') self.assertEqual(server_data['app_version'], 'Hathor v{}'.format(hathor.__version__)) self.assertEqual(server_data['network'], 'testnet') self.assertGreater(server_data['uptime'], 0) @inlineCallbacks def test_handshaking(self): response = yield self.web.get("status") data = response.json_value() server_data = data.get('server') known_peers = data.get('known_peers') connections = data.get('connections') self.assertEqual(server_data['app_version'], 'Hathor v{}'.format(hathor.__version__)) self.assertEqual(server_data['network'], 'testnet') self.assertGreater(server_data['uptime'], 0) handshake_peer = self.conn1.proto1.transport.getPeer() handshake_address = '{}:{}'.format(handshake_peer.host, handshake_peer.port) self.assertEqual(len(known_peers), 0) self.assertEqual(len(connections['connected_peers']), 0) self.assertEqual(len(connections['handshaking_peers']), 1) self.assertEqual(connections['handshaking_peers'][0]['address'], handshake_address) @inlineCallbacks def test_get_with_one_peer(self): self.conn1.run_one_step() # HELLO self.conn1.run_one_step() # PEER-ID self.conn1.run_one_step() # READY self.conn1.run_one_step() # BOTH PEERS ARE READY NOW response = yield self.web.get("status") data = response.json_value() server_data = data.get('server') known_peers = data.get('known_peers') connections = data.get('connections') self.assertEqual(server_data['app_version'], 'Hathor v{}'.format(hathor.__version__)) self.assertEqual(server_data['network'], 'testnet') self.assertGreater(server_data['uptime'], 0) self.assertEqual(len(known_peers), 1) self.assertEqual(known_peers[0]['id'], self.manager2.my_peer.id) self.assertEqual(len(connections['connected_peers']), 1) self.assertEqual(connections['connected_peers'][0]['id'], self.manager2.my_peer.id) @inlineCallbacks def test_connecting_peers(self): address = '192.168.1.1:54321' endpoint = endpoints.clientFromString(self.manager.reactor, 'tcp:{}'.format(address)) deferred = endpoint.connect self.manager.connections.connecting_peers[endpoint] = deferred response = yield self.web.get("status") data = response.json_value() connecting = data['connections']['connecting_peers'] self.assertEqual(len(connecting), 1) self.assertEqual(connecting[0]['address'], address) self.assertIsNotNone(connecting[0]['deferred'])