diff --git a/lbrynet/lbrynet_daemon/Daemon.py b/lbrynet/lbrynet_daemon/Daemon.py index ba6c60bac..13535a767 100644 --- a/lbrynet/lbrynet_daemon/Daemon.py +++ b/lbrynet/lbrynet_daemon/Daemon.py @@ -974,7 +974,7 @@ class Daemon(AuthJSONRPCServer): d.addCallback(lambda info: int(dict(info)['stream_size'])) return d - def get_est_cost_from_stream_size(self, size): + def _get_est_cost_from_stream_size(self, size): """ Calculate estimated LBC cost for a stream given its size in bytes """ @@ -983,6 +983,17 @@ class Daemon(AuthJSONRPCServer): return 0.0 return size / (10**6) * conf.settings.data_rate + def get_est_cost_using_known_size(self, name, size): + """ + Calculate estimated LBC cost for a stream given its size in bytes + """ + + cost = self._get_est_cost_from_stream_size(size) + + d = self._resolve_name(name) + d.addCallback(lambda metadata: self._add_key_fee_to_est_data_cost(metadata, cost)) + return d + def get_est_cost_from_sd_hash(self, sd_hash): """ Get estimated cost from a sd hash @@ -990,7 +1001,7 @@ class Daemon(AuthJSONRPCServer): d = self.get_or_download_sd_blob(sd_hash) d.addCallback(self.get_size_from_sd_blob) - d.addCallback(self.get_est_cost_from_stream_size) + d.addCallback(self._get_est_cost_from_stream_size) return d def _get_est_cost_from_metadata(self, metadata, name): @@ -1003,14 +1014,14 @@ class Daemon(AuthJSONRPCServer): raise err d.addErrback(_handle_err) - - def _add_key_fee(data_cost): - fee = self.exchange_rate_manager.to_lbc(metadata.get('fee', None)) - return data_cost if fee is None else data_cost + fee.amount - - d.addCallback(_add_key_fee) + d.addCallback(lambda data_cost: self._add_key_fee_to_est_data_cost(metadata, data_cost)) return d + def _add_key_fee_to_est_data_cost(self, metadata, data_cost): + fee = self.exchange_rate_manager.to_lbc(metadata.get('fee', None)) + fee_amount = 0.0 if fee is None else fee.amount + return data_cost + fee_amount + def get_est_cost_from_name(self, name): """ Resolve a name and return the estimated stream cost @@ -1020,15 +1031,15 @@ class Daemon(AuthJSONRPCServer): d.addCallback(self._get_est_cost_from_metadata, name) return d - def get_est_cost(self, name=None, size=None): + + def get_est_cost(self, name, size=None): """ - Get a cost estimate for a lbry stream, requires either a name to check or a given stream size in bytes - If no size is + Get a cost estimate for a lbry stream, if size is not provided the sd blob will be downloaded + to determine the stream size """ - if name is None and size is None: - return defer.fail(Exception("Neither name nor size was provided")) + if size is not None: - return defer.succeed(self.get_est_cost_from_stream_size(size)) + return self.get_est_cost_using_known_size(name, size) return self.get_est_cost_from_name(name) def _get_lbry_file_by_uri(self, name): diff --git a/tests/unit/lbrynet_daemon/test_Daemon.py b/tests/unit/lbrynet_daemon/test_Daemon.py index cb96e41a1..e6e34053f 100644 --- a/tests/unit/lbrynet_daemon/test_Daemon.py +++ b/tests/unit/lbrynet_daemon/test_Daemon.py @@ -1,8 +1,14 @@ import mock import requests +from tests.mocks import BlobAvailabilityTracker as DummyBlobAvailabilityTracker +from tests import util +from twisted.internet import defer from twisted.trial import unittest - from lbrynet.lbrynet_daemon import Daemon +from lbrynet.core import Session, PaymentRateManager +from lbrynet.lbrynet_daemon.Daemon import Daemon as LBRYDaemon +from lbrynet.lbrynet_daemon import ExchangeRateManager +from lbrynet import conf class MiscTests(unittest.TestCase): @@ -36,3 +42,63 @@ class MiscTests(unittest.TestCase): def test_error_is_thrown_when_version_cant_be_parsed(self): with self.assertRaises(Exception): Daemon.get_version_from_tag('garbage') + + +def get_test_daemon(data_rate=conf.settings.data_rate, generous=True, with_fee=False): + rates = { + 'BTCLBC': {'spot': 3.0, 'ts': util.DEFAULT_ISO_TIME + 1}, + 'USDBTC': {'spot': 2.0, 'ts': util.DEFAULT_ISO_TIME + 2} + } + daemon = LBRYDaemon(None, None) + daemon.session = mock.Mock(spec=Session.Session) + daemon.exchange_rate_manager = ExchangeRateManager.DummyExchangeRateManager(rates) + base_prm = PaymentRateManager.BasePaymentRateManager(rate=data_rate) + prm = PaymentRateManager.NegotiatedPaymentRateManager(base_prm, DummyBlobAvailabilityTracker(), generous=generous) + daemon.session.payment_rate_manager = prm + metadata = { + "author": "extra", + "content_type": "video/mp4", + "description": "How did the ancient civilization of Sumer first develop the concept of the written word? It all began with simple warehouse tallies in the temples, but as the scribes sought more simple ways to record information, those tallies gradually evolved from pictograms into cuneiform text which could be used to convey complex, abstract, or even lyrical ideas.", + "language": "en", + "license": "Creative Commons Attribution 3.0 United States", + "license_url": "https://creativecommons.org/licenses/by/3.0/us/legalcode", + "nsfw": False, + "sources": { + "lbry_sd_hash": "d2b8b6e907dde95245fe6d144d16c2fdd60c4e0c6463ec98b85642d06d8e9414e8fcfdcb7cb13532ec5454fb8fe7f280"}, + "thumbnail": "http://i.imgur.com/HFSRkKw.png", + "title": "The History of Writing - Where the Story Begins", + "ver": "0.0.3" + } + if with_fee: + metadata.update({"fee": {"USD": {"address": "bQ6BGboPV2SpTMEP7wLNiAcnsZiH8ye6eA", "amount": 0.75}}}) + daemon._resolve_name = lambda x: defer.succeed(metadata) + return daemon + + +class TestCostEst(unittest.TestCase): + def setUp(self): + util.resetTime(self) + + def test_cost_est_with_fee_and_generous(self): + size = 10000000 + fake_fee_amount = 4.5 + daemon = get_test_daemon(generous=True, with_fee=True) + self.assertEquals(daemon.get_est_cost("test", size).result, fake_fee_amount) + + def test_cost_est_with_fee_and_not_generous(self): + size = 10000000 + fake_fee_amount = 4.5 + data_rate = conf.settings.data_rate + daemon = get_test_daemon(generous=False, with_fee=True) + self.assertEquals(daemon.get_est_cost("test", size).result, (size / (10**6) * data_rate) + fake_fee_amount) + + def test_data_cost_with_generous(self): + size = 10000000 + daemon = get_test_daemon(generous=True) + self.assertEquals(daemon.get_est_cost("test", size).result, 0.0) + + def test_data_cost_with_non_generous(self): + size = 10000000 + data_rate = conf.settings.data_rate + daemon = get_test_daemon(generous=False) + self.assertEquals(daemon.get_est_cost("test", size).result, (size / (10**6) * data_rate))