Example #1
0
    def _setup_other_components(self):
        log.debug("Setting up the rest of the components")

        if self.rate_limiter is None:
            self.rate_limiter = RateLimiter()

        if self.blob_manager is None:
            if self.blob_dir is None:
                self.blob_manager = TempBlobManager(self.hash_announcer)
            else:
                self.blob_manager = DiskBlobManager(self.hash_announcer,
                                                    self.blob_dir,
                                                    self.db_dir)

        if self.blob_tracker is None:
            self.blob_tracker = self.blob_tracker_class(self.blob_manager,
                                                        self.peer_finder,
                                                        self.dht_node)
        if self.payment_rate_manager is None:
            self.payment_rate_manager = self.payment_rate_manager_class(
                self.base_payment_rate_manager,
                self.blob_tracker,
                self.is_generous)

        self.rate_limiter.start()
        d1 = self.blob_manager.setup()
        d2 = self.wallet.start()

        dl = defer.DeferredList([d1, d2], fireOnOneErrback=True, consumeErrors=True)
        dl.addCallback(lambda _: self.blob_tracker.start())
        return dl
Example #2
0
    def _setup_other_components(self):
        log.debug("Setting up the rest of the components")

        if self.rate_limiter is None:
            self.rate_limiter = RateLimiter()

        if self.blob_manager is None:
            if self.blob_dir is None:
                raise Exception(
                    "TempBlobManager is no longer supported, specify BlobManager or db_dir")
            else:
                self.blob_manager = DiskBlobManager(
                    self.hash_announcer, self.blob_dir, self.storage
                )

        if self.blob_tracker is None:
            self.blob_tracker = self.blob_tracker_class(
                self.blob_manager, self.peer_finder, self.dht_node
            )
        if self.payment_rate_manager is None:
            self.payment_rate_manager = self.payment_rate_manager_class(
                self.base_payment_rate_manager, self.blob_tracker, self.is_generous
            )

        self.rate_limiter.start()
        d = self.storage.setup()
        d.addCallback(lambda _: self.blob_manager.setup())
        d.addCallback(lambda _: self.wallet.start())
        d.addCallback(lambda _: self.blob_tracker.start())
        return d
Example #3
0
class BlobComponent(Component):
    component_name = BLOB_COMPONENT
    depends_on = [DATABASE_COMPONENT, DHT_COMPONENT]

    def __init__(self, component_manager):
        Component.__init__(self, component_manager)
        self.blob_manager = None

    @property
    def component(self):
        return self.blob_manager

    def start(self):
        storage = self.component_manager.get_component(DATABASE_COMPONENT)
        dht_node = self.component_manager.get_component(DHT_COMPONENT)
        self.blob_manager = DiskBlobManager(CS.get_blobfiles_dir(), storage,
                                            dht_node._dataStore)
        return self.blob_manager.setup()

    def stop(self):
        return self.blob_manager.stop()

    @defer.inlineCallbacks
    def get_status(self):
        count = 0
        if self.blob_manager:
            count = yield self.blob_manager.storage.count_finished_blobs()
        defer.returnValue({'finished_blobs': count})
Example #4
0
 def setUp(self):
     conf.initialize_settings()
     self.blob_dir = tempfile.mkdtemp()
     self.db_dir = tempfile.mkdtemp()
     hash_announcer = DummyHashAnnouncer()
     self.bm = DiskBlobManager(hash_announcer, self.blob_dir, self.db_dir)
     self.peer = Peer('somehost', 22)
Example #5
0
    def _setup_other_components(self):
        log.debug("Setting up the rest of the components")

        if self.rate_limiter is None:
            self.rate_limiter = RateLimiter()

        if self.blob_manager is None:
            if self.blob_dir is None:
                self.blob_manager = TempBlobManager(self.hash_announcer)
            else:
                self.blob_manager = DiskBlobManager(self.hash_announcer,
                                                    self.blob_dir,
                                                    self.db_dir)

        if self.blob_tracker is None:
            self.blob_tracker = self.blob_tracker_class(self.blob_manager,
                                                        self.peer_finder,
                                                        self.dht_node)
        if self.payment_rate_manager is None:
            self.payment_rate_manager = self.payment_rate_manager_class(
                self.base_payment_rate_manager,
                self.blob_tracker,
                self.is_generous)

        self.rate_limiter.start()
        d1 = self.blob_manager.setup()
        d2 = self.wallet.start()

        dl = defer.DeferredList([d1, d2], fireOnOneErrback=True, consumeErrors=True)
        dl.addCallback(lambda _: self.blob_tracker.start())

        dl.addErrback(self._subfailure)
        return dl
Example #6
0
 def setUp(self):
     conf.initialize_settings(False)
     self.blob_dir = tempfile.mkdtemp()
     self.db_dir = tempfile.mkdtemp()
     self.bm = DiskBlobManager(self.blob_dir, SQLiteStorage(self.db_dir))
     self.peer = Peer('somehost', 22)
     yield self.bm.storage.setup()
def encrypt_blob(filename, key, iv):
    dummy_announcer = DummyHashAnnouncer()
    manager = DiskBlobManager(dummy_announcer, '.', '.')
    yield manager.setup()
    creator = CryptStreamCreator(manager, filename, key, iv_generator(iv))
    with open(filename, 'r') as infile:
        producer = FileBodyProducer(infile, readSize=2**22)
        yield producer.startProducing(creator)
    yield creator.stop()
Example #8
0
def encrypt_blob(filename, key, iv):
    dummy_announcer = DummyHashAnnouncer()
    manager = DiskBlobManager(dummy_announcer, '.', '.')
    yield manager.setup()
    creator = CryptStreamCreator(manager, filename, key, iv_generator(iv))
    with open(filename, 'r') as infile:
        producer = FileBodyProducer(infile, readSize=2**22)
        yield producer.startProducing(creator)
    yield creator.stop()
 def download_temp_blob_from_peer(self, peer, timeout, blob_hash):
     tmp_dir = yield threads.deferToThread(tempfile.mkdtemp)
     tmp_blob_manager = DiskBlobManager(tmp_dir, tmp_dir)
     try:
         result = yield self.download_blob_from_peer(
             peer, timeout, blob_hash, tmp_blob_manager)
     finally:
         yield tmp_blob_manager.stop()
         yield threads.deferToThread(shutil.rmtree, tmp_dir)
     defer.returnValue(result)
Example #10
0
class TestTransfer(unittest.TestCase):
    @defer.inlineCallbacks
    def setUp(self):
        mocks.mock_conf_settings(self)
        self.db_dir, self.blob_dir = mk_db_and_blob_dir()
        self.wallet = FakeWallet()
        self.peer_manager = PeerManager()
        self.peer_finder = FakePeerFinder(5553, self.peer_manager, 1)
        self.rate_limiter = RateLimiter()
        self.prm = OnlyFreePaymentsManager()
        self.storage = SQLiteStorage(self.db_dir)
        self.blob_manager = DiskBlobManager(self.blob_dir, self.storage)
        self.sd_identifier = StreamDescriptorIdentifier()
        self.lbry_file_manager = EncryptedFileManager(
            self.peer_finder, self.rate_limiter, self.blob_manager,
            self.wallet, self.prm, self.storage, self.sd_identifier)

        self.uploader = LbryUploader(5209343)
        self.sd_hash = yield self.uploader.setup()
        yield self.storage.setup()
        yield self.blob_manager.setup()
        yield self.lbry_file_manager.setup()
        yield add_lbry_file_to_sd_identifier(self.sd_identifier)

    @defer.inlineCallbacks
    def tearDown(self):
        yield self.uploader.stop()
        lbry_files = self.lbry_file_manager.lbry_files
        for lbry_file in lbry_files:
            yield self.lbry_file_manager.delete_lbry_file(lbry_file)
        yield self.lbry_file_manager.stop()
        yield self.blob_manager.stop()
        yield self.storage.stop()
        rm_db_and_blob_dir(self.db_dir, self.blob_dir)
        if os.path.exists("test_file"):
            os.remove("test_file")

    @defer.inlineCallbacks
    def test_lbry_transfer(self):
        sd_blob = yield download_sd_blob(self.sd_hash, self.blob_manager,
                                         self.peer_finder, self.rate_limiter,
                                         self.prm, self.wallet)
        metadata = yield self.sd_identifier.get_metadata_for_sd_blob(sd_blob)
        downloader = yield metadata.factories[0].make_downloader(
            metadata,
            self.prm.min_blob_data_payment_rate,
            self.prm,
            self.db_dir,
            download_mirrors=None)
        yield downloader.start()
        with open(os.path.join(self.db_dir, 'test_file'), 'rb') as f:
            hashsum = md5()
            hashsum.update(f.read())
        self.assertEqual(hashsum.hexdigest(),
                         "4ca2aafb4101c1e42235aad24fbb83be")
Example #11
0
def download_it(peer, timeout, blob_hash):
    tmp_dir = yield threads.deferToThread(tempfile.mkdtemp)
    storage = SQLiteStorage(tmp_dir, reactor)
    yield storage.setup()
    tmp_blob_manager = DiskBlobManager(tmp_dir, storage)

    config = {'auto_connect': True}
    if conf.settings['lbryum_wallet_dir']:
        config['lbryum_wallet_dir'] = conf.settings['lbryum_wallet_dir']
        config['use_keyring'] = False
        config['blockchain_name'] = conf.settings['blockchain_name']
        config['lbryum_servers'] = []
    wallet = LbryWalletManager.from_lbrynet_config(config, storage)

    downloader = SinglePeerDownloader()
    downloader.setup(wallet)

    try:
        blob_downloaded = yield downloader.download_blob_from_peer(
            peer, timeout, blob_hash, tmp_blob_manager)
        if blob_downloaded:
            log.info("SUCCESS!")
            blob = yield tmp_blob_manager.get_blob(blob_hash)
            pprint(blob)
            if not blob.verified:
                log.error("except that its not verified....")
            else:
                reader = BlobStreamDescriptorReader(blob)
                info = None
                for x in range(0, 3):
                    try:
                        info = yield reader.get_info()
                    except ValueError:
                        pass
                    if info:
                        break

                    # there's some kind of race condition where it sometimes doesnt write the blob to disk in time
                    time.sleep(0.1)

                if info is not None:
                    pprint(info)
                    for content_blob in info['blobs']:
                        if 'blob_hash' in content_blob:
                            yield download_it(peer, timeout,
                                              content_blob['blob_hash'])
        else:
            log.error("Download failed")
    finally:
        yield tmp_blob_manager.stop()
        yield threads.deferToThread(shutil.rmtree, tmp_dir)

    defer.returnValue(True)
Example #12
0
    def start(self):
        if self.peer_manager is None:
            self.peer_manager = PeerManager()

        def match_port(h, p):
            return h, p

        def join_resolved_addresses(result):
            addresses = []
            for success, value in result:
                if success is True:
                    addresses.append(value)
            return addresses

        def start_dht(addresses):
            log.info("Starting the dht")
            log.info("lbry id: %s",
                     base64.encodestring(self.lbryid).strip("\n"))
            self.dht_node.joinNetwork(addresses)
            self.peer_finder.run_manage_loop()
            self.hash_announcer.run_manage_loop()

        ds = []

        for host, port in self.known_dht_nodes:
            d = reactor.resolve(host)
            d.addCallback(match_port, port)
            ds.append(d)

        if self.dht_node is None:
            self.dht_node = Node(udpPort=self.dht_node_port,
                                 lbryid=self.lbryid,
                                 externalIP=self.external_ip)
        if self.peer_finder is None:
            self.peer_finder = DHTPeerFinder(self.dht_node, self.peer_manager)
        if self.hash_announcer is None:
            self.hash_announcer = DHTHashAnnouncer(self.dht_node,
                                                   self.peer_port)
        if self.blob_manager is None:
            self.blob_manager = DiskBlobManager(self.hash_announcer,
                                                self.blob_dir,
                                                self.blob_data_dir)

        d1 = defer.DeferredList(ds)
        d1.addCallback(join_resolved_addresses)
        d1.addCallback(start_dht)
        d2 = self.blob_manager.setup()
        dl = defer.DeferredList([d1, d2],
                                fireOnOneErrback=True,
                                consumeErrors=True)
        return dl
Example #13
0
def download_it(peer, timeout, blob_hash):
    tmp_dir = yield threads.deferToThread(tempfile.mkdtemp)
    announcer = DummyHashAnnouncer()
    tmp_blob_manager = DiskBlobManager(announcer, tmp_dir, tmp_dir)

    config = {'auto_connect': True}
    if conf.settings['lbryum_wallet_dir']:
        config['lbryum_path'] = conf.settings['lbryum_wallet_dir']
    storage = Wallet.InMemoryStorage()
    wallet = Wallet.LBRYumWallet(storage, config)

    downloader = SinglePeerDownloader()
    downloader.setup(wallet)

    try:
        blob_downloaded = yield downloader.download_blob_from_peer(
            peer, timeout, blob_hash, tmp_blob_manager)
        if blob_downloaded:
            log.info("SUCCESS!")
            blob = yield tmp_blob_manager.get_blob(blob_hash)
            pprint(blob)
            if not blob.verified:
                log.error("except that its not verified....")
            else:
                reader = BlobStreamDescriptorReader(blob)
                info = None
                for x in range(0, 3):
                    try:
                        info = yield reader.get_info()
                    except ValueError:
                        pass
                    if info:
                        break
                    time.sleep(
                        0.1
                    )  # there's some kind of race condition where it sometimes doesnt write the blob to disk in time

                if info is not None:
                    pprint(info)
                    for content_blob in info['blobs']:
                        if 'blob_hash' in content_blob:
                            yield download_it(peer, timeout,
                                              content_blob['blob_hash'])
        else:
            log.error("Download failed")
    finally:
        yield tmp_blob_manager.stop()
        yield threads.deferToThread(shutil.rmtree, tmp_dir)

    defer.returnValue(True)
Example #14
0
 def setUp(self):
     conf.initialize_settings()
     self.blob_dir = tempfile.mkdtemp()
     self.db_dir = tempfile.mkdtemp()
     hash_announcer = DummyHashAnnouncer()
     self.bm = DiskBlobManager(hash_announcer, self.blob_dir, self.db_dir)
     self.peer = Peer('somehost', 22)
Example #15
0
 def setUp(self):
     mocks.mock_conf_settings(self)
     self.tmp_db_dir, self.tmp_blob_dir = mk_db_and_blob_dir()
     self.wallet = FakeWallet()
     self.peer_manager = PeerManager()
     self.peer_finder = FakePeerFinder(5553, self.peer_manager, 2)
     self.rate_limiter = DummyRateLimiter()
     self.sd_identifier = StreamDescriptorIdentifier()
     self.storage = SQLiteStorage(self.tmp_db_dir)
     self.blob_manager = DiskBlobManager(self.tmp_blob_dir, self.storage)
     self.prm = OnlyFreePaymentsManager()
     self.lbry_file_manager = EncryptedFileManager(
         self.peer_finder, self.rate_limiter, self.blob_manager,
         self.wallet, self.prm, self.storage, self.sd_identifier)
     d = self.storage.setup()
     d.addCallback(lambda _: self.lbry_file_manager.setup())
     return d
Example #16
0
    def setup(self):
        init_conf_windows()

        self.db_dir, self.blob_dir = mk_db_and_blob_dir()
        self.wallet = FakeWallet()
        self.peer_manager = PeerManager()
        self.rate_limiter = RateLimiter()
        if self.ul_rate_limit is not None:
            self.rate_limiter.set_ul_limit(self.ul_rate_limit)
        self.prm = OnlyFreePaymentsManager()
        self.storage = SQLiteStorage(self.db_dir)
        self.blob_manager = DiskBlobManager(self.blob_dir, self.storage)
        self.lbry_file_manager = EncryptedFileManager(
            FakePeerFinder(5553, self.peer_manager, 1), self.rate_limiter,
            self.blob_manager, self.wallet, self.prm, self.storage,
            StreamDescriptorIdentifier())

        yield self.storage.setup()
        yield self.blob_manager.setup()
        yield self.lbry_file_manager.setup()

        query_handler_factories = {
            1:
            BlobAvailabilityHandlerFactory(self.blob_manager),
            2:
            BlobRequestHandlerFactory(self.blob_manager, self.wallet, self.prm,
                                      None),
            3:
            self.wallet.get_wallet_info_query_handler_factory(),
        }
        server_factory = ServerProtocolFactory(self.rate_limiter,
                                               query_handler_factories,
                                               self.peer_manager)
        self.server_port = reactor.listenTCP(5553,
                                             server_factory,
                                             interface="localhost")
        test_file = GenFile(self.file_size,
                            b''.join([chr(i) for i in xrange(0, 64, 6)]))
        lbry_file = yield create_lbry_file(self.blob_manager, self.storage,
                                           self.prm, self.lbry_file_manager,
                                           "test_file", test_file)
        defer.returnValue(lbry_file.sd_hash)
Example #17
0
    def _setup_other_components(self):
        log.debug("Setting up the rest of the components")

        if self.rate_limiter is None:
            self.rate_limiter = RateLimiter()

        if self.blob_manager is None:
            if self.blob_dir is None:
                self.blob_manager = TempBlobManager(self.hash_announcer)
            else:
                self.blob_manager = DiskBlobManager(self.hash_announcer, self.blob_dir, self.db_dir)

        self.rate_limiter.start()
        d1 = self.blob_manager.setup()
        d2 = self.wallet.start()

        dl = defer.DeferredList([d1, d2], fireOnOneErrback=True, consumeErrors=True)

        dl.addErrback(lambda err: err.value.subFailure)
        return dl
Example #18
0
    def setUp(self):
        mocks.mock_conf_settings(self)
        self.db_dir, self.blob_dir = mk_db_and_blob_dir()
        self.wallet = FakeWallet()
        self.peer_manager = PeerManager()
        self.peer_finder = FakePeerFinder(5553, self.peer_manager, 1)
        self.rate_limiter = RateLimiter()
        self.prm = OnlyFreePaymentsManager()
        self.storage = SQLiteStorage(self.db_dir)
        self.blob_manager = DiskBlobManager(self.blob_dir, self.storage)
        self.sd_identifier = StreamDescriptorIdentifier()
        self.lbry_file_manager = EncryptedFileManager(
            self.peer_finder, self.rate_limiter, self.blob_manager,
            self.wallet, self.prm, self.storage, self.sd_identifier)

        self.uploader = LbryUploader(5209343)
        self.sd_hash = yield self.uploader.setup()
        yield self.storage.setup()
        yield self.blob_manager.setup()
        yield self.lbry_file_manager.setup()
        yield add_lbry_file_to_sd_identifier(self.sd_identifier)
Example #19
0
    def _setup_other_components(self):
        log.debug("Setting up the rest of the components")

        if self.rate_limiter is None:
            self.rate_limiter = RateLimiter()

        if self.blob_manager is None:
            if self.blob_dir is None:
                self.blob_manager = TempBlobManager(self.hash_announcer)
            else:
                self.blob_manager = DiskBlobManager(self.hash_announcer,
                                                    self.blob_dir, self.db_dir)

        self.rate_limiter.start()
        d1 = self.blob_manager.setup()
        d2 = self.wallet.start()

        dl = defer.DeferredList([d1, d2],
                                fireOnOneErrback=True,
                                consumeErrors=True)

        dl.addErrback(lambda err: err.value.subFailure)
        return dl
 def setUp(self):
     mocks.mock_conf_settings(self)
     self.session = None
     self.lbry_file_manager = None
     self.is_generous = True
     self.db_dir = tempfile.mkdtemp()
     self.blob_dir = os.path.join(self.db_dir, "blobfiles")
     os.mkdir(self.blob_dir)
     self.dht_node = FakeNode()
     self.wallet = FakeWallet()
     self.peer_manager = PeerManager()
     self.peer_finder = FakePeerFinder(5553, self.peer_manager, 2)
     self.rate_limiter = DummyRateLimiter()
     self.sd_identifier = StreamDescriptorIdentifier()
     self.storage = SQLiteStorage(self.db_dir)
     self.blob_manager = DiskBlobManager(self.blob_dir, self.storage,
                                         self.dht_node._dataStore)
     self.prm = OnlyFreePaymentsManager()
     self.lbry_file_manager = EncryptedFileManager(
         self.peer_finder, self.rate_limiter, self.blob_manager,
         self.wallet, self.prm, self.storage, self.sd_identifier)
     d = self.storage.setup()
     d.addCallback(lambda _: self.lbry_file_manager.setup())
     return d
Example #21
0
class LBRYSession(object):
    """This class manages all important services common to any application that uses the network:
    the hash announcer, which informs other peers that this peer is associated with some hash. Usually,
    this means this peer has a blob identified by the hash in question, but it can be used for other
    purposes.
    the peer finder, which finds peers that are associated with some hash.
    the blob manager, which keeps track of which blobs have been downloaded and provides access to them,
    the rate limiter, which attempts to ensure download and upload rates stay below a set maximum,
    and upnp, which opens holes in compatible firewalls so that remote peers can connect to this peer."""
    def __init__(self, blob_data_payment_rate, db_dir=None, lbryid=None, peer_manager=None, dht_node_port=None,
                 known_dht_nodes=None, peer_finder=None, hash_announcer=None,
                 blob_dir=None, blob_manager=None, peer_port=None, use_upnp=True,
                 rate_limiter=None, wallet=None):
        """
        @param blob_data_payment_rate: The default payment rate for blob data

        @param db_dir: The directory in which levelDB files should be stored

        @param lbryid: The unique ID of this node

        @param peer_manager: An object which keeps track of all known peers. If None, a PeerManager will be created

        @param dht_node_port: The port on which the dht node should listen for incoming connections

        @param known_dht_nodes: A list of nodes which the dht node should use to bootstrap into the dht

        @param peer_finder: An object which is used to look up peers that are associated with some hash. If None,
            a DHTPeerFinder will be used, which looks for peers in the distributed hash table.

        @param hash_announcer: An object which announces to other peers that this peer is associated with some hash.
            If None, and peer_port is not None, a DHTHashAnnouncer will be used. If None and
            peer_port is None, a DummyHashAnnouncer will be used, which will not actually announce
            anything.

        @param blob_dir: The directory in which blobs will be stored. If None and blob_manager is None, blobs will
            be stored in memory only.

        @param blob_manager: An object which keeps track of downloaded blobs and provides access to them. If None,
            and blob_dir is not None, a DiskBlobManager will be used, with the given blob_dir.
            If None and blob_dir is None, a TempBlobManager will be used, which stores blobs in
            memory only.

        @param peer_port: The port on which other peers should connect to this peer

        @param use_upnp: Whether or not to try to open a hole in the firewall so that outside peers can connect to
            this peer's peer_port and dht_node_port

        @param rate_limiter: An object which keeps track of the amount of data transferred to and from this peer,
            and can limit that rate if desired

        @param wallet: An object which will be used to keep track of expected payments and which will pay peers.
            If None, a wallet which uses the Point Trader system will be used, which is meant for testing
            only

        @return:
        """
        self.db_dir = db_dir

        self.lbryid = lbryid

        self.peer_manager = peer_manager

        self.dht_node_port = dht_node_port
        self.known_dht_nodes = known_dht_nodes
        if self.known_dht_nodes is None:
            self.known_dht_nodes = []
        self.peer_finder = peer_finder
        self.hash_announcer = hash_announcer

        self.blob_dir = blob_dir
        self.blob_manager = blob_manager

        self.peer_port = peer_port

        self.use_upnp = use_upnp

        self.rate_limiter = rate_limiter

        self.external_ip = '127.0.0.1'

        self.upnp_redirects = []

        self.wallet = wallet

        self.dht_node = None

        self.base_payment_rate_manager = BasePaymentRateManager(blob_data_payment_rate)

    def setup(self):
        """Create the blob directory and database if necessary, start all desired services"""

        log.debug("Setting up the lbry session")

        if self.lbryid is None:
            self.lbryid = generate_id()

        if self.wallet is None:
            self.wallet = PTCWallet(self.db_dir)

        if self.peer_manager is None:
            self.peer_manager = PeerManager()

        if self.use_upnp is True:
            d = self._try_upnp()
        else:
            d = defer.succeed(True)

        if self.peer_finder is None:
            d.addCallback(lambda _: self._setup_dht())
        else:
            if self.hash_announcer is None and self.peer_port is not None:
                log.warning("The server has no way to advertise its available blobs.")
                self.hash_announcer = DummyHashAnnouncer()

        d.addCallback(lambda _: self._setup_other_components())
        return d

    def shut_down(self):
        """Stop all services"""
        ds = []
        if self.dht_node is not None:
            ds.append(defer.maybeDeferred(self.dht_node.stop))
        if self.rate_limiter is not None:
            ds.append(defer.maybeDeferred(self.rate_limiter.stop))
        if self.peer_finder is not None:
            ds.append(defer.maybeDeferred(self.peer_finder.stop))
        if self.hash_announcer is not None:
            ds.append(defer.maybeDeferred(self.hash_announcer.stop))
        if self.wallet is not None:
            ds.append(defer.maybeDeferred(self.wallet.stop))
        if self.blob_manager is not None:
            ds.append(defer.maybeDeferred(self.blob_manager.stop))
        if self.use_upnp is True:
            ds.append(defer.maybeDeferred(self._unset_upnp))
        return defer.DeferredList(ds)

    def _try_upnp(self):

        log.debug("In _try_upnp")

        def threaded_try_upnp():
            if self.use_upnp is False:
                log.debug("Not using upnp")
                return False
            u = miniupnpc.UPnP()
            num_devices_found = u.discover()
            if num_devices_found > 0:
                u.selectigd()
                external_ip = u.externalipaddress()
                if external_ip != '0.0.0.0':
                    self.external_ip = external_ip
                if self.peer_port is not None:
                    if u.getspecificportmapping(self.peer_port, 'TCP') is None:
                        u.addportmapping(self.peer_port, 'TCP', u.lanaddr, self.peer_port, 'LBRY peer port', '')
                        self.upnp_redirects.append((self.peer_port, 'TCP'))
                        log.info("Set UPnP redirect for TCP port %d", self.peer_port)
                    else:
                        log.warning("UPnP redirect already set for TCP port %d", self.peer_port)
                if self.dht_node_port is not None:
                    if u.getspecificportmapping(self.dht_node_port, 'UDP') is None:
                        u.addportmapping(self.dht_node_port, 'UDP', u.lanaddr, self.dht_node_port, 'LBRY DHT port', '')
                        self.upnp_redirects.append((self.dht_node_port, 'UDP'))
                        log.info("Set UPnP redirect for UPD port %d", self.dht_node_port)
                    else:
                        log.warning("UPnP redirect already set for UDP port %d", self.dht_node_port)
                return True
            return False

        def upnp_failed(err):
            log.warning("UPnP failed. Reason: %s", err.getErrorMessage())
            return False

        d = threads.deferToThread(threaded_try_upnp)
        d.addErrback(upnp_failed)
        return d

    def _setup_dht(self):

        from twisted.internet import reactor

        log.debug("Starting the dht")

        def match_port(h, p):
            return h, p

        def join_resolved_addresses(result):
            addresses = []
            for success, value in result:
                if success is True:
                    addresses.append(value)
            return addresses

        def start_dht(addresses):
            self.dht_node.joinNetwork(addresses)
            self.peer_finder.run_manage_loop()
            self.hash_announcer.run_manage_loop()
            return True

        ds = []
        for host, port in self.known_dht_nodes:
            d = reactor.resolve(host)
            d.addCallback(match_port, port)
            ds.append(d)

        self.dht_node = node.Node(udpPort=self.dht_node_port, lbryid=self.lbryid,
                                  externalIP=self.external_ip)
        self.peer_finder = DHTPeerFinder(self.dht_node, self.peer_manager)
        if self.hash_announcer is None:
            self.hash_announcer = DHTHashAnnouncer(self.dht_node, self.peer_port)

        dl = defer.DeferredList(ds)
        dl.addCallback(join_resolved_addresses)
        dl.addCallback(start_dht)
        return dl

    def _setup_other_components(self):
        log.debug("Setting up the rest of the components")

        if self.rate_limiter is None:
            self.rate_limiter = RateLimiter()

        if self.blob_manager is None:
            if self.blob_dir is None:
                self.blob_manager = TempBlobManager(self.hash_announcer)
            else:
                self.blob_manager = DiskBlobManager(self.hash_announcer, self.blob_dir, self.db_dir)

        self.rate_limiter.start()
        d1 = self.blob_manager.setup()
        d2 = self.wallet.start()

        dl = defer.DeferredList([d1, d2], fireOnOneErrback=True, consumeErrors=True)

        dl.addErrback(lambda err: err.value.subFailure)
        return dl

    def _unset_upnp(self):

        def threaded_unset_upnp():
            u = miniupnpc.UPnP()
            num_devices_found = u.discover()
            if num_devices_found > 0:
                u.selectigd()
                for port, protocol in self.upnp_redirects:
                    if u.getspecificportmapping(port, protocol) is None:
                        log.warning("UPnP redirect for %s %d was removed by something else.", protocol, port)
                    else:
                        u.deleteportmapping(port, protocol)
                        log.info("Removed UPnP redirect for %s %d.", protocol, port)
                self.upnp_redirects = []

        d = threads.deferToThread(threaded_unset_upnp)
        d.addErrback(lambda err: str(err))
        return d
Example #22
0
class StreamAvailabilityManager(object):
    def __init__(self, storage):
        self.storage = storage
        self.lbryid = base64.decodestring(LBRYID)
        self.peer_manager = None
        self.peer_finder = None
        self.dht_node = None
        self.hash_announcer = None
        self.blob_manager = None
        self.dht_node_port = conf.settings['dht_node_port']
        self.blob_data_dir = conf.settings['data_dir']
        self.blob_dir = os.path.join(self.blob_data_dir,
                                     conf.settings['BLOBFILES_DIR'])
        self.peer_port = conf.settings['peer_port']
        self.known_dht_nodes = conf.settings['known_dht_nodes']
        self.external_ip = '127.0.0.1'

    def start(self):
        if self.peer_manager is None:
            self.peer_manager = PeerManager()

        def match_port(h, p):
            return h, p

        def join_resolved_addresses(result):
            addresses = []
            for success, value in result:
                if success is True:
                    addresses.append(value)
            return addresses

        def start_dht(addresses):
            log.info("Starting the dht")
            log.info("lbry id: %s",
                     base64.encodestring(self.lbryid).strip("\n"))
            self.dht_node.joinNetwork(addresses)
            self.peer_finder.run_manage_loop()
            self.hash_announcer.run_manage_loop()

        ds = []

        for host, port in self.known_dht_nodes:
            d = reactor.resolve(host)
            d.addCallback(match_port, port)
            ds.append(d)

        if self.dht_node is None:
            self.dht_node = Node(udpPort=self.dht_node_port,
                                 lbryid=self.lbryid,
                                 externalIP=self.external_ip)
        if self.peer_finder is None:
            self.peer_finder = DHTPeerFinder(self.dht_node, self.peer_manager)
        if self.hash_announcer is None:
            self.hash_announcer = DHTHashAnnouncer(self.dht_node,
                                                   self.peer_port)
        if self.blob_manager is None:
            self.blob_manager = DiskBlobManager(self.hash_announcer,
                                                self.blob_dir,
                                                self.blob_data_dir)

        d1 = defer.DeferredList(ds)
        d1.addCallback(join_resolved_addresses)
        d1.addCallback(start_dht)
        d2 = self.blob_manager.setup()
        dl = defer.DeferredList([d1, d2],
                                fireOnOneErrback=True,
                                consumeErrors=True)
        return dl

    def stop(self):
        log.info("Shutting down availability manager")
        ds = []
        if self.blob_manager is not None:
            ds.append(defer.maybeDeferred(self.blob_manager.stop))
        if self.dht_node is not None:
            ds.append(defer.maybeDeferred(self.dht_node.stop))
        if self.peer_finder is not None:
            ds.append(defer.maybeDeferred(self.peer_finder.stop))
        if self.hash_announcer is not None:
            ds.append(defer.maybeDeferred(self.hash_announcer.stop))
        return defer.DeferredList(ds)

    def get_peers_for_hash(self, blob_hash):
        return self.peer_finder.find_peers_for_blob(blob_hash)
Example #23
0
class BlobManagerTest(unittest.TestCase):
    @defer.inlineCallbacks
    def setUp(self):
        conf.initialize_settings(False)
        self.blob_dir = tempfile.mkdtemp()
        self.db_dir = tempfile.mkdtemp()
        self.bm = DiskBlobManager(self.blob_dir, SQLiteStorage(self.db_dir))
        self.peer = Peer('somehost', 22)
        yield self.bm.storage.setup()

    @defer.inlineCallbacks
    def tearDown(self):
        yield self.bm.stop()
        yield self.bm.storage.stop()
        shutil.rmtree(self.blob_dir)
        shutil.rmtree(self.db_dir)

    @defer.inlineCallbacks
    def _create_and_add_blob(self, should_announce=False):
        # create and add blob to blob manager
        data_len = random.randint(1, 1000)
        data = b''.join(
            random.choice(string.ascii_lowercase).encode()
            for _ in range(data_len))

        hashobj = get_lbry_hash_obj()
        hashobj.update(data)
        out = hashobj.hexdigest()
        blob_hash = out

        # create new blob
        yield self.bm.setup()
        blob = yield self.bm.get_blob(blob_hash, len(data))

        writer, finished_d = yield blob.open_for_writing(self.peer)
        yield writer.write(data)
        yield self.bm.blob_completed(blob, should_announce)

        # check to see if blob is there
        self.assertTrue(os.path.isfile(os.path.join(self.blob_dir, blob_hash)))
        blobs = yield self.bm.get_all_verified_blobs()
        self.assertTrue(blob_hash in blobs)
        defer.returnValue(blob_hash)

    @defer.inlineCallbacks
    def test_create_blob(self):
        blob_hashes = []

        # create a bunch of blobs
        for i in range(0, 10):
            blob_hash = yield self._create_and_add_blob()
            blob_hashes.append(blob_hash)
        blobs = yield self.bm.get_all_verified_blobs()
        self.assertEqual(10, len(blobs))

    @defer.inlineCallbacks
    def test_delete_blob(self):
        # create blob
        blob_hash = yield self._create_and_add_blob()
        blobs = yield self.bm.get_all_verified_blobs()
        self.assertEqual(len(blobs), 1)

        # delete blob
        yield self.bm.delete_blobs([blob_hash])
        self.assertFalse(os.path.isfile(os.path.join(self.blob_dir,
                                                     blob_hash)))
        blobs = yield self.bm.get_all_verified_blobs()
        self.assertEqual(len(blobs), 0)
        blobs = yield self.bm.storage.get_all_blob_hashes()
        self.assertEqual(len(blobs), 0)
        self.assertFalse(blob_hash in self.bm.blobs)

        # delete blob that was already deleted once
        yield self.bm.delete_blobs([blob_hash])

        # delete blob that does not exist, nothing will
        # happen
        blob_hash = random_lbry_hash()
        yield self.bm.delete_blobs([blob_hash])

    @defer.inlineCallbacks
    def test_delete_open_blob(self):
        # Test that a blob that is opened for writing will not be deleted

        # create blobs
        blob_hashes = []
        for i in range(0, 10):
            blob_hash = yield self._create_and_add_blob()
            blob_hashes.append(blob_hash)
        blobs = yield self.bm.get_all_verified_blobs()
        self.assertEqual(len(blobs), 10)

        # open the last blob
        blob = yield self.bm.get_blob(blob_hashes[-1])
        w, finished_d = yield blob.open_for_writing(self.peer)

        # schedule a close, just to leave the reactor clean
        finished_d.addBoth(lambda x: None)
        self.addCleanup(w.close)

        # delete the last blob and check if it still exists
        yield self.bm.delete_blobs([blob_hash])
        blobs = yield self.bm.get_all_verified_blobs()
        self.assertEqual(len(blobs), 10)
        self.assertTrue(blob_hashes[-1] in blobs)
        self.assertTrue(
            os.path.isfile(os.path.join(self.blob_dir, blob_hashes[-1])))

    @defer.inlineCallbacks
    def test_should_announce(self):
        # create blob with should announce
        blob_hash = yield self._create_and_add_blob(should_announce=True)
        out = yield self.bm.get_should_announce(blob_hash)
        self.assertTrue(out)
        count = yield self.bm.count_should_announce_blobs()
        self.assertEqual(1, count)

        # set should announce to False
        yield self.bm.set_should_announce(blob_hash, should_announce=False)
        out = yield self.bm.get_should_announce(blob_hash)
        self.assertFalse(out)
        count = yield self.bm.count_should_announce_blobs()
        self.assertEqual(0, count)
Example #24
0
 def start(self):
     storage = self.component_manager.get_component(DATABASE_COMPONENT)
     dht_node = self.component_manager.get_component(DHT_COMPONENT)
     self.blob_manager = DiskBlobManager(CS.get_blobfiles_dir(), storage,
                                         dht_node._dataStore)
     return self.blob_manager.setup()
Example #25
0
class LbryUploader(object):
    def __init__(self, file_size, ul_rate_limit=None):
        self.file_size = file_size
        self.ul_rate_limit = ul_rate_limit
        self.kill_check = None
        # these attributes get defined in `start`
        self.db_dir = None
        self.blob_dir = None
        self.wallet = None
        self.peer_manager = None
        self.rate_limiter = None
        self.prm = None
        self.storage = None
        self.blob_manager = None
        self.lbry_file_manager = None
        self.server_port = None

    @defer.inlineCallbacks
    def setup(self):
        init_conf_windows()

        self.db_dir, self.blob_dir = mk_db_and_blob_dir()
        self.wallet = FakeWallet()
        self.peer_manager = PeerManager()
        self.rate_limiter = RateLimiter()
        if self.ul_rate_limit is not None:
            self.rate_limiter.set_ul_limit(self.ul_rate_limit)
        self.prm = OnlyFreePaymentsManager()
        self.storage = SQLiteStorage(self.db_dir)
        self.blob_manager = DiskBlobManager(self.blob_dir, self.storage)
        self.lbry_file_manager = EncryptedFileManager(
            FakePeerFinder(5553, self.peer_manager, 1), self.rate_limiter,
            self.blob_manager, self.wallet, self.prm, self.storage,
            StreamDescriptorIdentifier())

        yield self.storage.setup()
        yield self.blob_manager.setup()
        yield self.lbry_file_manager.setup()

        query_handler_factories = {
            1:
            BlobAvailabilityHandlerFactory(self.blob_manager),
            2:
            BlobRequestHandlerFactory(self.blob_manager, self.wallet, self.prm,
                                      None),
            3:
            self.wallet.get_wallet_info_query_handler_factory(),
        }
        server_factory = ServerProtocolFactory(self.rate_limiter,
                                               query_handler_factories,
                                               self.peer_manager)
        self.server_port = reactor.listenTCP(5553,
                                             server_factory,
                                             interface="localhost")
        test_file = GenFile(self.file_size, bytes(i for i in range(0, 64, 6)))
        lbry_file = yield create_lbry_file(self.blob_manager, self.storage,
                                           self.prm, self.lbry_file_manager,
                                           "test_file", test_file)
        defer.returnValue(lbry_file.sd_hash)

    @defer.inlineCallbacks
    def stop(self):
        lbry_files = self.lbry_file_manager.lbry_files
        for lbry_file in lbry_files:
            yield self.lbry_file_manager.delete_lbry_file(lbry_file)
        yield self.lbry_file_manager.stop()
        yield self.blob_manager.stop()
        yield self.storage.stop()
        self.server_port.stopListening()
        rm_db_and_blob_dir(self.db_dir, self.blob_dir)
        if os.path.exists("test_file"):
            os.remove("test_file")
Example #26
0
class BlobManagerTest(unittest.TestCase):
    def setUp(self):
        conf.initialize_settings()
        self.blob_dir = tempfile.mkdtemp()
        self.db_dir = tempfile.mkdtemp()
        hash_announcer = DummyHashAnnouncer()
        self.bm = DiskBlobManager(hash_announcer, self.blob_dir, self.db_dir)
        self.peer = Peer('somehost', 22)

    def tearDown(self):
        self.bm.stop()
        # BlobFile will try to delete itself  in _close_writer
        # thus when calling rmtree we may get a FileNotFoundError
        # for the blob file
        shutil.rmtree(self.blob_dir, ignore_errors=True)
        shutil.rmtree(self.db_dir)

    @defer.inlineCallbacks
    def _create_and_add_blob(self):
        # create and add blob to blob manager
        data_len = random.randint(1, 1000)
        data = ''.join(
            random.choice(string.lowercase) for data_len in range(data_len))

        hashobj = get_lbry_hash_obj()
        hashobj.update(data)
        out = hashobj.hexdigest()
        blob_hash = out

        # create new blob
        yield self.bm.setup()
        blob = yield self.bm.get_blob(blob_hash, len(data))

        writer, finished_d = yield blob.open_for_writing(self.peer)
        yield writer.write(data)
        yield self.bm.blob_completed(blob)
        yield self.bm.add_blob_to_upload_history(blob_hash, 'test', len(data))

        # check to see if blob is there
        self.assertTrue(os.path.isfile(os.path.join(self.blob_dir, blob_hash)))
        blobs = yield self.bm.get_all_verified_blobs()
        self.assertTrue(blob_hash in blobs)
        defer.returnValue(blob_hash)

    @defer.inlineCallbacks
    def test_create_blob(self):
        blob_hashes = []

        # create a bunch of blobs
        for i in range(0, 10):
            blob_hash = yield self._create_and_add_blob()
            blob_hashes.append(blob_hash)
        blobs = yield self.bm.get_all_verified_blobs()
        self.assertEqual(10, len(blobs))

    @defer.inlineCallbacks
    def test_delete_blob(self):
        # create blob
        blob_hash = yield self._create_and_add_blob()
        blobs = yield self.bm.get_all_verified_blobs()
        self.assertEqual(len(blobs), 1)

        # delete blob
        yield self.bm.delete_blobs([blob_hash])
        self.assertFalse(os.path.isfile(os.path.join(self.blob_dir,
                                                     blob_hash)))
        blobs = yield self.bm.get_all_verified_blobs()
        self.assertEqual(len(blobs), 0)
        blobs = yield self.bm._get_all_blob_hashes()
        self.assertEqual(len(blobs), 0)

        # delete blob that does not exist, nothing will
        # happen
        blob_hash = random_lbry_hash()
        out = yield self.bm.delete_blobs([blob_hash])

    @defer.inlineCallbacks
    def test_delete_open_blob(self):
        # Test that a blob that is opened for writing will not be deleted

        # create blobs
        blob_hashes = []
        for i in range(0, 10):
            blob_hash = yield self._create_and_add_blob()
            blob_hashes.append(blob_hash)
        blobs = yield self.bm.get_all_verified_blobs()
        self.assertEqual(len(blobs), 10)

        # open the last blob
        blob = yield self.bm.get_blob(blob_hashes[-1])
        writer, finished_d = yield blob.open_for_writing(self.peer)

        # delete the last blob and check if it still exists
        out = yield self.bm.delete_blobs([blob_hash])
        blobs = yield self.bm.get_all_verified_blobs()
        self.assertEqual(len(blobs), 10)
        self.assertTrue(blob_hashes[-1] in blobs)
        self.assertTrue(
            os.path.isfile(os.path.join(self.blob_dir, blob_hashes[-1])))
Example #27
0
class LBRYSession(object):
    """This class manages all important services common to any application that uses the network:
    the hash announcer, which informs other peers that this peer is associated with some hash. Usually,
    this means this peer has a blob identified by the hash in question, but it can be used for other
    purposes.
    the peer finder, which finds peers that are associated with some hash.
    the blob manager, which keeps track of which blobs have been downloaded and provides access to them,
    the rate limiter, which attempts to ensure download and upload rates stay below a set maximum,
    and upnp, which opens holes in compatible firewalls so that remote peers can connect to this peer."""
    def __init__(self,
                 blob_data_payment_rate,
                 db_dir=None,
                 lbryid=None,
                 peer_manager=None,
                 dht_node_port=None,
                 known_dht_nodes=None,
                 peer_finder=None,
                 hash_announcer=None,
                 blob_dir=None,
                 blob_manager=None,
                 peer_port=None,
                 use_upnp=True,
                 rate_limiter=None,
                 wallet=None,
                 dht_node_class=node.Node):
        """
        @param blob_data_payment_rate: The default payment rate for blob data

        @param db_dir: The directory in which levelDB files should be stored

        @param lbryid: The unique ID of this node

        @param peer_manager: An object which keeps track of all known peers. If None, a PeerManager will be created

        @param dht_node_port: The port on which the dht node should listen for incoming connections

        @param known_dht_nodes: A list of nodes which the dht node should use to bootstrap into the dht

        @param peer_finder: An object which is used to look up peers that are associated with some hash. If None,
            a DHTPeerFinder will be used, which looks for peers in the distributed hash table.

        @param hash_announcer: An object which announces to other peers that this peer is associated with some hash.
            If None, and peer_port is not None, a DHTHashAnnouncer will be used. If None and
            peer_port is None, a DummyHashAnnouncer will be used, which will not actually announce
            anything.

        @param blob_dir: The directory in which blobs will be stored. If None and blob_manager is None, blobs will
            be stored in memory only.

        @param blob_manager: An object which keeps track of downloaded blobs and provides access to them. If None,
            and blob_dir is not None, a DiskBlobManager will be used, with the given blob_dir.
            If None and blob_dir is None, a TempBlobManager will be used, which stores blobs in
            memory only.

        @param peer_port: The port on which other peers should connect to this peer

        @param use_upnp: Whether or not to try to open a hole in the firewall so that outside peers can connect to
            this peer's peer_port and dht_node_port

        @param rate_limiter: An object which keeps track of the amount of data transferred to and from this peer,
            and can limit that rate if desired

        @param wallet: An object which will be used to keep track of expected payments and which will pay peers.
            If None, a wallet which uses the Point Trader system will be used, which is meant for testing
            only

        @return:
        """
        self.db_dir = db_dir

        self.lbryid = lbryid

        self.peer_manager = peer_manager

        self.dht_node_port = dht_node_port
        self.known_dht_nodes = known_dht_nodes
        if self.known_dht_nodes is None:
            self.known_dht_nodes = []
        self.peer_finder = peer_finder
        self.hash_announcer = hash_announcer

        self.blob_dir = blob_dir
        self.blob_manager = blob_manager

        self.peer_port = peer_port

        self.use_upnp = use_upnp

        self.rate_limiter = rate_limiter

        self.external_ip = '127.0.0.1'

        self.upnp_redirects = []

        self.wallet = wallet
        self.dht_node_class = dht_node_class
        self.dht_node = None

        self.base_payment_rate_manager = BasePaymentRateManager(
            blob_data_payment_rate)

    def setup(self):
        """Create the blob directory and database if necessary, start all desired services"""

        log.debug("Setting up the lbry session")

        if self.lbryid is None:
            self.lbryid = generate_id()

        if self.wallet is None:
            self.wallet = PTCWallet(self.db_dir)

        if self.peer_manager is None:
            self.peer_manager = PeerManager()

        if self.use_upnp is True:
            d = self._try_upnp()
        else:
            d = defer.succeed(True)

        if self.peer_finder is None:
            d.addCallback(lambda _: self._setup_dht())
        else:
            if self.hash_announcer is None and self.peer_port is not None:
                log.warning(
                    "The server has no way to advertise its available blobs.")
                self.hash_announcer = DummyHashAnnouncer()

        d.addCallback(lambda _: self._setup_other_components())
        return d

    def shut_down(self):
        """Stop all services"""
        ds = []
        if self.dht_node is not None:
            ds.append(defer.maybeDeferred(self.dht_node.stop))
        if self.rate_limiter is not None:
            ds.append(defer.maybeDeferred(self.rate_limiter.stop))
        if self.peer_finder is not None:
            ds.append(defer.maybeDeferred(self.peer_finder.stop))
        if self.hash_announcer is not None:
            ds.append(defer.maybeDeferred(self.hash_announcer.stop))
        if self.wallet is not None:
            ds.append(defer.maybeDeferred(self.wallet.stop))
        if self.blob_manager is not None:
            ds.append(defer.maybeDeferred(self.blob_manager.stop))
        if self.use_upnp is True:
            ds.append(defer.maybeDeferred(self._unset_upnp))
        return defer.DeferredList(ds)

    def _try_upnp(self):

        log.debug("In _try_upnp")

        def threaded_try_upnp():
            if self.use_upnp is False:
                log.debug("Not using upnp")
                return False
            u = miniupnpc.UPnP()
            num_devices_found = u.discover()
            if num_devices_found > 0:
                u.selectigd()
                external_ip = u.externalipaddress()
                if external_ip != '0.0.0.0':
                    self.external_ip = external_ip
                if self.peer_port is not None:
                    if u.getspecificportmapping(self.peer_port, 'TCP') is None:
                        u.addportmapping(self.peer_port, 'TCP', u.lanaddr,
                                         self.peer_port, 'LBRY peer port', '')
                        self.upnp_redirects.append((self.peer_port, 'TCP'))
                        log.info("Set UPnP redirect for TCP port %d",
                                 self.peer_port)
                    else:
                        # see comment below
                        log.warning(
                            "UPnP redirect already set for TCP port %d",
                            self.peer_port)
                        self.upnp_redirects.append((self.peer_port, 'TCP'))
                if self.dht_node_port is not None:
                    if u.getspecificportmapping(self.dht_node_port,
                                                'UDP') is None:
                        u.addportmapping(self.dht_node_port, 'UDP', u.lanaddr,
                                         self.dht_node_port, 'LBRY DHT port',
                                         '')
                        self.upnp_redirects.append((self.dht_node_port, 'UDP'))
                        log.info("Set UPnP redirect for UPD port %d",
                                 self.dht_node_port)
                    else:
                        # TODO: check that the existing redirect was put up by an old lbrynet session before grabbing it
                        # if such a disconnected redirect exists, then upnp won't work unless the redirect is appended
                        # or is torn down and set back up. a bad shutdown of lbrynet could leave such a redirect up
                        # and cause problems on the next start.
                        # this could be problematic if a previous lbrynet session didn't make the redirect, and it was
                        # made by another application
                        log.warning(
                            "UPnP redirect already set for UDP port %d",
                            self.dht_node_port)
                        self.upnp_redirects.append((self.dht_node_port, 'UDP'))
                return True
            return False

        def upnp_failed(err):
            log.warning("UPnP failed. Reason: %s", err.getErrorMessage())
            return False

        d = threads.deferToThread(threaded_try_upnp)
        d.addErrback(upnp_failed)
        return d

    def _setup_dht(self):

        from twisted.internet import reactor

        log.debug("Starting the dht")

        def match_port(h, p):
            return h, p

        def join_resolved_addresses(result):
            addresses = []
            for success, value in result:
                if success is True:
                    addresses.append(value)
            return addresses

        def start_dht(addresses):
            self.dht_node.joinNetwork(addresses)
            self.peer_finder.run_manage_loop()
            self.hash_announcer.run_manage_loop()
            return True

        ds = []
        for host, port in self.known_dht_nodes:
            d = reactor.resolve(host)
            d.addCallback(match_port, port)
            ds.append(d)

        self.dht_node = self.dht_node_class(udpPort=self.dht_node_port,
                                            lbryid=self.lbryid,
                                            externalIP=self.external_ip)
        self.peer_finder = DHTPeerFinder(self.dht_node, self.peer_manager)
        if self.hash_announcer is None:
            self.hash_announcer = DHTHashAnnouncer(self.dht_node,
                                                   self.peer_port)

        dl = defer.DeferredList(ds)
        dl.addCallback(join_resolved_addresses)
        dl.addCallback(start_dht)
        return dl

    def _setup_other_components(self):
        log.debug("Setting up the rest of the components")

        if self.rate_limiter is None:
            self.rate_limiter = RateLimiter()

        if self.blob_manager is None:
            if self.blob_dir is None:
                self.blob_manager = TempBlobManager(self.hash_announcer)
            else:
                self.blob_manager = DiskBlobManager(self.hash_announcer,
                                                    self.blob_dir, self.db_dir)

        self.rate_limiter.start()
        d1 = self.blob_manager.setup()
        d2 = self.wallet.start()

        dl = defer.DeferredList([d1, d2],
                                fireOnOneErrback=True,
                                consumeErrors=True)

        dl.addErrback(lambda err: err.value.subFailure)
        return dl

    def _unset_upnp(self):
        def threaded_unset_upnp():
            u = miniupnpc.UPnP()
            num_devices_found = u.discover()
            if num_devices_found > 0:
                u.selectigd()
                for port, protocol in self.upnp_redirects:
                    if u.getspecificportmapping(port, protocol) is None:
                        log.warning(
                            "UPnP redirect for %s %d was removed by something else.",
                            protocol, port)
                    else:
                        u.deleteportmapping(port, protocol)
                        log.info("Removed UPnP redirect for %s %d.", protocol,
                                 port)
                self.upnp_redirects = []

        d = threads.deferToThread(threaded_unset_upnp)
        d.addErrback(lambda err: str(err))
        return d
Example #28
0
class CreateEncryptedFileTest(unittest.TestCase):
    timeout = 5

    def setUp(self):
        mocks.mock_conf_settings(self)
        self.tmp_db_dir, self.tmp_blob_dir = mk_db_and_blob_dir()
        self.wallet = FakeWallet()
        self.peer_manager = PeerManager()
        self.peer_finder = FakePeerFinder(5553, self.peer_manager, 2)
        self.rate_limiter = DummyRateLimiter()
        self.sd_identifier = StreamDescriptorIdentifier()
        self.storage = SQLiteStorage(self.tmp_db_dir)
        self.blob_manager = DiskBlobManager(self.tmp_blob_dir, self.storage)
        self.prm = OnlyFreePaymentsManager()
        self.lbry_file_manager = EncryptedFileManager(
            self.peer_finder, self.rate_limiter, self.blob_manager,
            self.wallet, self.prm, self.storage, self.sd_identifier)
        d = self.storage.setup()
        d.addCallback(lambda _: self.lbry_file_manager.setup())
        return d

    @defer.inlineCallbacks
    def tearDown(self):
        yield self.lbry_file_manager.stop()
        yield self.blob_manager.stop()
        yield self.storage.stop()
        rm_db_and_blob_dir(self.tmp_db_dir, self.tmp_blob_dir)

    @defer.inlineCallbacks
    def create_file(self, filename):
        handle = mocks.GenFile(3 * MB, b'1')
        key = b'2' * (AES.block_size // 8)
        out = yield EncryptedFileCreator.create_lbry_file(
            self.blob_manager, self.storage, self.prm, self.lbry_file_manager,
            filename, handle, key, iv_generator())
        defer.returnValue(out)

    @defer.inlineCallbacks
    def test_can_create_file(self):
        expected_stream_hash = "41e6b247d923d191b154fb6f1b8529d6ddd6a73d65c35" \
                               "7b1acb742dd83151fb66393a7709e9f346260a4f4db6de10c25"
        expected_sd_hash = "40c485432daec586c1a2d247e6c08d137640a5af6e81f3f652" \
                           "3e62e81a2e8945b0db7c94f1852e70e371d917b994352c"
        filename = 'test.file'
        lbry_file = yield self.create_file(filename)
        sd_hash = yield self.storage.get_sd_blob_hash_for_stream(
            lbry_file.stream_hash)

        # read the sd blob file
        sd_blob = self.blob_manager.blobs[sd_hash]
        sd_reader = BlobStreamDescriptorReader(sd_blob)
        sd_file_info = yield sd_reader.get_info()

        # this comes from the database, the blobs returned are sorted
        sd_info = yield get_sd_info(self.storage,
                                    lbry_file.stream_hash,
                                    include_blobs=True)
        self.maxDiff = None
        unicode_sd_info = json.loads(
            json.dumps(sd_info, sort_keys=True, cls=JSONBytesEncoder))
        self.assertDictEqual(unicode_sd_info, sd_file_info)
        self.assertEqual(sd_info['stream_hash'], expected_stream_hash)
        self.assertEqual(len(sd_info['blobs']), 3)
        self.assertNotEqual(sd_info['blobs'][0]['length'], 0)
        self.assertNotEqual(sd_info['blobs'][1]['length'], 0)
        self.assertEqual(sd_info['blobs'][2]['length'], 0)
        self.assertEqual(expected_stream_hash, lbry_file.stream_hash)
        self.assertEqual(sd_hash, lbry_file.sd_hash)
        self.assertEqual(sd_hash, expected_sd_hash)
        blobs = yield self.blob_manager.get_all_verified_blobs()
        self.assertEqual(3, len(blobs))
        num_should_announce_blobs = yield self.blob_manager.count_should_announce_blobs(
        )
        self.assertEqual(2, num_should_announce_blobs)

    @defer.inlineCallbacks
    def test_can_create_file_with_unicode_filename(self):
        expected_stream_hash = (
            'd1da4258f3ce12edb91d7e8e160d091d3ab1432c2e55a6352dce0'
            '2fd5adb86fe144e93e110075b5865fff8617776c6c0')
        filename = '☃.file'
        lbry_file = yield self.create_file(filename)
        self.assertEqual(expected_stream_hash, lbry_file.stream_hash)
Example #29
0
class Session(object):
    """This class manages all important services common to any application that uses the network.

    the hash announcer, which informs other peers that this peer is
    associated with some hash. Usually, this means this peer has a
    blob identified by the hash in question, but it can be used for
    other purposes.

    the peer finder, which finds peers that are associated with some
    hash.

    the blob manager, which keeps track of which blobs have been
    downloaded and provides access to them,

    the rate limiter, which attempts to ensure download and upload
    rates stay below a set maximum

    upnp, which opens holes in compatible firewalls so that remote
    peers can connect to this peer.
    """

    def __init__(self, blob_data_payment_rate, db_dir=None,
                 node_id=None, peer_manager=None, dht_node_port=None,
                 known_dht_nodes=None, peer_finder=None,
                 hash_announcer=None, blob_dir=None,
                 blob_manager=None, peer_port=None, use_upnp=True,
                 rate_limiter=None, wallet=None,
                 dht_node_class=node.Node, blob_tracker_class=None,
                 payment_rate_manager_class=None, is_generous=True, external_ip=None):
        """@param blob_data_payment_rate: The default payment rate for blob data

        @param db_dir: The directory in which levelDB files should be stored

        @param node_id: The unique ID of this node

        @param peer_manager: An object which keeps track of all known
            peers. If None, a PeerManager will be created

        @param dht_node_port: The port on which the dht node should
            listen for incoming connections

        @param known_dht_nodes: A list of nodes which the dht node
        should use to bootstrap into the dht

        @param peer_finder: An object which is used to look up peers
            that are associated with some hash. If None, a
            DHTPeerFinder will be used, which looks for peers in the
            distributed hash table.

        @param hash_announcer: An object which announces to other
            peers that this peer is associated with some hash.  If
            None, and peer_port is not None, a DHTHashAnnouncer will
            be used. If None and peer_port is None, a
            DummyHashAnnouncer will be used, which will not actually
            announce anything.

        @param blob_dir: The directory in which blobs will be
            stored. If None and blob_manager is None, blobs will be
            stored in memory only.

        @param blob_manager: An object which keeps track of downloaded
            blobs and provides access to them. If None, and blob_dir
            is not None, a DiskBlobManager will be used, with the
            given blob_dir.  If None and blob_dir is None, a
            TempBlobManager will be used, which stores blobs in memory
            only.

        @param peer_port: The port on which other peers should connect
            to this peer

        @param use_upnp: Whether or not to try to open a hole in the
            firewall so that outside peers can connect to this peer's
            peer_port and dht_node_port

        @param rate_limiter: An object which keeps track of the amount
            of data transferred to and from this peer, and can limit
            that rate if desired

        @param wallet: An object which will be used to keep track of
            expected payments and which will pay peers.  If None, a
            wallet which uses the Point Trader system will be used,
            which is meant for testing only

        """
        self.db_dir = db_dir

        self.node_id = node_id

        self.peer_manager = peer_manager

        self.dht_node_port = dht_node_port
        self.known_dht_nodes = known_dht_nodes
        if self.known_dht_nodes is None:
            self.known_dht_nodes = []
        self.peer_finder = peer_finder
        self.hash_announcer = hash_announcer

        self.blob_dir = blob_dir
        self.blob_manager = blob_manager

        self.blob_tracker = None
        self.blob_tracker_class = blob_tracker_class or BlobAvailabilityTracker

        self.peer_port = peer_port

        self.use_upnp = use_upnp

        self.rate_limiter = rate_limiter

        self.external_ip = external_ip

        self.upnp_redirects = []

        self.wallet = wallet
        self.dht_node_class = dht_node_class
        self.dht_node = None

        self.base_payment_rate_manager = BasePaymentRateManager(blob_data_payment_rate)
        self.payment_rate_manager = None
        self.payment_rate_manager_class = payment_rate_manager_class or NegotiatedPaymentRateManager
        self.is_generous = is_generous

    def setup(self):
        """Create the blob directory and database if necessary, start all desired services"""

        log.debug("Starting session.")

        if self.node_id is None:
            self.node_id = generate_id()

        if self.wallet is None:
            from lbrynet.core.PTCWallet import PTCWallet
            self.wallet = PTCWallet(self.db_dir)

        if self.peer_manager is None:
            self.peer_manager = PeerManager()

        if self.use_upnp is True:
            d = self._try_upnp()
        else:
            d = defer.succeed(True)

        if self.peer_finder is None:
            d.addCallback(lambda _: self._setup_dht())
        else:
            if self.hash_announcer is None and self.peer_port is not None:
                log.warning("The server has no way to advertise its available blobs.")
                self.hash_announcer = DummyHashAnnouncer()

        d.addCallback(lambda _: self._setup_other_components())
        return d

    def shut_down(self):
        """Stop all services"""
        log.info('Stopping session.')
        ds = []
        if self.blob_tracker is not None:
            ds.append(defer.maybeDeferred(self.blob_tracker.stop))
        if self.dht_node is not None:
            ds.append(defer.maybeDeferred(self.dht_node.stop))
        if self.rate_limiter is not None:
            ds.append(defer.maybeDeferred(self.rate_limiter.stop))
        if self.peer_finder is not None:
            ds.append(defer.maybeDeferred(self.peer_finder.stop))
        if self.hash_announcer is not None:
            ds.append(defer.maybeDeferred(self.hash_announcer.stop))
        if self.wallet is not None:
            ds.append(defer.maybeDeferred(self.wallet.stop))
        if self.blob_manager is not None:
            ds.append(defer.maybeDeferred(self.blob_manager.stop))
        if self.use_upnp is True:
            ds.append(defer.maybeDeferred(self._unset_upnp))
        return defer.DeferredList(ds)

    def _try_upnp(self):

        log.debug("In _try_upnp")

        def get_free_port(upnp, port, protocol):
            # returns an existing mapping if it exists
            mapping = upnp.getspecificportmapping(port, protocol)
            if not mapping:
                return port
            if upnp.lanaddr == mapping[0]:
                return mapping
            return get_free_port(upnp, port + 1, protocol)

        def get_port_mapping(upnp, internal_port, protocol, description):
            # try to map to the requested port, if there is already a mapping use the next external
            # port available
            if protocol not in ['UDP', 'TCP']:
                raise Exception("invalid protocol")
            external_port = get_free_port(upnp, internal_port, protocol)
            if isinstance(external_port, tuple):
                log.info("Found existing UPnP redirect %s:%i (%s) to %s:%i, using it",
                         self.external_ip, external_port[1], protocol, upnp.lanaddr, internal_port)
                return external_port[1], protocol
            upnp.addportmapping(external_port, protocol, upnp.lanaddr, internal_port,
                                description, '')
            log.info("Set UPnP redirect %s:%i (%s) to %s:%i", self.external_ip, external_port,
                     protocol, upnp.lanaddr, internal_port)
            return external_port, protocol

        def threaded_try_upnp():
            if self.use_upnp is False:
                log.debug("Not using upnp")
                return False
            u = miniupnpc.UPnP()
            num_devices_found = u.discover()
            if num_devices_found > 0:
                u.selectigd()
                external_ip = u.externalipaddress()
                if external_ip != '0.0.0.0' and not self.external_ip:
                    # best not to rely on this external ip, the router can be behind layers of NATs
                    self.external_ip = external_ip
                if self.peer_port:
                    self.upnp_redirects.append(get_port_mapping(u, self.peer_port, 'TCP',
                                                                'LBRY peer port'))
                if self.dht_node_port:
                    self.upnp_redirects.append(get_port_mapping(u, self.dht_node_port, 'UDP',
                                                                'LBRY DHT port'))
                return True
            return False

        def upnp_failed(err):
            log.warning("UPnP failed. Reason: %s", err.getErrorMessage())
            return False

        d = threads.deferToThread(threaded_try_upnp)
        d.addErrback(upnp_failed)
        return d

    def _setup_dht(self):

        from twisted.internet import reactor

        log.info("Starting DHT")

        def join_resolved_addresses(result):
            addresses = []
            for success, value in result:
                if success is True:
                    addresses.append(value)
            return addresses

        def start_dht(join_network_result):
            self.peer_finder.run_manage_loop()
            self.hash_announcer.run_manage_loop()
            return True

        ds = []
        for host, port in self.known_dht_nodes:
            d = reactor.resolve(host)
            d.addCallback(lambda h: (h, port))  # match host to port
            ds.append(d)

        self.dht_node = self.dht_node_class(
            udpPort=self.dht_node_port,
            node_id=self.node_id,
            externalIP=self.external_ip,
            peerPort=self.peer_port
        )
        self.peer_finder = DHTPeerFinder(self.dht_node, self.peer_manager)
        if self.hash_announcer is None:
            self.hash_announcer = DHTHashAnnouncer(self.dht_node, self.peer_port)

        dl = defer.DeferredList(ds)
        dl.addCallback(join_resolved_addresses)
        dl.addCallback(self.dht_node.joinNetwork)
        dl.addCallback(start_dht)
        return dl

    def _setup_other_components(self):
        log.debug("Setting up the rest of the components")

        if self.rate_limiter is None:
            self.rate_limiter = RateLimiter()

        if self.blob_manager is None:
            if self.blob_dir is None:
                raise Exception(
                    "TempBlobManager is no longer supported, specify BlobManager or db_dir")
            else:
                self.blob_manager = DiskBlobManager(self.hash_announcer,
                                                    self.blob_dir,
                                                    self.db_dir)

        if self.blob_tracker is None:
            self.blob_tracker = self.blob_tracker_class(self.blob_manager,
                                                        self.peer_finder,
                                                        self.dht_node)
        if self.payment_rate_manager is None:
            self.payment_rate_manager = self.payment_rate_manager_class(
                self.base_payment_rate_manager,
                self.blob_tracker,
                self.is_generous)

        self.rate_limiter.start()
        d1 = self.blob_manager.setup()
        d2 = self.wallet.start()

        dl = defer.DeferredList([d1, d2], fireOnOneErrback=True, consumeErrors=True)
        dl.addCallback(lambda _: self.blob_tracker.start())
        return dl

    def _unset_upnp(self):
        log.info("Unsetting upnp for session")

        def threaded_unset_upnp():
            u = miniupnpc.UPnP()
            num_devices_found = u.discover()
            if num_devices_found > 0:
                u.selectigd()
                for port, protocol in self.upnp_redirects:
                    if u.getspecificportmapping(port, protocol) is None:
                        log.warning(
                            "UPnP redirect for %s %d was removed by something else.",
                            protocol, port)
                    else:
                        u.deleteportmapping(port, protocol)
                        log.info("Removed UPnP redirect for %s %d.", protocol, port)
                self.upnp_redirects = []

        d = threads.deferToThread(threaded_unset_upnp)
        d.addErrback(lambda err: str(err))
        return d
Example #30
0
class CreateEncryptedFileTest(unittest.TestCase):
    timeout = 5

    def setUp(self):
        mocks.mock_conf_settings(self)
        self.tmp_db_dir, self.tmp_blob_dir = mk_db_and_blob_dir()
        self.wallet = FakeWallet()
        self.peer_manager = PeerManager()
        self.peer_finder = FakePeerFinder(5553, self.peer_manager, 2)
        self.rate_limiter = DummyRateLimiter()
        self.sd_identifier = StreamDescriptorIdentifier()
        self.storage = SQLiteStorage(self.tmp_db_dir)
        self.blob_manager = DiskBlobManager(self.tmp_blob_dir, self.storage)
        self.prm = OnlyFreePaymentsManager()
        self.lbry_file_manager = EncryptedFileManager(
            self.peer_finder, self.rate_limiter, self.blob_manager,
            self.wallet, self.prm, self.storage, self.sd_identifier)
        d = self.storage.setup()
        d.addCallback(lambda _: self.lbry_file_manager.setup())
        return d

    @defer.inlineCallbacks
    def tearDown(self):
        yield self.lbry_file_manager.stop()
        yield self.blob_manager.stop()
        yield self.storage.stop()
        rm_db_and_blob_dir(self.tmp_db_dir, self.tmp_blob_dir)

    @defer.inlineCallbacks
    def create_file(self, filename):
        handle = mocks.GenFile(3 * MB, '1')
        key = '2' * (AES.block_size / 8)
        out = yield EncryptedFileCreator.create_lbry_file(
            self.blob_manager, self.storage, self.prm, self.lbry_file_manager,
            filename, handle, key, iv_generator())
        defer.returnValue(out)

    @defer.inlineCallbacks
    def test_can_create_file(self):
        expected_stream_hash = "41e6b247d923d191b154fb6f1b8529d6ddd6a73d65c35" \
                               "7b1acb742dd83151fb66393a7709e9f346260a4f4db6de10c25"
        expected_sd_hash = "db043b44384c149126685990f6bb6563aa565ae331303d522" \
                           "c8728fe0534dd06fbcacae92b0891787ad9b68ffc8d20c1"
        filename = 'test.file'
        lbry_file = yield self.create_file(filename)
        sd_hash = yield self.storage.get_sd_blob_hash_for_stream(
            lbry_file.stream_hash)

        # read the sd blob file
        sd_blob = self.blob_manager.blobs[sd_hash]
        sd_reader = BlobStreamDescriptorReader(sd_blob)
        sd_file_info = yield sd_reader.get_info()

        # this comes from the database, the blobs returned are sorted
        sd_info = yield get_sd_info(self.storage,
                                    lbry_file.stream_hash,
                                    include_blobs=True)
        self.assertDictEqual(sd_info, sd_file_info)
        self.assertListEqual(sd_info['blobs'], sd_file_info['blobs'])
        self.assertEqual(sd_info['stream_hash'], expected_stream_hash)
        self.assertEqual(len(sd_info['blobs']), 3)
        self.assertNotEqual(sd_info['blobs'][0]['length'], 0)
        self.assertNotEqual(sd_info['blobs'][1]['length'], 0)
        self.assertEqual(sd_info['blobs'][2]['length'], 0)
        self.assertEqual(expected_stream_hash, lbry_file.stream_hash)
        self.assertEqual(sd_hash, lbry_file.sd_hash)
        self.assertEqual(sd_hash, expected_sd_hash)
        blobs = yield self.blob_manager.get_all_verified_blobs()
        self.assertEqual(3, len(blobs))
        num_should_announce_blobs = yield self.blob_manager.count_should_announce_blobs(
        )
        self.assertEqual(2, num_should_announce_blobs)

    @defer.inlineCallbacks
    def test_can_create_file_with_unicode_filename(self):
        expected_stream_hash = (
            'd1da4258f3ce12edb91d7e8e160d091d3ab1432c2e55a6352dce0'
            '2fd5adb86fe144e93e110075b5865fff8617776c6c0')
        filename = u'☃.file'
        lbry_file = yield self.create_file(filename)
        self.assertEqual(expected_stream_hash, lbry_file.stream_hash)
Example #31
0
class Session(object):
    """This class manages all important services common to any application that uses the network.

    the hash announcer, which informs other peers that this peer is
    associated with some hash. Usually, this means this peer has a
    blob identified by the hash in question, but it can be used for
    other purposes.

    the peer finder, which finds peers that are associated with some
    hash.

    the blob manager, which keeps track of which blobs have been
    downloaded and provides access to them,

    the rate limiter, which attempts to ensure download and upload
    rates stay below a set maximum

    upnp, which opens holes in compatible firewalls so that remote
    peers can connect to this peer.
    """
    def __init__(self,
                 blob_data_payment_rate,
                 db_dir=None,
                 node_id=None,
                 peer_manager=None,
                 dht_node_port=None,
                 known_dht_nodes=None,
                 peer_finder=None,
                 hash_announcer=None,
                 blob_dir=None,
                 blob_manager=None,
                 peer_port=None,
                 use_upnp=True,
                 rate_limiter=None,
                 wallet=None,
                 dht_node_class=node.Node,
                 blob_tracker_class=None,
                 payment_rate_manager_class=None,
                 is_generous=True,
                 external_ip=None):
        """@param blob_data_payment_rate: The default payment rate for blob data

        @param db_dir: The directory in which levelDB files should be stored

        @param node_id: The unique ID of this node

        @param peer_manager: An object which keeps track of all known
            peers. If None, a PeerManager will be created

        @param dht_node_port: The port on which the dht node should
            listen for incoming connections

        @param known_dht_nodes: A list of nodes which the dht node
        should use to bootstrap into the dht

        @param peer_finder: An object which is used to look up peers
            that are associated with some hash. If None, a
            DHTPeerFinder will be used, which looks for peers in the
            distributed hash table.

        @param hash_announcer: An object which announces to other
            peers that this peer is associated with some hash.  If
            None, and peer_port is not None, a DHTHashAnnouncer will
            be used. If None and peer_port is None, a
            DummyHashAnnouncer will be used, which will not actually
            announce anything.

        @param blob_dir: The directory in which blobs will be
            stored. If None and blob_manager is None, blobs will be
            stored in memory only.

        @param blob_manager: An object which keeps track of downloaded
            blobs and provides access to them. If None, and blob_dir
            is not None, a DiskBlobManager will be used, with the
            given blob_dir.  If None and blob_dir is None, a
            TempBlobManager will be used, which stores blobs in memory
            only.

        @param peer_port: The port on which other peers should connect
            to this peer

        @param use_upnp: Whether or not to try to open a hole in the
            firewall so that outside peers can connect to this peer's
            peer_port and dht_node_port

        @param rate_limiter: An object which keeps track of the amount
            of data transferred to and from this peer, and can limit
            that rate if desired

        @param wallet: An object which will be used to keep track of
            expected payments and which will pay peers.  If None, a
            wallet which uses the Point Trader system will be used,
            which is meant for testing only

        """
        self.db_dir = db_dir

        self.node_id = node_id

        self.peer_manager = peer_manager

        self.dht_node_port = dht_node_port
        self.known_dht_nodes = known_dht_nodes
        if self.known_dht_nodes is None:
            self.known_dht_nodes = []
        self.peer_finder = peer_finder
        self.hash_announcer = hash_announcer

        self.blob_dir = blob_dir
        self.blob_manager = blob_manager

        self.blob_tracker = None
        self.blob_tracker_class = blob_tracker_class or BlobAvailabilityTracker

        self.peer_port = peer_port

        self.use_upnp = use_upnp

        self.rate_limiter = rate_limiter

        self.external_ip = external_ip

        self.upnp_redirects = []

        self.wallet = wallet
        self.dht_node_class = dht_node_class
        self.dht_node = None

        self.base_payment_rate_manager = BasePaymentRateManager(
            blob_data_payment_rate)
        self.payment_rate_manager = None
        self.payment_rate_manager_class = payment_rate_manager_class or NegotiatedPaymentRateManager
        self.is_generous = is_generous

    def setup(self):
        """Create the blob directory and database if necessary, start all desired services"""

        log.debug("Starting session.")

        if self.node_id is None:
            self.node_id = generate_id()

        if self.wallet is None:
            from lbrynet.core.PTCWallet import PTCWallet
            self.wallet = PTCWallet(self.db_dir)

        if self.peer_manager is None:
            self.peer_manager = PeerManager()

        if self.use_upnp is True:
            d = self._try_upnp()
        else:
            d = defer.succeed(True)

        if self.peer_finder is None:
            d.addCallback(lambda _: self._setup_dht())
        else:
            if self.hash_announcer is None and self.peer_port is not None:
                log.warning(
                    "The server has no way to advertise its available blobs.")
                self.hash_announcer = DummyHashAnnouncer()

        d.addCallback(lambda _: self._setup_other_components())
        return d

    def shut_down(self):
        """Stop all services"""
        log.info('Stopping session.')
        ds = []
        if self.blob_tracker is not None:
            ds.append(defer.maybeDeferred(self.blob_tracker.stop))
        if self.dht_node is not None:
            ds.append(defer.maybeDeferred(self.dht_node.stop))
        if self.rate_limiter is not None:
            ds.append(defer.maybeDeferred(self.rate_limiter.stop))
        if self.peer_finder is not None:
            ds.append(defer.maybeDeferred(self.peer_finder.stop))
        if self.hash_announcer is not None:
            ds.append(defer.maybeDeferred(self.hash_announcer.stop))
        if self.wallet is not None:
            ds.append(defer.maybeDeferred(self.wallet.stop))
        if self.blob_manager is not None:
            ds.append(defer.maybeDeferred(self.blob_manager.stop))
        if self.use_upnp is True:
            ds.append(defer.maybeDeferred(self._unset_upnp))
        return defer.DeferredList(ds)

    def _try_upnp(self):

        log.debug("In _try_upnp")

        def get_free_port(upnp, port, protocol):
            # returns an existing mapping if it exists
            mapping = upnp.getspecificportmapping(port, protocol)
            if not mapping:
                return port
            if upnp.lanaddr == mapping[0]:
                return mapping
            return get_free_port(upnp, port + 1, protocol)

        def get_port_mapping(upnp, internal_port, protocol, description):
            # try to map to the requested port, if there is already a mapping use the next external
            # port available
            if protocol not in ['UDP', 'TCP']:
                raise Exception("invalid protocol")
            external_port = get_free_port(upnp, internal_port, protocol)
            if isinstance(external_port, tuple):
                log.info(
                    "Found existing UPnP redirect %s:%i (%s) to %s:%i, using it",
                    self.external_ip, external_port[1], protocol, upnp.lanaddr,
                    internal_port)
                return external_port[1], protocol
            upnp.addportmapping(external_port, protocol, upnp.lanaddr,
                                internal_port, description, '')
            log.info("Set UPnP redirect %s:%i (%s) to %s:%i", self.external_ip,
                     external_port, protocol, upnp.lanaddr, internal_port)
            return external_port, protocol

        def threaded_try_upnp():
            if self.use_upnp is False:
                log.debug("Not using upnp")
                return False
            u = miniupnpc.UPnP()
            num_devices_found = u.discover()
            if num_devices_found > 0:
                u.selectigd()
                external_ip = u.externalipaddress()
                if external_ip != '0.0.0.0' and not self.external_ip:
                    # best not to rely on this external ip, the router can be behind layers of NATs
                    self.external_ip = external_ip
                if self.peer_port:
                    self.upnp_redirects.append(
                        get_port_mapping(u, self.peer_port, 'TCP',
                                         'LBRY peer port'))
                if self.dht_node_port:
                    self.upnp_redirects.append(
                        get_port_mapping(u, self.dht_node_port, 'UDP',
                                         'LBRY DHT port'))
                return True
            return False

        def upnp_failed(err):
            log.warning("UPnP failed. Reason: %s", err.getErrorMessage())
            return False

        d = threads.deferToThread(threaded_try_upnp)
        d.addErrback(upnp_failed)
        return d

    def _setup_dht(self):

        from twisted.internet import reactor

        log.info("Starting DHT")

        def join_resolved_addresses(result):
            addresses = []
            for success, value in result:
                if success is True:
                    addresses.append(value)
            return addresses

        def start_dht(join_network_result):
            self.peer_finder.run_manage_loop()
            self.hash_announcer.run_manage_loop()
            return True

        ds = []
        for host, port in self.known_dht_nodes:
            d = reactor.resolve(host)
            d.addCallback(lambda h: (h, port))  # match host to port
            ds.append(d)

        self.dht_node = self.dht_node_class(udpPort=self.dht_node_port,
                                            node_id=self.node_id,
                                            externalIP=self.external_ip,
                                            peerPort=self.peer_port)
        self.peer_finder = DHTPeerFinder(self.dht_node, self.peer_manager)
        if self.hash_announcer is None:
            self.hash_announcer = DHTHashAnnouncer(self.dht_node,
                                                   self.peer_port)

        dl = defer.DeferredList(ds)
        dl.addCallback(join_resolved_addresses)
        dl.addCallback(self.dht_node.joinNetwork)
        dl.addCallback(start_dht)
        return dl

    def _setup_other_components(self):
        log.debug("Setting up the rest of the components")

        if self.rate_limiter is None:
            self.rate_limiter = RateLimiter()

        if self.blob_manager is None:
            if self.blob_dir is None:
                raise Exception(
                    "TempBlobManager is no longer supported, specify BlobManager or db_dir"
                )
            else:
                self.blob_manager = DiskBlobManager(self.hash_announcer,
                                                    self.blob_dir, self.db_dir)

        if self.blob_tracker is None:
            self.blob_tracker = self.blob_tracker_class(
                self.blob_manager, self.peer_finder, self.dht_node)
        if self.payment_rate_manager is None:
            self.payment_rate_manager = self.payment_rate_manager_class(
                self.base_payment_rate_manager, self.blob_tracker,
                self.is_generous)

        self.rate_limiter.start()
        d1 = self.blob_manager.setup()
        d2 = self.wallet.start()

        dl = defer.DeferredList([d1, d2],
                                fireOnOneErrback=True,
                                consumeErrors=True)
        dl.addCallback(lambda _: self.blob_tracker.start())
        return dl

    def _unset_upnp(self):
        log.info("Unsetting upnp for session")

        def threaded_unset_upnp():
            u = miniupnpc.UPnP()
            num_devices_found = u.discover()
            if num_devices_found > 0:
                u.selectigd()
                for port, protocol in self.upnp_redirects:
                    if u.getspecificportmapping(port, protocol) is None:
                        log.warning(
                            "UPnP redirect for %s %d was removed by something else.",
                            protocol, port)
                    else:
                        u.deleteportmapping(port, protocol)
                        log.info("Removed UPnP redirect for %s %d.", protocol,
                                 port)
                self.upnp_redirects = []

        d = threads.deferToThread(threaded_unset_upnp)
        d.addErrback(lambda err: str(err))
        return d
Example #32
0
class BlobManagerTest(unittest.TestCase):
    def setUp(self):
        conf.initialize_settings()
        self.blob_dir = tempfile.mkdtemp()
        self.db_dir = tempfile.mkdtemp()
        hash_announcer = DummyHashAnnouncer()
        self.bm = DiskBlobManager(hash_announcer, self.blob_dir, self.db_dir)
        self.peer = Peer('somehost', 22)

    def tearDown(self):
        self.bm.stop()
        # BlobFile will try to delete itself  in _close_writer
        # thus when calling rmtree we may get a FileNotFoundError
        # for the blob file
        shutil.rmtree(self.blob_dir, ignore_errors=True)
        shutil.rmtree(self.db_dir)

    @defer.inlineCallbacks
    def _create_and_add_blob(self, should_announce=False):
        # create and add blob to blob manager
        data_len = random.randint(1, 1000)
        data = ''.join(random.choice(string.lowercase) for data_len in range(data_len))

        hashobj = get_lbry_hash_obj()
        hashobj.update(data)
        out = hashobj.hexdigest()
        blob_hash = out

        # create new blob
        yield self.bm.setup()
        blob = yield self.bm.get_blob(blob_hash, len(data))

        writer, finished_d = yield blob.open_for_writing(self.peer)
        yield writer.write(data)
        yield self.bm.blob_completed(blob, should_announce)
        yield self.bm.add_blob_to_upload_history(blob_hash, 'test', len(data))

        # check to see if blob is there
        self.assertTrue(os.path.isfile(os.path.join(self.blob_dir, blob_hash)))
        blobs = yield self.bm.get_all_verified_blobs()
        self.assertTrue(blob_hash in blobs)
        defer.returnValue(blob_hash)

    @defer.inlineCallbacks
    def test_create_blob(self):
        blob_hashes = []

        # create a bunch of blobs
        for i in range(0, 10):
            blob_hash = yield self._create_and_add_blob()
            blob_hashes.append(blob_hash)
        blobs = yield self.bm.get_all_verified_blobs()
        self.assertEqual(10, len(blobs))


    @defer.inlineCallbacks
    def test_delete_blob(self):
        # create blob
        blob_hash = yield self._create_and_add_blob()
        blobs = yield self.bm.get_all_verified_blobs()
        self.assertEqual(len(blobs), 1)

        # delete blob
        yield self.bm.delete_blobs([blob_hash])
        self.assertFalse(os.path.isfile(os.path.join(self.blob_dir, blob_hash)))
        blobs = yield self.bm.get_all_verified_blobs()
        self.assertEqual(len(blobs), 0)
        blobs = yield self.bm._get_all_blob_hashes()
        self.assertEqual(len(blobs), 0)
        self.assertFalse(blob_hash in self.bm.blobs)

        # delete blob that was already deleted once
        out = yield self.bm.delete_blobs([blob_hash])

        # delete blob that does not exist, nothing will
        # happen
        blob_hash = random_lbry_hash()
        out = yield self.bm.delete_blobs([blob_hash])


    @defer.inlineCallbacks
    def test_delete_open_blob(self):
        # Test that a blob that is opened for writing will not be deleted

        # create blobs
        blob_hashes = []
        for i in range(0, 10):
            blob_hash = yield self._create_and_add_blob()
            blob_hashes.append(blob_hash)
        blobs = yield self.bm.get_all_verified_blobs()
        self.assertEqual(len(blobs), 10)

        # open the last blob
        blob = yield self.bm.get_blob(blob_hashes[-1])
        writer, finished_d = yield blob.open_for_writing(self.peer)

        # delete the last blob and check if it still exists
        out = yield self.bm.delete_blobs([blob_hash])
        blobs = yield self.bm.get_all_verified_blobs()
        self.assertEqual(len(blobs), 10)
        self.assertTrue(blob_hashes[-1] in blobs)
        self.assertTrue(os.path.isfile(os.path.join(self.blob_dir, blob_hashes[-1])))

    @defer.inlineCallbacks
    def test_should_announce(self):
        # create blob with should announce
        blob_hash = yield self._create_and_add_blob(should_announce=True)
        out = yield self.bm.get_should_announce(blob_hash)
        self.assertTrue(out)
        count = yield self.bm.count_should_announce_blobs()
        self.assertEqual(1, count)

        # set should annouce to False
        out = yield self.bm.set_should_announce(blob_hash, should_announce=False)
        out = yield self.bm.get_should_announce(blob_hash)
        self.assertFalse(out)
        count = yield self.bm.count_should_announce_blobs()
        self.assertEqual(0, count)