BTC/USD denominated fees, metadata revisions

This commit is contained in:
Jack 2016-07-25 01:40:26 -04:00
parent 6bfb33adb2
commit 2e2d309d38
7 changed files with 112 additions and 114 deletions

View file

@ -51,4 +51,6 @@ SOURCE_TYPES = ['lbry_sd_hash', 'url', 'btih']
CURRENCIES = [ CURRENCIES = [
{'BTC': {'type': 'crypto'}}, {'BTC': {'type': 'crypto'}},
{'LBC': {'type': 'crypto'}}, {'LBC': {'type': 'crypto'}},
{'USD': {'type': 'fiat'}},
] ]

View file

@ -1,39 +0,0 @@
import requests
import json
from lbrynet.conf import CURRENCIES
class LBRYFee(object):
def __init__(self, currency, amount, address=None):
assert currency in [c.keys()[0] for c in CURRENCIES], "Unsupported currency: %s" % str(currency)
self.address = address
self.currency_symbol = currency
self.currency = [c for c in CURRENCIES if self.currency_symbol in c][0]
if not isinstance(amount, float):
self.amount = float(amount)
else:
self.amount = amount
def convert_to(self, to_currency, rate_dict={}):
if to_currency is self.currency_symbol:
return self.as_dict()
if self.currency[self.currency_symbol]['type'] is 'fiat':
raise NotImplemented
else:
if to_currency not in rate_dict:
params = {'market': '%s-%s' % (self.currency_symbol, to_currency)}
r = requests.get("https://bittrex.com/api/v1.1/public/getticker", params)
last = json.loads(r.text)['result']['Last']
converted = self.amount / float(last)
else:
converted = self.amount / float(rate_dict[to_currency]['last'])
return LBRYFee(to_currency, converted, self.address).as_dict()
def as_dict(self):
return {self.currency_symbol: {'amount': self.amount, 'address': self.address}}
def from_dict(self, fee_dict):
s = fee_dict.keys()[0]
return LBRYFee(s, fee_dict[s]['amount'], fee_dict[s]['address'])

View file

@ -1,13 +1,63 @@
import requests
import json import json
BASE_METADATA_FIELDS = ['title', 'description', 'author', 'language', 'license', 'content-type'] from googlefinance import getQuotes
from lbrynet.conf import CURRENCIES
SOURCE_TYPES = ['lbry_sd_hash', 'url', 'btih']
BASE_METADATA_FIELDS = ['title', 'description', 'author', 'language', 'license', 'content-type', 'sources']
OPTIONAL_METADATA_FIELDS = ['thumbnail', 'preview', 'fee', 'contact', 'pubkey'] OPTIONAL_METADATA_FIELDS = ['thumbnail', 'preview', 'fee', 'contact', 'pubkey']
#v0.0.1 metadata #v0.0.1 metadata
METADATA_REVISIONS = {'0.0.1': {'required': BASE_METADATA_FIELDS, 'optional': OPTIONAL_METADATA_FIELDS}} METADATA_REVISIONS = {'0.0.1': {'required': BASE_METADATA_FIELDS, 'optional': OPTIONAL_METADATA_FIELDS}}
#v0.0.2 metadata additions #v0.0.2 metadata additions
METADATA_REVISIONS['0.0.2'] = {'required': ['nsfw'], 'optional': []} METADATA_REVISIONS['0.0.2'] = {'required': ['nsfw', 'ver'], 'optional': ['licence_url']}
CURRENT_METADATA_VERSION = '0.0.2'
class LBRYFee(object):
def __init__(self, currency, amount, address=None):
assert currency in [c.keys()[0] for c in CURRENCIES], "Unsupported currency: %s" % str(currency)
self.address = address
self.currency_symbol = currency
self.currency = next(c for c in CURRENCIES if self.currency_symbol in c)
if not isinstance(amount, float):
self.amount = float(amount)
else:
self.amount = amount
def __call__(self):
return {self.currency_symbol: {'amount': self.amount, 'address': self.address}}
def convert(self, amount_only=False):
if self.currency_symbol == "LBC":
r = round(float(self.amount), 5)
elif self.currency_symbol == "BTC":
r = round(float(self.BTC_to_LBC(self.amount)), 5)
elif self.currency_symbol == "USD":
r = round(float(self.BTC_to_LBC(self.USD_to_BTC(self.amount))), 5)
if not amount_only:
return {'LBC': {'amount': r, 'address': self.address}}
else:
return r
def USD_to_BTC(self, usd):
r = float(getQuotes('CURRENCY:%sBTC' % self.currency_symbol)[0]['LastTradePrice']) * float(usd)
return r
def BTC_to_LBC(self, btc):
r = requests.get("https://bittrex.com/api/v1.1/public/getticker", {'market': 'BTC-LBC'})
last = json.loads(r.text)['result']['Last']
converted = float(btc) / float(last)
return converted
def fee_from_dict(fee_dict):
s = fee_dict.keys()[0]
return LBRYFee(s, fee_dict[s]['amount'], fee_dict[s]['address'])
class Metadata(dict): class Metadata(dict):
@ -15,6 +65,14 @@ class Metadata(dict):
dict.__init__(self) dict.__init__(self)
self.metaversion = None self.metaversion = None
m = metadata.copy() m = metadata.copy()
if 'fee' in metadata:
assert fee_from_dict(metadata['fee'])
assert "sources" in metadata, "No sources given"
for source in metadata['sources']:
assert source in SOURCE_TYPES, "Unknown source type"
for version in METADATA_REVISIONS: for version in METADATA_REVISIONS:
for k in METADATA_REVISIONS[version]['required']: for k in METADATA_REVISIONS[version]['required']:
assert k in metadata, "Missing required metadata field: %s" % k assert k in metadata, "Missing required metadata field: %s" % k

View file

@ -4,7 +4,8 @@ from lbrynet.core.client.ClientRequest import ClientRequest
from lbrynet.core.Error import UnknownNameError, InvalidStreamInfoError, RequestCanceledError from lbrynet.core.Error import UnknownNameError, InvalidStreamInfoError, RequestCanceledError
from lbrynet.core.Error import InsufficientFundsError from lbrynet.core.Error import InsufficientFundsError
from lbrynet.core.sqlite_helpers import rerun_if_locked from lbrynet.core.sqlite_helpers import rerun_if_locked
from lbrynet.conf import BASE_METADATA_FIELDS, SOURCE_TYPES, OPTIONAL_METADATA_FIELDS from lbrynet.conf import SOURCE_TYPES
from lbrynet.core.LBRYMetadata import Metadata
from lbryum import SimpleConfig, Network from lbryum import SimpleConfig, Network
from lbryum.lbrycrd import COIN, TYPE_ADDRESS from lbryum.lbrycrd import COIN, TYPE_ADDRESS
@ -316,7 +317,6 @@ class LBRYWallet(object):
return d return d
def _get_stream_info_from_value(self, result, name): def _get_stream_info_from_value(self, result, name):
r_dict = {}
if 'value' in result: if 'value' in result:
value = result['value'] value = result['value']
@ -324,16 +324,11 @@ class LBRYWallet(object):
value_dict = json.loads(value) value_dict = json.loads(value)
except (ValueError, TypeError): except (ValueError, TypeError):
return Failure(InvalidStreamInfoError(name)) return Failure(InvalidStreamInfoError(name))
r_dict['sources'] = value_dict['sources'] m = Metadata(value_dict)
for field in BASE_METADATA_FIELDS:
r_dict[field] = value_dict[field]
for field in value_dict:
if field in OPTIONAL_METADATA_FIELDS:
r_dict[field] = value_dict[field]
if 'txid' in result: if 'txid' in result:
d = self._save_name_metadata(name, r_dict['sources']['lbry_sd_hash'], str(result['txid'])) d = self._save_name_metadata(name, m['sources']['lbry_sd_hash'], str(result['txid']))
d.addCallback(lambda _: r_dict) d.addCallback(lambda _: log.info("lbry://%s complies with %s" % (name, m.metaversion)))
d.addCallback(lambda _: m)
return d return d
elif 'error' in result: elif 'error' in result:
log.warning("Got an error looking up a name: %s", result['error']) log.warning("Got an error looking up a name: %s", result['error'])
@ -342,21 +337,14 @@ class LBRYWallet(object):
log.warning("Got an error looking up a name: %s", json.dumps(result)) log.warning("Got an error looking up a name: %s", json.dumps(result))
return Failure(UnknownNameError(name)) return Failure(UnknownNameError(name))
def claim_name(self, name, bid, sources, metadata, fee=None): def claim_name(self, name, bid, m):
value = {'sources': {}}
for k in SOURCE_TYPES:
if k in sources:
value['sources'][k] = sources[k]
if value['sources'] == {}:
return defer.fail("No source given")
if fee is not None:
if "LBC" in fee:
value['fee'] = {'LBC': {'amount': fee['LBC']['amount'], 'address': fee['LBC']['address']}}
d = self._send_name_claim(name, json.dumps(value), bid) metadata = Metadata(m)
d = self._send_name_claim(name, json.dumps(metadata), bid)
def _save_metadata(txid): def _save_metadata(txid):
d = self._save_name_metadata(name, value['sources']['lbry_sd_hash'], txid) d = self._save_name_metadata(name, metadata['sources']['lbry_sd_hash'], txid)
d.addCallback(lambda _: txid) d.addCallback(lambda _: txid)
return d return d

View file

@ -54,8 +54,6 @@ from lbrynet.lbryfilemanager.LBRYFileManager import LBRYFileManager
from lbrynet.lbryfile.LBRYFileMetadataManager import DBLBRYFileMetadataManager, TempLBRYFileMetadataManager from lbrynet.lbryfile.LBRYFileMetadataManager import DBLBRYFileMetadataManager, TempLBRYFileMetadataManager
# from lbryum import LOG_PATH as lbryum_log # from lbryum import LOG_PATH as lbryum_log
log = logging.getLogger(__name__)
if sys.platform != "darwin": if sys.platform != "darwin":
log_dir = os.path.join(os.path.expanduser("~"), ".lbrynet") log_dir = os.path.join(os.path.expanduser("~"), ".lbrynet")
@ -67,13 +65,12 @@ if not os.path.isdir(log_dir):
lbrynet_log = os.path.join(log_dir, LOG_FILE_NAME) lbrynet_log = os.path.join(log_dir, LOG_FILE_NAME)
log = logging.getLogger(__name__)
# TODO: configuring a logger on module import drastically reduces the # TODO: configuring a logger on module import drastically reduces the
# amount of control the caller of this code has over logging # amount of control the caller of this code has over logging
# #
# Better would be to configure all logging at runtime. # Better would be to configure all logging at runtime.
handler = logging.handlers.RotatingFileHandler(lbrynet_log, maxBytes=2097152, backupCount=5) handler = logging.handlers.RotatingFileHandler(lbrynet_log, maxBytes=2097152, backupCount=5)
log = logging.getLogger(__name__)
log.addHandler(handler) log.addHandler(handler)
log.setLevel(logging.INFO) log.setLevel(logging.INFO)
@ -1908,19 +1905,30 @@ class LBRYDaemon(jsonrpc.JSONRPC):
Returns: Returns:
Claim txid Claim txid
""" """
# start(self, name, file_path, bid, metadata, fee=None, sources=None):
name = p['name'] name = p['name']
bid = p['bid'] bid = p['bid']
file_path = p['file_path'] file_path = p['file_path']
metadata = Metadata(p['metadata']) metadata = p['metadata']
def _set_address(address):
metadata['fee']['address'] = address
return defer.succeed(None)
if 'fee' in p: if 'fee' in p:
fee = LBRYFee.from_dict(p['fee']) metadata['fee'] = p['fee']
if 'address' not in metadata['fee']:
d = self.session.wallet.get_new_address()
d.addCallback(_set_address)
else: else:
fee = None d = defer.succeed(None)
else:
d = defer.succeed(None)
pub = Publisher(self.session, self.lbry_file_manager, self.session.wallet) pub = Publisher(self.session, self.lbry_file_manager, self.session.wallet)
d = pub.start(name, file_path, bid, metadata, fee) d.addCallback(lambda _: pub.start(name, file_path, bid, metadata))
d.addCallbacks(lambda msg: self._render_response(msg, OK_CODE), d.addCallbacks(lambda msg: self._render_response(msg, OK_CODE),
lambda err: self._render_response(err.getTraceback(), BAD_REQUEST)) lambda err: self._render_response(err.getTraceback(), BAD_REQUEST))

View file

@ -11,7 +11,7 @@ from twisted.internet.task import LoopingCall
from lbrynet.core.Error import InvalidStreamInfoError, InsufficientFundsError from lbrynet.core.Error import InvalidStreamInfoError, InsufficientFundsError
from lbrynet.core.PaymentRateManager import PaymentRateManager from lbrynet.core.PaymentRateManager import PaymentRateManager
from lbrynet.core.StreamDescriptor import download_sd_blob from lbrynet.core.StreamDescriptor import download_sd_blob
from lbrynet.core.LBRYFee import LBRYFee from lbrynet.core.LBRYMetadata import Metadata, fee_from_dict
from lbrynet.lbryfilemanager.LBRYFileDownloader import ManagedLBRYFileDownloaderFactory from lbrynet.lbryfilemanager.LBRYFileDownloader import ManagedLBRYFileDownloaderFactory
from lbrynet.conf import DEFAULT_TIMEOUT, LOG_FILE_NAME from lbrynet.conf import DEFAULT_TIMEOUT, LOG_FILE_NAME
@ -48,8 +48,7 @@ class GetStream(object):
self.wallet = wallet self.wallet = wallet
self.resolved_name = None self.resolved_name = None
self.description = None self.description = None
self.key_fee = None self.fee = None
self.key_fee_address = None
self.data_rate = data_rate self.data_rate = data_rate
self.name = None self.name = None
self.file_name = file_name self.file_name = file_name
@ -59,7 +58,7 @@ class GetStream(object):
self.sd_identifier = sd_identifier self.sd_identifier = sd_identifier
self.stream_hash = None self.stream_hash = None
self.max_key_fee = max_key_fee self.max_key_fee = max_key_fee
self.stream_info = None self.metadata = None
self.stream_info_manager = None self.stream_info_manager = None
self.d = defer.Deferred(None) self.d = defer.Deferred(None)
self.timeout = timeout self.timeout = timeout
@ -87,22 +86,14 @@ class GetStream(object):
def start(self, stream_info, name): def start(self, stream_info, name):
self.resolved_name = name self.resolved_name = name
self.stream_info = stream_info self.metadata = stream_info
if 'sources' in self.stream_info: self.stream_hash = self.metadata['sources']['lbry_sd_hash']
self.stream_hash = self.stream_info['sources']['lbry_sd_hash']
else: if 'fee' in self.metadata:
raise InvalidStreamInfoError(self.stream_info) fee = fee_from_dict(self.metadata['fee'])
if 'description' in self.stream_info: if fee.convert(amount_only=True) > self.max_key_fee:
self.description = self.stream_info['description']
if 'fee' in self.stream_info:
self.fee = LBRYFee.from_dict(stream_info['fee'])
else:
self.fee = None
if self.key_fee > self.max_key_fee:
log.info("Key fee %f above limit of %f didn't download lbry://%s" % (self.key_fee, self.max_key_fee, self.resolved_name)) log.info("Key fee %f above limit of %f didn't download lbry://%s" % (self.key_fee, self.max_key_fee, self.resolved_name))
return defer.fail(None) return defer.fail(None)
else:
pass
def _cause_timeout(): def _cause_timeout():
self.timeout_counter = self.timeout * 2 self.timeout_counter = self.timeout * 2
@ -132,8 +123,8 @@ class GetStream(object):
def _start_download(self, downloader): def _start_download(self, downloader):
def _pay_key_fee(): def _pay_key_fee():
if self.key_fee is not None and self.key_fee_address is not None: if self.fee is not None:
reserved_points = self.wallet.reserve_points(self.key_fee_address, self.key_fee) reserved_points = self.wallet.reserve_points(self.fee.address, self.fee.convert(amount_only=True))
if reserved_points is None: if reserved_points is None:
return defer.fail(InsufficientFundsError()) return defer.fail(InsufficientFundsError())
log.info("Key fee: %f --> %s" % (self.key_fee, self.key_fee_address)) log.info("Key fee: %f --> %s" % (self.key_fee, self.key_fee_address))

View file

@ -10,6 +10,7 @@ from lbrynet.core.Error import InsufficientFundsError
from lbrynet.lbryfilemanager.LBRYFileCreator import create_lbry_file from lbrynet.lbryfilemanager.LBRYFileCreator import create_lbry_file
from lbrynet.lbryfile.StreamDescriptor import publish_sd_blob from lbrynet.lbryfile.StreamDescriptor import publish_sd_blob
from lbrynet.core.PaymentRateManager import PaymentRateManager from lbrynet.core.PaymentRateManager import PaymentRateManager
from lbrynet.core.LBRYMetadata import Metadata, CURRENT_METADATA_VERSION
from lbrynet.lbryfilemanager.LBRYFileDownloader import ManagedLBRYFileDownloader from lbrynet.lbryfilemanager.LBRYFileDownloader import ManagedLBRYFileDownloader
from lbrynet.conf import LOG_FILE_NAME from lbrynet.conf import LOG_FILE_NAME
from twisted.internet import threads, defer from twisted.internet import threads, defer
@ -42,10 +43,9 @@ class Publisher(object):
self.verified = False self.verified = False
self.lbry_file = None self.lbry_file = None
self.txid = None self.txid = None
self.sources = {} self.metadata = {}
self.fee = None
def start(self, name, file_path, bid, metadata, fee=None, sources={}): def start(self, name, file_path, bid, metadata):
def _show_result(): def _show_result():
log.info("Published %s --> lbry://%s txid: %s", self.file_name, self.publish_name, self.txid) log.info("Published %s --> lbry://%s txid: %s", self.file_name, self.publish_name, self.txid)
@ -54,7 +54,6 @@ class Publisher(object):
self.publish_name = name self.publish_name = name
self.file_path = file_path self.file_path = file_path
self.bid_amount = bid self.bid_amount = bid
self.fee = fee
self.metadata = metadata self.metadata = metadata
d = self._check_file_path(self.file_path) d = self._check_file_path(self.file_path)
@ -75,16 +74,6 @@ class Publisher(object):
return True return True
return threads.deferToThread(check_file_threaded) return threads.deferToThread(check_file_threaded)
def _get_new_address(self):
d = self.wallet.get_new_address()
def set_address(address):
self.key_fee_address = address
return True
d.addCallback(set_address)
return d
def set_status(self, lbry_file_downloader): def set_status(self, lbry_file_downloader):
self.lbry_file = lbry_file_downloader self.lbry_file = lbry_file_downloader
d = self.lbry_file_manager.change_lbry_file_status(self.lbry_file, ManagedLBRYFileDownloader.STATUS_FINISHED) d = self.lbry_file_manager.change_lbry_file_status(self.lbry_file, ManagedLBRYFileDownloader.STATUS_FINISHED)
@ -102,7 +91,9 @@ class Publisher(object):
self.lbry_file.stream_hash) self.lbry_file.stream_hash)
def set_sd_hash(sd_hash): def set_sd_hash(sd_hash):
self.sources['lbry_sd_hash'] = sd_hash if 'sources' not in self.metadata:
self.metadata['sources'] = {}
self.metadata['sources']['lbry_sd_hash'] = sd_hash
d.addCallback(set_sd_hash) d.addCallback(set_sd_hash)
return d return d
@ -110,11 +101,10 @@ class Publisher(object):
def _claim_name(self): def _claim_name(self):
self.metadata['content-type'] = mimetypes.guess_type(os.path.join(self.lbry_file.download_directory, self.metadata['content-type'] = mimetypes.guess_type(os.path.join(self.lbry_file.download_directory,
self.lbry_file.file_name))[0] self.lbry_file.file_name))[0]
self.metadata['ver'] = CURRENT_METADATA_VERSION
d = self.wallet.claim_name(self.publish_name, d = self.wallet.claim_name(self.publish_name,
self.bid_amount, self.bid_amount,
self.sources, Metadata(self.metadata))
self.metadata,
fee=self.fee)
def set_tx_hash(txid): def set_tx_hash(txid):
self.txid = txid self.txid = txid