forked from LBRYCommunity/lbry-sdk
Merge pull request #657 from lbryio/remove-old-metadata
remove lbrynet.metadata
This commit is contained in:
commit
522cb9cb82
15 changed files with 129 additions and 766 deletions
|
@ -14,6 +14,7 @@ at anytime.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
* Do not catch base exception in API command resolve
|
* Do not catch base exception in API command resolve
|
||||||
|
* Remove deprecated `lbrynet.metadata` and update what used it to instead use `lbryschema`
|
||||||
*
|
*
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
|
@ -45,6 +45,7 @@ class NegotiatedPaymentRateManager(object):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.base = base
|
self.base = base
|
||||||
|
self.min_blob_data_payment_rate = base.min_blob_data_payment_rate
|
||||||
self.points_paid = 0.0
|
self.points_paid = 0.0
|
||||||
self.blob_tracker = availability_tracker
|
self.blob_tracker = availability_tracker
|
||||||
self.generous = generous if generous is not None else conf.settings['is_generous_host']
|
self.generous = generous if generous is not None else conf.settings['is_generous_host']
|
||||||
|
|
|
@ -17,6 +17,7 @@ from twisted.python.failure import Failure
|
||||||
from lbryschema.claim import ClaimDict
|
from lbryschema.claim import ClaimDict
|
||||||
from lbryschema.uri import parse_lbry_uri
|
from lbryschema.uri import parse_lbry_uri
|
||||||
from lbryschema.error import URIParseError
|
from lbryschema.error import URIParseError
|
||||||
|
from lbryschema.fee import Fee
|
||||||
|
|
||||||
# TODO: importing this when internet is disabled raises a socket.gaierror
|
# TODO: importing this when internet is disabled raises a socket.gaierror
|
||||||
from lbryum.version import LBRYUM_VERSION
|
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.conf import LBRYCRD_WALLET, LBRYUM_WALLET, PTC_WALLET
|
||||||
from lbrynet.reflector import reupload
|
from lbrynet.reflector import reupload
|
||||||
from lbrynet.reflector import ServerFactory as reflector_server_factory
|
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 EncryptedFileSaverFactory
|
||||||
from lbrynet.lbryfile.client.EncryptedFileDownloader import EncryptedFileOpenerFactory
|
from lbrynet.lbryfile.client.EncryptedFileDownloader import EncryptedFileOpenerFactory
|
||||||
|
@ -448,7 +447,7 @@ class Daemon(AuthJSONRPCServer):
|
||||||
isinstance(settings[key], setting_type) or
|
isinstance(settings[key], setting_type) or
|
||||||
(
|
(
|
||||||
key == "max_key_fee" and
|
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,
|
publisher = Publisher(self.session, self.lbry_file_manager, self.session.wallet,
|
||||||
certificate_id)
|
certificate_id)
|
||||||
verify_name_characters(name)
|
parse_lbry_uri(name)
|
||||||
if bid <= 0.0:
|
if bid <= 0.0:
|
||||||
raise Exception("Invalid bid")
|
raise Exception("Invalid bid")
|
||||||
if not file_path:
|
if not file_path:
|
||||||
|
@ -823,7 +822,9 @@ class Daemon(AuthJSONRPCServer):
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def _add_key_fee_to_est_data_cost(self, fee, data_cost):
|
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
|
return data_cost + fee_amount
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
|
@ -1596,7 +1597,7 @@ class Daemon(AuthJSONRPCServer):
|
||||||
|
|
||||||
name = resolved['name']
|
name = resolved['name']
|
||||||
claim_id = resolved['claim_id']
|
claim_id = resolved['claim_id']
|
||||||
stream_info = resolved['value']
|
stream_info = ClaimDict.load_dict(resolved['value'])
|
||||||
|
|
||||||
if claim_id in self.streams:
|
if claim_id in self.streams:
|
||||||
log.info("Already waiting on lbry://%s to start downloading", name)
|
log.info("Already waiting on lbry://%s to start downloading", name)
|
||||||
|
@ -1856,8 +1857,11 @@ class Daemon(AuthJSONRPCServer):
|
||||||
If no path is given but a metadata dict is provided,
|
If no path is given but a metadata dict is provided,
|
||||||
the source from the given metadata will be used.
|
the source from the given metadata will be used.
|
||||||
--fee=<fee> : Dictionary representing key fee to download content:
|
--fee=<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
|
supported currencies: LBC, USD, BTC
|
||||||
If an address is not provided a new one will be
|
If an address is not provided a new one will be
|
||||||
automatically generated. Default fee is zero.
|
automatically generated. Default fee is zero.
|
||||||
|
@ -1933,6 +1937,8 @@ class Daemon(AuthJSONRPCServer):
|
||||||
elif 'address' not in metadata['fee']:
|
elif 'address' not in metadata['fee']:
|
||||||
address = yield self.session.wallet.get_unused_address()
|
address = yield self.session.wallet.get_unused_address()
|
||||||
metadata['fee']['address'] = address
|
metadata['fee']['address'] = address
|
||||||
|
if 'fee' in metadata and 'version' not in metadata['fee']:
|
||||||
|
metadata['fee']['version'] = '_0_0_1'
|
||||||
|
|
||||||
claim_dict = {
|
claim_dict = {
|
||||||
'version': '_0_0_1',
|
'version': '_0_0_1',
|
||||||
|
|
|
@ -3,10 +3,10 @@ import os
|
||||||
from twisted.internet import defer, threads
|
from twisted.internet import defer, threads
|
||||||
from twisted.internet.task import LoopingCall
|
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.Error import InsufficientFundsError, KeyFeeAboveMaxAllowed
|
||||||
from lbrynet.core.StreamDescriptor import download_sd_blob
|
from lbrynet.core.StreamDescriptor import download_sd_blob
|
||||||
from lbrynet.metadata.Fee import FeeValidator
|
|
||||||
from lbrynet.lbryfilemanager.EncryptedFileDownloader import ManagedEncryptedFileDownloaderFactory
|
from lbrynet.lbryfilemanager.EncryptedFileDownloader import ManagedEncryptedFileDownloaderFactory
|
||||||
from lbrynet import conf
|
from lbrynet import conf
|
||||||
|
|
||||||
|
@ -94,25 +94,28 @@ class GetStream(object):
|
||||||
log.info("Downloading stream descriptor blob (%i seconds)", self.timeout_counter)
|
log.info("Downloading stream descriptor blob (%i seconds)", self.timeout_counter)
|
||||||
|
|
||||||
def convert_max_fee(self):
|
def convert_max_fee(self):
|
||||||
max_fee = FeeValidator(self.max_key_fee)
|
currency, amount = self.max_key_fee['currency'], self.max_key_fee['amount']
|
||||||
if max_fee.currency_symbol == "LBC":
|
return self.exchange_rate_manager.convert_currency(currency, "LBC", amount)
|
||||||
return max_fee.amount
|
|
||||||
return self.exchange_rate_manager.to_lbc(self.max_key_fee).amount
|
|
||||||
|
|
||||||
def set_status(self, status, name):
|
def set_status(self, status, name):
|
||||||
log.info("Download lbry://%s status changed to %s" % (name, status))
|
log.info("Download lbry://%s status changed to %s" % (name, status))
|
||||||
self.code = next(s for s in STREAM_STAGES if s[0] == status)
|
self.code = next(s for s in STREAM_STAGES if s[0] == status)
|
||||||
|
|
||||||
def check_fee(self, fee):
|
def check_fee_and_convert(self, fee):
|
||||||
validated_fee = FeeValidator(fee)
|
max_key_fee_amount = self.convert_max_fee()
|
||||||
max_key_fee = self.convert_max_fee()
|
converted_fee_amount = self.exchange_rate_manager.convert_currency(fee.currency, "LBC",
|
||||||
converted_fee = self.exchange_rate_manager.to_lbc(validated_fee).amount
|
fee.amount)
|
||||||
if converted_fee > self.wallet.get_balance():
|
if converted_fee_amount > self.wallet.get_balance():
|
||||||
raise InsufficientFundsError('Unable to pay the key fee of %s' % converted_fee)
|
raise InsufficientFundsError('Unable to pay the key fee of %s' % converted_fee_amount)
|
||||||
if converted_fee > max_key_fee:
|
if converted_fee_amount > max_key_fee_amount:
|
||||||
raise KeyFeeAboveMaxAllowed('Key fee %s above max allowed %s' % (converted_fee,
|
raise KeyFeeAboveMaxAllowed('Key fee %s above max allowed %s' % (converted_fee_amount,
|
||||||
max_key_fee))
|
max_key_fee_amount))
|
||||||
return validated_fee
|
converted_fee = {
|
||||||
|
'currency': 'LBC',
|
||||||
|
'amount': converted_fee_amount,
|
||||||
|
'address': fee.address
|
||||||
|
}
|
||||||
|
return Fee(converted_fee)
|
||||||
|
|
||||||
def get_downloader_factory(self, factories):
|
def get_downloader_factory(self, factories):
|
||||||
for factory in factories:
|
for factory in factories:
|
||||||
|
@ -143,8 +146,7 @@ class GetStream(object):
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def pay_key_fee(self, fee, name):
|
def pay_key_fee(self, fee, name):
|
||||||
if fee is not None:
|
if fee is not None:
|
||||||
fee_lbc = self.exchange_rate_manager.to_lbc(fee).amount
|
yield self._pay_key_fee(fee.address, fee.amount, name)
|
||||||
yield self._pay_key_fee(fee.address, fee_lbc, name)
|
|
||||||
else:
|
else:
|
||||||
defer.returnValue(None)
|
defer.returnValue(None)
|
||||||
|
|
||||||
|
@ -165,12 +167,12 @@ class GetStream(object):
|
||||||
self._running = True
|
self._running = True
|
||||||
|
|
||||||
self.set_status(INITIALIZING_CODE, name)
|
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:
|
try:
|
||||||
fee = yield threads.deferToThread(self.check_fee,
|
fee = yield threads.deferToThread(self.check_fee_and_convert,
|
||||||
stream_info['stream']['metadata']['fee'])
|
stream_info.source_fee)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
self._running = False
|
self._running = False
|
||||||
self.finished_deferred.errback(err)
|
self.finished_deferred.errback(err)
|
||||||
|
|
|
@ -6,7 +6,6 @@ from twisted.internet import defer, threads
|
||||||
from twisted.internet.task import LoopingCall
|
from twisted.internet.task import LoopingCall
|
||||||
|
|
||||||
from lbrynet import conf
|
from lbrynet import conf
|
||||||
from lbrynet.metadata.Fee import FeeValidator
|
|
||||||
from lbrynet.core.Error import InvalidExchangeRateResponse
|
from lbrynet.core.Error import InvalidExchangeRateResponse
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
@ -19,6 +18,7 @@ COINBASE_FEE = 0.0 #add fee
|
||||||
class ExchangeRate(object):
|
class ExchangeRate(object):
|
||||||
def __init__(self, market, spot, ts):
|
def __init__(self, market, spot, ts):
|
||||||
assert int(time.time()) - ts < 600
|
assert int(time.time()) - ts < 600
|
||||||
|
assert spot > 0
|
||||||
self.currency_pair = (market[0:3], market[3:6])
|
self.currency_pair = (market[0:3], market[3:6])
|
||||||
self.spot = spot
|
self.spot = spot
|
||||||
self.ts = ts
|
self.ts = ts
|
||||||
|
@ -200,74 +200,5 @@ class ExchangeRateManager(object):
|
||||||
def fee_dict(self):
|
def fee_dict(self):
|
||||||
return {market: market.rate.as_dict() for market in self.market_feeds}
|
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, FeeValidator):
|
|
||||||
fee_in = FeeValidator(fee)
|
|
||||||
else:
|
|
||||||
fee_in = fee
|
|
||||||
|
|
||||||
return FeeValidator({
|
|
||||||
'currency':fee_in.currency_symbol,
|
|
||||||
'amount': self.convert_currency(fee_in.currency_symbol, "LBC", fee_in.amount),
|
|
||||||
'address': fee_in.address
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
def to_lbc(self, fee):
|
|
||||||
if fee is None:
|
|
||||||
return None
|
|
||||||
if not isinstance(fee, FeeValidator):
|
|
||||||
fee_in = FeeValidator(fee)
|
|
||||||
else:
|
|
||||||
fee_in = fee
|
|
||||||
|
|
||||||
return FeeValidator({
|
|
||||||
'currency':fee_in.currency_symbol,
|
|
||||||
'amount': self.convert_currency(fee_in.currency_symbol, "LBC", fee_in.amount),
|
|
||||||
'address': fee_in.address
|
|
||||||
})
|
|
||||||
|
|
|
@ -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
|
|
|
@ -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)
|
|
|
@ -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
|
|
|
@ -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'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
|
@ -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']
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,6 +6,7 @@ from twisted.internet import defer
|
||||||
|
|
||||||
from lbrynet.core import PTCWallet
|
from lbrynet.core import PTCWallet
|
||||||
from lbrynet.core import BlobAvailability
|
from lbrynet.core import BlobAvailability
|
||||||
|
from lbrynet.lbrynet_daemon import ExchangeRateManager as ERM
|
||||||
from lbrynet import conf
|
from lbrynet import conf
|
||||||
|
|
||||||
KB = 2**10
|
KB = 2**10
|
||||||
|
@ -40,6 +41,37 @@ class FakeNetwork(object):
|
||||||
return 1
|
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):
|
class Wallet(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.private_key = RSA.generate(1024)
|
self.private_key = RSA.generate(1024)
|
||||||
|
@ -254,3 +286,6 @@ def mock_conf_settings(obj, settings={}):
|
||||||
conf.settings = original_settings
|
conf.settings = original_settings
|
||||||
|
|
||||||
obj.addCleanup(_reset_settings)
|
obj.addCleanup(_reset_settings)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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)
|
|
|
@ -13,7 +13,8 @@ from lbrynet.lbrynet_daemon import ExchangeRateManager
|
||||||
from tests import util
|
from tests import util
|
||||||
from tests.mocks import mock_conf_settings, FakeNetwork
|
from tests.mocks import mock_conf_settings, FakeNetwork
|
||||||
from tests.mocks import BlobAvailabilityTracker as DummyBlobAvailabilityTracker
|
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):
|
def get_test_daemon(data_rate=None, generous=True, with_fee=False):
|
||||||
if data_rate is None:
|
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 = LBRYDaemon(None, None)
|
||||||
daemon.session = mock.Mock(spec=Session.Session)
|
daemon.session = mock.Mock(spec=Session.Session)
|
||||||
daemon.session.wallet = mock.Mock(spec=Wallet.LBRYumWallet)
|
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)
|
base_prm = PaymentRateManager.BasePaymentRateManager(rate=data_rate)
|
||||||
prm = PaymentRateManager.NegotiatedPaymentRateManager(base_prm, DummyBlobAvailabilityTracker(),
|
prm = PaymentRateManager.NegotiatedPaymentRateManager(base_prm, DummyBlobAvailabilityTracker(),
|
||||||
generous=generous)
|
generous=generous)
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
from lbrynet.metadata import Fee
|
from lbryschema.fee import Fee
|
||||||
from lbrynet.lbrynet_daemon import ExchangeRateManager
|
from lbrynet.lbrynet_daemon import ExchangeRateManager
|
||||||
from lbrynet import conf
|
|
||||||
from lbrynet.core.Error import InvalidExchangeRateResponse
|
from lbrynet.core.Error import InvalidExchangeRateResponse
|
||||||
|
|
||||||
from twisted.trial import unittest
|
from twisted.trial import unittest
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
from tests import util
|
from tests import util
|
||||||
|
from tests.mocks import ExchangeRateManager as DummyExchangeRateManager
|
||||||
|
from tests.mocks import BTCLBCFeed, USDBTCFeed
|
||||||
|
|
||||||
|
|
||||||
class FeeFormatTest(unittest.TestCase):
|
class FeeFormatTest(unittest.TestCase):
|
||||||
|
@ -15,29 +16,70 @@ class FeeFormatTest(unittest.TestCase):
|
||||||
'amount': 10.0,
|
'amount': 10.0,
|
||||||
'address': "bRcHraa8bYJZL7vkh5sNmGwPDERFUjGPP9"
|
'address': "bRcHraa8bYJZL7vkh5sNmGwPDERFUjGPP9"
|
||||||
}
|
}
|
||||||
fee = Fee.FeeValidator(fee_dict)
|
fee = Fee(fee_dict)
|
||||||
self.assertEqual(10.0, fee['amount'])
|
self.assertEqual(10.0, fee['amount'])
|
||||||
self.assertEqual('USD', fee['currency'])
|
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):
|
class FeeTest(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
util.resetTime(self)
|
util.resetTime(self)
|
||||||
|
|
||||||
def test_fee_converts_to_lbc(self):
|
def test_fee_converts_to_lbc(self):
|
||||||
fee_dict = {
|
fee = Fee({
|
||||||
'currency':'USD',
|
'currency':'USD',
|
||||||
'amount': 10.0,
|
'amount': 10.0,
|
||||||
'address': "bRcHraa8bYJZL7vkh5sNmGwPDERFUjGPP9"
|
'address': "bRcHraa8bYJZL7vkh5sNmGwPDERFUjGPP9"
|
||||||
}
|
})
|
||||||
|
|
||||||
rates = {
|
rates = {
|
||||||
'BTCLBC': {'spot': 3.0, 'ts': util.DEFAULT_ISO_TIME + 1},
|
'BTCLBC': {'spot': 3.0, 'ts': util.DEFAULT_ISO_TIME + 1},
|
||||||
'USDBTC': {'spot': 2.0, 'ts': util.DEFAULT_ISO_TIME + 2}
|
'USDBTC': {'spot': 2.0, 'ts': util.DEFAULT_ISO_TIME + 2}
|
||||||
}
|
}
|
||||||
manager = ExchangeRateManager.DummyExchangeRateManager(rates)
|
|
||||||
result = manager.to_lbc(fee_dict).amount
|
market_feeds = [BTCLBCFeed(), USDBTCFeed()]
|
||||||
|
manager = DummyExchangeRateManager(market_feeds,rates)
|
||||||
|
result = manager.convert_currency(fee.currency, "LBC", fee.amount)
|
||||||
self.assertEqual(60.0, result)
|
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):
|
class GoogleBTCFeedTest(unittest.TestCase):
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
|
|
Loading…
Reference in a new issue