From 96927ec98518a1838ce002ec90215001f66a5038 Mon Sep 17 00:00:00 2001 From: Jack Robison Date: Mon, 29 May 2017 16:38:08 -0400 Subject: [PATCH 1/4] remove lbrynet.metadata --- CHANGELOG.md | 1 + lbrynet/core/PaymentRateManager.py | 1 + lbrynet/lbrynet_daemon/Daemon.py | 9 +- lbrynet/lbrynet_daemon/Downloader.py | 17 +- lbrynet/lbrynet_daemon/ExchangeRateManager.py | 23 +- lbrynet/metadata/Fee.py | 39 --- lbrynet/metadata/Metadata.py | 44 --- lbrynet/metadata/StructuredDict.py | 64 ---- lbrynet/metadata/__init__.py | 0 lbrynet/metadata/fee_schemas.py | 16 - lbrynet/metadata/metadata_schemas.py | 276 ------------------ tests/unit/core/test_Metadata.py | 218 -------------- .../test_ExchangeRateManager.py | 5 +- 13 files changed, 28 insertions(+), 685 deletions(-) delete mode 100644 lbrynet/metadata/Fee.py delete mode 100644 lbrynet/metadata/Metadata.py delete mode 100644 lbrynet/metadata/StructuredDict.py delete mode 100644 lbrynet/metadata/__init__.py delete mode 100644 lbrynet/metadata/fee_schemas.py delete mode 100644 lbrynet/metadata/metadata_schemas.py delete mode 100644 tests/unit/core/test_Metadata.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 414f1e563..d1323716b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ at anytime. ### Changed * Do not catch base exception in API command resolve + * Remove deprecated `lbrynet.metadata` and update what used it to instead use `lbryschema` * ### Fixed diff --git a/lbrynet/core/PaymentRateManager.py b/lbrynet/core/PaymentRateManager.py index cdda9b630..f72bb5154 100644 --- a/lbrynet/core/PaymentRateManager.py +++ b/lbrynet/core/PaymentRateManager.py @@ -45,6 +45,7 @@ class NegotiatedPaymentRateManager(object): """ self.base = base + self.min_blob_data_payment_rate = base.min_blob_data_payment_rate self.points_paid = 0.0 self.blob_tracker = availability_tracker self.generous = generous if generous is not None else conf.settings['is_generous_host'] diff --git a/lbrynet/lbrynet_daemon/Daemon.py b/lbrynet/lbrynet_daemon/Daemon.py index 1147e329c..7989f5e9e 100644 --- a/lbrynet/lbrynet_daemon/Daemon.py +++ b/lbrynet/lbrynet_daemon/Daemon.py @@ -17,6 +17,7 @@ from twisted.python.failure import Failure from lbryschema.claim import ClaimDict from lbryschema.uri import parse_lbry_uri from lbryschema.error import URIParseError +from lbryschema.fee import Fee # TODO: importing this when internet is disabled raises a socket.gaierror from lbryum.version import LBRYUM_VERSION @@ -25,8 +26,6 @@ from lbrynet import conf, analytics from lbrynet.conf import LBRYCRD_WALLET, LBRYUM_WALLET, PTC_WALLET from lbrynet.reflector import reupload from lbrynet.reflector import ServerFactory as reflector_server_factory -from lbrynet.metadata.Fee import FeeValidator -from lbrynet.metadata.Metadata import verify_name_characters from lbrynet.lbryfile.client.EncryptedFileDownloader import EncryptedFileSaverFactory from lbrynet.lbryfile.client.EncryptedFileDownloader import EncryptedFileOpenerFactory @@ -448,7 +447,7 @@ class Daemon(AuthJSONRPCServer): isinstance(settings[key], setting_type) or ( key == "max_key_fee" and - isinstance(FeeValidator(settings[key]).amount, setting_type) + isinstance(Fee(settings[key]).amount, setting_type) ) ) @@ -686,7 +685,7 @@ class Daemon(AuthJSONRPCServer): publisher = Publisher(self.session, self.lbry_file_manager, self.session.wallet, certificate_id) - verify_name_characters(name) + parse_lbry_uri(name) if bid <= 0.0: raise Exception("Invalid bid") if not file_path: @@ -1596,7 +1595,7 @@ class Daemon(AuthJSONRPCServer): name = resolved['name'] claim_id = resolved['claim_id'] - stream_info = resolved['value'] + stream_info = ClaimDict.load_dict(resolved['value']) if claim_id in self.streams: log.info("Already waiting on lbry://%s to start downloading", name) diff --git a/lbrynet/lbrynet_daemon/Downloader.py b/lbrynet/lbrynet_daemon/Downloader.py index 826ee58ef..7e5727ee4 100644 --- a/lbrynet/lbrynet_daemon/Downloader.py +++ b/lbrynet/lbrynet_daemon/Downloader.py @@ -3,10 +3,10 @@ import os from twisted.internet import defer, threads from twisted.internet.task import LoopingCall -from lbrynet.core import utils +from lbryschema.fee import Fee + from lbrynet.core.Error import InsufficientFundsError, KeyFeeAboveMaxAllowed from lbrynet.core.StreamDescriptor import download_sd_blob -from lbrynet.metadata.Fee import FeeValidator from lbrynet.lbryfilemanager.EncryptedFileDownloader import ManagedEncryptedFileDownloaderFactory from lbrynet import conf @@ -94,7 +94,7 @@ class GetStream(object): log.info("Downloading stream descriptor blob (%i seconds)", self.timeout_counter) def convert_max_fee(self): - max_fee = FeeValidator(self.max_key_fee) + max_fee = Fee(self.max_key_fee) if max_fee.currency_symbol == "LBC": return max_fee.amount return self.exchange_rate_manager.to_lbc(self.max_key_fee).amount @@ -104,15 +104,14 @@ class GetStream(object): self.code = next(s for s in STREAM_STAGES if s[0] == status) def check_fee(self, fee): - validated_fee = FeeValidator(fee) max_key_fee = self.convert_max_fee() - converted_fee = self.exchange_rate_manager.to_lbc(validated_fee).amount + converted_fee = self.exchange_rate_manager.to_lbc(fee).amount if converted_fee > self.wallet.get_balance(): raise InsufficientFundsError('Unable to pay the key fee of %s' % converted_fee) if converted_fee > max_key_fee: raise KeyFeeAboveMaxAllowed('Key fee %s above max allowed %s' % (converted_fee, max_key_fee)) - return validated_fee + return fee def get_downloader_factory(self, factories): for factory in factories: @@ -165,12 +164,12 @@ class GetStream(object): self._running = True self.set_status(INITIALIZING_CODE, name) - self.sd_hash = utils.get_sd_hash(stream_info) + self.sd_hash = stream_info.source_hash - if 'fee' in stream_info['stream']['metadata']: + if stream_info.has_fee: try: fee = yield threads.deferToThread(self.check_fee, - stream_info['stream']['metadata']['fee']) + stream_info.source_fee) except Exception as err: self._running = False self.finished_deferred.errback(err) diff --git a/lbrynet/lbrynet_daemon/ExchangeRateManager.py b/lbrynet/lbrynet_daemon/ExchangeRateManager.py index b553ef5be..68262bed2 100644 --- a/lbrynet/lbrynet_daemon/ExchangeRateManager.py +++ b/lbrynet/lbrynet_daemon/ExchangeRateManager.py @@ -5,8 +5,9 @@ import json from twisted.internet import defer, threads from twisted.internet.task import LoopingCall +from lbryschema.fee import Fee + from lbrynet import conf -from lbrynet.metadata.Fee import FeeValidator from lbrynet.core.Error import InvalidExchangeRateResponse log = logging.getLogger(__name__) @@ -203,14 +204,14 @@ class ExchangeRateManager(object): def to_lbc(self, fee): if fee is None: return None - if not isinstance(fee, FeeValidator): - fee_in = FeeValidator(fee) + if not isinstance(fee, Fee): + fee_in = Fee(fee) else: fee_in = fee - return FeeValidator({ - 'currency':fee_in.currency_symbol, - 'amount': self.convert_currency(fee_in.currency_symbol, "LBC", fee_in.amount), + return Fee({ + 'currency':fee_in.currency, + 'amount': self.convert_currency(fee_in.currency, "LBC", fee_in.amount), 'address': fee_in.address }) @@ -261,13 +262,13 @@ class DummyExchangeRateManager(object): def to_lbc(self, fee): if fee is None: return None - if not isinstance(fee, FeeValidator): - fee_in = FeeValidator(fee) + if not isinstance(fee, Fee): + fee_in = Fee(fee) else: fee_in = fee - return FeeValidator({ - 'currency':fee_in.currency_symbol, - 'amount': self.convert_currency(fee_in.currency_symbol, "LBC", fee_in.amount), + return Fee({ + 'currency':fee_in.currency, + 'amount': self.convert_currency(fee_in.currency, "LBC", fee_in.amount), 'address': fee_in.address }) diff --git a/lbrynet/metadata/Fee.py b/lbrynet/metadata/Fee.py deleted file mode 100644 index c9bc97a55..000000000 --- a/lbrynet/metadata/Fee.py +++ /dev/null @@ -1,39 +0,0 @@ -import logging -import fee_schemas - -from lbrynet.metadata.StructuredDict import StructuredDict - -log = logging.getLogger(__name__) - - -class FeeValidator(StructuredDict): - def __init__(self, fee): - self._versions = [ - ('0.0.1', fee_schemas.VER_001, None) - ] - - StructuredDict.__init__(self, fee, fee.get('ver', '0.0.1')) - - self.currency_symbol = self['currency'] - self.amount = self._get_amount() - self.address = self['address'] - - def _get_amount(self): - amt = self['amount'] - try: - return float(amt) - except TypeError: - log.error('Failed to convert fee amount %s to float', amt) - raise - - -class LBCFeeValidator(StructuredDict): - pass - - -class BTCFeeValidator(StructuredDict): - pass - - -class USDFeeValidator(StructuredDict): - pass diff --git a/lbrynet/metadata/Metadata.py b/lbrynet/metadata/Metadata.py deleted file mode 100644 index b1b7de8b0..000000000 --- a/lbrynet/metadata/Metadata.py +++ /dev/null @@ -1,44 +0,0 @@ -import logging - -from lbrynet.core import Error -from lbrynet.metadata.StructuredDict import StructuredDict -import metadata_schemas - -log = logging.getLogger(__name__) -NAME_ALLOWED_CHARSET = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0987654321-' - - -def verify_name_characters(name): - assert len(name) > 0, "Empty uri" - invalid_characters = {c for c in name if c not in NAME_ALLOWED_CHARSET} - if invalid_characters: - raise Error.InvalidName(name, invalid_characters) - return True - - -def migrate_001_to_002(metadata): - metadata['ver'] = '0.0.2' - metadata['nsfw'] = False - -def migrate_002_to_003(metadata): - metadata['ver'] = '0.0.3' - if 'content-type' in metadata: - metadata['content_type'] = metadata['content-type'] - del metadata['content-type'] - - -class Metadata(StructuredDict): - current_version = '0.0.3' - - _versions = [ - ('0.0.1', metadata_schemas.VER_001, None), - ('0.0.2', metadata_schemas.VER_002, migrate_001_to_002), - ('0.0.3', metadata_schemas.VER_003, migrate_002_to_003) - ] - - def __init__(self, metadata, migrate=True, target_version=None): - if not isinstance(metadata, dict): - raise TypeError("{} is not a dictionary".format(metadata)) - starting_version = metadata.get('ver', '0.0.1') - - StructuredDict.__init__(self, metadata, starting_version, migrate, target_version) diff --git a/lbrynet/metadata/StructuredDict.py b/lbrynet/metadata/StructuredDict.py deleted file mode 100644 index 943dcaf3c..000000000 --- a/lbrynet/metadata/StructuredDict.py +++ /dev/null @@ -1,64 +0,0 @@ -import jsonschema -import logging - -from jsonschema import ValidationError - -log = logging.getLogger(__name__) - - -class StructuredDict(dict): - """ - A dictionary that enforces a structure specified by a schema, and supports - migration between different versions of the schema. - """ - - # To be specified in sub-classes, an array in the format - # [(version, schema, migration), ...] - _versions = [] - - # Used internally to allow schema lookups by version number - _schemas = {} - - version = None - - def __init__(self, value, starting_version, migrate=True, target_version=None): - dict.__init__(self, value) - - self.version = starting_version - self._schemas = dict([(version, schema) for (version, schema, _) in self._versions]) - - self.validate(starting_version) - - if migrate: - self.migrate(target_version) - - def _upgrade_version_range(self, start_version, end_version): - after_starting_version = False - for version, schema, migration in self._versions: - if not after_starting_version: - if version == self.version: - after_starting_version = True - continue - - yield version, schema, migration - - if end_version and version == end_version: - break - - def validate(self, version): - jsonschema.validate(self, self._schemas[version]) - - def migrate(self, target_version=None): - if target_version: - assert self._versions.index(target_version) > self.versions.index(self.version), \ - "Current version is above target version" - - for version, schema, migration in self._upgrade_version_range(self.version, target_version): - migration(self) - try: - self.validate(version) - except ValidationError as e: - raise ValidationError( - "Could not migrate to version %s due to validation error: %s" % - (version, e.message)) - self.version = version diff --git a/lbrynet/metadata/__init__.py b/lbrynet/metadata/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/lbrynet/metadata/fee_schemas.py b/lbrynet/metadata/fee_schemas.py deleted file mode 100644 index 18efd64b6..000000000 --- a/lbrynet/metadata/fee_schemas.py +++ /dev/null @@ -1,16 +0,0 @@ -VER_001 = { - '$schema': 'http://json-schema.org/draft-04/schema#', - 'title': 'LBRY fee schema 0.0.1', - 'type': 'object', - - 'properties': { - 'amount': { - 'type': 'number', - 'minimum': 0, - 'exclusiveMinimum': True, - }, - 'address': { - 'type': 'string' - } - }, -} diff --git a/lbrynet/metadata/metadata_schemas.py b/lbrynet/metadata/metadata_schemas.py deleted file mode 100644 index 6165ccaa6..000000000 --- a/lbrynet/metadata/metadata_schemas.py +++ /dev/null @@ -1,276 +0,0 @@ -VER_001 = { - '$schema': 'http://json-schema.org/draft-04/schema#', - 'title': 'LBRY metadata schema 0.0.1', - 'definitions': { - 'fee_info': { - 'type': 'object', - 'properties': { - 'amount': { - 'type': 'number', - 'minimum': 0, - 'exclusiveMinimum': True, - }, - 'address': { - 'type': 'string' - } - }, - } - }, - 'type': 'object', - - 'properties': { - 'ver': { - 'type': 'string', - 'default': '0.0.1' - }, - 'title': { - 'type': 'string' - }, - 'description': { - 'type': 'string' - }, - 'author': { - 'type': 'string' - }, - 'language': { - 'type': 'string' - }, - 'license': { - 'type': 'string' - }, - 'content-type': { - 'type': 'string' - }, - 'sources': { - 'type': 'object', - 'properties': { - 'lbry_sd_hash': { - 'type': 'string' - }, - 'btih': { - 'type': 'string' - }, - 'url': { - 'type': 'string' - } - }, - 'additionalProperties': False - }, - 'thumbnail': { - 'type': 'string' - }, - 'preview': { - 'type': 'string' - }, - 'fee': { - 'properties': { - 'LBC': {'$ref': '#/definitions/fee_info'}, - 'BTC': {'$ref': '#/definitions/fee_info'}, - 'USD': {'$ref': '#/definitions/fee_info'} - } - }, - 'contact': { - 'type': 'number' - }, - 'pubkey': { - 'type': 'string' - }, - }, - 'required': [ - 'title', 'description', 'author', 'language', 'license', 'content-type', 'sources'], - 'additionalProperties': False -} - - -VER_002 = { - '$schema': 'http://json-schema.org/draft-04/schema#', - 'title': 'LBRY metadata schema 0.0.2', - 'definitions': { - 'fee_info': { - 'type': 'object', - 'properties': { - 'amount': { - 'type': 'number', - 'minimum': 0, - 'exclusiveMinimum': True, - }, - 'address': { - 'type': 'string' - } - }, - } - }, - 'type': 'object', - - 'properties': { - 'ver': { - 'type': 'string', - 'enum': ['0.0.2'], - }, - 'title': { - 'type': 'string' - }, - 'description': { - 'type': 'string' - }, - 'author': { - 'type': 'string' - }, - 'language': { - 'type': 'string' - }, - 'license': { - 'type': 'string' - }, - 'content-type': { - 'type': 'string' - }, - 'sources': { - 'type': 'object', - 'properties': { - 'lbry_sd_hash': { - 'type': 'string' - }, - 'btih': { - 'type': 'string' - }, - 'url': { - 'type': 'string' - } - }, - 'additionalProperties': False - }, - 'thumbnail': { - 'type': 'string' - }, - 'preview': { - 'type': 'string' - }, - 'fee': { - 'properties': { - 'LBC': {'$ref': '#/definitions/fee_info'}, - 'BTC': {'$ref': '#/definitions/fee_info'}, - 'USD': {'$ref': '#/definitions/fee_info'} - } - }, - 'contact': { - 'type': 'number' - }, - 'pubkey': { - 'type': 'string' - }, - 'license_url': { - 'type': 'string' - }, - 'nsfw': { - 'type': 'boolean', - 'default': False - }, - - }, - 'required': [ - 'ver', 'title', 'description', 'author', 'language', 'license', - 'content-type', 'sources', 'nsfw' - ], - 'additionalProperties': False -} - - -VER_003 = { - '$schema': 'http://json-schema.org/draft-04/schema#', - 'title': 'LBRY metadata schema 0.0.3', - 'definitions': { - 'fee_info': { - 'type': 'object', - 'properties': { - 'amount': { - 'type': 'number', - 'minimum': 0, - 'exclusiveMinimum': True, - }, - 'address': { - 'type': 'string' - } - }, - } - }, - 'type': 'object', - - 'properties': { - 'ver': { - 'type': 'string', - 'enum': ['0.0.3'], - }, - 'title': { - 'type': 'string' - }, - 'description': { - 'type': 'string' - }, - 'author': { - 'type': 'string' - }, - 'language': { - 'type': 'string' - }, - 'license': { - 'type': 'string' - }, - 'content_type': { - 'type': 'string' - }, - 'sources': { - 'type': 'object', - 'properties': { - 'lbry_sd_hash': { - 'type': 'string' - }, - 'btih': { - 'type': 'string' - }, - 'url': { - 'type': 'string' - } - }, - 'additionalProperties': False - }, - 'thumbnail': { - 'type': 'string' - }, - 'preview': { - 'type': 'string' - }, - 'fee': { - 'properties': { - 'LBC': {'$ref': '#/definitions/fee_info'}, - 'BTC': {'$ref': '#/definitions/fee_info'}, - 'USD': {'$ref': '#/definitions/fee_info'} - } - }, - 'contact': { - 'type': 'number' - }, - 'pubkey': { - 'type': 'string' - }, - 'license_url': { - 'type': 'string' - }, - 'nsfw': { - 'type': 'boolean', - 'default': False - }, - 'sig': { - 'type': 'string' - } - }, - 'required': [ - 'ver', 'title', 'description', 'author', 'language', 'license', - 'content_type', 'sources', 'nsfw' - ], - 'additionalProperties': False, - 'dependencies': { - 'pubkey': ['sig'], - 'sig': ['pubkey'] - } -} diff --git a/tests/unit/core/test_Metadata.py b/tests/unit/core/test_Metadata.py deleted file mode 100644 index 0cf6d8168..000000000 --- a/tests/unit/core/test_Metadata.py +++ /dev/null @@ -1,218 +0,0 @@ -from twisted.trial import unittest -from jsonschema import ValidationError - -from lbrynet.core import Error -from lbrynet.metadata import Metadata - - -class MetadataTest(unittest.TestCase): - def test_name_error_if_blank(self): - with self.assertRaises(AssertionError): - Metadata.verify_name_characters("") - - def test_name_error_if_contains_bad_chrs(self): - with self.assertRaises(Error.InvalidName): - Metadata.verify_name_characters("wu tang") - with self.assertRaises(Error.InvalidName): - Metadata.verify_name_characters("$wutang") - with self.assertRaises(Error.InvalidName): - Metadata.verify_name_characters("#wutang") - - def test_validation_error_if_no_metadata(self): - metadata = {} - with self.assertRaises(ValidationError): - Metadata.Metadata(metadata) - - def test_validation_error_if_source_is_missing(self): - metadata = { - 'license': 'Oscilloscope Laboratories', - 'description': 'Four couples meet for Sunday brunch only to discover they are stuck in a house together as the world may be about to end.', - 'language': 'en', - 'title': "It's a Disaster", - 'author': 'Written and directed by Todd Berger', - 'content-type': 'audio/mpeg', - 'thumbnail': 'http://ia.media-imdb.com/images/M/MV5BMTQwNjYzMTQ0Ml5BMl5BanBnXkFtZTcwNDUzODM5Nw@@._V1_SY1000_CR0,0,673,1000_AL_.jpg', - } - with self.assertRaises(ValidationError): - Metadata.Metadata(metadata) - - def test_metadata_works_without_fee(self): - metadata = { - 'license': 'Oscilloscope Laboratories', - 'description': 'Four couples meet for Sunday brunch only to discover they are stuck in a house together as the world may be about to end.', - 'language': 'en', - 'title': "It's a Disaster", - 'author': 'Written and directed by Todd Berger', - 'sources': { - 'lbry_sd_hash': '8d0d6ea64d09f5aa90faf5807d8a761c32a27047861e06f81f41e35623a348a4b0104052161d5f89cf190f9672bc4ead'}, - 'content-type': 'audio/mpeg', - 'thumbnail': 'http://ia.media-imdb.com/images/M/MV5BMTQwNjYzMTQ0Ml5BMl5BanBnXkFtZTcwNDUzODM5Nw@@._V1_SY1000_CR0,0,673,1000_AL_.jpg', - } - m = Metadata.Metadata(metadata) - self.assertFalse('fee' in m) - - def test_validation_error_if_invalid_source(self): - metadata = { - 'license': 'Oscilloscope Laboratories', - 'description': 'Four couples meet for Sunday brunch only to discover they are stuck in a house together as the world may be about to end.', - 'language': 'en', - 'title': "It's a Disaster", - 'author': 'Written and directed by Todd Berger', - 'sources': { - 'fake': 'source'}, - 'content-type': 'audio/mpeg', - 'thumbnail': 'http://ia.media-imdb.com/images/M/MV5BMTQwNjYzMTQ0Ml5BMl5BanBnXkFtZTcwNDUzODM5Nw@@._V1_SY1000_CR0,0,673,1000_AL_.jpg', - } - with self.assertRaises(ValidationError): - Metadata.Metadata(metadata) - - def test_validation_error_if_missing_v001_field(self): - metadata = { - 'license': 'Oscilloscope Laboratories', - 'fee': {'LBC': {'amount': 50.0, 'address': 'bRQJASJrDbFZVAvcpv3NoNWoH74LQd5JNV'}}, - 'description': 'Four couples meet for Sunday brunch only to discover they are stuck in a house together as the world may be about to end.', - 'language': 'en', - 'author': 'Written and directed by Todd Berger', - 'sources': { - 'lbry_sd_hash': '8d0d6ea64d09f5aa90faf5807d8a761c32a27047861e06f81f41e35623a348a4b0104052161d5f89cf190f9672bc4ead'}, - 'content-type': 'audio/mpeg', - 'thumbnail': 'http://ia.media-imdb.com/images/M/MV5BMTQwNjYzMTQ0Ml5BMl5BanBnXkFtZTcwNDUzODM5Nw@@._V1_SY1000_CR0,0,673,1000_AL_.jpg' - } - with self.assertRaises(ValidationError): - Metadata.Metadata(metadata) - - def test_version_is_001_if_all_fields_are_present(self): - metadata = { - 'license': 'Oscilloscope Laboratories', - 'description': 'Four couples meet for Sunday brunch only to discover they are stuck in a house together as the world may be about to end.', - 'language': 'en', - 'title': "It's a Disaster", - 'author': 'Written and directed by Todd Berger', - 'sources': { - 'lbry_sd_hash': '8d0d6ea64d09f5aa90faf5807d8a761c32a27047861e06f81f41e35623a348a4b0104052161d5f89cf190f9672bc4ead'}, - 'content-type': 'audio/mpeg', - 'thumbnail': 'http://ia.media-imdb.com/images/M/MV5BMTQwNjYzMTQ0Ml5BMl5BanBnXkFtZTcwNDUzODM5Nw@@._V1_SY1000_CR0,0,673,1000_AL_.jpg', - } - m = Metadata.Metadata(metadata, migrate=False) - self.assertEquals('0.0.1', m.version) - - def test_validation_error_if_there_is_an_extra_field(self): - metadata = { - 'license': 'Oscilloscope Laboratories', - 'description': 'Four couples meet for Sunday brunch only to discover they are stuck in a house together as the world may be about to end.', - 'language': 'en', - 'title': "It's a Disaster", - 'author': 'Written and directed by Todd Berger', - 'sources': { - 'lbry_sd_hash': '8d0d6ea64d09f5aa90faf5807d8a761c32a27047861e06f81f41e35623a348a4b0104052161d5f89cf190f9672bc4ead'}, - 'content-type': 'audio/mpeg', - 'thumbnail': 'http://ia.media-imdb.com/images/M/MV5BMTQwNjYzMTQ0Ml5BMl5BanBnXkFtZTcwNDUzODM5Nw@@._V1_SY1000_CR0,0,673,1000_AL_.jpg', - 'MYSTERYFIELD': '?' - } - with self.assertRaises(ValidationError): - Metadata.Metadata(metadata, migrate=False) - - def test_version_is_002_if_all_fields_are_present(self): - metadata = { - 'license': 'NASA', - 'fee': {'USD': {'amount': 0.01, 'address': 'baBYSK7CqGSn5KrEmNmmQwAhBSFgo6v47z'}}, - 'ver': '0.0.2', - 'description': 'SDO captures images of the sun in 10 different wavelengths, each of which helps highlight a different temperature of solar material. Different temperatures can, in turn, show specific structures on the sun such as solar flares, which are gigantic explosions of light and x-rays, or coronal loops, which are stream of solar material travelling up and down looping magnetic field lines', - 'language': 'en', - 'author': 'The SDO Team, Genna Duberstein and Scott Wiessinger', - 'title': 'Thermonuclear Art', - 'sources': { - 'lbry_sd_hash': '8655f713819344980a9a0d67b198344e2c462c90f813e86f0c63789ab0868031f25c54d0bb31af6658e997e2041806eb'}, - 'nsfw': False, - 'content-type': 'video/mp4', - 'thumbnail': 'https://svs.gsfc.nasa.gov/vis/a010000/a012000/a012034/Combined.00_08_16_17.Still004.jpg' - } - m = Metadata.Metadata(metadata, migrate=False) - self.assertEquals('0.0.2', m.version) - - def test_version_is_003_if_all_fields_are_present(self): - metadata = { - 'license': 'NASA', - 'fee': {'USD': {'amount': 0.01, 'address': 'baBYSK7CqGSn5KrEmNmmQwAhBSFgo6v47z'}}, - 'ver': '0.0.3', - 'description': 'SDO captures images of the sun in 10 different wavelengths, each of which helps highlight a different temperature of solar material. Different temperatures can, in turn, show specific structures on the sun such as solar flares, which are gigantic explosions of light and x-rays, or coronal loops, which are stream of solar material travelling up and down looping magnetic field lines', - 'language': 'en', - 'author': 'The SDO Team, Genna Duberstein and Scott Wiessinger', - 'title': 'Thermonuclear Art', - 'sources': { - 'lbry_sd_hash': '8655f713819344980a9a0d67b198344e2c462c90f813e86f0c63789ab0868031f25c54d0bb31af6658e997e2041806eb'}, - 'nsfw': False, - 'content_type': 'video/mp4', - 'thumbnail': 'https://svs.gsfc.nasa.gov/vis/a010000/a012000/a012034/Combined.00_08_16_17.Still004.jpg' - } - m = Metadata.Metadata(metadata, migrate=False) - self.assertEquals('0.0.3', m.version) - - def test_version_claimed_is_001_but_version_is_002(self): - metadata = { - 'license': 'NASA', - 'fee': {'USD': {'amount': 0.01, 'address': 'baBYSK7CqGSn5KrEmNmmQwAhBSFgo6v47z'}}, - 'ver': '0.0.1', - 'description': 'SDO captures images of the sun in 10 different wavelengths, each of which helps highlight a different temperature of solar material. Different temperatures can, in turn, show specific structures on the sun such as solar flares, which are gigantic explosions of light and x-rays, or coronal loops, which are stream of solar material travelling up and down looping magnetic field lines', - 'language': 'en', - 'author': 'The SDO Team, Genna Duberstein and Scott Wiessinger', - 'title': 'Thermonuclear Art', - 'sources': { - 'lbry_sd_hash': '8655f713819344980a9a0d67b198344e2c462c90f813e86f0c63789ab0868031f25c54d0bb31af6658e997e2041806eb'}, - 'nsfw': False, - 'content-type': 'video/mp4', - 'thumbnail': 'https://svs.gsfc.nasa.gov/vis/a010000/a012000/a012034/Combined.00_08_16_17.Still004.jpg' - } - with self.assertRaises(ValidationError): - Metadata.Metadata(metadata, migrate=False) - - def test_version_claimed_is_002_but_version_is_003(self): - metadata = { - 'license': 'NASA', - 'fee': {'USD': {'amount': 0.01, 'address': 'baBYSK7CqGSn5KrEmNmmQwAhBSFgo6v47z'}}, - 'ver': '0.0.2', - 'description': 'SDO captures images of the sun in 10 different wavelengths, each of which helps highlight a different temperature of solar material. Different temperatures can, in turn, show specific structures on the sun such as solar flares, which are gigantic explosions of light and x-rays, or coronal loops, which are stream of solar material travelling up and down looping magnetic field lines', - 'language': 'en', - 'author': 'The SDO Team, Genna Duberstein and Scott Wiessinger', - 'title': 'Thermonuclear Art', - 'sources': { - 'lbry_sd_hash': '8655f713819344980a9a0d67b198344e2c462c90f813e86f0c63789ab0868031f25c54d0bb31af6658e997e2041806eb'}, - 'nsfw': False, - 'content_type': 'video/mp4', - 'thumbnail': 'https://svs.gsfc.nasa.gov/vis/a010000/a012000/a012034/Combined.00_08_16_17.Still004.jpg' - } - with self.assertRaises(ValidationError): - Metadata.Metadata(metadata, migrate=False) - - def test_version_001_ports_to_003(self): - metadata = { - 'license': 'Oscilloscope Laboratories', - 'description': 'Four couples meet for Sunday brunch only to discover they are stuck in a house together as the world may be about to end.', - 'language': 'en', - 'title': "It's a Disaster", - 'author': 'Written and directed by Todd Berger', - 'sources': { - 'lbry_sd_hash': '8d0d6ea64d09f5aa90faf5807d8a761c32a27047861e06f81f41e35623a348a4b0104052161d5f89cf190f9672bc4ead'}, - 'content-type': 'audio/mpeg', - 'thumbnail': 'http://ia.media-imdb.com/images/M/MV5BMTQwNjYzMTQ0Ml5BMl5BanBnXkFtZTcwNDUzODM5Nw@@._V1_SY1000_CR0,0,673,1000_AL_.jpg', - } - m = Metadata.Metadata(metadata, migrate=True) - self.assertEquals('0.0.3', m.version) - - def test_version_002_ports_to_003(self): - metadata = { - 'license': 'NASA', - 'fee': {'USD': {'amount': 0.01, 'address': 'baBYSK7CqGSn5KrEmNmmQwAhBSFgo6v47z'}}, - 'ver': '0.0.2', - 'description': 'SDO captures images of the sun in 10 different wavelengths, each of which helps highlight a different temperature of solar material. Different temperatures can, in turn, show specific structures on the sun such as solar flares, which are gigantic explosions of light and x-rays, or coronal loops, which are stream of solar material travelling up and down looping magnetic field lines', - 'language': 'en', - 'author': 'The SDO Team, Genna Duberstein and Scott Wiessinger', - 'title': 'Thermonuclear Art', - 'sources': { - 'lbry_sd_hash': '8655f713819344980a9a0d67b198344e2c462c90f813e86f0c63789ab0868031f25c54d0bb31af6658e997e2041806eb'}, - 'nsfw': False, - 'content-type': 'video/mp4', - 'thumbnail': 'https://svs.gsfc.nasa.gov/vis/a010000/a012000/a012034/Combined.00_08_16_17.Still004.jpg' - } - m = Metadata.Metadata(metadata, migrate=True) - self.assertEquals('0.0.3', m.version) diff --git a/tests/unit/lbrynet_daemon/test_ExchangeRateManager.py b/tests/unit/lbrynet_daemon/test_ExchangeRateManager.py index 2e8937e75..eacf7708b 100644 --- a/tests/unit/lbrynet_daemon/test_ExchangeRateManager.py +++ b/tests/unit/lbrynet_daemon/test_ExchangeRateManager.py @@ -1,6 +1,5 @@ -from lbrynet.metadata import Fee +from lbryschema.fee import Fee from lbrynet.lbrynet_daemon import ExchangeRateManager -from lbrynet import conf from lbrynet.core.Error import InvalidExchangeRateResponse from twisted.trial import unittest @@ -15,7 +14,7 @@ class FeeFormatTest(unittest.TestCase): 'amount': 10.0, 'address': "bRcHraa8bYJZL7vkh5sNmGwPDERFUjGPP9" } - fee = Fee.FeeValidator(fee_dict) + fee = Fee(fee_dict) self.assertEqual(10.0, fee['amount']) self.assertEqual('USD', fee['currency']) From 9f87d502c3d0442b35847c50e0e9139fa0916e49 Mon Sep 17 00:00:00 2001 From: Jack Robison Date: Tue, 30 May 2017 17:07:23 -0400 Subject: [PATCH 2/4] update currency conversion --- lbrynet/lbrynet_daemon/Daemon.py | 11 ++++-- lbrynet/lbrynet_daemon/Downloader.py | 35 ++++++++++--------- lbrynet/lbrynet_daemon/ExchangeRateManager.py | 30 ---------------- .../test_ExchangeRateManager.py | 6 ++-- 4 files changed, 30 insertions(+), 52 deletions(-) diff --git a/lbrynet/lbrynet_daemon/Daemon.py b/lbrynet/lbrynet_daemon/Daemon.py index 7989f5e9e..0c8aabcf4 100644 --- a/lbrynet/lbrynet_daemon/Daemon.py +++ b/lbrynet/lbrynet_daemon/Daemon.py @@ -822,7 +822,9 @@ class Daemon(AuthJSONRPCServer): return d def _add_key_fee_to_est_data_cost(self, fee, data_cost): - fee_amount = 0.0 if not fee else self.exchange_rate_manager.to_lbc(fee).amount + fee_amount = 0.0 if not fee else self.exchange_rate_manager.convert_currency(fee.currency, + "LBC", + fee.amount) return data_cost + fee_amount @defer.inlineCallbacks @@ -1855,8 +1857,11 @@ class Daemon(AuthJSONRPCServer): If no path is given but a metadata dict is provided, the source from the given metadata will be used. --fee= : Dictionary representing key fee to download content: - {currency_symbol: {'amount': float, - 'address': str, optional}} + { + 'currency': currency_symbol, + 'amount': float, + 'address': str, optional + } supported currencies: LBC, USD, BTC If an address is not provided a new one will be automatically generated. Default fee is zero. diff --git a/lbrynet/lbrynet_daemon/Downloader.py b/lbrynet/lbrynet_daemon/Downloader.py index 7e5727ee4..b69c8914c 100644 --- a/lbrynet/lbrynet_daemon/Downloader.py +++ b/lbrynet/lbrynet_daemon/Downloader.py @@ -94,24 +94,28 @@ class GetStream(object): log.info("Downloading stream descriptor blob (%i seconds)", self.timeout_counter) def convert_max_fee(self): - max_fee = Fee(self.max_key_fee) - if max_fee.currency_symbol == "LBC": - return max_fee.amount - return self.exchange_rate_manager.to_lbc(self.max_key_fee).amount + currency, amount = self.max_key_fee['currency'], self.max_key_fee['amount'] + return self.exchange_rate_manager.convert_currency(currency, "LBC", amount) def set_status(self, status, name): log.info("Download lbry://%s status changed to %s" % (name, status)) self.code = next(s for s in STREAM_STAGES if s[0] == status) - def check_fee(self, fee): - max_key_fee = self.convert_max_fee() - converted_fee = self.exchange_rate_manager.to_lbc(fee).amount - if converted_fee > self.wallet.get_balance(): - raise InsufficientFundsError('Unable to pay the key fee of %s' % converted_fee) - if converted_fee > max_key_fee: - raise KeyFeeAboveMaxAllowed('Key fee %s above max allowed %s' % (converted_fee, - max_key_fee)) - return fee + def check_fee_and_convert(self, fee): + max_key_fee_amount = self.convert_max_fee() + converted_fee_amount = self.exchange_rate_manager.convert_currency(fee.currency, "LBC", + fee.amount) + if converted_fee_amount > self.wallet.get_balance(): + raise InsufficientFundsError('Unable to pay the key fee of %s' % converted_fee_amount) + if converted_fee_amount > max_key_fee_amount: + raise KeyFeeAboveMaxAllowed('Key fee %s above max allowed %s' % (converted_fee_amount, + max_key_fee_amount)) + converted_fee = { + 'currency': 'LBC', + 'amount': converted_fee_amount, + 'address': fee.address + } + return Fee(converted_fee) def get_downloader_factory(self, factories): for factory in factories: @@ -142,8 +146,7 @@ class GetStream(object): @defer.inlineCallbacks def pay_key_fee(self, fee, name): if fee is not None: - fee_lbc = self.exchange_rate_manager.to_lbc(fee).amount - yield self._pay_key_fee(fee.address, fee_lbc, name) + yield self._pay_key_fee(fee.address, fee.amount, name) else: defer.returnValue(None) @@ -168,7 +171,7 @@ class GetStream(object): if stream_info.has_fee: try: - fee = yield threads.deferToThread(self.check_fee, + fee = yield threads.deferToThread(self.check_fee_and_convert, stream_info.source_fee) except Exception as err: self._running = False diff --git a/lbrynet/lbrynet_daemon/ExchangeRateManager.py b/lbrynet/lbrynet_daemon/ExchangeRateManager.py index 68262bed2..3cd43dcc0 100644 --- a/lbrynet/lbrynet_daemon/ExchangeRateManager.py +++ b/lbrynet/lbrynet_daemon/ExchangeRateManager.py @@ -5,8 +5,6 @@ import json from twisted.internet import defer, threads from twisted.internet.task import LoopingCall -from lbryschema.fee import Fee - from lbrynet import conf from lbrynet.core.Error import InvalidExchangeRateResponse @@ -201,20 +199,6 @@ class ExchangeRateManager(object): def fee_dict(self): return {market: market.rate.as_dict() for market in self.market_feeds} - def to_lbc(self, fee): - if fee is None: - return None - if not isinstance(fee, Fee): - fee_in = Fee(fee) - else: - fee_in = fee - - return Fee({ - 'currency':fee_in.currency, - 'amount': self.convert_currency(fee_in.currency, "LBC", fee_in.amount), - 'address': fee_in.address - }) - class DummyBTCLBCFeed(MarketFeed): def __init__(self): @@ -258,17 +242,3 @@ class DummyExchangeRateManager(object): market.rate.currency_pair[0] == from_currency): return self.convert_currency( market.rate.currency_pair[1], to_currency, amount * market.rate.spot) - - def to_lbc(self, fee): - if fee is None: - return None - if not isinstance(fee, Fee): - fee_in = Fee(fee) - else: - fee_in = fee - - return Fee({ - 'currency':fee_in.currency, - 'amount': self.convert_currency(fee_in.currency, "LBC", fee_in.amount), - 'address': fee_in.address - }) diff --git a/tests/unit/lbrynet_daemon/test_ExchangeRateManager.py b/tests/unit/lbrynet_daemon/test_ExchangeRateManager.py index eacf7708b..7e00192ca 100644 --- a/tests/unit/lbrynet_daemon/test_ExchangeRateManager.py +++ b/tests/unit/lbrynet_daemon/test_ExchangeRateManager.py @@ -23,18 +23,18 @@ class FeeTest(unittest.TestCase): util.resetTime(self) def test_fee_converts_to_lbc(self): - fee_dict = { + fee = Fee({ 'currency':'USD', 'amount': 10.0, 'address': "bRcHraa8bYJZL7vkh5sNmGwPDERFUjGPP9" - } + }) rates = { 'BTCLBC': {'spot': 3.0, 'ts': util.DEFAULT_ISO_TIME + 1}, 'USDBTC': {'spot': 2.0, 'ts': util.DEFAULT_ISO_TIME + 2} } manager = ExchangeRateManager.DummyExchangeRateManager(rates) - result = manager.to_lbc(fee_dict).amount + result = manager.convert_currency(fee.currency, "LBC", fee.amount) self.assertEqual(60.0, result) class GoogleBTCFeedTest(unittest.TestCase): From c3f61b3e6955dd07a041d227df195f12c7162e07 Mon Sep 17 00:00:00 2001 From: Kay Kurokawa Date: Wed, 31 May 2017 14:15:15 -0400 Subject: [PATCH 3/4] improve tests for ExchangeRateManager --- lbrynet/lbrynet_daemon/ExchangeRateManager.py | 42 +---------------- tests/mocks.py | 35 +++++++++++++++ tests/unit/lbrynet_daemon/test_Daemon.py | 6 ++- .../test_ExchangeRateManager.py | 45 ++++++++++++++++++- 4 files changed, 84 insertions(+), 44 deletions(-) diff --git a/lbrynet/lbrynet_daemon/ExchangeRateManager.py b/lbrynet/lbrynet_daemon/ExchangeRateManager.py index 3cd43dcc0..87455ee81 100644 --- a/lbrynet/lbrynet_daemon/ExchangeRateManager.py +++ b/lbrynet/lbrynet_daemon/ExchangeRateManager.py @@ -18,6 +18,7 @@ COINBASE_FEE = 0.0 #add fee class ExchangeRate(object): def __init__(self, market, spot, ts): assert int(time.time()) - ts < 600 + assert spot > 0 self.currency_pair = (market[0:3], market[3:6]) self.spot = spot self.ts = ts @@ -200,45 +201,4 @@ class ExchangeRateManager(object): return {market: market.rate.as_dict() for market in self.market_feeds} -class DummyBTCLBCFeed(MarketFeed): - def __init__(self): - MarketFeed.__init__( - self, - "BTCLBC", - "market name", - "derp.com", - None, - 0.0 - ) - -class DummyUSDBTCFeed(MarketFeed): - def __init__(self): - MarketFeed.__init__( - self, - "USDBTC", - "market name", - "derp.com", - None, - 0.0 - ) - - -class DummyExchangeRateManager(object): - def __init__(self, rates): - self.market_feeds = [DummyBTCLBCFeed(), DummyUSDBTCFeed()] - for feed in self.market_feeds: - feed.rate = ExchangeRate( - feed.market, rates[feed.market]['spot'], rates[feed.market]['ts']) - - def convert_currency(self, from_currency, to_currency, amount): - log.debug("Converting %f %s to %s" % (amount, from_currency, to_currency)) - for market in self.market_feeds: - if (market.rate_is_initialized and - market.rate.currency_pair == (from_currency, to_currency)): - return amount * market.rate.spot - for market in self.market_feeds: - if (market.rate_is_initialized and - market.rate.currency_pair[0] == from_currency): - return self.convert_currency( - market.rate.currency_pair[1], to_currency, amount * market.rate.spot) diff --git a/tests/mocks.py b/tests/mocks.py index 63daea1a0..63f451a23 100644 --- a/tests/mocks.py +++ b/tests/mocks.py @@ -6,6 +6,7 @@ from twisted.internet import defer from lbrynet.core import PTCWallet from lbrynet.core import BlobAvailability +from lbrynet.lbrynet_daemon import ExchangeRateManager as ERM from lbrynet import conf KB = 2**10 @@ -40,6 +41,37 @@ class FakeNetwork(object): return 1 +class BTCLBCFeed(ERM.MarketFeed): + def __init__(self): + ERM.MarketFeed.__init__( + self, + "BTCLBC", + "market name", + "derp.com", + None, + 0.0 + ) + +class USDBTCFeed(ERM.MarketFeed): + def __init__(self): + ERM.MarketFeed.__init__( + self, + "USDBTC", + "market name", + "derp.com", + None, + 0.0 + ) + +class ExchangeRateManager(ERM.ExchangeRateManager): + def __init__(self, market_feeds, rates): + self.market_feeds = market_feeds + for feed in self.market_feeds: + feed.rate = ERM.ExchangeRate( + feed.market, rates[feed.market]['spot'], rates[feed.market]['ts']) + + + class Wallet(object): def __init__(self): self.private_key = RSA.generate(1024) @@ -254,3 +286,6 @@ def mock_conf_settings(obj, settings={}): conf.settings = original_settings obj.addCleanup(_reset_settings) + + + diff --git a/tests/unit/lbrynet_daemon/test_Daemon.py b/tests/unit/lbrynet_daemon/test_Daemon.py index 1eed7d446..c3a3c6cfd 100644 --- a/tests/unit/lbrynet_daemon/test_Daemon.py +++ b/tests/unit/lbrynet_daemon/test_Daemon.py @@ -13,7 +13,8 @@ from lbrynet.lbrynet_daemon import ExchangeRateManager from tests import util from tests.mocks import mock_conf_settings, FakeNetwork from tests.mocks import BlobAvailabilityTracker as DummyBlobAvailabilityTracker - +from tests.mocks import ExchangeRateManager as DummyExchangeRateManager +from tests.mocks import BTCLBCFeed, USDBTCFeed def get_test_daemon(data_rate=None, generous=True, with_fee=False): if data_rate is None: @@ -26,7 +27,8 @@ def get_test_daemon(data_rate=None, generous=True, with_fee=False): daemon = LBRYDaemon(None, None) daemon.session = mock.Mock(spec=Session.Session) daemon.session.wallet = mock.Mock(spec=Wallet.LBRYumWallet) - daemon.exchange_rate_manager = ExchangeRateManager.DummyExchangeRateManager(rates) + market_feeds = [BTCLBCFeed(), USDBTCFeed()] + daemon.exchange_rate_manager = DummyExchangeRateManager(market_feeds, rates) base_prm = PaymentRateManager.BasePaymentRateManager(rate=data_rate) prm = PaymentRateManager.NegotiatedPaymentRateManager(base_prm, DummyBlobAvailabilityTracker(), generous=generous) diff --git a/tests/unit/lbrynet_daemon/test_ExchangeRateManager.py b/tests/unit/lbrynet_daemon/test_ExchangeRateManager.py index 7e00192ca..9d3decbb0 100644 --- a/tests/unit/lbrynet_daemon/test_ExchangeRateManager.py +++ b/tests/unit/lbrynet_daemon/test_ExchangeRateManager.py @@ -5,6 +5,8 @@ from lbrynet.core.Error import InvalidExchangeRateResponse from twisted.trial import unittest from twisted.internet import defer from tests import util +from tests.mocks import ExchangeRateManager as DummyExchangeRateManager +from tests.mocks import BTCLBCFeed, USDBTCFeed class FeeFormatTest(unittest.TestCase): @@ -18,6 +20,28 @@ class FeeFormatTest(unittest.TestCase): self.assertEqual(10.0, fee['amount']) self.assertEqual('USD', fee['currency']) + def test_fee_zero(self): + fee_dict = { + 'currency':'LBC', + 'amount': 0.0, + 'address': "bRcHraa8bYJZL7vkh5sNmGwPDERFUjGPP9" + } + fee = Fee(fee_dict) + self.assertEqual(0.0, fee['amount']) + self.assertEqual('LBC', fee['currency']) + + +class ExchangeRateTest(unittest.TestCase): + def setUp(self): + util.resetTime(self) + + def test_invalid_rates(self): + with self.assertRaises(AssertionError): + ExchangeRateManager.ExchangeRate('USDBTC', 0, util.DEFAULT_ISO_TIME) + with self.assertRaises(AssertionError): + ExchangeRateManager.ExchangeRate('USDBTC', -1, util.DEFAULT_ISO_TIME) + + class FeeTest(unittest.TestCase): def setUp(self): util.resetTime(self) @@ -33,10 +57,29 @@ class FeeTest(unittest.TestCase): 'BTCLBC': {'spot': 3.0, 'ts': util.DEFAULT_ISO_TIME + 1}, 'USDBTC': {'spot': 2.0, 'ts': util.DEFAULT_ISO_TIME + 2} } - manager = ExchangeRateManager.DummyExchangeRateManager(rates) + + market_feeds = [BTCLBCFeed(), USDBTCFeed()] + manager = DummyExchangeRateManager(market_feeds,rates) result = manager.convert_currency(fee.currency, "LBC", fee.amount) self.assertEqual(60.0, result) + def test_missing_feed(self): + # test when a feed is missing for conversion + fee = Fee({ + 'currency':'USD', + 'amount': 1.0, + 'address': "bRcHraa8bYJZL7vkh5sNmGwPDERFUjGPP9" + }) + + rates = { + 'BTCLBC': {'spot': 1.0, 'ts': util.DEFAULT_ISO_TIME + 1}, + } + market_feeds = [BTCLBCFeed()] + manager = DummyExchangeRateManager(market_feeds,rates) + with self.assertRaises(Exception): + result = manager.convert_currency(fee.currency, "LBC", fee.amount) + + class GoogleBTCFeedTest(unittest.TestCase): @defer.inlineCallbacks From 5b297f165f9580b1cde42e9c0c275073abad74c3 Mon Sep 17 00:00:00 2001 From: Jack Robison Date: Fri, 2 Jun 2017 11:50:36 -0400 Subject: [PATCH 4/4] set fee version --- lbrynet/lbrynet_daemon/Daemon.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lbrynet/lbrynet_daemon/Daemon.py b/lbrynet/lbrynet_daemon/Daemon.py index 0c8aabcf4..d8ec576cd 100644 --- a/lbrynet/lbrynet_daemon/Daemon.py +++ b/lbrynet/lbrynet_daemon/Daemon.py @@ -1937,6 +1937,8 @@ class Daemon(AuthJSONRPCServer): elif 'address' not in metadata['fee']: address = yield self.session.wallet.get_unused_address() metadata['fee']['address'] = address + if 'fee' in metadata and 'version' not in metadata['fee']: + metadata['fee']['version'] = '_0_0_1' claim_dict = { 'version': '_0_0_1',