forked from LBRYCommunity/lbry-sdk
update lbrynet api and tests
This commit is contained in:
parent
ccc94a0db9
commit
e9cfbea75f
5 changed files with 139 additions and 61 deletions
|
@ -135,7 +135,7 @@ class EncryptedFileManager(object):
|
|||
rowid, stream_hash, payment_rate_manager, blob_data_rate=options)
|
||||
yield downloader.restore()
|
||||
except Exception:
|
||||
log.exception('An error occurred while starting a lbry file (%s, %s, %s)',
|
||||
log.error('An error occurred while starting a lbry file (%s, %s, %s)',
|
||||
rowid, stream_hash, options)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
|
|
|
@ -12,15 +12,10 @@ from requests import exceptions as requests_exceptions
|
|||
import random
|
||||
|
||||
from twisted.web import server
|
||||
from twisted.internet import defer, threads, error, reactor, task
|
||||
from twisted.internet import defer, threads, error, reactor
|
||||
from twisted.internet.task import LoopingCall
|
||||
from twisted.python.failure import Failure
|
||||
|
||||
from lbryschema.decode import smart_decode
|
||||
from lbryschema.claim import ClaimDict
|
||||
from lbryschema.uri import parse_lbry_uri
|
||||
from lbryschema.error import DecodeError
|
||||
|
||||
# TODO: importing this when internet is disabled raises a socket.gaierror
|
||||
from lbryum.version import LBRYUM_VERSION
|
||||
from lbrynet import __version__ as LBRYNET_VERSION
|
||||
|
@ -30,7 +25,7 @@ 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 lbryschema.decode import smart_decode
|
||||
|
||||
from lbrynet.lbryfile.client.EncryptedFileDownloader import EncryptedFileSaverFactory
|
||||
from lbrynet.lbryfile.client.EncryptedFileDownloader import EncryptedFileOpenerFactory
|
||||
from lbrynet.lbryfile.client.EncryptedFileOptions import add_lbry_file_to_sd_identifier
|
||||
|
@ -737,9 +732,10 @@ class Daemon(AuthJSONRPCServer):
|
|||
defer.returnValue(result)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def _publish_stream(self, name, bid, claim_dict, file_path=None):
|
||||
def _publish_stream(self, name, bid, claim_dict, file_path=None, certificate_id=None):
|
||||
|
||||
publisher = Publisher(self.session, self.lbry_file_manager, self.session.wallet)
|
||||
publisher = Publisher(self.session, self.lbry_file_manager, self.session.wallet,
|
||||
certificate_id)
|
||||
verify_name_characters(name)
|
||||
if bid <= 0.0:
|
||||
raise Exception("Invalid bid")
|
||||
|
@ -916,7 +912,7 @@ class Daemon(AuthJSONRPCServer):
|
|||
lbry_file.txid,
|
||||
lbry_file.nout)
|
||||
try:
|
||||
metadata = smart_decode(claim['value']).claim_dict
|
||||
metadata = claim['value']
|
||||
except:
|
||||
metadata = None
|
||||
try:
|
||||
|
@ -972,6 +968,7 @@ class Daemon(AuthJSONRPCServer):
|
|||
lbry_file_dict = yield self._get_lbry_file_dict(lbry_file, full_status=full_status)
|
||||
file_dicts.append(lbry_file_dict)
|
||||
lbry_files = file_dicts
|
||||
log.info("Collected %i lbry files", len(lbry_files))
|
||||
defer.returnValue(lbry_files)
|
||||
|
||||
# TODO: do this and get_blobs_for_sd_hash in the stream info manager
|
||||
|
@ -1363,10 +1360,6 @@ class Daemon(AuthJSONRPCServer):
|
|||
resolvable
|
||||
"""
|
||||
|
||||
if not name:
|
||||
# TODO: seems like we should raise an error here
|
||||
defer.returnValue(None)
|
||||
|
||||
try:
|
||||
metadata = yield self._resolve_name(name, force_refresh=force)
|
||||
except UnknownNameError:
|
||||
|
@ -1412,6 +1405,31 @@ class Daemon(AuthJSONRPCServer):
|
|||
|
||||
@AuthJSONRPCServer.auth_required
|
||||
@defer.inlineCallbacks
|
||||
def jsonrpc_resolve(self, uri):
|
||||
"""
|
||||
Resolve a LBRY URI
|
||||
|
||||
Args:
|
||||
'uri': (str) uri to download
|
||||
Returns:
|
||||
{
|
||||
'claim_id': (str) claim id,
|
||||
'claim_sequence': (int) claim sequence number,
|
||||
'decoded_claim': (bool) whether or not the claim value was decoded,
|
||||
'depth': (int) claim depth,
|
||||
'has_signature': (bool) included if decoded_claim
|
||||
'name': (str) claim name,
|
||||
'txid': (str) claim txid,
|
||||
'nout': (str) claim nout,
|
||||
'signature_is_valid': (bool), included if has_signature,
|
||||
'value': ClaimDict if decoded, otherwise hex string
|
||||
}
|
||||
"""
|
||||
|
||||
resolved = yield self.session.wallet.resolve_uri(uri)
|
||||
results = yield self._render_response(resolved)
|
||||
defer.returnValue(results)
|
||||
|
||||
@AuthJSONRPCServer.auth_required
|
||||
@defer.inlineCallbacks
|
||||
def jsonrpc_get(self, uri, file_name=None, timeout=None, download_directory=None):
|
||||
|
@ -1536,7 +1554,7 @@ class Daemon(AuthJSONRPCServer):
|
|||
|
||||
@AuthJSONRPCServer.auth_required
|
||||
@defer.inlineCallbacks
|
||||
def jsonrpc_file_delete(self, delete_target_file=True, **kwargs):
|
||||
def jsonrpc_file_delete(self, delete_target_file=True, delete_all=False, **kwargs):
|
||||
"""
|
||||
Delete a lbry file
|
||||
|
||||
|
@ -1556,21 +1574,27 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
|
||||
lbry_files = yield self._get_lbry_files(return_json=False, **kwargs)
|
||||
|
||||
if len(lbry_files) > 1:
|
||||
log.warning("There are %i files to delete, use narrower filters to select one",
|
||||
len(lbry_files))
|
||||
result = False
|
||||
elif not lbry_files:
|
||||
if not delete_all:
|
||||
log.warning("There are %i files to delete, use narrower filters to select one",
|
||||
len(lbry_files))
|
||||
result = False
|
||||
else:
|
||||
log.warning("Deleting %i files",
|
||||
len(lbry_files))
|
||||
|
||||
if not lbry_files:
|
||||
log.warning("There is no file to delete")
|
||||
result = False
|
||||
else:
|
||||
lbry_file = lbry_files[0]
|
||||
file_name, stream_hash = lbry_file.file_name, lbry_file.stream_hash
|
||||
if lbry_file.claim_id in self.streams:
|
||||
del self.streams[lbry_file.claim_id]
|
||||
yield self.lbry_file_manager.delete_lbry_file(lbry_file,
|
||||
delete_file=delete_target_file)
|
||||
log.info("Deleted %s (%s)", file_name, utils.short_hash(stream_hash))
|
||||
for lbry_file in lbry_files:
|
||||
file_name, stream_hash = lbry_file.file_name, lbry_file.stream_hash
|
||||
if lbry_file.claim_id in self.streams:
|
||||
del self.streams[lbry_file.claim_id]
|
||||
yield self.lbry_file_manager.delete_lbry_file(lbry_file,
|
||||
delete_file=delete_target_file)
|
||||
log.info("Deleted %s (%s)", file_name, utils.short_hash(stream_hash))
|
||||
result = True
|
||||
response = yield self._render_response(result)
|
||||
defer.returnValue(response)
|
||||
|
@ -1596,11 +1620,51 @@ class Daemon(AuthJSONRPCServer):
|
|||
cost = yield self.get_est_cost(name, size)
|
||||
defer.returnValue(cost)
|
||||
|
||||
@AuthJSONRPCServer.auth_required
|
||||
@defer.inlineCallbacks
|
||||
def jsonrpc_channel_new(self, channel_name, amount):
|
||||
"""
|
||||
Generate a publisher key and create a new certificate claim
|
||||
|
||||
Args:
|
||||
'name': (str) '@' prefixed name
|
||||
'amount': (float) amount to claim name
|
||||
|
||||
Returns:
|
||||
(dict) Dictionary containing result of the claim
|
||||
{
|
||||
'tx' : (str) hex encoded transaction
|
||||
'txid' : (str) txid of resulting claim
|
||||
'nout' : (int) nout of the resulting claim
|
||||
'fee' : (float) fee paid for the claim transaction
|
||||
'claim_id' : (str) claim ID of the resulting claim
|
||||
}
|
||||
"""
|
||||
|
||||
result = yield self.session.wallet.claim_new_channel(channel_name, amount)
|
||||
response = yield self._render_response(result)
|
||||
defer.returnValue(response)
|
||||
|
||||
@AuthJSONRPCServer.auth_required
|
||||
@defer.inlineCallbacks
|
||||
def jsonrpc_channel_list_mine(self):
|
||||
"""
|
||||
Get my channels
|
||||
|
||||
Returns:
|
||||
(list) ClaimDict
|
||||
"""
|
||||
|
||||
result = yield self.session.wallet.channel_list()
|
||||
response = yield self._render_response(result)
|
||||
defer.returnValue(response)
|
||||
|
||||
@AuthJSONRPCServer.auth_required
|
||||
@defer.inlineCallbacks
|
||||
def jsonrpc_publish(self, name, bid, metadata=None, file_path=None, fee=None, title=None,
|
||||
description=None, author=None, language=None, license=None,
|
||||
license_url=None, thumbnail=None, preview=None, nsfw=None, sources=None):
|
||||
license_url=None, thumbnail=None, preview=None, nsfw=None, sources=None,
|
||||
channel_name=None):
|
||||
"""
|
||||
Make a new name claim and publish associated data to lbrynet,
|
||||
update over existing claim if user already has a claim for name.
|
||||
|
@ -1640,6 +1704,7 @@ class Daemon(AuthJSONRPCServer):
|
|||
'preview'(optional): (str) preview URL for the file
|
||||
'nsfw'(optional): (bool) True if not safe for work
|
||||
'sources'(optional): (dict){'lbry_sd_hash':sd_hash} specifies sd hash of file
|
||||
'channel_name' (optional): (str) name of the publisher channel
|
||||
|
||||
Returns:
|
||||
(dict) Dictionary containing result of the claim
|
||||
|
@ -1690,10 +1755,11 @@ class Daemon(AuthJSONRPCServer):
|
|||
else:
|
||||
address = fee_dict['address']
|
||||
new_fee_dict = {
|
||||
'version':'_0_0_1',
|
||||
'version': '_0_0_1',
|
||||
'currency': currency,
|
||||
'address':address,
|
||||
'amount':fee_dict['amount']}
|
||||
'address': address,
|
||||
'amount': fee_dict['amount']
|
||||
}
|
||||
metadata['fee'] = new_fee_dict
|
||||
|
||||
log.info("Publish: %s", {
|
||||
|
@ -1705,14 +1771,30 @@ class Daemon(AuthJSONRPCServer):
|
|||
})
|
||||
|
||||
claim_dict = {
|
||||
'version':'_0_0_1',
|
||||
'claimType':'streamType',
|
||||
'stream':{'metadata':metadata, 'version':'_0_0_1'}}
|
||||
'version': '_0_0_1',
|
||||
'claimType': 'streamType',
|
||||
'stream': {
|
||||
'metadata': metadata,
|
||||
'version': '_0_0_1'
|
||||
}
|
||||
}
|
||||
|
||||
if sources is not None:
|
||||
claim_dict['stream']['source'] = sources
|
||||
|
||||
result = yield self._publish_stream(name, bid, claim_dict, file_path)
|
||||
if channel_name:
|
||||
certificate_id = None
|
||||
my_certificates = yield self.session.wallet.channel_list()
|
||||
for certificate in my_certificates:
|
||||
if channel_name == certificate['name']:
|
||||
certificate_id = certificate['claim_id']
|
||||
break
|
||||
if not certificate_id:
|
||||
raise Exception("Cannot publish using channel %s" % channel_name)
|
||||
else:
|
||||
certificate_id = None
|
||||
|
||||
result = yield self._publish_stream(name, bid, claim_dict, file_path, certificate_id)
|
||||
response = yield self._render_response(result)
|
||||
defer.returnValue(response)
|
||||
|
||||
|
@ -1725,13 +1807,12 @@ class Daemon(AuthJSONRPCServer):
|
|||
|
||||
@AuthJSONRPCServer.auth_required
|
||||
@defer.inlineCallbacks
|
||||
def jsonrpc_claim_abandon(self, txid, nout):
|
||||
def jsonrpc_claim_abandon(self, claim_id):
|
||||
"""
|
||||
Abandon a name and reclaim credits from the claim
|
||||
|
||||
Args:
|
||||
'txid': (str) txid of claim
|
||||
'nout': (int) nout of claim
|
||||
'claim_id': (str) claim_id of claim
|
||||
Return:
|
||||
(dict) Dictionary containing result of the claim
|
||||
{
|
||||
|
@ -1741,7 +1822,7 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
|
||||
try:
|
||||
abandon_claim_tx = yield self.session.wallet.abandon_claim(txid, nout)
|
||||
abandon_claim_tx = yield self.session.wallet.abandon_claim(claim_id)
|
||||
response = yield self._render_response(abandon_claim_tx)
|
||||
except BaseException as err:
|
||||
log.warning(err)
|
||||
|
@ -1861,6 +1942,7 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
return self.jsonrpc_claim_list(**kwargs)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def jsonrpc_claim_list(self, name):
|
||||
"""
|
||||
Get claims for a name
|
||||
|
@ -1889,10 +1971,8 @@ class Daemon(AuthJSONRPCServer):
|
|||
}
|
||||
"""
|
||||
|
||||
d = self.session.wallet.get_claims_for_name(name)
|
||||
d.addCallback(format_json_out_amount_as_float)
|
||||
d.addCallback(lambda r: self._render_response(r))
|
||||
return d
|
||||
claims = yield self.session.wallet.get_claims_for_name(name)
|
||||
defer.returnValue(claims)
|
||||
|
||||
@AuthJSONRPCServer.auth_required
|
||||
def jsonrpc_get_transaction_history(self):
|
||||
|
@ -2409,7 +2489,7 @@ class _ResolveNameHelper(object):
|
|||
def get_deferred(self):
|
||||
if self.need_fresh_stream():
|
||||
log.info("Resolving stream info for lbry://%s", self.name)
|
||||
d = self.wallet.get_stream_info_for_name(self.name)
|
||||
d = self.wallet.get_claim_by_name(self.name)
|
||||
d.addCallback(self._cache_stream_info)
|
||||
else:
|
||||
log.debug("Returning cached stream info for lbry://%s", self.name)
|
||||
|
@ -2433,11 +2513,10 @@ class _ResolveNameHelper(object):
|
|||
|
||||
def _cache_stream_info(self, stream_info):
|
||||
self.daemon.name_cache[self.name] = {
|
||||
'claim_metadata': stream_info,
|
||||
'claim_metadata': stream_info['value'],
|
||||
'timestamp': self.now()
|
||||
}
|
||||
d = self.wallet.get_txid_for_name(self.name)
|
||||
d.addCallback(self._add_txid)
|
||||
d = self._add_txid(stream_info['txid'])
|
||||
d.addCallback(lambda _: self.daemon._update_claim_cache())
|
||||
d.addCallback(lambda _: self.name_data['claim_metadata'])
|
||||
return d
|
||||
|
|
|
@ -64,7 +64,6 @@ class MarketFeed(object):
|
|||
self.rate = ExchangeRate(self.market, price, int(time.time()))
|
||||
|
||||
def _log_error(self, err):
|
||||
log.error(err)
|
||||
log.warning(
|
||||
"There was a problem updating %s exchange rate information from %s",
|
||||
self.market, self.name)
|
||||
|
|
|
@ -13,10 +13,11 @@ log = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class Publisher(object):
|
||||
def __init__(self, session, lbry_file_manager, wallet):
|
||||
def __init__(self, session, lbry_file_manager, wallet, certificate_id):
|
||||
self.session = session
|
||||
self.lbry_file_manager = lbry_file_manager
|
||||
self.wallet = wallet
|
||||
self.certificate_id = certificate_id
|
||||
self.lbry_file = None
|
||||
|
||||
"""
|
||||
|
@ -56,7 +57,8 @@ class Publisher(object):
|
|||
|
||||
@defer.inlineCallbacks
|
||||
def make_claim(self, name, bid, claim_dict):
|
||||
claim_out = yield self.wallet.claim_name(name, bid, claim_dict)
|
||||
claim_out = yield self.wallet.claim_name(name, bid, claim_dict,
|
||||
certificate_id=self.certificate_id)
|
||||
defer.returnValue(claim_out)
|
||||
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ from twisted.trial import unittest
|
|||
from twisted.internet import threads, defer
|
||||
|
||||
from lbrynet.core.Error import InsufficientFundsError
|
||||
from lbrynet.core.Wallet import Wallet, ReservedPoints
|
||||
from lbrynet.core.Wallet import Wallet, ReservedPoints, InMemoryStorage
|
||||
|
||||
test_metadata = {
|
||||
'license': 'NASA',
|
||||
|
@ -29,6 +29,8 @@ class MocLbryumWallet(Wallet):
|
|||
self.wallet_balance = Decimal(10.0)
|
||||
self.total_reserved_points = Decimal(0.0)
|
||||
self.queued_payments = defaultdict(Decimal)
|
||||
self._storage = InMemoryStorage()
|
||||
|
||||
def get_name_claims(self):
|
||||
return threads.deferToThread(lambda: [])
|
||||
|
||||
|
@ -50,7 +52,7 @@ class WalletTest(unittest.TestCase):
|
|||
|
||||
def test_successful_send_name_claim(self):
|
||||
expected_claim_out = {
|
||||
"claimid": "f43dc06256a69988bdbea09a58c80493ba15dcfa",
|
||||
"claim_id": "f43dc06256a69988bdbea09a58c80493ba15dcfa",
|
||||
"fee": "0.00012",
|
||||
"nout": 0,
|
||||
"success": True,
|
||||
|
@ -59,12 +61,12 @@ class WalletTest(unittest.TestCase):
|
|||
|
||||
def check_out(claim_out):
|
||||
self.assertTrue('success' not in claim_out)
|
||||
self.assertEqual(expected_claim_out['claimid'], claim_out['claimid'])
|
||||
self.assertEqual(expected_claim_out['claim_id'], claim_out['claim_id'])
|
||||
self.assertEqual(expected_claim_out['fee'], claim_out['fee'])
|
||||
self.assertEqual(expected_claim_out['nout'], claim_out['nout'])
|
||||
self.assertEqual(expected_claim_out['txid'], claim_out['txid'])
|
||||
|
||||
def success_send_name_claim(self, name, val, amount):
|
||||
def success_send_name_claim(self, name, val, amount, certificate_id=None):
|
||||
return expected_claim_out
|
||||
|
||||
MocLbryumWallet._send_name_claim = success_send_name_claim
|
||||
|
@ -111,8 +113,8 @@ class WalletTest(unittest.TestCase):
|
|||
return threads.deferToThread(lambda: claim_out)
|
||||
MocLbryumWallet._abandon_claim = failed_abandon_claim
|
||||
wallet = MocLbryumWallet()
|
||||
d = wallet.abandon_claim("11030a76521e5f552ca87ad70765d0cc52e6ea4c0dc0063335e6cf2a9a85085f", 1)
|
||||
self.assertFailure(d,Exception)
|
||||
d = wallet.abandon_claim("f43dc06256a69988bdbea09a58c80493ba15dcfa")
|
||||
self.assertFailure(d, Exception)
|
||||
return d
|
||||
|
||||
def test_successful_abandon(self):
|
||||
|
@ -132,7 +134,7 @@ class WalletTest(unittest.TestCase):
|
|||
|
||||
MocLbryumWallet._abandon_claim = success_abandon_claim
|
||||
wallet = MocLbryumWallet()
|
||||
d = wallet.abandon_claim("0578c161ad8d36a7580c557d7444f967ea7f988e194c20d0e3c42c3cabf110dd", 1)
|
||||
d = wallet.abandon_claim("f43dc06256a69988bdbea09a58c80493ba15dcfa")
|
||||
d.addCallback(lambda claim_out: check_out(claim_out))
|
||||
return d
|
||||
|
||||
|
@ -187,7 +189,3 @@ class WalletTest(unittest.TestCase):
|
|||
d.addCallback(lambda _: wallet.support_claim('test', "f43dc06256a69988bdbea09a58c80493ba15dcfa", 4))
|
||||
self.assertFailure(d,InsufficientFundsError)
|
||||
return d
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue