negotiated downloads

-uploads are still underway
This commit is contained in:
Jack 2016-09-27 13:52:44 -04:00
parent e115158f86
commit 1720cce3b0
22 changed files with 486 additions and 253 deletions

View file

@ -14,7 +14,7 @@ MAX_BLOB_INFOS_TO_REQUEST = 20
BLOBFILES_DIR = ".blobfiles" BLOBFILES_DIR = ".blobfiles"
BLOB_SIZE = 2**21 BLOB_SIZE = 2**21
MIN_BLOB_DATA_PAYMENT_RATE = .005 # points/megabyte MIN_BLOB_DATA_PAYMENT_RATE = .001 # points/megabyte
MIN_BLOB_INFO_PAYMENT_RATE = .02 # points/1000 infos MIN_BLOB_INFO_PAYMENT_RATE = .02 # points/1000 infos
MIN_VALUABLE_BLOB_INFO_PAYMENT_RATE = .05 # points/1000 infos MIN_VALUABLE_BLOB_INFO_PAYMENT_RATE = .05 # points/1000 infos
MIN_VALUABLE_BLOB_HASH_PAYMENT_RATE = .05 # points/1000 infos MIN_VALUABLE_BLOB_HASH_PAYMENT_RATE = .05 # points/1000 infos

View file

@ -2,53 +2,21 @@ import logging
from twisted.internet import defer from twisted.internet import defer
from twisted.internet.task import LoopingCall from twisted.internet.task import LoopingCall
from lbrynet.conf import MIN_BLOB_DATA_PAYMENT_RATE as min_price
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
base_price = min_price * 10
# how heavily to value blobs towards the front of the stream class BlobAvailabilityTracker(object):
alpha = 1.0
def frontload(index):
""" """
Get frontload multipler Class to track peer counts for known blobs, and to discover new popular blobs
@param index: blob position in stream
@return: frontload multipler
"""
return 2.0 - (alpha**index)
def calculate_price(mean_availability, availability, index_position=0):
"""
Calculate mean-blob-availability and stream-position weighted price for a blob
@param mean_availability: sum of blob availabilities over the number of known blobs
@param availability: number of known peers for blob
@param index_position: blob index position in stream
@return: price
"""
price = max(min_price, base_price * (mean_availability/max(1, availability)) * frontload(index_position))
return price
class BlobPriceAndAvailabilityTracker(object):
"""
Class to track peer counts for known blobs and update price targets
Attributes: Attributes:
prices (dict): dictionary of blob prices
availability (dict): dictionary of peers for known blobs availability (dict): dictionary of peers for known blobs
""" """
def __init__(self, blob_manager, peer_finder, dht_node): def __init__(self, blob_manager, peer_finder, dht_node):
self.availability = {} self.availability = {}
self.prices = {} self.last_mean_availability = 0.0
self._blob_manager = blob_manager self._blob_manager = blob_manager
self._peer_finder = peer_finder self._peer_finder = peer_finder
self._dht_node = dht_node self._dht_node = dht_node
@ -70,9 +38,6 @@ class BlobPriceAndAvailabilityTracker(object):
def _save_peer_info(blob_hash, peers): def _save_peer_info(blob_hash, peers):
v = {blob_hash: peers} v = {blob_hash: peers}
self.availability.update(v) self.availability.update(v)
new_price = self._get_price(blob)
self.prices.update({blob: new_price})
return v return v
d = self._peer_finder.find_peers_for_blob(blob) d = self._peer_finder.find_peers_for_blob(blob)
@ -88,6 +53,7 @@ class BlobPriceAndAvailabilityTracker(object):
dl.append(self._update_peers_for_blob(encoded)) dl.append(self._update_peers_for_blob(encoded))
return defer.DeferredList(dl) return defer.DeferredList(dl)
d = _get_most_popular() d = _get_most_popular()
d.addCallback(lambda _: self._get_mean_peers())
def _update_mine(self): def _update_mine(self):
def _get_peers(blobs): def _get_peers(blobs):
@ -97,14 +63,9 @@ class BlobPriceAndAvailabilityTracker(object):
return defer.DeferredList(dl) return defer.DeferredList(dl)
d = self._blob_manager.get_all_verified_blobs() d = self._blob_manager.get_all_verified_blobs()
d.addCallback(_get_peers) d.addCallback(_get_peers)
d.addCallback(lambda _: self._get_mean_peers())
def _get_mean_peers(self): def _get_mean_peers(self):
num_peers = [len(self.availability[blob]) for blob in self.availability] num_peers = [len(self.availability[blob]) for blob in self.availability]
mean = float(sum(num_peers)) / float(max(1, len(num_peers))) mean = float(sum(num_peers)) / float(max(1, len(num_peers)))
return mean self.last_mean_availability = mean
def _get_price(self, blob):
mean_available = self._get_mean_peers()
blob_availability = len(self.availability.get(blob, []))
price = calculate_price(mean_available, blob_availability)
return price

View file

@ -1,52 +0,0 @@
import os
from twisted.enterprise import adbapi
import time
class BlobHistoryManager(object):
"""
Class to archive historical blob upload and download information
This class creates two tables in lbry data folder/blob_history.db, 'download' and 'upload'
The tables store information about what blob was uploaded or downloaded, to or from which peer,
at what price, and when.
"""
def __init__(self, db_dir):
self.db = None
self.db_dir = db_dir
def _open_db(self):
self.db = adbapi.ConnectionPool('sqlite3', os.path.join(self.db_dir, "blob_history.db"),
check_same_thread=False)
def create_tables(transaction):
transaction.execute("create table if not exists download (" +
" id integer primary key autoincrement, " +
" blob text, " +
" host text, " +
" rate float, " +
" ts integer)")
transaction.execute("create table if not exists upload (" +
" id integer primary key autoincrement, " +
" blob text, " +
" host text, " +
" rate float, " +
" ts integer)")
return self.db.runInteraction(create_tables)
def add_transaction(self, blob_hash, host, rate, upload=False):
ts = int(time.time())
if upload:
d = self.db.runQuery("insert into upload values (null, ?, ?, ?, ?) ", (blob_hash, str(host), float(rate), ts))
else:
d = self.db.runQuery("insert into download values (null, ?, ?, ?, ?) ", (blob_hash, str(host), float(rate), ts))
return d
def start(self):
d = self._open_db()
return d

View file

@ -2,6 +2,7 @@ import logging
import os import os
import time import time
import sqlite3 import sqlite3
from twisted.internet import threads, defer from twisted.internet import threads, defer
from twisted.python.failure import Failure from twisted.python.failure import Failure
from twisted.enterprise import adbapi from twisted.enterprise import adbapi
@ -11,8 +12,6 @@ from lbrynet.core.utils import is_valid_blobhash
from lbrynet.core.cryptoutils import get_lbry_hash_obj from lbrynet.core.cryptoutils import get_lbry_hash_obj
from lbrynet.core.Error import NoSuchBlobError from lbrynet.core.Error import NoSuchBlobError
from lbrynet.core.sqlite_helpers import rerun_if_locked from lbrynet.core.sqlite_helpers import rerun_if_locked
from lbrynet.core.BlobHistory import BlobHistoryManager
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -71,6 +70,12 @@ class BlobManager(DHTHashSupplier):
def get_all_verified_blobs(self): def get_all_verified_blobs(self):
pass pass
def add_blob_to_download_history(self, blob_hash, host, rate):
pass
def add_blob_to_upload_history(self, blob_hash, host, rate):
pass
class DiskBlobManager(BlobManager): class DiskBlobManager(BlobManager):
"""This class stores blobs on the hard disk""" """This class stores blobs on the hard disk"""
@ -84,13 +89,11 @@ class DiskBlobManager(BlobManager):
self.blobs = {} self.blobs = {}
self.blob_hashes_to_delete = {} # {blob_hash: being_deleted (True/False)} self.blob_hashes_to_delete = {} # {blob_hash: being_deleted (True/False)}
self._next_manage_call = None self._next_manage_call = None
self.blob_history_manager = BlobHistoryManager(db_dir)
def setup(self): def setup(self):
log.info("Setting up the DiskBlobManager. blob_dir: %s, db_file: %s", str(self.blob_dir), log.info("Setting up the DiskBlobManager. blob_dir: %s, db_file: %s", str(self.blob_dir),
str(self.db_file)) str(self.db_file))
d = self._open_db() d = self._open_db()
d.addCallback(lambda _: self.blob_history_manager.start())
d.addCallback(lambda _: self._manage()) d.addCallback(lambda _: self._manage())
return d return d
@ -188,6 +191,14 @@ class DiskBlobManager(BlobManager):
d.addCallback(self.completed_blobs) d.addCallback(self.completed_blobs)
return d return d
def add_blob_to_download_history(self, blob_hash, host, rate):
d = self._add_blob_to_download_history(blob_hash, host, rate)
return d
def add_blob_to_upload_history(self, blob_hash, host, rate):
d = self._add_blob_to_upload_history(blob_hash, host, rate)
return d
def _manage(self): def _manage(self):
from twisted.internet import reactor from twisted.internet import reactor
@ -246,12 +257,29 @@ class DiskBlobManager(BlobManager):
# one that opened it. The individual connections in the pool are not used in multiple # one that opened it. The individual connections in the pool are not used in multiple
# threads. # threads.
self.db_conn = adbapi.ConnectionPool('sqlite3', self.db_file, check_same_thread=False) self.db_conn = adbapi.ConnectionPool('sqlite3', self.db_file, check_same_thread=False)
return self.db_conn.runQuery("create table if not exists blobs (" +
" blob_hash text primary key, " + def create_tables(transaction):
" blob_length integer, " + transaction.execute("create table if not exists blobs (" +
" last_verified_time real, " + " blob_hash text primary key, " +
" next_announce_time real" " blob_length integer, " +
")") " last_verified_time real, " +
" next_announce_time real)")
transaction.execute("create table if not exists download (" +
" id integer primary key autoincrement, " +
" blob text, " +
" host text, " +
" rate float, " +
" ts integer)")
transaction.execute("create table if not exists upload (" +
" id integer primary key autoincrement, " +
" blob text, " +
" host text, " +
" rate float, " +
" ts integer)")
return self.db_conn.runInteraction(create_tables)
@rerun_if_locked @rerun_if_locked
def _add_completed_blob(self, blob_hash, length, timestamp, next_announce_time=None): def _add_completed_blob(self, blob_hash, length, timestamp, next_announce_time=None):
@ -429,6 +457,18 @@ class DiskBlobManager(BlobManager):
d.addCallback(lambda blobs: threads.deferToThread(get_verified_blobs, blobs)) d.addCallback(lambda blobs: threads.deferToThread(get_verified_blobs, blobs))
return d return d
@rerun_if_locked
def _add_blob_to_download_history(self, blob_hash, host, rate):
ts = int(time.time())
d = self.db_conn.runQuery("insert into download values (null, ?, ?, ?, ?) ", (blob_hash, str(host), float(rate), ts))
return d
@rerun_if_locked
def _add_blob_to_upload_history(self, blob_hash, host, rate):
ts = int(time.time())
d = self.db_conn.runQuery("insert into upload values (null, ?, ?, ?, ?) ", (blob_hash, str(host), float(rate), ts))
return d
class TempBlobManager(BlobManager): class TempBlobManager(BlobManager):
"""This class stores blobs in memory""" """This class stores blobs in memory"""
@ -529,7 +569,6 @@ class TempBlobManager(BlobManager):
d.addCallback(lambda _: set_next_manage_call()) d.addCallback(lambda _: set_next_manage_call())
def _delete_blobs_marked_for_deletion(self): def _delete_blobs_marked_for_deletion(self):
def remove_from_list(b_h): def remove_from_list(b_h):
del self.blob_hashes_to_delete[b_h] del self.blob_hashes_to_delete[b_h]
log.info("Deleted blob %s", blob_hash) log.info("Deleted blob %s", blob_hash)
@ -558,4 +597,4 @@ class TempBlobManager(BlobManager):
d = defer.fail(Failure(NoSuchBlobError(blob_hash))) d = defer.fail(Failure(NoSuchBlobError(blob_hash)))
log.warning("Blob %s cannot be deleted because it is unknown") log.warning("Blob %s cannot be deleted because it is unknown")
ds.append(d) ds.append(d)
return defer.DeferredList(ds) return defer.DeferredList(ds)

View file

@ -87,4 +87,8 @@ class NoSuchStreamHashError(Exception):
class InvalidBlobHashError(Exception): class InvalidBlobHashError(Exception):
pass
class NegotiationError(Exception):
pass pass

70
lbrynet/core/Offer.py Normal file
View file

@ -0,0 +1,70 @@
from lbrynet.core.Error import NegotiationError
class Offer(object):
"""
A rate offer to download blobs from a host
"""
def __init__(self, offer):
self._state = None
self.rate = None
if isinstance(offer, float):
self.rate = round(offer, 5)
elif offer == Negotiate.RATE_ACCEPTED:
self.accept()
elif offer == Negotiate.RATE_TOO_LOW:
self.reject()
@property
def accepted(self):
return self._state is Negotiate.RATE_ACCEPTED
@property
def too_low(self):
return self._state is Negotiate.RATE_TOO_LOW
@property
def message(self):
if self.accepted:
return Negotiate.RATE_ACCEPTED
elif self.too_low:
return Negotiate.RATE_TOO_LOW
elif self.rate is None:
return Negotiate.RATE_UNSET
def accept(self):
if self._state is None:
self._state = Negotiate.RATE_ACCEPTED
def reject(self):
if self._state is None:
self._state = Negotiate.RATE_TOO_LOW
class Negotiate(object):
"""
Helper class for converting to and from Offers
"""
RATE_ACCEPTED = "RATE_ACCEPTED"
RATE_TOO_LOW = "RATE_TOO_LOW"
RATE_UNSET = "RATE_UNSET"
PAYMENT_RATE = "blob_data_payment_rate"
ERROR = "error"
@staticmethod
def get_offer_from_request(request_dict):
error = request_dict.get(Negotiate.ERROR, False)
if error:
raise NegotiationError()
return Offer(request_dict.get(Negotiate.PAYMENT_RATE))
@staticmethod
def make_dict_from_offer(offer):
if offer.message:
request_dict = {Negotiate.PAYMENT_RATE: offer.message}
else:
request_dict = {Negotiate.PAYMENT_RATE: offer.rate}
return request_dict

View file

@ -1,3 +1,5 @@
from lbrynet.core.Strategy import get_default_strategy
class BasePaymentRateManager(object): class BasePaymentRateManager(object):
def __init__(self, rate): def __init__(self, rate):
self.min_blob_data_payment_rate = rate self.min_blob_data_payment_rate = rate
@ -26,4 +28,30 @@ class PaymentRateManager(object):
return self.min_blob_data_payment_rate return self.min_blob_data_payment_rate
def record_points_paid(self, amount): def record_points_paid(self, amount):
self.points_paid += amount self.points_paid += amount
class NegotiatedPaymentRateManager(object):
def __init__(self, base, availability_tracker):
"""
@param base: a BasePaymentRateManager
@param availability_tracker: a BlobAvailabilityTracker
@param rate: the min blob data payment rate
"""
self.base = base
self.min_blob_data_payment_rate = self.base.min_blob_data_payment_rate
self.points_paid = 0.0
self.blob_tracker = availability_tracker
self.strategy = get_default_strategy(self.blob_tracker)
def get_rate_blob_data(self, peer, blobs):
response = self.strategy.make_offer(peer, blobs)
return response.rate
def accept_rate_blob_data(self, peer, blobs, offer):
response = self.strategy.respond_to_offer(offer, peer, blobs)
return response.accepted
def record_points_paid(self, amount):
self.points_paid += amount

View file

@ -12,8 +12,7 @@ class Peer(object):
self.stats = defaultdict(float) # {string stat_type, float count} self.stats = defaultdict(float) # {string stat_type, float count}
def is_available(self): def is_available(self):
if (self.attempt_connection_at is None or if self.attempt_connection_at is None or datetime.datetime.today() > self.attempt_connection_at:
datetime.datetime.today() > self.attempt_connection_at):
return True return True
return False return False
@ -33,4 +32,4 @@ class Peer(object):
self.stats[stat_type] += count self.stats[stat_type] += count
def __str__(self): def __str__(self):
return self.host + ":" + str(self.port) return self.host + ":" + str(self.port)

View file

@ -0,0 +1,37 @@
from lbrynet.conf import MIN_BLOB_DATA_PAYMENT_RATE
class MeanAvailabilityWeightedPrice(object):
"""
Calculate mean-blob-availability and stream-position weighted price for a blob
Attributes:
min_price (float): minimum accepted price
base_price (float): base price to shift from
alpha (float): constant used to more highly value blobs at the beginning of a stream
alpha defaults to 1.0, which has a null effect
blob_tracker (BlobAvailabilityTracker): blob availability tracker
"""
def __init__(self, tracker, min_price=MIN_BLOB_DATA_PAYMENT_RATE, base_price=None, alpha=1.0):
self.blob_tracker = tracker
self.min_price = min_price
self.base_price = base_price if base_price is not None else min_price * 10
self.alpha = alpha
def calculate_price(self, blob):
mean_availability = self.blob_tracker.last_mean_availability
availability = self.blob_tracker.availability.get(blob, [])
index = 0 # blob.index
price = self.base_price * (mean_availability / max(1, len(availability))) * self._frontload(index)
return round(max(self.min_price, price), 5)
def _frontload(self, index):
"""
Get frontload multipler
@param index: blob position in stream
@return: frontload multipler
"""
return 2.0 - (self.alpha ** index)

View file

@ -9,8 +9,8 @@ from lbrynet.core.client.DHTPeerFinder import DHTPeerFinder
from lbrynet.core.HashAnnouncer import DummyHashAnnouncer from lbrynet.core.HashAnnouncer import DummyHashAnnouncer
from lbrynet.core.server.DHTHashAnnouncer import DHTHashAnnouncer from lbrynet.core.server.DHTHashAnnouncer import DHTHashAnnouncer
from lbrynet.core.utils import generate_id from lbrynet.core.utils import generate_id
from lbrynet.core.PaymentRateManager import BasePaymentRateManager from lbrynet.core.PaymentRateManager import BasePaymentRateManager, NegotiatedPaymentRateManager
from lbrynet.core.BlobPrice import BlobPriceAndAvailabilityTracker from lbrynet.core.BlobAvailability import BlobAvailabilityTracker
from twisted.internet import threads, defer from twisted.internet import threads, defer
@ -29,7 +29,7 @@ class LBRYSession(object):
def __init__(self, blob_data_payment_rate, db_dir=None, lbryid=None, peer_manager=None, dht_node_port=None, 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, known_dht_nodes=None, peer_finder=None, hash_announcer=None,
blob_dir=None, blob_manager=None, peer_port=None, use_upnp=True, blob_dir=None, blob_manager=None, peer_port=None, use_upnp=True,
rate_limiter=None, wallet=None, dht_node_class=node.Node): rate_limiter=None, wallet=None, dht_node_class=node.Node, blob_tracker=None):
""" """
@param blob_data_payment_rate: The default payment rate for blob data @param blob_data_payment_rate: The default payment rate for blob data
@ -88,6 +88,7 @@ class LBRYSession(object):
self.blob_dir = blob_dir self.blob_dir = blob_dir
self.blob_manager = blob_manager self.blob_manager = blob_manager
self.blob_tracker = blob_tracker
self.peer_port = peer_port self.peer_port = peer_port
@ -104,7 +105,7 @@ class LBRYSession(object):
self.dht_node = None self.dht_node = None
self.base_payment_rate_manager = BasePaymentRateManager(blob_data_payment_rate) self.base_payment_rate_manager = BasePaymentRateManager(blob_data_payment_rate)
self.blob_tracker = None self.payment_rate_manager = None
def setup(self): def setup(self):
"""Create the blob directory and database if necessary, start all desired services""" """Create the blob directory and database if necessary, start all desired services"""
@ -260,7 +261,9 @@ class LBRYSession(object):
self.blob_manager = DiskBlobManager(self.hash_announcer, self.blob_dir, self.db_dir) self.blob_manager = DiskBlobManager(self.hash_announcer, self.blob_dir, self.db_dir)
if self.blob_tracker is None: if self.blob_tracker is None:
self.blob_tracker = BlobPriceAndAvailabilityTracker(self.blob_manager, self.peer_finder, self.dht_node) self.blob_tracker = BlobAvailabilityTracker(self.blob_manager, self.peer_finder, self.dht_node)
if self.payment_rate_manager is None:
self.payment_rate_manager = NegotiatedPaymentRateManager(self.base_payment_rate_manager, self.blob_tracker)
self.rate_limiter.start() self.rate_limiter.start()
d1 = self.blob_manager.setup() d1 = self.blob_manager.setup()

78
lbrynet/core/Strategy.py Normal file
View file

@ -0,0 +1,78 @@
import logging
from lbrynet.core.Offer import Offer
from lbrynet.core.PriceModel import MeanAvailabilityWeightedPrice
log = logging.getLogger(__name__)
def get_default_strategy(blob_tracker, **kwargs):
return BasicAvailabilityWeightedStrategy(blob_tracker, **kwargs)
class BasicAvailabilityWeightedStrategy(object):
"""
Basic strategy to target blob prices based on supply relative to mean supply
Discount price target with each incoming request, and raise it with each outgoing from the modeled price
until the rate is accepted or a threshold is reached
"""
def __init__(self, blob_tracker, acceleration=1.25, deceleration=0.9, max_rate=0.005):
self._acceleration = acceleration # rate of how quickly to ramp offer
self._deceleration = deceleration
self._max_rate = max_rate
self._count_up = {}
self._count_down = {}
self._requested = {}
self._offers_to_peers = {}
self.model = MeanAvailabilityWeightedPrice(blob_tracker)
def respond_to_offer(self, offer, peer, blobs):
request_count = self._count_up.get(peer, 0)
rates = [self._calculate_price(blob) for blob in blobs]
rate = self._discount(sum(rates) / max(len(blobs), 1), request_count)
log.info("Target rate: %s", rate)
self._inc_up_count(peer)
if offer.accepted:
return offer
elif offer.rate >= rate:
log.info("Accept: %f", offer.rate)
offer.accept()
return offer
else:
log.info("Reject: %f", offer.rate)
offer.reject()
return offer
def make_offer(self, peer, blobs):
# use mean turn-discounted price for all the blobs requested
request_count = self._count_down.get(peer, 0)
self._inc_down_count(peer)
if request_count == 0:
# Try asking for it for free
offer = Offer(0.0)
else:
rates = [self._calculate_price(blob) for blob in blobs]
mean_rate = sum(rates) / max(len(blobs), 1)
with_premium = self._premium(mean_rate, request_count)
offer = Offer(with_premium)
return offer
def _inc_up_count(self, peer):
turn = self._count_up.get(peer, 0) + 1
self._count_up.update({peer: turn})
def _inc_down_count(self, peer):
turn = self._count_down.get(peer, 0) + 1
self._count_down.update({peer: turn})
def _calculate_price(self, blob):
return self.model.calculate_price(blob)
def _premium(self, rate, turn):
return min(rate * (self._acceleration ** turn), self._max_rate)
def _discount(self, rate, turn):
return min(rate * (self._deceleration ** turn), self._max_rate)

View file

@ -1,14 +1,16 @@
from collections import defaultdict
import logging import logging
from collections import defaultdict
from twisted.internet import defer from twisted.internet import defer
from twisted.python.failure import Failure from twisted.python.failure import Failure
from zope.interface import implements from zope.interface import implements
from lbrynet.core.Error import PriceDisagreementError, DownloadCanceledError, InsufficientFundsError
from lbrynet.core.Error import InvalidResponseError, RequestCanceledError, NoResponseError
from lbrynet.core.Error import ConnectionClosedBeforeResponseError from lbrynet.core.Error import ConnectionClosedBeforeResponseError
from lbrynet.core.Error import InvalidResponseError, RequestCanceledError, NoResponseError
from lbrynet.core.Error import PriceDisagreementError, DownloadCanceledError, InsufficientFundsError
from lbrynet.core.client.ClientRequest import ClientRequest, ClientBlobRequest from lbrynet.core.client.ClientRequest import ClientRequest, ClientBlobRequest
from lbrynet.interfaces import IRequestCreator from lbrynet.interfaces import IRequestCreator
from lbrynet.core.Offer import Negotiate, Offer
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -27,6 +29,7 @@ class BlobRequester(object):
self._unavailable_blobs = defaultdict(list) # {Peer: [blob_hash]}} self._unavailable_blobs = defaultdict(list) # {Peer: [blob_hash]}}
self._protocol_prices = {} # {ClientProtocol: price} self._protocol_prices = {} # {ClientProtocol: price}
self._price_disagreements = [] # [Peer] self._price_disagreements = [] # [Peer]
self._protocol_tries = {}
self._incompatible_peers = [] self._incompatible_peers = []
######## IRequestCreator ######### ######## IRequestCreator #########
@ -46,7 +49,8 @@ class BlobRequester(object):
d1.addCallback(self._handle_availability, peer, a_r) d1.addCallback(self._handle_availability, peer, a_r)
d1.addErrback(self._request_failed, "availability request", peer) d1.addErrback(self._request_failed, "availability request", peer)
sent_request = True sent_request = True
if d_r is not None:
if d_r is not None and protocol in self._protocol_prices:
reserved_points = self._reserve_points(peer, protocol, d_r.max_pay_units) reserved_points = self._reserve_points(peer, protocol, d_r.max_pay_units)
if reserved_points is not None: if reserved_points is not None:
# Note: The following three callbacks will be called when the blob has been # Note: The following three callbacks will be called when the blob has been
@ -68,17 +72,18 @@ class BlobRequester(object):
# downloaded. # downloaded.
d2.addCallback(self._handle_incoming_blob, peer, d_r) d2.addCallback(self._handle_incoming_blob, peer, d_r)
d2.addErrback(self._request_failed, "download request", peer) d2.addErrback(self._request_failed, "download request", peer)
sent_request = True sent_request = True
else: else:
d_r.cancel(InsufficientFundsError()) d_r.cancel(InsufficientFundsError())
d_r.finished_deferred.addErrback(lambda _: True) d_r.finished_deferred.addErrback(lambda _: True)
return defer.fail(InsufficientFundsError()) return defer.fail(InsufficientFundsError())
if sent_request is True: if sent_request is True:
if p_r is not None: if p_r is not None:
d3 = protocol.add_request(p_r) d3 = protocol.add_request(p_r)
d3.addCallback(self._handle_price_response, peer, p_r, protocol) d3.addCallback(self._handle_price_response, peer, p_r, protocol)
d3.addErrback(self._request_failed, "price request", peer) d3.addErrback(self._request_failed, "price request", peer)
return defer.succeed(sent_request) return defer.succeed(sent_request)
def get_new_peers(self): def get_new_peers(self):
@ -88,6 +93,56 @@ class BlobRequester(object):
######### internal calls ######### ######### internal calls #########
def _blobs_to_download(self):
needed_blobs = self.download_manager.needed_blobs()
return sorted(needed_blobs, key=lambda b: b.is_downloading())
def _get_blobs_to_request_from_peer(self, peer):
all_needed = [b.blob_hash for b in self._blobs_to_download() if not b.blob_hash in self._available_blobs[peer]]
# sort them so that the peer will be asked first for blobs it hasn't said it doesn't have
to_request = sorted(all_needed, key=lambda b: b in self._unavailable_blobs[peer])[:20]
return to_request
def _price_settled(self, protocol):
if protocol in self._protocol_prices:
return True
return False
def _get_price_request(self, peer, protocol):
request = None
response_identifier = Negotiate.PAYMENT_RATE
if protocol not in self._protocol_prices:
blobs_to_request = self._available_blobs[peer]
if blobs_to_request:
rate = self.payment_rate_manager.get_rate_blob_data(peer, blobs_to_request)
self._protocol_prices[protocol] = rate
offer = Offer(rate)
request = ClientRequest(Negotiate.make_dict_from_offer(offer), response_identifier)
log.debug("Offer rate %s to %s for %i blobs", str(rate), str(peer), len(blobs_to_request))
else:
log.debug("No blobs to request from %s", str(peer))
return request
def _handle_price_response(self, response_dict, peer, request, protocol):
if not request.response_identifier in response_dict:
return InvalidResponseError("response identifier not in response")
assert protocol in self._protocol_prices
offer = Negotiate.get_offer_from_request(response_dict)
rate = self._protocol_prices[protocol]
if offer.accepted:
log.info("Offered rate %f/mb accepted by %s", rate, str(peer.host))
return True
elif offer.too_low:
log.info("Offered rate %f/mb rejected by %s", rate, str(peer.host))
del self._protocol_prices[protocol]
return True
else:
log.warning("Price disagreement")
log.warning(offer.rate)
del self._protocol_prices[protocol]
self._price_disagreements.append(peer)
return False
def _download_succeeded(self, arg, peer, blob): def _download_succeeded(self, arg, peer, blob):
log.info("Blob %s has been successfully downloaded from %s", str(blob), str(peer)) log.info("Blob %s has been successfully downloaded from %s", str(blob), str(peer))
self._update_local_score(peer, 5.0) self._update_local_score(peer, 5.0)
@ -101,13 +156,13 @@ class BlobRequester(object):
self._update_local_score(peer, -10.0) self._update_local_score(peer, -10.0)
return reason return reason
def _record_blob_aquired(self, blob, host, rate): def _record_blob_acquired(self, blob, host, rate):
self.blob_manager.blob_history_manager.add_transaction(blob, host, rate, upload=False) d = self.blob_manager.add_blob_to_download_history(blob, host, rate)
def _pay_or_cancel_payment(self, arg, protocol, reserved_points, blob): def _pay_or_cancel_payment(self, arg, protocol, reserved_points, blob):
if blob.length != 0 and (not isinstance(arg, Failure) or arg.check(DownloadCanceledError)): if blob.length != 0 and (not isinstance(arg, Failure) or arg.check(DownloadCanceledError)):
self._pay_peer(protocol, blob.length, reserved_points) self._pay_peer(protocol, blob.length, reserved_points)
self._record_blob_aquired(str(blob), protocol.peer.host, reserved_points.amount) self._record_blob_acquired(str(blob), protocol.peer.host, reserved_points.amount)
else: else:
self._cancel_points(reserved_points) self._cancel_points(reserved_points)
@ -178,17 +233,11 @@ class BlobRequester(object):
return True return True
return False return False
def _blobs_to_download(self):
needed_blobs = self.download_manager.needed_blobs()
return sorted(needed_blobs, key=lambda b: b.is_downloading())
def _blobs_without_sources(self): def _blobs_without_sources(self):
return [b for b in self.download_manager.needed_blobs() if not self._hash_available(b.blob_hash)] return [b for b in self.download_manager.needed_blobs() if not self._hash_available(b.blob_hash)]
def _get_availability_request(self, peer): def _get_availability_request(self, peer):
all_needed = [b.blob_hash for b in self._blobs_to_download() if not b.blob_hash in self._available_blobs[peer]] to_request = self._get_blobs_to_request_from_peer(peer)
# sort them so that the peer will be asked first for blobs it hasn't said it doesn't have
to_request = sorted(all_needed, key=lambda b: b in self._unavailable_blobs[peer])[:20]
if to_request: if to_request:
r_dict = {'requested_blobs': to_request} r_dict = {'requested_blobs': to_request}
response_identifier = 'available_blobs' response_identifier = 'available_blobs'
@ -217,36 +266,24 @@ class BlobRequester(object):
request = ClientBlobRequest(request_dict, response_identifier, counting_write_func, d, request = ClientBlobRequest(request_dict, response_identifier, counting_write_func, d,
cancel_func, blob_to_download) cancel_func, blob_to_download)
log.info("Requesting blob %s from %s", str(blob_to_download), str(peer)) # log.info("Requesting blob %s from %s", str(blob_to_download), str(peer))
return request
def _price_settled(self, protocol):
if protocol in self._protocol_prices:
return True
return False
def _get_price_request(self, peer, protocol):
request = None
if not protocol in self._protocol_prices:
self._protocol_prices[protocol] = self.payment_rate_manager.get_rate_blob_data(peer)
request_dict = {'blob_data_payment_rate': self._protocol_prices[protocol]}
request = ClientRequest(request_dict, 'blob_data_payment_rate')
return request return request
def _update_local_score(self, peer, amount): def _update_local_score(self, peer, amount):
self._peers[peer] += amount self._peers[peer] += amount
def _reserve_points(self, peer, protocol, max_bytes): def _reserve_points(self, peer, protocol, max_bytes):
assert protocol in self._protocol_prices if protocol in self._protocol_prices:
points_to_reserve = 1.0 * max_bytes * self._protocol_prices[protocol] / 2**20 points_to_reserve = 1.0 * max_bytes * self._protocol_prices[protocol] / 2 ** 20
return self.wallet.reserve_points(peer, points_to_reserve) return self.wallet.reserve_points(peer, points_to_reserve)
return None
def _pay_peer(self, protocol, num_bytes, reserved_points): def _pay_peer(self, protocol, num_bytes, reserved_points):
assert num_bytes != 0 if num_bytes != 0 and protocol in self._protocol_prices:
assert protocol in self._protocol_prices point_amount = 1.0 * num_bytes * self._protocol_prices[protocol] / 2**20
point_amount = 1.0 * num_bytes * self._protocol_prices[protocol] / 2**20 self.wallet.send_points(reserved_points, point_amount)
self.wallet.send_points(reserved_points, point_amount) self.payment_rate_manager.record_points_paid(point_amount)
self.payment_rate_manager.record_points_paid(point_amount) log.debug("Pay peer %s", str(point_amount))
def _cancel_points(self, reserved_points): def _cancel_points(self, reserved_points):
self.wallet.cancel_point_reservation(reserved_points) self.wallet.cancel_point_reservation(reserved_points)
@ -268,6 +305,7 @@ class BlobRequester(object):
return True return True
def _handle_incoming_blob(self, response_dict, peer, request): def _handle_incoming_blob(self, response_dict, peer, request):
log.debug("Handling incoming blob: %s", str(response_dict))
if not request.response_identifier in response_dict: if not request.response_identifier in response_dict:
return InvalidResponseError("response identifier not in response") return InvalidResponseError("response identifier not in response")
if not type(response_dict[request.response_identifier]) == dict: if not type(response_dict[request.response_identifier]) == dict:
@ -295,18 +333,6 @@ class BlobRequester(object):
return InvalidResponseError("Could not set the length of the blob") return InvalidResponseError("Could not set the length of the blob")
return True return True
def _handle_price_response(self, response_dict, peer, request, protocol):
if not request.response_identifier in response_dict:
return InvalidResponseError("response identifier not in response")
assert protocol in self._protocol_prices
response = response_dict[request.response_identifier]
if response == "RATE_ACCEPTED":
return True
else:
del self._protocol_prices[protocol]
self._price_disagreements.append(peer)
return True
def _request_failed(self, reason, request_type, peer): def _request_failed(self, reason, request_type, peer):
if reason.check(RequestCanceledError): if reason.check(RequestCanceledError):
return return

View file

@ -191,7 +191,9 @@ class ClientProtocol(Protocol):
for success, result in results: for success, result in results:
if success is False: if success is False:
failed = True failed = True
log.info("The connection is closing due to an error: %s", str(result.getTraceback())) if not isinstance(result.value, DownloadCanceledError):
log.info(result.value)
log.info("The connection is closing due to an error: %s", str(result.getTraceback()))
if failed is False: if failed is False:
log.debug("Asking for another request.") log.debug("Asking for another request.")
from twisted.internet import reactor from twisted.internet import reactor
@ -215,7 +217,7 @@ class ClientProtocol(Protocol):
# TODO: always be this way. it's done this way now because the client has no other way # TODO: always be this way. it's done this way now because the client has no other way
# TODO: of telling the server it wants the download to stop. It would be great if the # TODO: of telling the server it wants the download to stop. It would be great if the
# TODO: protocol had such a mechanism. # TODO: protocol had such a mechanism.
log.info("Closing the connection to %s because the download of blob %s was canceled", log.debug("Closing the connection to %s because the download of blob %s was canceled",
str(self.peer), str(self._blob_download_request.blob)) str(self.peer), str(self._blob_download_request.blob))
#self.transport.loseConnection() #self.transport.loseConnection()
#return True #return True

View file

@ -78,8 +78,7 @@ def disable_third_party_loggers():
def disable_noisy_loggers(): def disable_noisy_loggers():
logging.getLogger('BitcoinRPC').setLevel(logging.INFO) logging.getLogger('BitcoinRPC').setLevel(logging.INFO)
logging.getLogger('lbrynet.analytics.api').setLevel(logging.INFO) logging.getLogger('lbrynet.analytics.api').setLevel(logging.INFO)
logging.getLogger('lbrynet.core.client').setLevel(logging.INFO) logging.getLogger('lbrynet.core').setLevel(logging.INFO)
logging.getLogger('lbrynet.core.server').setLevel(logging.INFO)
logging.getLogger('lbrynet.dht').setLevel(logging.INFO) logging.getLogger('lbrynet.dht').setLevel(logging.INFO)
logging.getLogger('lbrynet.lbrynet_daemon').setLevel(logging.INFO) logging.getLogger('lbrynet.lbrynet_daemon').setLevel(logging.INFO)
logging.getLogger('lbrynet.core.LBRYWallet').setLevel(logging.INFO) logging.getLogger('lbrynet.core.LBRYWallet').setLevel(logging.INFO)

View file

@ -40,7 +40,7 @@ class BlobAvailabilityHandler(object):
def handle_queries(self, queries): def handle_queries(self, queries):
if self.query_identifiers[0] in queries: if self.query_identifiers[0] in queries:
log.debug("Received the client's list of requested blobs") log.info("Received the client's list of requested blobs")
d = self._get_available_blobs(queries[self.query_identifiers[0]]) d = self._get_available_blobs(queries[self.query_identifiers[0]])
def set_field(available_blobs): def set_field(available_blobs):

View file

@ -1,8 +1,12 @@
import logging import logging
from twisted.internet import defer from twisted.internet import defer
from twisted.protocols.basic import FileSender from twisted.protocols.basic import FileSender
from twisted.python.failure import Failure from twisted.python.failure import Failure
from zope.interface import implements from zope.interface import implements
from lbrynet.core.Offer import Offer, Negotiate
from lbrynet.core.Strategy import get_default_strategy
from lbrynet.interfaces import IQueryHandlerFactory, IQueryHandler, IBlobSender from lbrynet.interfaces import IQueryHandlerFactory, IQueryHandler, IBlobSender
@ -12,7 +16,8 @@ log = logging.getLogger(__name__)
class BlobRequestHandlerFactory(object): class BlobRequestHandlerFactory(object):
implements(IQueryHandlerFactory) implements(IQueryHandlerFactory)
def __init__(self, blob_manager, wallet, payment_rate_manager): def __init__(self, blob_manager, blob_tracker, wallet, payment_rate_manager):
self.blob_tracker = blob_tracker
self.blob_manager = blob_manager self.blob_manager = blob_manager
self.wallet = wallet self.wallet = wallet
self.payment_rate_manager = payment_rate_manager self.payment_rate_manager = payment_rate_manager
@ -20,7 +25,7 @@ class BlobRequestHandlerFactory(object):
######### IQueryHandlerFactory ######### ######### IQueryHandlerFactory #########
def build_query_handler(self): def build_query_handler(self):
q_h = BlobRequestHandler(self.blob_manager, self.wallet, self.payment_rate_manager) q_h = BlobRequestHandler(self.blob_manager, self.blob_tracker, self.wallet, self.payment_rate_manager)
return q_h return q_h
def get_primary_query_identifier(self): def get_primary_query_identifier(self):
@ -33,17 +38,20 @@ class BlobRequestHandlerFactory(object):
class BlobRequestHandler(object): class BlobRequestHandler(object):
implements(IQueryHandler, IBlobSender) implements(IQueryHandler, IBlobSender)
def __init__(self, blob_manager, wallet, payment_rate_manager): def __init__(self, blob_manager, blob_tracker, wallet, payment_rate_manager):
self.blob_manager = blob_manager self.blob_manager = blob_manager
self.blob_tracker = blob_tracker
self.payment_rate_manager = payment_rate_manager self.payment_rate_manager = payment_rate_manager
self.wallet = wallet self.wallet = wallet
self.query_identifiers = ['blob_data_payment_rate', 'requested_blob'] self.query_identifiers = ['blob_data_payment_rate', 'requested_blob', 'requested_blobs']
self.peer = None self.peer = None
self.blob_data_payment_rate = None self.blob_data_payment_rate = None
self.read_handle = None self.read_handle = None
self.currently_uploading = None self.currently_uploading = None
self.file_sender = None self.file_sender = None
self.blob_bytes_uploaded = 0 self.blob_bytes_uploaded = 0
self.strategy = get_default_strategy(self.blob_tracker)
self._blobs_requested = []
######### IQueryHandler ######### ######### IQueryHandler #########
@ -53,52 +61,22 @@ class BlobRequestHandler(object):
request_handler.register_blob_sender(self) request_handler.register_blob_sender(self)
def handle_queries(self, queries): def handle_queries(self, queries):
response = {} response = defer.succeed({})
if self.query_identifiers[2] in queries:
self._blobs_requested = queries[self.query_identifiers[2]]
response.addCallback(lambda r: self._reply_to_availability(r, self._blobs_requested))
if self.query_identifiers[0] in queries: if self.query_identifiers[0] in queries:
requested_rate = queries[self.query_identifiers[0]] offer = Offer(queries[self.query_identifiers[0]])
if not self.handle_blob_data_payment_rate(requested_rate): response.addCallback(lambda r: self.reply_to_offer(offer, r))
response['blob_data_payment_rate'] = "RATE_TOO_LOW"
else:
response['blob_data_payment_rate'] = 'RATE_ACCEPTED'
log.debug(response['blob_data_payment_rate'])
if self.query_identifiers[1] in queries: if self.query_identifiers[1] in queries:
log.debug("Received the client's request to send a blob") incoming = queries[self.query_identifiers[1]]
response_fields = {} log.info("Request download: %s", str(incoming))
response['incoming_blob'] = response_fields response.addCallback(lambda r: self._reply_to_send_request({}, incoming))
if self.blob_data_payment_rate is None: return response
response_fields['error'] = "RATE_UNSET"
return defer.succeed(response)
else:
d = self.blob_manager.get_blob(queries[self.query_identifiers[1]], True)
def open_blob_for_reading(blob):
if blob.is_validated():
read_handle = blob.open_for_reading()
if read_handle is not None:
self.currently_uploading = blob
self.read_handle = read_handle
log.info("Sending %s to client", str(blob))
response_fields['blob_hash'] = blob.blob_hash
response_fields['length'] = blob.length
return response
log.warning("We can not send %s", str(blob))
response_fields['error'] = "BLOB_UNAVAILABLE"
return response, blob
def record_transaction(response, blob, rate):
d = self.blob_manager.blob_history_manager.add_transaction(str(blob), self.peer.host, rate, upload=True)
d.addCallback(lambda _: response)
return d
d.addCallback(open_blob_for_reading)
d.addCallback(lambda (response, blob): record_transaction(response, blob, queries[self.query_identifiers[0]]))
return d
else:
return defer.succeed(response)
######### IBlobSender ######### ######### IBlobSender #########
@ -116,12 +94,72 @@ class BlobRequestHandler(object):
######### internal ######### ######### internal #########
def handle_blob_data_payment_rate(self, requested_payment_rate): def _add_to_response(self, response, to_add):
if not self.payment_rate_manager.accept_rate_blob_data(self.peer, requested_payment_rate):
return False return response
def _reply_to_availability(self, request, blobs):
d = self._get_available_blobs(blobs)
def set_available(available_blobs):
log.debug("available blobs: %s", str(available_blobs))
request.update({'available_blobs': available_blobs})
return request
d.addCallback(set_available)
return d
def open_blob_for_reading(self, blob, response):
response_fields = {}
if blob.is_validated():
read_handle = blob.open_for_reading()
if read_handle is not None:
self.currently_uploading = blob
self.read_handle = read_handle
log.info("Sending %s to client", str(blob))
response_fields['blob_hash'] = blob.blob_hash
response_fields['length'] = blob.length
response['incoming_blob'] = response_fields
log.info(response)
return response, blob
log.warning("We can not send %s", str(blob))
response['error'] = "BLOB_UNAVAILABLE"
return response, blob
def record_transaction(self, response, blob, rate):
d = self.blob_manager.add_blob_to_upload_history(str(blob), self.peer.host, rate)
d.addCallback(lambda _: response)
log.info(response)
return d
def _reply_to_send_request(self, response, incoming):
response_fields = {}
response['incoming_blob'] = response_fields
rate = self.blob_data_payment_rate
if self.blob_data_payment_rate is None:
log.warning("Rate not set yet")
response['error'] = "RATE_UNSET"
return defer.succeed(response)
else: else:
self.blob_data_payment_rate = requested_payment_rate d = self.blob_manager.get_blob(incoming, True)
return True d.addCallback(lambda blob: self.open_blob_for_reading(blob, response))
d.addCallback(lambda (r, blob): self.record_transaction(r, blob, rate))
return d
def reply_to_offer(self, offer, request):
blobs = request.get("available_blobs", [])
log.info("Offered rate %f/mb for %i blobs", offer.rate, len(blobs))
reply = self.strategy.respond_to_offer(offer, self.peer, blobs)
if reply.accepted:
self.blob_data_payment_rate = reply.rate
r = Negotiate.make_dict_from_offer(reply)
request.update(r)
return request
def _get_available_blobs(self, requested_blobs):
d = self.blob_manager.completed_blobs(requested_blobs)
return d
def send_file(self, consumer): def send_file(self, consumer):
@ -140,7 +178,7 @@ class BlobRequestHandler(object):
def start_transfer(): def start_transfer():
self.file_sender = FileSender() self.file_sender = FileSender()
log.debug("Starting the file upload") log.info("Starting the file upload")
assert self.read_handle is not None, "self.read_handle was None when trying to start the transfer" assert self.read_handle is not None, "self.read_handle was None when trying to start the transfer"
d = self.file_sender.beginFileTransfer(self.read_handle, consumer, count_bytes) d = self.file_sender.beginFileTransfer(self.read_handle, consumer, count_bytes)
return d return d

View file

@ -48,7 +48,7 @@ class CryptStreamDownloader(object):
@param blob_manager: A BlobManager object @param blob_manager: A BlobManager object
@param payment_rate_manager: A PaymentRateManager object @param payment_rate_manager: A NegotiatedPaymentRateManager object
@param wallet: An object which implements the ILBRYWallet interface @param wallet: An object which implements the ILBRYWallet interface

View file

@ -12,7 +12,7 @@ from twisted.python.failure import Failure
from lbrynet.lbryfilemanager.LBRYFileDownloader import ManagedLBRYFileDownloader from lbrynet.lbryfilemanager.LBRYFileDownloader import ManagedLBRYFileDownloader
from lbrynet.lbryfilemanager.LBRYFileDownloader import ManagedLBRYFileDownloaderFactory from lbrynet.lbryfilemanager.LBRYFileDownloader import ManagedLBRYFileDownloaderFactory
from lbrynet.lbryfile.StreamDescriptor import LBRYFileStreamType from lbrynet.lbryfile.StreamDescriptor import LBRYFileStreamType
from lbrynet.core.PaymentRateManager import PaymentRateManager from lbrynet.core.PaymentRateManager import NegotiatedPaymentRateManager
from lbrynet.cryptstream.client.CryptStreamDownloader import AlreadyStoppedError, CurrentlyStoppingError from lbrynet.cryptstream.client.CryptStreamDownloader import AlreadyStoppedError, CurrentlyStoppingError
from lbrynet.core.sqlite_helpers import rerun_if_locked from lbrynet.core.sqlite_helpers import rerun_if_locked
@ -74,7 +74,8 @@ class LBRYFileManager(object):
def _start_lbry_files(self): def _start_lbry_files(self):
def set_options_and_restore(rowid, stream_hash, options): def set_options_and_restore(rowid, stream_hash, options):
payment_rate_manager = PaymentRateManager(self.session.base_payment_rate_manager) payment_rate_manager = NegotiatedPaymentRateManager(self.session.base_payment_rate_manager,
self.session.blob_tracker)
d = self.start_lbry_file(rowid, stream_hash, payment_rate_manager, d = self.start_lbry_file(rowid, stream_hash, payment_rate_manager,
blob_data_rate=options) blob_data_rate=options)
d.addCallback(lambda downloader: downloader.restore()) d.addCallback(lambda downloader: downloader.restore())

View file

@ -644,7 +644,7 @@ class AddStream(CommandHandler):
for option, option_value in zip(self.download_options, self.options_chosen): for option, option_value in zip(self.download_options, self.options_chosen):
if option.short_description == "data payment rate": if option.short_description == "data payment rate":
if option_value == None: if option_value == None:
rate = self.payment_rate_manager.get_effective_min_blob_data_payment_rate() rate = 0.0
else: else:
rate = option_value rate = option_value
stream_size = None stream_size = None

View file

@ -27,8 +27,7 @@ from txjsonrpc.web.jsonrpc import Handler
from lbrynet import __version__ as lbrynet_version from lbrynet import __version__ as lbrynet_version
from lbryum.version import LBRYUM_VERSION as lbryum_version from lbryum.version import LBRYUM_VERSION as lbryum_version
from lbrynet import analytics from lbrynet import analytics
from lbrynet.core.PaymentRateManager import PaymentRateManager from lbrynet.core.PaymentRateManager import NegotiatedPaymentRateManager
from lbrynet.core.server.BlobAvailabilityHandler import BlobAvailabilityHandlerFactory
from lbrynet.core.server.BlobRequestHandler import BlobRequestHandlerFactory from lbrynet.core.server.BlobRequestHandler import BlobRequestHandlerFactory
from lbrynet.core.server.ServerProtocol import ServerProtocolFactory from lbrynet.core.server.ServerProtocol import ServerProtocolFactory
from lbrynet.core.Error import UnknownNameError, InsufficientFundsError, InvalidNameError from lbrynet.core.Error import UnknownNameError, InsufficientFundsError, InvalidNameError
@ -545,7 +544,6 @@ class LBRYDaemon(jsonrpc.JSONRPC):
d.addCallback(lambda _: add_lbry_file_to_sd_identifier(self.sd_identifier)) d.addCallback(lambda _: add_lbry_file_to_sd_identifier(self.sd_identifier))
d.addCallback(lambda _: self._setup_stream_identifier()) d.addCallback(lambda _: self._setup_stream_identifier())
d.addCallback(lambda _: self._setup_lbry_file_manager()) d.addCallback(lambda _: self._setup_lbry_file_manager())
d.addCallback(lambda _: self._setup_lbry_file_opener())
d.addCallback(lambda _: self._setup_query_handlers()) d.addCallback(lambda _: self._setup_query_handlers())
d.addCallback(lambda _: self._setup_server()) d.addCallback(lambda _: self._setup_server())
d.addCallback(lambda _: _log_starting_vals()) d.addCallback(lambda _: _log_starting_vals())
@ -776,17 +774,15 @@ class LBRYDaemon(jsonrpc.JSONRPC):
handlers = [ handlers = [
# CryptBlobInfoQueryHandlerFactory(self.lbry_file_metadata_manager, self.session.wallet, # CryptBlobInfoQueryHandlerFactory(self.lbry_file_metadata_manager, self.session.wallet,
# self._server_payment_rate_manager), # self._server_payment_rate_manager),
BlobAvailabilityHandlerFactory(self.session.blob_manager), # BlobAvailabilityHandlerFactory(self.session.blob_manager),
# BlobRequestHandlerFactory(self.session.blob_manager, self.session.wallet, BlobRequestHandlerFactory(self.session.blob_manager, self.session.blob_tracker, self.session.wallet,
# self._server_payment_rate_manager), self.session.payment_rate_manager),
self.session.wallet.get_wallet_info_query_handler_factory(), self.session.wallet.get_wallet_info_query_handler_factory(),
] ]
def get_blob_request_handler_factory(rate): def get_blob_request_handler_factory(rate):
self.blob_request_payment_rate_manager = PaymentRateManager( self.blob_request_payment_rate_manager = self.session.payment_rate_manager
self.session.base_payment_rate_manager, rate handlers.append(BlobRequestHandlerFactory(self.session.blob_manager, self.session.blob_tracker, self.session.wallet,
)
handlers.append(BlobRequestHandlerFactory(self.session.blob_manager, self.session.wallet,
self.blob_request_payment_rate_manager)) self.blob_request_payment_rate_manager))
d1 = self.settings.get_server_data_payment_rate() d1 = self.settings.get_server_data_payment_rate()
@ -1097,14 +1093,6 @@ class LBRYDaemon(jsonrpc.JSONRPC):
self.sd_identifier.add_stream_downloader_factory(LBRYFileStreamType, file_opener_factory) self.sd_identifier.add_stream_downloader_factory(LBRYFileStreamType, file_opener_factory)
return defer.succeed(None) return defer.succeed(None)
def _setup_lbry_file_opener(self):
downloader_factory = LBRYFileOpenerFactory(self.session.peer_finder, self.session.rate_limiter,
self.session.blob_manager, self.stream_info_manager,
self.session.wallet)
self.sd_identifier.add_stream_downloader_factory(LBRYFileStreamType, downloader_factory)
return defer.succeed(True)
def _download_sd_blob(self, sd_hash, timeout=DEFAULT_SD_DOWNLOAD_TIMEOUT): def _download_sd_blob(self, sd_hash, timeout=DEFAULT_SD_DOWNLOAD_TIMEOUT):
def cb(result): def cb(result):
if not r.called: if not r.called:
@ -1116,7 +1104,7 @@ class LBRYDaemon(jsonrpc.JSONRPC):
r = defer.Deferred(None) r = defer.Deferred(None)
reactor.callLater(timeout, eb) reactor.callLater(timeout, eb)
d = download_sd_blob(self.session, sd_hash, PaymentRateManager(self.session.base_payment_rate_manager)) d = download_sd_blob(self.session, sd_hash, self.session.payment_rate_manager)
d.addCallback(BlobStreamDescriptorReader) d.addCallback(BlobStreamDescriptorReader)
d.addCallback(lambda blob: blob.get_info()) d.addCallback(lambda blob: blob.get_info())
d.addCallback(cb) d.addCallback(cb)
@ -2565,6 +2553,20 @@ class LBRYDaemon(jsonrpc.JSONRPC):
return d return d
def jsonrpc_get_mean_availability(self):
"""
Get mean blob availability
Args:
None
Returns:
Mean peers for a blob
"""
d = self._render_response(self.session.blob_tracker.last_mean_availability, OK_CODE)
return d
def get_lbrynet_version_from_github(): def get_lbrynet_version_from_github():
"""Return the latest released version from github.""" """Return the latest released version from github."""
response = requests.get('https://api.github.com/repos/lbryio/lbry/releases/latest') response = requests.get('https://api.github.com/repos/lbryio/lbry/releases/latest')

View file

@ -9,7 +9,6 @@ from twisted.internet import defer
from twisted.internet.task import LoopingCall from twisted.internet.task import LoopingCall
from lbrynet.core.Error import InsufficientFundsError, KeyFeeAboveMaxAllowed from lbrynet.core.Error import InsufficientFundsError, KeyFeeAboveMaxAllowed
from lbrynet.core.PaymentRateManager import PaymentRateManager
from lbrynet.core.StreamDescriptor import download_sd_blob from lbrynet.core.StreamDescriptor import download_sd_blob
from lbrynet.metadata.LBRYFee import LBRYFeeValidator from lbrynet.metadata.LBRYFee import LBRYFeeValidator
from lbrynet.lbryfilemanager.LBRYFileDownloader import ManagedLBRYFileDownloaderFactory from lbrynet.lbryfilemanager.LBRYFileDownloader import ManagedLBRYFileDownloaderFactory
@ -52,7 +51,7 @@ class GetStream(object):
self.file_name = file_name self.file_name = file_name
self.session = session self.session = session
self.exchange_rate_manager = exchange_rate_manager self.exchange_rate_manager = exchange_rate_manager
self.payment_rate_manager = PaymentRateManager(self.session.base_payment_rate_manager) self.payment_rate_manager = self.session.payment_rate_manager
self.lbry_file_manager = lbry_file_manager self.lbry_file_manager = lbry_file_manager
self.sd_identifier = sd_identifier self.sd_identifier = sd_identifier
self.stream_hash = None self.stream_hash = None

View file

@ -9,7 +9,6 @@ from appdirs import user_data_dir
from lbrynet.core.Error import InsufficientFundsError from lbrynet.core.Error import InsufficientFundsError
from lbrynet.lbryfilemanager.LBRYFileCreator import create_lbry_file from lbrynet.lbryfilemanager.LBRYFileCreator import create_lbry_file
from lbrynet.lbryfile.StreamDescriptor import publish_sd_blob from lbrynet.lbryfile.StreamDescriptor import publish_sd_blob
from lbrynet.core.PaymentRateManager import PaymentRateManager
from lbrynet.metadata.LBRYMetadata import Metadata from lbrynet.metadata.LBRYMetadata import Metadata
from lbrynet.lbryfilemanager.LBRYFileDownloader import ManagedLBRYFileDownloader from lbrynet.lbryfilemanager.LBRYFileDownloader import ManagedLBRYFileDownloader
from lbrynet import reflector from lbrynet import reflector
@ -102,7 +101,7 @@ class Publisher(object):
def add_to_lbry_files(self, stream_hash): def add_to_lbry_files(self, stream_hash):
self.stream_hash = stream_hash self.stream_hash = stream_hash
prm = PaymentRateManager(self.session.base_payment_rate_manager) prm = self.session.payment_rate_manager
d = self.lbry_file_manager.add_lbry_file(stream_hash, prm) d = self.lbry_file_manager.add_lbry_file(stream_hash, prm)
d.addCallback(self.set_lbry_file) d.addCallback(self.set_lbry_file)
return d return d