changes from jacks review

This commit is contained in:
Lex Berezhny 2018-08-15 19:23:06 -04:00 committed by Jack Robison
parent e6fd8cc0f2
commit 3686b1d970
No known key found for this signature in database
GPG key ID: DF25C68FE0239BB2
10 changed files with 201 additions and 1685 deletions

View file

@ -1,4 +1,16 @@
import sys
from twisted.internet import asyncioreactor
if 'twisted.internet.reactor' not in sys.modules:
asyncioreactor.install()
else:
from twisted.internet import reactor
if not isinstance(reactor, asyncioreactor.AsyncioSelectorReactor):
# pyinstaller hooks install the default reactor before
# any of our code runs, see kivy for similar problem:
# https://github.com/kivy/kivy/issues/4182
del sys.modules['twisted.internet.reactor']
asyncioreactor.install()
import json
import asyncio
from aiohttp.client_exceptions import ClientConnectorError

File diff suppressed because it is too large Load diff

View file

@ -73,7 +73,7 @@ class ClientProtocol(Protocol, TimeoutMixin):
log.debug("Connection lost to %s: %s", self.peer, reason)
self.setTimeout(None)
self.connection_closed = True
if reason.check(error.ConnectionDone) or reason is None:
if reason is None or reason.check(error.ConnectionDone):
err = failure.Failure(ConnectionClosedBeforeResponseError())
else:
err = reason

View file

@ -6,19 +6,7 @@ import urllib
import json
import textwrap
#import sys
#from twisted.internet import asyncioreactor
#if 'twisted.internet.reactor' not in sys.modules:
# asyncioreactor.install()
#else:
# from twisted.internet import reactor
# if not isinstance(reactor, asyncioreactor.AsyncioSelectorReactor):
# # pyinstaller hooks install the default reactor before
# # any of our code runs, see kivy for similar problem:
# # https://github.com/kivy/kivy/issues/4182
# del sys.modules['twisted.internet.reactor']
# asyncioreactor.install()
from operator import itemgetter
from binascii import hexlify, unhexlify
from copy import deepcopy
from decimal import Decimal, InvalidOperation
@ -92,6 +80,7 @@ DIRECTION_ASCENDING = 'asc'
DIRECTION_DESCENDING = 'desc'
DIRECTIONS = DIRECTION_ASCENDING, DIRECTION_DESCENDING
class IterableContainer:
def __iter__(self):
for attr in dir(self):
@ -251,7 +240,10 @@ class Daemon(AuthJSONRPCServer):
@property
def ledger(self):
return self.wallet.default_account.ledger
try:
return self.wallet.default_account.ledger
except AttributeError:
return None
@defer.inlineCallbacks
def setup(self):
@ -395,51 +387,15 @@ class Daemon(AuthJSONRPCServer):
log.exception)
self.analytics_manager.send_claim_action('publish')
nout = 0
log.info("Success! Published to lbry://%s txid: %s nout: %d", name, tx.id, nout)
defer.returnValue(self._txo_to_response(tx, nout))
def _txo_to_response(self, tx, nout):
txo = tx.outputs[nout]
return {
log.info("Success! Published to lbry://%s txid: %s nout: %d", name, tx.id, nout)
defer.returnValue({
"success": True,
"txid": tx.id,
"nout": nout,
"tx": hexlify(tx.raw),
"fee": str(Decimal(tx.fee) / COIN),
"tx": tx,
"claim_id": txo.claim_id,
"value": hexlify(txo.claim).decode(),
"claim_address": self.ledger.hash160_to_address(txo.script.values['pubkey_hash'])
}
@defer.inlineCallbacks
def _resolve(self, *uris, **kwargs):
"""Resolves a URI. Can check the cache first before going out to the blockchain and stores the result.
Args:
name: the lbry://<name> to resolve
force_refresh: if True, always go out to the blockchain to resolve.
"""
page = kwargs.get('page', 0)
page_size = kwargs.get('page_size', 10)
check_cache = kwargs.get('check_cache', False) # TODO: put caching back (was force_refresh parameter)
results = yield self.wallet.resolve(*uris, page=page, page_size=page_size)
self.save_claims((value for value in results.values() if 'error' not in value))
yield defer.returnValue(results)
@defer.inlineCallbacks
def save_claims(self, claim_infos):
to_save = []
for info in claim_infos:
if 'value' in info:
if info['value']:
to_save.append(info)
else:
if 'certificate' in info and info['certificate']['value']:
to_save.append(info['certificate'])
if 'claim' in info and info['claim']['value']:
to_save.append(info['claim'])
yield self.storage.save_claims(to_save)
"claim_address": self.ledger.hash160_to_address(txo.script.values['pubkey_hash']),
"output": tx.outputs[nout]
})
def _get_or_download_sd_blob(self, blob, sd_hash):
if blob:
@ -484,7 +440,7 @@ class Daemon(AuthJSONRPCServer):
cost = self._get_est_cost_from_stream_size(size)
resolved = yield self._resolve(uri)
resolved = yield self.wallet.resolve(uri)
if uri in resolved and 'claim' in resolved[uri]:
claim = ClaimDict.load_dict(resolved[uri]['claim']['value'])
@ -531,7 +487,7 @@ class Daemon(AuthJSONRPCServer):
Resolve a name and return the estimated stream cost
"""
resolved = (yield self._resolve(uri))[uri]
resolved = (yield self.wallet.resolve(uri))[uri]
if resolved:
claim_response = resolved[uri]
else:
@ -1012,7 +968,8 @@ class Daemon(AuthJSONRPCServer):
Returns:
(float) amount of lbry credits in wallet
"""
assert address is None, "Limiting by address needs to be re-implemented in new wallet."
if address is not None:
raise NotImplementedError("Limiting by address needs to be re-implemented in new wallet.")
dewies = yield self.wallet.default_account.get_balance(
0 if include_unconfirmed else 6
)
@ -1046,7 +1003,6 @@ class Daemon(AuthJSONRPCServer):
defer.returnValue(response)
@requires(WALLET_COMPONENT, conditions=[WALLET_IS_UNLOCKED])
@defer.inlineCallbacks
def jsonrpc_wallet_decrypt(self):
"""
Decrypt an encrypted wallet, this will remove the wallet password
@ -1060,13 +1016,9 @@ class Daemon(AuthJSONRPCServer):
Returns:
(bool) true if wallet is decrypted, otherwise false
"""
result = self.wallet.decrypt_wallet()
response = yield self._render_response(result)
defer.returnValue(response)
return defer.succeed(self.wallet.decrypt_wallet())
@requires(WALLET_COMPONENT, conditions=[WALLET_IS_UNLOCKED])
@defer.inlineCallbacks
def jsonrpc_wallet_encrypt(self, new_password):
"""
Encrypt a wallet with a password, if the wallet is already encrypted this will update
@ -1081,13 +1033,11 @@ class Daemon(AuthJSONRPCServer):
Returns:
(bool) true if wallet is decrypted, otherwise false
"""
self.wallet.encrypt_wallet(new_password)
response = yield self._render_response(self.wallet.wallet.use_encryption)
defer.returnValue(response)
return defer.succeed(self.wallet.encrypt_wallet(new_password))
@defer.inlineCallbacks
def jsonrpc_stop(self):
@AuthJSONRPCServer.deprecated("stop")
def jsonrpc_daemon_stop(self):
"""
Stop lbrynet-daemon
@ -1100,11 +1050,24 @@ class Daemon(AuthJSONRPCServer):
Returns:
(string) Shutdown message
"""
return self.jsonrpc_stop()
def jsonrpc_stop(self):
"""
Stop lbrynet
Usage:
stop
Options:
None
Returns:
(string) Shutdown message
"""
log.info("Shutting down lbrynet daemon")
response = yield self._render_response("Shutting down")
reactor.callLater(0.1, reactor.fireSystemEvent, "shutdown")
defer.returnValue(response)
defer.returnValue("Shutting down")
@requires(FILE_MANAGER_COMPONENT)
@defer.inlineCallbacks
@ -1198,7 +1161,7 @@ class Daemon(AuthJSONRPCServer):
try:
name = parse_lbry_uri(name).name
metadata = yield self._resolve(name, check_cache=not force)
metadata = yield self.wallet.resolve(name, check_cache=not force)
if name in metadata:
metadata = metadata[name]
except UnknownNameError:
@ -1337,7 +1300,7 @@ class Daemon(AuthJSONRPCServer):
except URIParseError:
results[u] = {"error": "%s is not a valid uri" % u}
resolved = yield self._resolve(*valid_uris, check_cache=not force)
resolved = yield self.wallet.resolve(*valid_uris, check_cache=not force)
for resolved_uri in resolved:
results[resolved_uri] = resolved[resolved_uri]
@ -1398,7 +1361,7 @@ class Daemon(AuthJSONRPCServer):
if parsed_uri.is_channel and not parsed_uri.path:
raise Exception("cannot download a channel claim, specify a /path")
resolved_result = yield self._resolve(uri)
resolved_result = yield self.wallet.resolve(uri)
if resolved_result and uri in resolved_result:
resolved = resolved_result[uri]
else:
@ -1584,13 +1547,30 @@ class Daemon(AuthJSONRPCServer):
'claim_id' : (str) claim ID of the resulting claim
}
"""
try:
parsed = parse_lbry_uri(channel_name)
if not parsed.is_channel:
raise Exception("Cannot make a new channel for a non channel name")
if parsed.path:
raise Exception("Invalid channel uri")
except (TypeError, URIParseError):
raise Exception("Invalid channel name")
if amount <= 0:
raise Exception("Invalid amount")
amount = int(amount * COIN)
tx = yield self.wallet.claim_new_channel(channel_name, amount)
self.wallet.save()
result = self._txo_to_response(tx, 0)
self.analytics_manager.send_new_channel()
log.info("Claimed a new channel! Result: %s", result)
defer.returnValue(result)
nout = 0
txo = tx.outputs[nout]
log.info("Claimed a new channel! lbry://%s txid: %s nout: %d", channel_name, tx.id, nout)
defer.returnValue({
"success": True,
"tx": tx,
"claim_id": txo.claim_id,
"claim_address": self.ledger.hash160_to_address(txo.script.values['pubkey_hash']),
"output": txo
})
@requires(WALLET_COMPONENT)
@defer.inlineCallbacks
@ -1764,6 +1744,11 @@ class Daemon(AuthJSONRPCServer):
bid = int(bid * COIN)
for address in [claim_address, change_address]:
if address is not None:
# raises an error if the address is invalid
decode_address(address)
available = yield self.wallet.default_account.get_balance()
if bid >= available:
# TODO: add check for existing claim balance
@ -1880,8 +1865,7 @@ class Daemon(AuthJSONRPCServer):
result = yield self._publish_stream(name, bid, claim_dict, file_path, certificate,
claim_address, change_address)
response = yield self._render_response(result)
defer.returnValue(response)
defer.returnValue(result)
@requires(WALLET_COMPONENT, conditions=[WALLET_IS_UNLOCKED])
@defer.inlineCallbacks
@ -2179,7 +2163,7 @@ class Daemon(AuthJSONRPCServer):
except URIParseError:
results[chan_uri] = {"error": "%s is not a valid uri" % chan_uri}
resolved = yield self._resolve(*valid_uris, page=page, page_size=page_size)
resolved = yield self.wallet.resolve(*valid_uris, page=page, page_size=page_size)
for u in resolved:
if 'error' in resolved[u]:
results[u] = resolved[u]
@ -2384,6 +2368,32 @@ class Daemon(AuthJSONRPCServer):
d.addCallback(lambda address: self._render_response(address))
return d
@requires(WALLET_COMPONENT, conditions=[WALLET_IS_UNLOCKED])
@AuthJSONRPCServer.deprecated("wallet_send")
@defer.inlineCallbacks
def jsonrpc_send_amount_to_address(self, amount, address):
"""
Queue a payment of credits to an address
Usage:
send_amount_to_address (<amount> | --amount=<amount>) (<address> | --address=<address>)
Options:
--amount=<amount> : (float) amount to send
--address=<address> : (str) address to send credits to
Returns:
(bool) true if payment successfully scheduled
"""
if amount < 0:
raise NegativeFundsError()
elif not amount:
raise NullFundsError()
reserved_points = self.wallet.reserve_points(address, amount)
if reserved_points is None:
raise InsufficientFundsError()
yield self.wallet.send_points_to_address(reserved_points, amount)
self.analytics_manager.send_credits_sent()
defer.returnValue(True)
@requires(WALLET_COMPONENT, conditions=[WALLET_IS_UNLOCKED])
@defer.inlineCallbacks
def jsonrpc_wallet_send(self, amount, address=None, claim_id=None):
@ -2749,7 +2759,7 @@ class Daemon(AuthJSONRPCServer):
"""
if uri or stream_hash or sd_hash:
if uri:
metadata = (yield self._resolve(uri))[uri]
metadata = (yield self.wallet.resolve(uri))[uri]
sd_hash = utils.get_sd_hash(metadata)
stream_hash = yield self.storage.get_stream_hash_for_sd_hash(sd_hash)
elif stream_hash:
@ -2898,7 +2908,7 @@ class Daemon(AuthJSONRPCServer):
hosts = {}
for k, v in data_store.items():
for contact, _ in v:
for contact in map(itemgetter(0), v):
hosts.setdefault(contact, []).append(hexlify(k).decode())
contact_set = set()
@ -2907,7 +2917,7 @@ class Daemon(AuthJSONRPCServer):
for i in range(len(self.dht_node._routingTable._buckets)):
for contact in self.dht_node._routingTable._buckets[i]._contacts:
blobs = [hexlify(raw_hash).decode() for raw_hash in hosts.pop(contact)] if contact in hosts else []
blobs = list(hosts.pop(contact)) if contact in hosts else []
blob_hashes.update(blobs)
host = {
"address": contact.address,
@ -2949,6 +2959,24 @@ class Daemon(AuthJSONRPCServer):
return self._blob_availability(blob_hash, search_timeout, blob_timeout)
@requires(UPNP_COMPONENT, WALLET_COMPONENT, DHT_COMPONENT, conditions=[WALLET_IS_UNLOCKED])
@AuthJSONRPCServer.deprecated("stream_availability")
def jsonrpc_get_availability(self, uri, sd_timeout=None, peer_timeout=None):
"""
Get stream availability for lbry uri
Usage:
get_availability (<uri> | --uri=<uri>) [<sd_timeout> | --sd_timeout=<sd_timeout>]
[<peer_timeout> | --peer_timeout=<peer_timeout>]
Options:
--uri=<uri> : (str) check availability for this uri
--sd_timeout=<sd_timeout> : (int) sd blob download timeout
--peer_timeout=<peer_timeout> : (int) how long to look for peers
Returns:
(float) Peers per blob / total blobs
"""
return self.jsonrpc_stream_availability(uri, peer_timeout, sd_timeout)
@requires(UPNP_COMPONENT, WALLET_COMPONENT, DHT_COMPONENT, conditions=[WALLET_IS_UNLOCKED])
@defer.inlineCallbacks
def jsonrpc_stream_availability(self, uri, search_timeout=None, blob_timeout=None):
@ -3002,7 +3030,7 @@ class Daemon(AuthJSONRPCServer):
}
try:
resolved_result = (yield self._resolve(uri))[uri]
resolved_result = (yield self.wallet.resolve(uri))[uri]
response['did_resolve'] = True
except UnknownNameError:
response['error'] = "Failed to resolve name"
@ -3229,10 +3257,9 @@ class Daemon(AuthJSONRPCServer):
everything=False, outputs=1, broadcast=False):
"""
Transfer some amount (or --everything) to an account from another
account (can be the same account). Decimal amounts are interpreted
as LBC and non-decimal amounts are interpreted as dewies. You can
also spread the transfer across a number of --outputs (cannot be
used together with --everything).
account (can be the same account). Amounts are interpreted as LBC.
You can also spread the transfer across a number of --outputs (cannot
be used together with --everything).
Usage:
fund (<to_account> | --to_account=<to_account>)
@ -3244,7 +3271,7 @@ class Daemon(AuthJSONRPCServer):
Options:
--to_account=<to_account> : (str) send to this account
--from_account=<from_account> : (str) spend from this account
--amount=<amount> : (str) the amount to transfer (lbc or dewies)
--amount=<amount> : (str) the amount to transfer lbc
--everything : (bool) transfer everything (excluding claims), default: false.
--outputs=<outputs> : (int) split payment across many outputs, default: 1.
--broadcast : (bool) actually broadcast the transaction, default: false.
@ -3263,25 +3290,7 @@ class Daemon(AuthJSONRPCServer):
return from_account.fund(
to_account=to_account, amount=amount, everything=everything,
outputs=outputs, broadcast=broadcast
).addCallback(lambda tx: self.tx_to_json(tx, from_account.ledger))
@staticmethod
def tx_to_json(tx, ledger):
return {
'txid': tx.id,
'inputs': [
{'amount': txi.amount, 'address': txi.txo_ref.txo.get_address(ledger)}
for txi in tx.inputs
],
'outputs': [
{'amount': txo.amount, 'address': txo.get_address(ledger)}
for txo in tx.outputs
],
'total_input': tx.input_sum,
'total_output': tx.input_sum,
'total_fee': tx.fee,
'xhex': hexlify(tx.raw).decode(),
}
)
def get_account_or_error(self, argument: str, account_name: str, lbc_only=False):
for account in self.wallet.default_wallet.accounts:
@ -3306,9 +3315,9 @@ class Daemon(AuthJSONRPCServer):
if '.' in amount:
return int(Decimal(amount) * COIN)
elif amount.isdigit():
return int(amount)
elif isinstance(amount, int):
return amount
amount = int(amount)
if isinstance(amount, int):
return amount * COIN
raise ValueError("Invalid value for '{}' argument: {}".format(argument, amount))

View file

@ -1,225 +0,0 @@
# pylint: skip-file
import json
import os
import sys
import colorama
from docopt import docopt
from collections import OrderedDict
from lbrynet import conf
from lbrynet.core import utils
from lbrynet.daemon.auth.client import JSONRPCException, LBRYAPIClient, AuthAPIClient
from lbrynet.daemon.Daemon import Daemon
from lbrynet.core.system_info import get_platform
from jsonrpc.common import RPCError
from requests.exceptions import ConnectionError
from urllib2 import URLError, HTTPError
from httplib import UNAUTHORIZED
def remove_brackets(key):
if key.startswith("<") and key.endswith(">"):
return str(key[1:-1])
return key
def set_kwargs(parsed_args):
kwargs = OrderedDict()
for key, arg in parsed_args.iteritems():
if arg is None:
continue
elif key.startswith("--") and remove_brackets(key[2:]) not in kwargs:
k = remove_brackets(key[2:])
elif remove_brackets(key) not in kwargs:
k = remove_brackets(key)
kwargs[k] = guess_type(arg, k)
return kwargs
def main():
argv = sys.argv[1:]
# check if a config file has been specified. If so, shift
# all the arguments so that the parsing can continue without
# noticing
if len(argv) and argv[0] == "--conf":
if len(argv) < 2:
print_error("No config file specified for --conf option")
print_help()
return
conf.conf_file = argv[1]
argv = argv[2:]
if len(argv):
method, args = argv[0], argv[1:]
else:
print_help()
return
if method in ['help', '--help', '-h']:
if len(args) == 1:
print_help_for_command(args[0])
else:
print_help()
return
elif method in ['version', '--version']:
print(utils.json_dumps_pretty(get_platform(get_ip=False)))
return
if method not in Daemon.callable_methods:
if method not in Daemon.deprecated_methods:
print_error("\"%s\" is not a valid command." % method)
return
new_method = Daemon.deprecated_methods[method].new_command
print_error("\"%s\" is deprecated, using \"%s\"." % (method, new_method))
method = new_method
fn = Daemon.callable_methods[method]
parsed = docopt(fn.__doc__, args)
kwargs = set_kwargs(parsed)
colorama.init()
conf.initialize_settings()
try:
api = LBRYAPIClient.get_client()
api.status()
except (URLError, ConnectionError) as err:
if isinstance(err, HTTPError) and err.code == UNAUTHORIZED:
api = AuthAPIClient.config()
# this can happen if the daemon is using auth with the --http-auth flag
# when the config setting is to not use it
try:
api.status()
except:
print_error("Daemon requires authentication, but none was provided.",
suggest_help=False)
return 1
else:
print_error("Could not connect to daemon. Are you sure it's running?",
suggest_help=False)
return 1
# TODO: check if port is bound. Error if its not
try:
result = api.call(method, kwargs)
if isinstance(result, basestring):
# printing the undumped string is prettier
print(result)
else:
print(utils.json_dumps_pretty(result))
except (RPCError, KeyError, JSONRPCException, HTTPError) as err:
if isinstance(err, HTTPError):
error_body = err.read()
try:
error_data = json.loads(error_body)
except ValueError:
print(
"There was an error, and the response was not valid JSON.\n" +
"Raw JSONRPC response:\n" + error_body
)
return 1
print_error(error_data['error']['message'] + "\n", suggest_help=False)
if 'data' in error_data['error'] and 'traceback' in error_data['error']['data']:
print("Here's the traceback for the error you encountered:")
print("\n".join(error_data['error']['data']['traceback']))
print_help_for_command(method)
elif isinstance(err, RPCError):
print_error(err.msg, suggest_help=False)
# print_help_for_command(method)
else:
print_error("Something went wrong\n", suggest_help=False)
print(str(err))
return 1
def guess_type(x, key=None):
if not isinstance(x, (unicode, str)):
return x
if key in ('uri', 'channel_name', 'name', 'file_name', 'download_directory'):
return x
if x in ('true', 'True', 'TRUE'):
return True
if x in ('false', 'False', 'FALSE'):
return False
if '.' in x:
try:
return float(x)
except ValueError:
# not a float
pass
try:
return int(x)
except ValueError:
return x
def print_help_suggestion():
print("See `{} help` for more information.".format(os.path.basename(sys.argv[0])))
def print_error(message, suggest_help=True):
error_style = colorama.Style.BRIGHT + colorama.Fore.RED
print(error_style + "ERROR: " + message + colorama.Style.RESET_ALL)
if suggest_help:
print_help_suggestion()
def print_help():
print("\n".join([
"NAME",
" lbrynet-cli - LBRY command line client.",
"",
"USAGE",
" lbrynet-cli [--conf <config file>] <command> [<args>]",
"",
"EXAMPLES",
" lbrynet-cli commands # list available commands",
" lbrynet-cli status # get daemon status",
" lbrynet-cli --conf ~/l1.conf status # like above but using ~/l1.conf as config file",
" lbrynet-cli resolve_name what # resolve a name",
" lbrynet-cli help resolve_name # get help for a command",
]))
def print_help_for_command(command):
fn = Daemon.callable_methods.get(command)
if fn:
print("Help for %s method:\n%s" % (command, fn.__doc__))
def wrap_list_to_term_width(l, width=None, separator=', ', prefix=''):
if width is None:
try:
_, width = os.popen('stty size', 'r').read().split()
width = int(width)
except:
pass
if not width:
width = 80
lines = []
curr_line = ''
for item in l:
new_line = curr_line + item + separator
if len(new_line) + len(prefix) > width:
lines.append(curr_line)
curr_line = item + separator
else:
curr_line = new_line
lines.append(curr_line)
ret = prefix + ("\n" + prefix).join(lines)
if ret.endswith(separator):
ret = ret[:-len(separator)]
return ret
if __name__ == '__main__':
sys.exit(main())

View file

@ -64,15 +64,6 @@ def start(argv=None, conf_path=None):
log_support.configure_loggly_handler()
log.debug('Final Settings: %s', conf.settings.get_current_settings_dict())
# fixme: fix that, JSONRPCProxy is gone on py3
#try:
# log.debug('Checking for an existing lbrynet daemon instance')
# JSONRPCProxy.from_url(conf.settings.get_api_connection_string()).status()
# log.info("lbrynet-daemon is already running")
# return
#except Exception:
# log.debug('No lbrynet instance found, continuing to start')
log.info("Starting lbrynet-daemon from command line")
if test_internet_connection():

View file

@ -22,6 +22,7 @@ from lbrynet.daemon.ComponentManager import ComponentManager
from .util import APIKey, get_auth_message, LBRY_SECRET
from .undecorated import undecorated
from .factory import AuthJSONRPCResource
from lbrynet.daemon.json_response_encoder import JSONResponseEncoder
log = logging.getLogger(__name__)
EMPTY_PARAMS = [{}]
@ -85,11 +86,6 @@ class JSONRPCError:
return cls(message, code=code, traceback=traceback)
def default_decimal(obj):
if isinstance(obj, Decimal):
return float(obj)
class UnknownAPIMethodError(Exception):
pass
@ -109,7 +105,7 @@ def jsonrpc_dumps_pretty(obj, **kwargs):
else:
data = {"jsonrpc": "2.0", "result": obj, "id": id_}
return json.dumps(data, cls=jsonrpclib.JSONRPCEncoder, sort_keys=True, indent=2, **kwargs) + "\n"
return json.dumps(data, cls=JSONResponseEncoder, sort_keys=True, indent=2, **kwargs) + "\n"
class JSONRPCServerType(type):
@ -314,7 +310,7 @@ class AuthJSONRPCServer(AuthorizedBase):
# last resort, just cast it as a string
error = JSONRPCError(str(failure))
response_content = jsonrpc_dumps_pretty(error, id=id_)
response_content = jsonrpc_dumps_pretty(error, id=id_, ledger=self.ledger)
self._set_headers(request, response_content)
request.setResponseCode(200)
self._render_message(request, response_content)
@ -575,7 +571,7 @@ class AuthJSONRPCServer(AuthorizedBase):
def _callback_render(self, result, request, id_, auth_required=False):
try:
message = jsonrpc_dumps_pretty(result, id=id_, default=default_decimal)
message = jsonrpc_dumps_pretty(result, id=id_, ledger=self.ledger)
request.setResponseCode(200)
self._set_headers(request, message, auth_required)
self._render_message(request, message)

View file

@ -0,0 +1,44 @@
from decimal import Decimal
from binascii import hexlify
from datetime import datetime
from json import JSONEncoder
from wallet.transaction import Transaction, Output
class JSONResponseEncoder(JSONEncoder):
def __init__(self, *args, ledger, **kwargs):
super().__init__(*args, **kwargs)
self.ledger = ledger
def default(self, obj):
if isinstance(obj, Transaction):
return self.encode_transaction(obj)
if isinstance(obj, Output):
return self.encode_output(obj)
if isinstance(obj, datetime):
return obj.strftime("%Y%m%dT%H:%M:%S")
if isinstance(obj, Decimal):
return float(obj)
return super().default(obj)
def encode_transaction(self, tx):
return {
'txid': tx.id,
'inputs': [self.encode_input(txo) for txo in tx.inputs],
'outputs': [self.encode_output(txo) for txo in tx.outputs],
'total_input': tx.input_sum,
'total_output': tx.input_sum - tx.fee,
'total_fee': tx.fee,
'hex': hexlify(tx.raw).decode(),
}
def encode_output(self, txo):
return {
'nout': txo.position,
'amount': txo.amount,
'address': txo.get_address(self.ledger)
}
def encode_input(self, txi):
return self.encode_output(txi.txo_ref.txo)

View file

@ -656,6 +656,19 @@ class SQLiteStorage:
if support_dl:
yield defer.DeferredList(support_dl)
def save_claims_for_resolve(self, claim_infos):
to_save = []
for info in claim_infos:
if 'value' in info:
if info['value']:
to_save.append(info)
else:
if 'certificate' in info and info['certificate']['value']:
to_save.append(info['certificate'])
if 'claim' in info and info['claim']['value']:
to_save.append(info['claim'])
return self.save_claims(to_save)
def get_old_stream_hashes_for_claim_id(self, claim_id, new_stream_hash):
return self.run_and_return_list(
"select f.stream_hash from file f "

View file

@ -147,11 +147,16 @@ class LbryWalletManager(BaseWalletManager):
def get_info_exchanger(self):
return LBRYcrdAddressRequester(self)
@defer.inlineCallbacks
def resolve(self, *uris, **kwargs):
page = kwargs.get('page', 0)
page_size = kwargs.get('page_size', 10)
check_cache = kwargs.get('check_cache', False) # TODO: put caching back (was force_refresh parameter)
ledger = self.default_account.ledger # type: MainNetLedger
return ledger.resolve(page, page_size, *uris)
results = ledger.resolve(page, page_size, *uris)
yield self.old_db.save_claims_for_resolve(
(value for value in results.values() if 'error' not in value))
defer.returnValue(results)
def get_name_claims(self):
return defer.succeed([])
@ -214,16 +219,6 @@ class LbryWalletManager(BaseWalletManager):
@defer.inlineCallbacks
def claim_new_channel(self, channel_name, amount):
try:
parsed = parse_lbry_uri(channel_name)
if not parsed.is_channel:
raise Exception("Cannot make a new channel for a non channel name")
if parsed.path:
raise Exception("Invalid channel uri")
except (TypeError, URIParseError):
raise Exception("Invalid channel name")
if amount <= 0:
raise Exception("Invalid amount")
account = self.default_account
address = yield account.receiving.get_or_create_usable_address()
cert, key = generate_certificate()