Ejemplo n.º 1
0
 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)
Ejemplo n.º 2
0
    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)
Ejemplo n.º 3
0
 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)
Ejemplo n.º 4
0
    def _simulate_run(self, run_i, simulator):
        # XXX: the following was adapted from test_new_syncing_peer, it doesn't matter too much, but has good coverage
        #      of different behaviors that can be affected by non-determinism on the fullnode implementation

        self.log.debug(f'run{run_i}: simulator{run_i}')

        nodes = []
        miners = []
        tx_generators = []
        peer_id_pool = self.new_peer_id_pool()

        manager = self.create_simulator_peer(simulator, peer_id_pool)
        nodes.append(manager)
        miner = simulator.create_miner(manager, hashpower=10e6)
        miner.start()
        miners.append(miner)

        simulator.run(10)

        for i, hashpower in enumerate([10e6, 8e6, 5e6]):
            manager = self.create_simulator_peer(simulator, peer_id_pool)
            for node in nodes:
                conn = FakeConnection(manager, node, latency=0.085)
                simulator.add_connection(conn)
            nodes.append(manager)
            miner = simulator.create_miner(manager, hashpower=hashpower)
            miner.start()
            miners.append(miner)

        for i, rate in enumerate([5, 4, 3]):
            tx_gen = simulator.create_tx_generator(nodes[i], rate=rate * 1 / 60., hashpower=1e6, ignore_no_funds=True)
            tx_gen.start()
            tx_generators.append(tx_gen)

        simulator.run(10)

        self.log.debug(f'run{run_i}: adding late node')

        late_manager = self.create_simulator_peer(simulator, peer_id_pool)
        for node in nodes:
            conn = FakeConnection(late_manager, node, latency=0.300)
            simulator.add_connection(conn)
        nodes.append(late_manager)

        simulator.run(10)

        for tx_gen in tx_generators:
            tx_gen.stop()
        for miner in miners:
            miner.stop()

        simulator.run(10)
        return nodes
Ejemplo n.º 5
0
    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)
Ejemplo n.º 6
0
    def test_new_syncing_peer(self):
        nodes = []
        miners = []
        tx_generators = []

        manager = self.create_peer()
        nodes.append(manager)
        miner = self.simulator.create_miner(manager, hashpower=10e6)
        miner.start()
        miners.append(miner)
        self.simulator.run(600)

        for hashpower in [10e6, 8e6, 5e6]:
            manager = self.create_peer()
            for node in nodes:
                conn = FakeConnection(manager, node, latency=0.085)
                self.simulator.add_connection(conn)
            nodes.append(manager)

            miner = self.simulator.create_miner(manager, hashpower=hashpower)
            miner.start()
            miners.append(miner)

        for i, rate in enumerate([5, 4, 3]):
            tx_gen = self.simulator.create_tx_generator(nodes[i], rate=rate * 1 / 60., hashpower=1e6,
                                                        ignore_no_funds=True)
            tx_gen.start()
            tx_generators.append(tx_gen)

        self.simulator.run(600)

        self.log.debug('adding late node')
        late_manager = self.create_peer()
        for node in nodes:
            conn = FakeConnection(late_manager, node, latency=0.300)
            self.simulator.add_connection(conn)

        self.simulator.run(600)

        for tx_gen in tx_generators:
            tx_gen.stop()
        for miner in miners:
            miner.stop()

        self.simulator.run_until_complete(600)

        for idx, node in enumerate(nodes):
            self.log.debug(f'checking node {idx}')
            self.assertConsensusValid(node)
            self.assertConsensusEqual(node, late_manager)
Ejemplo n.º 7
0
    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')
Ejemplo n.º 8
0
    def test_many_miners_since_beginning(self):
        nodes = []
        miners = []

        for hashpower in [10e6, 5e6, 1e6, 1e6, 1e6]:
            manager = self.create_peer()
            for node in nodes:
                conn = FakeConnection(manager, node, latency=0.085)
                self.simulator.add_connection(conn)

            nodes.append(manager)

            miner = self.simulator.create_miner(manager, hashpower=hashpower)
            miner.start()
            miners.append(miner)

        self.simulator.run(600)

        for miner in miners:
            miner.stop()

        self.simulator.run(15)

        for node in nodes[1:]:
            self.assertTipsEqual(nodes[0], node)
Ejemplo n.º 9
0
    def test_two_nodes(self):
        manager1 = self.create_peer()
        manager2 = self.create_peer()

        miner1 = self.simulator.create_miner(manager1, hashpower=10e6)
        miner1.start()
        self.simulator.run(10)

        gen_tx1 = self.simulator.create_tx_generator(manager1, rate=3 / 60., hashpower=1e6, ignore_no_funds=True)
        gen_tx1.start()
        self.simulator.run(60)

        conn12 = FakeConnection(manager1, manager2, latency=0.150)
        self.simulator.add_connection(conn12)
        self.simulator.run(60)

        miner2 = self.simulator.create_miner(manager2, hashpower=100e6)
        miner2.start()
        self.simulator.run(120)

        gen_tx2 = self.simulator.create_tx_generator(manager2, rate=10 / 60., hashpower=1e6, ignore_no_funds=True)
        gen_tx2.start()
        self.simulator.run(10 * 60)

        miner1.stop()
        miner2.stop()
        gen_tx1.stop()
        gen_tx2.stop()

        self.simulator.run(5 * 60)

        self.assertTrue(conn12.is_connected)
        self.assertTipsEqual(manager1, manager2)
Ejemplo n.º 10
0
 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
Ejemplo n.º 11
0
    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)
Ejemplo n.º 12
0
    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)
Ejemplo n.º 13
0
    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))
Ejemplo n.º 14
0
    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)
Ejemplo n.º 15
0
    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()
Ejemplo n.º 16
0
    def test_split_brain(self):
        debug_pdf = False

        manager1 = self.create_peer()
        manager1.allow_mining_without_peers()

        manager2 = self.create_peer()
        manager2.allow_mining_without_peers()

        miner11 = self.simulator.create_miner(manager1, hashpower=10e6)
        miner11.start()
        gen_tx11 = self.simulator.create_tx_generator(manager1,
                                                      rate=10 / 60.,
                                                      hashpower=1e6,
                                                      ignore_no_funds=True)
        gen_tx11.start()
        gen_tx12 = self.simulator.create_tx_generator(manager1,
                                                      rate=10 / 60.,
                                                      hashpower=1e6,
                                                      ignore_no_funds=True)
        gen_tx12.enable_double_spending()
        gen_tx12.start()

        miner21 = self.simulator.create_miner(manager2, hashpower=10e6)
        miner21.start()
        gen_tx21 = self.simulator.create_tx_generator(manager2,
                                                      rate=10 / 60.,
                                                      hashpower=1e6,
                                                      ignore_no_funds=True)
        gen_tx21.start()
        gen_tx22 = self.simulator.create_tx_generator(manager2,
                                                      rate=10 / 60.,
                                                      hashpower=1e6,
                                                      ignore_no_funds=True)
        gen_tx22.enable_double_spending()
        gen_tx22.start()

        self.simulator.run(400)

        if debug_pdf:
            dot1 = GraphvizVisualizer(manager1.tx_storage,
                                      include_verifications=True,
                                      include_funds=True).dot()
            dot1.render('dot1-pre')
            dot2 = GraphvizVisualizer(manager2.tx_storage,
                                      include_verifications=True,
                                      include_funds=True).dot()
            dot2.render('dot2-pre')

        self.assertTipsNotEqual(manager1, manager2)
        self.assertConsensusValid(manager1)
        self.assertConsensusValid(manager2)

        # input('Press enter to continue...')

        miner11.stop()
        gen_tx11.stop()
        gen_tx12.stop()
        miner21.stop()
        gen_tx21.stop()
        gen_tx22.stop()

        conn12 = FakeConnection(manager1, manager2)
        self.simulator.add_connection(conn12)

        self.simulator.run(300)

        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 = conn12.proto1.state.sync_manager
        self.assertEqual(node_sync.synced_timestamp, node_sync.peer_timestamp)
        node_sync = conn12.proto2.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)
Ejemplo n.º 17
0
    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)
Ejemplo n.º 18
0
    def setUp(self):
        super().setUp()
        self.web = StubSite(StatusResource(self.manager))

        self.manager2 = self.create_peer('testnet')
        self.conn1 = FakeConnection(self.manager, self.manager2)
Ejemplo n.º 19
0
    def test_determinism_interleaved(self):
        # sanity assert as to not mess up with it on the setup
        self.assertEqual(self.simulator1.seed, self.simulator2.seed)

        # XXX: the following was adapted from test_new_syncing_peer, it doesn't matter too much, but has good coverage
        #      of different behaviors that can be affected by non-determinism on the fullnode implementation

        nodes1 = []
        nodes2 = []
        miners1 = []
        miners2 = []
        tx_generators1 = []
        tx_generators2 = []
        peer_id_pool1 = self.new_peer_id_pool()
        peer_id_pool2 = self.new_peer_id_pool()

        self.log.debug('part1 simulator1')
        manager1 = self.create_simulator_peer(self.simulator1, peer_id_pool1)
        nodes1.append(manager1)
        miner1 = self.simulator1.create_miner(manager1, hashpower=10e6)
        miner1.start()
        miners1.append(miner1)

        self.log.debug('part1 simulator2')
        manager2 = self.create_simulator_peer(self.simulator2, peer_id_pool2)
        nodes2.append(manager2)
        miner2 = self.simulator2.create_miner(manager2, hashpower=10e6)
        miner2.start()
        miners2.append(miner2)

        for _ in range(3):
            self.simulator1.run(10)
            self.simulator2.run(10)

            for idx, (node1, node2) in enumerate(zip(nodes1, nodes2)):
                self.log.debug(f'checking node {idx}')
                self.assertConsensusEqual(node1, node2)

        for i, hashpower in enumerate([10e6, 8e6, 5e6]):
            self.log.debug(f'part2.{i} simulator1')
            manager1 = self.create_simulator_peer(self.simulator1, peer_id_pool1)
            for node in nodes1:
                conn = FakeConnection(manager1, node, latency=0.085)
                self.simulator1.add_connection(conn)
            nodes1.append(manager1)
            miner1 = self.simulator1.create_miner(manager1, hashpower=hashpower)
            miner1.start()
            miners1.append(miner1)

            self.log.debug(f'part2.{i} simulator2')
            manager2 = self.create_simulator_peer(self.simulator2, peer_id_pool2)
            for node in nodes2:
                conn = FakeConnection(manager2, node, latency=0.085)
                self.simulator2.add_connection(conn)
            nodes2.append(manager2)
            miner2 = self.simulator2.create_miner(manager2, hashpower=hashpower)
            miner2.start()
            miners2.append(miner2)

        for i, rate in enumerate([5, 4, 3]):
            self.log.debug(f'part3.{i} simulator1')
            tx_gen1 = self.simulator1.create_tx_generator(nodes1[i], rate=rate * 1 / 60., hashpower=1e6,
                                                          ignore_no_funds=True)
            tx_gen1.start()
            tx_generators1.append(tx_gen1)

            self.log.debug(f'part3.{i} simulator2')
            tx_gen2 = self.simulator2.create_tx_generator(nodes2[i], rate=rate * 1 / 60., hashpower=1e6,
                                                          ignore_no_funds=True)
            tx_gen2.start()
            tx_generators2.append(tx_gen2)

        for _ in range(3):
            self.simulator1.run(10)
            self.simulator2.run(10)

            for idx, (node1, node2) in enumerate(zip(nodes1, nodes2)):
                self.log.debug(f'checking node {idx}')
                self.assertConsensusEqual(node1, node2)

        self.log.debug('adding late node')

        self.log.debug('part4 simulator1')
        late_manager1 = self.create_simulator_peer(self.simulator1, peer_id_pool1)
        for node in nodes1:
            conn = FakeConnection(late_manager1, node, latency=0.300)
            self.simulator1.add_connection(conn)
        nodes1.append(late_manager1)

        self.log.debug('part4 simulator2')
        late_manager2 = self.create_simulator_peer(self.simulator2, peer_id_pool2)
        for node in nodes2:
            conn = FakeConnection(late_manager2, node, latency=0.300)
            self.simulator2.add_connection(conn)
        nodes2.append(late_manager2)

        for _ in range(3):
            self.simulator1.run(10)
            self.simulator2.run(10)

            for idx, (node1, node2) in enumerate(zip(nodes1, nodes2)):
                self.log.debug(f'checking node {idx}')
                self.assertConsensusEqual(node1, node2)

        self.log.debug('part5 simulator1')
        for tx_gen in tx_generators1:
            tx_gen.stop()
        for miner in miners1:
            miner.stop()

        self.log.debug('part5 simulator2')
        for tx_gen in tx_generators2:
            tx_gen.stop()
        for miner in miners2:
            miner.stop()

        for _ in range(3):
            self.simulator1.run(10)
            self.simulator2.run(10)

            for idx, (node1, node2) in enumerate(zip(nodes1, nodes2)):
                self.log.debug(f'checking node {idx}')
                self.assertConsensusEqual(node1, node2)
Ejemplo n.º 20
0
    def test_soft_voided(self):
        txA_hash = bytes.fromhex(
            '4586c5428e8d666ea59684c1cd9286d2b9d9e89b4939207db47412eeaabc48b2')
        soft_voided_tx_ids = set([
            txA_hash,
        ])

        manager1 = self.create_peer(soft_voided_tx_ids=soft_voided_tx_ids)
        manager1.allow_mining_without_peers()

        miner1 = self.simulator.create_miner(manager1, hashpower=5e6)
        miner1.start()
        self.simulator.run(60)

        gen_tx1 = self.simulator.create_tx_generator(manager1,
                                                     rate=3 / 60.,
                                                     hashpower=1e6,
                                                     ignore_no_funds=True)
        gen_tx1.start()
        self.simulator.run(300)

        manager2 = self.create_peer(soft_voided_tx_ids=soft_voided_tx_ids)
        manager2.soft_voided_tx_ids = soft_voided_tx_ids

        graphviz = GraphvizVisualizer(manager2.tx_storage,
                                      include_verifications=True,
                                      include_funds=True)

        conn12 = FakeConnection(manager1, manager2, latency=0.001)
        self.simulator.add_connection(conn12)

        miner2 = self.simulator.create_miner(manager2, hashpower=10e6)
        miner2.start()
        gen_tx2 = self.simulator.create_tx_generator(manager2,
                                                     rate=10 / 60.,
                                                     hashpower=1e6,
                                                     ignore_no_funds=True)
        gen_tx2.start()
        self.simulator.run(900)

        txA = manager2.tx_storage.get_transaction(txA_hash)
        metaA = txA.get_metadata()
        self.assertEqual({settings.SOFT_VOIDED_ID, txA.hash}, metaA.voided_by)
        graphviz.labels[txA.hash] = 'txA'

        txB = add_custom_tx(manager2, [(txA, 0)])
        metaB = txB.get_metadata()
        self.assertEqual({txA.hash}, metaB.voided_by)
        graphviz.labels[txB.hash] = 'txB'

        txD1 = add_custom_tx(manager2, [(txB, 0)])
        metaD1 = txD1.get_metadata()
        self.assertEqual({txA.hash}, metaD1.voided_by)
        graphviz.labels[txD1.hash] = 'txD1'

        txD2 = add_custom_tx(manager2, [(txB, 0)])
        metaD2 = txD2.get_metadata()
        self.assertEqual({txA.hash, txD2.hash}, metaD2.voided_by)
        graphviz.labels[txD2.hash] = 'txD2'
        metaD1 = txD1.get_metadata()
        self.assertEqual({txA.hash, txD1.hash}, metaD1.voided_by)

        address = manager2.wallet.get_unused_address(mark_as_used=False)
        value = 1
        txC = gen_new_tx(manager2, address, value)
        txC.parents[0] = txA.hash
        txC.timestamp = max(txC.timestamp, txA.timestamp + 1)
        txC.weight = 25
        txC.update_hash()
        self.assertTrue(manager2.propagate_tx(txC, fails_silently=False))
        metaC = txC.get_metadata()
        self.assertIsNone(metaC.voided_by)
        graphviz.labels[txC.hash] = 'txC'

        blk1 = manager2.generate_mining_block()
        self.assertNoParentsAreSoftVoided(blk1)
        blk1.parents[1] = txA.hash
        blk1.nonce = self.rng.getrandbits(32)
        blk1.update_hash()
        self.assertTrue(manager2.propagate_tx(blk1, fails_silently=False))
        blk1meta = blk1.get_metadata()
        self.assertIsNone(blk1meta.voided_by)
        graphviz.labels[blk1.hash] = 'b1'

        blk2 = manager2.generate_mining_block()
        self.assertNoParentsAreSoftVoided(blk2)
        if txD1.hash not in blk2.parents:
            blk2.parents[1] = txD1.hash
        blk2.nonce = self.rng.getrandbits(32)
        blk2.update_hash()
        self.assertTrue(manager2.propagate_tx(blk2, fails_silently=False))
        blk2meta = blk2.get_metadata()
        self.assertIsNone(blk2meta.voided_by)
        graphviz.labels[blk2.hash] = 'b2'

        blk3 = manager2.generate_mining_block()
        self.assertNoParentsAreSoftVoided(blk3)
        blk3.parents[1] = txD2.hash
        blk3.nonce = self.rng.getrandbits(32)
        blk3.update_hash()
        self.assertTrue(manager2.propagate_tx(blk3, fails_silently=False))
        blk3meta = blk3.get_metadata()
        self.assertIsNone(blk3meta.voided_by)
        graphviz.labels[blk3.hash] = 'b3'

        for tx in manager1.tx_storage.get_all_transactions():
            meta = tx.get_metadata()
            voided_by = meta.voided_by or set()
            if settings.SOFT_VOIDED_ID in voided_by:
                self.assertTrue({settings.SOFT_VOIDED_ID,
                                 tx.hash}.issubset(voided_by))
Ejemplo n.º 21
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)
Ejemplo n.º 22
0
    def test_soft_voided(self):
        txA_hash = bytes.fromhex(
            '4586c5428e8d666ea59684c1cd9286d2b9d9e89b4939207db47412eeaabc48b2')
        soft_voided_tx_ids = set([
            txA_hash,
        ])

        manager1 = self.create_peer(soft_voided_tx_ids=soft_voided_tx_ids)
        manager1.allow_mining_without_peers()

        miner1 = self.simulator.create_miner(manager1, hashpower=5e6)
        miner1.start()
        self.simulator.run(60)

        gen_tx1 = self.simulator.create_tx_generator(manager1,
                                                     rate=3 / 60.,
                                                     hashpower=1e6,
                                                     ignore_no_funds=True)
        gen_tx1.start()
        self.simulator.run(300)

        manager2 = self.create_peer(soft_voided_tx_ids=soft_voided_tx_ids)
        manager2.soft_voided_tx_ids = soft_voided_tx_ids

        conn12 = FakeConnection(manager1, manager2, latency=0.001)
        self.simulator.add_connection(conn12)

        miner2 = self.simulator.create_miner(manager2, hashpower=10e6)
        miner2.start()

        gen_tx2 = self.simulator.create_tx_generator(manager2,
                                                     rate=10 / 60.,
                                                     hashpower=1e6,
                                                     ignore_no_funds=True)
        gen_tx2.start()
        self.simulator.run(900)
        miner2.stop()
        gen_tx2.stop()

        txA = manager2.tx_storage.get_transaction(txA_hash)
        metaA = txA.get_metadata()
        self.assertEqual({settings.SOFT_VOIDED_ID, txA.hash}, metaA.voided_by)

        txB = add_custom_tx(manager2, [(txA, 0)])
        metaB = txB.get_metadata()
        self.assertEqual({txA.hash}, metaB.voided_by)

        txD1 = add_custom_tx(manager2, [(txB, 0)])
        metaD1 = txD1.get_metadata()
        self.assertEqual({txA.hash}, metaD1.voided_by)

        blk1 = manager2.generate_mining_block()
        self.assertNoParentsAreSoftVoided(blk1)
        if txD1.hash not in blk1.parents:
            blk1.parents[1] = txD1.hash
        blk1.timestamp = txD1.timestamp + 1
        blk1.nonce = self.rng.getrandbits(32)
        blk1.update_hash()
        self.assertTrue(manager2.propagate_tx(blk1, fails_silently=False))
        blk1meta = blk1.get_metadata()
        self.assertIsNone(blk1meta.voided_by)

        self.simulator.run(10)
        address = manager2.wallet.get_unused_address(mark_as_used=True)
        txC = gen_new_tx(manager2, address, 6400)
        if txD1.hash not in txC.parents:
            txC.parents[1] = txD1.hash
        txC.weight = 25
        txC.update_hash()
        manager2.propagate_tx(txC, fails_silently=False)
        metaC = txC.get_metadata()
        self.assertIsNone(metaC.voided_by)

        txD2 = gen_custom_tx(manager2, [(txB, 0)])
        txD2.timestamp = txD1.timestamp + 2
        txD2.update_hash()
        manager2.propagate_tx(txD2, fails_silently=False)

        blk1meta = blk1.get_metadata()
        self.assertIsNone(blk1meta.voided_by)
        metaC = txC.get_metadata()
        self.assertIsNone(metaC.voided_by)
Ejemplo n.º 23
0
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()
Ejemplo n.º 24
0
 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)
Ejemplo n.º 25
0
    def test_soft_voided(self):
        txA_hash = bytes.fromhex('4586c5428e8d666ea59684c1cd9286d2b9d9e89b4939207db47412eeaabc48b2')
        txB_hash = bytes.fromhex('d19bee149abdce63bbfd523c90c3cf110563970a34100b2a62583a1eba48dfc8')
        soft_voided_tx_ids = set([
            txA_hash,
            txB_hash,
        ])

        manager1 = self.create_peer(soft_voided_tx_ids=soft_voided_tx_ids)
        manager1.allow_mining_without_peers()

        miner1 = self.simulator.create_miner(manager1, hashpower=5e6)
        miner1.start()
        self.simulator.run(60)

        gen_tx1 = self.simulator.create_tx_generator(manager1, rate=3 / 60., hashpower=1e6, ignore_no_funds=True)
        gen_tx1.start()
        self.simulator.run(300)

        manager2 = self.create_peer(soft_voided_tx_ids=soft_voided_tx_ids)
        manager2.soft_voided_tx_ids = soft_voided_tx_ids

        self.graphviz = GraphvizVisualizer(manager2.tx_storage, include_verifications=True, include_funds=True)

        conn12 = FakeConnection(manager1, manager2, latency=0.001)
        self.simulator.add_connection(conn12)

        miner2 = self.simulator.create_miner(manager2, hashpower=10e6)
        miner2.start()

        gen_tx2 = self.simulator.create_tx_generator(manager2, rate=10 / 60., hashpower=1e6, ignore_no_funds=True)
        gen_tx2.start()
        self.simulator.run(900)
        miner2.stop()
        gen_tx2.stop()

        txB = manager2.tx_storage.get_transaction(txB_hash)

        # Get the tx confirmed by the soft voided that will be voided
        tx_base = manager2.tx_storage.get_transaction(txB.parents[0])
        txC = gen_new_double_spending(manager2, use_same_parents=False, tx=tx_base)
        txC.weight = 25
        txC.parents = tx_base.parents
        txC.update_hash()
        self.graphviz.labels[txC.hash] = 'txC'
        self.assertTrue(manager2.propagate_tx(txC, fails_silently=False))
        metaC = txC.get_metadata()
        self.assertIsNone(metaC.voided_by)

        meta_base = tx_base.get_metadata()
        self.assertEqual(meta_base.voided_by, {tx_base.hash})

        # Create 2 blocks confirming C in order to keep this voidance when we add
        # the block confirming the soft voided tx
        blk1 = manager2.generate_mining_block()
        if txC.hash not in blk1.parents:
            blk1.parents[1] = txC.hash
        blk1.nonce = self.rng.getrandbits(32)
        blk1.update_hash()
        self.assertTrue(manager2.propagate_tx(blk1, fails_silently=False))
        blk1meta = blk1.get_metadata()
        self.assertIsNone(blk1meta.voided_by)

        blk2 = manager2.generate_mining_block()
        if txC.hash not in blk2.parents:
            blk2.parents[1] = txC.hash
        blk2.nonce = self.rng.getrandbits(32)
        blk2.update_hash()
        self.assertTrue(manager2.propagate_tx(blk2, fails_silently=False))
        blk2meta = blk2.get_metadata()
        self.assertIsNone(blk2meta.voided_by)

        # Create block that confirms soft voided
        blk3 = manager2.generate_mining_block()
        if txB.hash not in blk3.parents:
            blk3.parents[1] = txB.hash
        blk3.nonce = self.rng.getrandbits(32)
        blk3.update_hash()
        self.assertTrue(manager2.propagate_tx(blk3, fails_silently=False))
        blk3meta = blk3.get_metadata()

        self.simulator.run(10)
        txD = add_custom_tx(manager2, [(txC, 0)], base_parent=txB)

        # dot = self.graphviz.dot()
        # dot.render('dot0')

        blk3meta = blk3.get_metadata()
        self.assertEqual(blk3meta.voided_by, {tx_base.hash, blk3meta.hash})
        metaD = txD.get_metadata()
        self.assertEqual(metaD.voided_by, {tx_base.hash})
Ejemplo n.º 26
0
    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)
Ejemplo n.º 27
0
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'])