diff --git a/lbrynet/conf.py b/lbrynet/conf.py index 9c90cdeba..cf270ec92 100644 --- a/lbrynet/conf.py +++ b/lbrynet/conf.py @@ -498,7 +498,7 @@ class Config(CLIConfig): ) max_key_fee = MaxKeyFee( "Don't download streams with fees exceeding this amount", {'currency': 'USD', 'amount': 50.0} - ) # TODO: use this + ) # reflector settings reflect_streams = Toggle( diff --git a/lbrynet/extras/daemon/exchange_rate_manager.py b/lbrynet/extras/daemon/exchange_rate_manager.py index 30121729a..b304f265f 100644 --- a/lbrynet/extras/daemon/exchange_rate_manager.py +++ b/lbrynet/extras/daemon/exchange_rate_manager.py @@ -219,7 +219,7 @@ class ExchangeRateManager: def convert_currency(self, from_currency, to_currency, amount): rates = [market.rate for market in self.market_feeds] - log.info("Converting %f %s to %s, rates: %s" % (amount, from_currency, to_currency, rates)) + log.debug("Converting %f %s to %s, rates: %s" % (amount, from_currency, to_currency, rates)) if from_currency == to_currency: return amount diff --git a/lbrynet/stream/stream_manager.py b/lbrynet/stream/stream_manager.py index b31882793..1a9d77ef6 100644 --- a/lbrynet/stream/stream_manager.py +++ b/lbrynet/stream/stream_manager.py @@ -4,7 +4,7 @@ import typing import binascii import logging import random -from lbrynet.error import ResolveError, InvalidStreamDescriptorError +from lbrynet.error import ResolveError, InvalidStreamDescriptorError, KeyFeeAboveMaxAllowed, InsufficientFundsError from lbrynet.stream.downloader import StreamDownloader from lbrynet.stream.managed_stream import ManagedStream from lbrynet.schema.claim import ClaimDict @@ -287,9 +287,6 @@ class StreamManager: already_started = tuple(filter(lambda s: s.descriptor.sd_hash == sd_hash, self.streams)) if already_started: return already_started[0] - if should_pay and fee_address and fee_amount and fee_amount > await self.wallet.default_account.get_balance(): - raise Exception("not enough funds") - self.starting_streams[sd_hash] = asyncio.Future(loop=self.loop) stream_task = self.loop.create_task( self._download_stream_from_claim(node, self.config.download_dir, claim_info, file_name) @@ -354,7 +351,7 @@ class StreamManager: timeout = timeout or self.config.download_timeout parsed_uri = parse_lbry_uri(uri) if parsed_uri.is_channel: - raise Exception("cannot download a channel claim, specify a /path") + raise ResolveError("cannot download a channel claim, specify a /path") resolved = (await self.wallet.resolve(uri)).get(uri, {}) resolved = resolved if 'value' in resolved else resolved.get('claim') @@ -372,6 +369,19 @@ class StreamManager: fee_amount = round(exchange_rate_manager.convert_currency( claim.source_fee.currency, "LBC", claim.source_fee.amount ), 5) + max_fee_amount = round(exchange_rate_manager.convert_currency( + self.config.max_key_fee['currency'], "LBC", self.config.max_key_fee['amount'] + ), 5) + if fee_amount > max_fee_amount: + msg = f"fee of {fee_amount} exceeds max configured to allow of {max_fee_amount}" + log.warning(msg) + raise KeyFeeAboveMaxAllowed(msg) + else: + balance = await self.wallet.default_account.get_balance() + if fee_amount > balance: + msg = f"fee of {fee_amount} exceeds max available balance" + log.warning(msg) + raise InsufficientFundsError(msg) fee_address = claim.source_fee.address.decode() outpoint = f"{resolved['txid']}:{resolved['nout']}" existing = self.get_filtered_streams(outpoint=outpoint) diff --git a/tests/unit/lbrynet_daemon/test_ExchangeRateManager.py b/tests/unit/lbrynet_daemon/test_ExchangeRateManager.py index 3eff3a17d..928ea618c 100644 --- a/tests/unit/lbrynet_daemon/test_ExchangeRateManager.py +++ b/tests/unit/lbrynet_daemon/test_ExchangeRateManager.py @@ -40,7 +40,7 @@ def get_dummy_exchange_rate_manager(time): 'BTCLBC': {'spot': 3.0, 'ts': time.time() + 1}, 'USDBTC': {'spot': 2.0, 'ts': time.time() + 2} } - return DummyExchangeRateManager([BTCLBCFeed()], rates) + return DummyExchangeRateManager([BTCLBCFeed(), USDBTCFeed()], rates) class FeeFormatTest(unittest.TestCase): diff --git a/tests/unit/stream/test_stream_manager.py b/tests/unit/stream/test_stream_manager.py index e8fa67670..a2076c15d 100644 --- a/tests/unit/stream/test_stream_manager.py +++ b/tests/unit/stream/test_stream_manager.py @@ -5,7 +5,7 @@ import asyncio import time from tests.unit.blob_exchange.test_transfer_blob import BlobExchangeTestBase from tests.unit.lbrynet_daemon.test_ExchangeRateManager import get_dummy_exchange_rate_manager - +from lbrynet.error import InsufficientFundsError, KeyFeeAboveMaxAllowed from lbrynet.extras.wallet.manager import LbryWalletManager from lbrynet.stream.stream_manager import StreamManager from lbrynet.stream.descriptor import StreamDescriptor @@ -26,7 +26,7 @@ def get_mock_node(peer): return mock_node -def get_mock_wallet(sd_hash, storage): +def get_mock_wallet(sd_hash, storage, balance=10.0, fee=None): claim = { "address": "bYFeMtSL7ARuG1iMpjFyrnTe4oJHSAVNXF", "amount": "0.1", @@ -69,6 +69,8 @@ def get_mock_wallet(sd_hash, storage): "version": "_0_0_1" } } + if fee: + claim['value']['stream']['metadata']['fee'] = fee claim_dict = ClaimDict.load_dict(claim['value']) claim['hex'] = binascii.hexlify(claim_dict.serialized).decode() @@ -80,6 +82,11 @@ def get_mock_wallet(sd_hash, storage): mock_wallet = mock.Mock(spec=LbryWalletManager) mock_wallet.resolve = mock_resolve + + async def get_balance(*_): + return balance + + mock_wallet.default_account.get_balance = get_balance return mock_wallet, claim['permanent_url'] @@ -91,12 +98,15 @@ class TestStreamManager(BlobExchangeTestBase): f.write(os.urandom(20000000)) descriptor = await StreamDescriptor.create_stream(self.loop, self.server_blob_manager.blob_dir, file_path) self.sd_hash = descriptor.calculate_sd_hash() - self.mock_wallet, self.uri = get_mock_wallet(self.sd_hash, self.client_storage) + + async def setup_stream_manager(self, balance=10.0, fee=None): + self.mock_wallet, self.uri = get_mock_wallet(self.sd_hash, self.client_storage, balance, fee) self.stream_manager = StreamManager(self.loop, self.client_config, self.client_blob_manager, self.mock_wallet, self.client_storage, get_mock_node(self.server_from_client)) self.exchange_rate_manager = get_dummy_exchange_rate_manager(time) async def test_download_stop_resume_delete(self): + await self.setup_stream_manager() self.assertSetEqual(self.stream_manager.streams, set()) stream = await self.stream_manager.download_stream_from_uri(self.uri, self.exchange_rate_manager) stream_hash = stream.stream_hash @@ -137,3 +147,25 @@ class TestStreamManager(BlobExchangeTestBase): "select status from file where stream_hash=?", stream_hash ) self.assertEqual(stored_status, None) + + async def test_insufficient_funds(self): + fee = { + 'currency': 'LBC', + 'amount': 11.0, + 'address': 'bYFeMtSL7ARuG1iMpjFyrnTe4oJHSAVNXF', + 'version': '_0_0_1' + } + await self.setup_stream_manager(10.0, fee) + with self.assertRaises(InsufficientFundsError): + await self.stream_manager.download_stream_from_uri(self.uri, self.exchange_rate_manager) + + async def test_fee_above_max_allowed(self): + fee = { + 'currency': 'USD', + 'amount': 51.0, + 'address': 'bYFeMtSL7ARuG1iMpjFyrnTe4oJHSAVNXF', + 'version': '_0_0_1' + } + await self.setup_stream_manager(1000000.0, fee) + with self.assertRaises(KeyFeeAboveMaxAllowed): + await self.stream_manager.download_stream_from_uri(self.uri, self.exchange_rate_manager)