Merge pull request #331 from lbryio/fix_wallet_threading_issue
Fix wallet concurrency issue
This commit is contained in:
commit
27a09d4a4f
2 changed files with 79 additions and 124 deletions
|
@ -14,10 +14,9 @@ from jsonschema import ValidationError
|
|||
from decimal import Decimal
|
||||
|
||||
from lbryum import SimpleConfig, Network
|
||||
from lbryum.lbrycrd import COIN
|
||||
from lbryum.lbrycrd import COIN, RECOMMENDED_CLAIMTRIE_HASH_CONFIRMS
|
||||
import lbryum.wallet
|
||||
from lbryum.commands import known_commands, Commands
|
||||
from lbryum.transaction import Transaction
|
||||
|
||||
from lbrynet.interfaces import IRequestCreator, IQueryHandlerFactory, IQueryHandler, IWallet
|
||||
from lbrynet.core.client.ClientRequest import ClientRequest
|
||||
|
@ -93,7 +92,6 @@ class Wallet(object):
|
|||
self._first_run = self._FIRST_RUN_UNKNOWN
|
||||
|
||||
def start(self):
|
||||
|
||||
def start_manage():
|
||||
self.stopped = False
|
||||
self.manage()
|
||||
|
@ -500,11 +498,6 @@ class Wallet(object):
|
|||
def support_claim(self, name, claim_id, amount):
|
||||
return self._support_claim(name, claim_id, amount)
|
||||
|
||||
def get_tx(self, txid):
|
||||
d = self._get_raw_tx(txid)
|
||||
d.addCallback(self._get_decoded_tx)
|
||||
return d
|
||||
|
||||
def get_block_info(self, height):
|
||||
d = self._get_blockhash(height)
|
||||
return d
|
||||
|
@ -715,15 +708,9 @@ class Wallet(object):
|
|||
def _check_first_run(self):
|
||||
return defer.fail(NotImplementedError())
|
||||
|
||||
def _get_raw_tx(self, txid):
|
||||
return defer.fail(NotImplementedError())
|
||||
|
||||
def _send_name_claim(self, name, val, amount):
|
||||
return defer.fail(NotImplementedError())
|
||||
|
||||
def _get_decoded_tx(self, raw_tx):
|
||||
return defer.fail(NotImplementedError())
|
||||
|
||||
def _abandon_claim(self, claim_outpoint):
|
||||
return defer.fail(NotImplementedError())
|
||||
|
||||
|
@ -751,9 +738,6 @@ class Wallet(object):
|
|||
def _address_is_mine(self, address):
|
||||
return defer.fail(NotImplementedError())
|
||||
|
||||
def _get_transaction(self, txid):
|
||||
return defer.fail(NotImplementedError())
|
||||
|
||||
def _start(self):
|
||||
pass
|
||||
|
||||
|
@ -780,7 +764,6 @@ class LBRYumWallet(Wallet):
|
|||
self.max_behind = 0
|
||||
|
||||
def _start(self):
|
||||
|
||||
network_start_d = defer.Deferred()
|
||||
|
||||
def setup_network():
|
||||
|
@ -789,6 +772,7 @@ class LBRYumWallet(Wallet):
|
|||
alert.info("Loading the wallet...")
|
||||
return defer.succeed(self.network.start())
|
||||
|
||||
|
||||
d = setup_network()
|
||||
|
||||
def check_started():
|
||||
|
@ -853,6 +837,7 @@ class LBRYumWallet(Wallet):
|
|||
wallet.create_main_account()
|
||||
wallet.synchronize()
|
||||
self.wallet = wallet
|
||||
return defer.succeed(True)
|
||||
|
||||
blockchain_caught_d = defer.Deferred()
|
||||
|
||||
|
@ -896,8 +881,7 @@ class LBRYumWallet(Wallet):
|
|||
return defer.fail(err)
|
||||
|
||||
self._catch_up_check = task.LoopingCall(check_caught_up)
|
||||
|
||||
d = threads.deferToThread(get_wallet)
|
||||
d = get_wallet()
|
||||
d.addCallback(self._save_wallet)
|
||||
d.addCallback(lambda _: self.wallet.start_threads(self.network))
|
||||
d.addCallback(lambda _: self._catch_up_check.start(.1))
|
||||
|
@ -908,24 +892,36 @@ class LBRYumWallet(Wallet):
|
|||
def _get_cmd_runner(self):
|
||||
self.cmd_runner = Commands(self.config, self.wallet, self.network)
|
||||
|
||||
def get_balance(self):
|
||||
cmd = known_commands['getbalance']
|
||||
# run commands as a defer.succeed,
|
||||
# lbryum commands should be run this way , unless if the command
|
||||
# only makes a lbrum server query, use _run_cmd_as_defer_to_thread()
|
||||
def _run_cmd_as_defer_succeed(self, command_name, *args):
|
||||
cmd = known_commands[command_name]
|
||||
func = getattr(self.cmd_runner, cmd.name)
|
||||
return defer.succeed(func(*args))
|
||||
|
||||
|
||||
# run commands as a deferToThread, lbryum commands that only make
|
||||
# queries to lbryum server should be run this way
|
||||
def _run_cmd_as_defer_to_thread(self, command_name, *args):
|
||||
cmd = known_commands[command_name]
|
||||
func = getattr(self.cmd_runner, cmd.name)
|
||||
return threads.deferToThread(func, *args)
|
||||
|
||||
def get_balance(self):
|
||||
accounts = None
|
||||
exclude_claimtrietx = True
|
||||
d = threads.deferToThread(func, accounts, exclude_claimtrietx)
|
||||
d = self._run_cmd_as_defer_succeed('getbalance', accounts, exclude_claimtrietx)
|
||||
d.addCallback(lambda result: Decimal(result['confirmed']) + Decimal(result.get('unconfirmed', 0.0)))
|
||||
return d
|
||||
|
||||
def get_new_address(self):
|
||||
d = threads.deferToThread(self.wallet.create_new_address)
|
||||
d = defer.succeed(self.wallet.create_new_address())
|
||||
d.addCallback(self._save_wallet)
|
||||
return d
|
||||
|
||||
def get_block(self, blockhash):
|
||||
cmd = known_commands['getblock']
|
||||
func = getattr(self.cmd_runner, cmd.name)
|
||||
return threads.deferToThread(func, blockhash)
|
||||
return self._run_cmd_as_defer_to_thread('getblock', blockhash)
|
||||
|
||||
def get_most_recent_blocktime(self):
|
||||
header = self.network.get_header(self.network.get_local_height())
|
||||
|
@ -933,137 +929,112 @@ class LBRYumWallet(Wallet):
|
|||
|
||||
def get_best_blockhash(self):
|
||||
height = self.network.get_local_height()
|
||||
d = threads.deferToThread(self.network.blockchain.read_header, height)
|
||||
d.addCallback(lambda header: self.network.blockchain.hash_header(header))
|
||||
return d
|
||||
header = self.network.blockchain.read_header(height)
|
||||
return defer.succeed(self.network.blockchain.hash_header(header))
|
||||
|
||||
def _get_blockhash(self, height):
|
||||
d = threads.deferToThread(self.network.blockchain.read_header, height)
|
||||
d.addCallback(lambda header: self.network.blockchain.hash_header(header))
|
||||
return d
|
||||
header = self.network.blockchain.read_header(height)
|
||||
return defer.succeed(self.network.blockchain.hash_header(header))
|
||||
|
||||
def get_name_claims(self):
|
||||
cmd = known_commands['getnameclaims']
|
||||
func = getattr(self.cmd_runner, cmd.name)
|
||||
return threads.deferToThread(func)
|
||||
return self._run_cmd_as_defer_succeed('getnameclaims')
|
||||
|
||||
def _check_first_run(self):
|
||||
return defer.succeed(self.first_run)
|
||||
|
||||
def _get_raw_tx(self, txid):
|
||||
cmd = known_commands['gettransaction']
|
||||
func = getattr(self.cmd_runner, cmd.name)
|
||||
return threads.deferToThread(func, txid)
|
||||
|
||||
def _get_transaction(self, txid):
|
||||
def _add_confirms(tx):
|
||||
tx['confirmations'] = self.wallet.get_confirmations(txid)
|
||||
return tx
|
||||
|
||||
d = self._get_raw_tx(txid)
|
||||
d.addCallback(self._get_decoded_tx)
|
||||
d.addCallback(_add_confirms)
|
||||
return d
|
||||
def _get_claims_for_name(self, name):
|
||||
return self._run_cmd_as_defer_to_thread('getclaimsforname', name)
|
||||
|
||||
def _send_name_claim(self, name, val, amount):
|
||||
cmd = known_commands['claim']
|
||||
func = getattr(self.cmd_runner, cmd.name)
|
||||
return threads.deferToThread(func, name, json.dumps(val), amount)
|
||||
|
||||
def _get_claims_for_name(self, name):
|
||||
cmd = known_commands['getclaimsforname']
|
||||
func = getattr(self.cmd_runner, cmd.name)
|
||||
return threads.deferToThread(func, name)
|
||||
broadcast = False
|
||||
log.debug("Name claim %s %s %f", name, val, amount)
|
||||
d = self._run_cmd_as_defer_succeed('claim', name, json.dumps(val), amount, broadcast)
|
||||
d.addCallback(lambda claim_out: self._broadcast_claim_transaction(claim_out))
|
||||
return d
|
||||
|
||||
def _send_name_claim_update(self, name, claim_id, claim_outpoint, value, amount):
|
||||
metadata = json.dumps(value)
|
||||
log.debug("Update %s %d %f %s %s '%s'", claim_outpoint['txid'], claim_outpoint['nout'],
|
||||
amount, name, claim_id, metadata)
|
||||
cmd = known_commands['update']
|
||||
func = getattr(self.cmd_runner, cmd.name)
|
||||
return threads.deferToThread(func, claim_outpoint['txid'], claim_outpoint['nout'], name, claim_id, metadata, amount)
|
||||
|
||||
|
||||
def _get_decoded_tx(self, raw_tx):
|
||||
tx = Transaction(raw_tx)
|
||||
decoded_tx = {}
|
||||
decoded_tx['vout'] = []
|
||||
for output in tx.outputs():
|
||||
out = {}
|
||||
out['value'] = Decimal(output[2]) / Decimal(COIN)
|
||||
decoded_tx['vout'].append(out)
|
||||
return decoded_tx
|
||||
broadcast = False
|
||||
d = self._run_cmd_as_defer_succeed('update', claim_outpoint['txid'], claim_outpoint['nout'],
|
||||
name, claim_id, metadata, amount, broadcast)
|
||||
d.addCallback(lambda claim_out: self._broadcast_claim_transaction(claim_out))
|
||||
return d
|
||||
|
||||
def _abandon_claim(self, claim_outpoint):
|
||||
log.debug("Abandon %s %s" % (claim_outpoint['txid'], claim_outpoint['nout']))
|
||||
cmd = known_commands['abandon']
|
||||
func = getattr(self.cmd_runner, cmd.name)
|
||||
d = threads.deferToThread(func, claim_outpoint['txid'], claim_outpoint['nout'])
|
||||
broadcast = False
|
||||
d = self._run_cmd_as_defer_succeed('abandon', claim_outpoint['txid'], claim_outpoint['nout'], broadcast)
|
||||
d.addCallback(lambda claim_out: self._broadcast_claim_transaction(claim_out))
|
||||
return d
|
||||
|
||||
def _support_claim(self, name, claim_id, amount):
|
||||
log.debug("Support %s %s %f" % (name, claim_id, amount))
|
||||
cmd = known_commands['support']
|
||||
func = getattr(self.cmd_runner, cmd.name)
|
||||
d = threads.deferToThread(func, name, claim_id, amount)
|
||||
broadcast = False
|
||||
d = self._run_cmd_as_defer_succeed('support', name, claim_id, amount, broadcast)
|
||||
d.addCallback(lambda claim_out: self._broadcast_claim_transaction(claim_out))
|
||||
return d
|
||||
|
||||
def _broadcast_claim_transaction(self, claim_out):
|
||||
if 'success' not in claim_out:
|
||||
raise Exception('Unexpected claim command output:{}'.format(claim_out))
|
||||
if claim_out['success']:
|
||||
d = self._broadcast_transaction(claim_out['tx'])
|
||||
d.addCallback(lambda _: claim_out)
|
||||
return d
|
||||
else:
|
||||
return defer.succeed(claim_out)
|
||||
|
||||
def _broadcast_transaction(self, raw_tx):
|
||||
def _log_tx(r):
|
||||
log.debug("Broadcast tx: %s", r)
|
||||
return r
|
||||
cmd = known_commands['broadcast']
|
||||
func = getattr(self.cmd_runner, cmd.name)
|
||||
d = threads.deferToThread(func, raw_tx)
|
||||
d = self._run_cmd_as_defer_to_thread('broadcast', raw_tx)
|
||||
d.addCallback(_log_tx)
|
||||
d.addCallback(
|
||||
lambda r: r if len(r) == 64 else defer.fail(Exception("Transaction rejected")))
|
||||
d.addCallback(self._save_wallet)
|
||||
return d
|
||||
|
||||
def _do_send_many(self, payments_to_send):
|
||||
log.warning("Doing send many. payments to send: %s", str(payments_to_send))
|
||||
cmd = known_commands['paytomanyandsend']
|
||||
func = getattr(self.cmd_runner, cmd.name)
|
||||
return threads.deferToThread(func, payments_to_send.iteritems())
|
||||
def broadcast_send_many(paytomany_out):
|
||||
if 'hex' not in paytomany_out:
|
||||
raise Exception('Unepxected paytomany output:{}'.format(paytomany_out))
|
||||
return self._broadcast_transaction(paytomany_out['hex'])
|
||||
log.debug("Doing send many. payments to send: %s", str(payments_to_send))
|
||||
d = self._run_cmd_as_defer_succeed('paytomany', payments_to_send.iteritems())
|
||||
d.addCallback(lambda out: broadcast_send_many(out))
|
||||
return d
|
||||
|
||||
def _get_value_for_name(self, name):
|
||||
cmd = known_commands['getvalueforname']
|
||||
func = getattr(self.cmd_runner, cmd.name)
|
||||
return threads.deferToThread(func, name)
|
||||
block_header = self.network.blockchain.read_header(self.network.get_local_height() - RECOMMENDED_CLAIMTRIE_HASH_CONFIRMS)
|
||||
block_hash = self.network.blockchain.hash_header(block_header)
|
||||
d = self._run_cmd_as_defer_to_thread('requestvalueforname', name, block_hash)
|
||||
d.addCallback(lambda response: Commands._verify_proof(name, block_header['claim_trie_root'], response))
|
||||
return d
|
||||
|
||||
def get_claims_from_tx(self, txid):
|
||||
cmd = known_commands['getclaimsfromtx']
|
||||
func = getattr(self.cmd_runner, cmd.name)
|
||||
return threads.deferToThread(func, txid)
|
||||
return self._run_cmd_as_defer_to_thread('getclaimsfromtx', txid)
|
||||
|
||||
def _get_balance_for_address(self, address):
|
||||
return defer.succeed(Decimal(self.wallet.get_addr_received(address))/COIN)
|
||||
|
||||
def get_nametrie(self):
|
||||
cmd = known_commands['getclaimtrie']
|
||||
func = getattr(self.cmd_runner, cmd.name)
|
||||
return threads.deferToThread(func)
|
||||
return self._run_cmd_as_defer_to_thread('getclaimtrie')
|
||||
|
||||
def _get_history(self):
|
||||
cmd = known_commands['history']
|
||||
func = getattr(self.cmd_runner, cmd.name)
|
||||
return threads.deferToThread(func)
|
||||
return self._run_cmd_as_defer_succeed('history')
|
||||
|
||||
def _address_is_mine(self, address):
|
||||
cmd = known_commands['ismine']
|
||||
func = getattr(self.cmd_runner, cmd.name)
|
||||
return threads.deferToThread(func, address)
|
||||
return self._run_cmd_as_defer_succeed('ismine', address)
|
||||
|
||||
def get_pub_keys(self, wallet):
|
||||
cmd = known_commands['getpubkeys']
|
||||
func = getattr(self.cmd_runner, cmd.name)
|
||||
return threads.deferToThread(func, wallet)
|
||||
return self._run_cmd_as_defer_succeed('getpubkyes', wallet)
|
||||
|
||||
def _save_wallet(self, val):
|
||||
d = threads.deferToThread(self.wallet.storage.write)
|
||||
d.addCallback(lambda _: val)
|
||||
return d
|
||||
self.wallet.storage.write()
|
||||
return defer.succeed(val)
|
||||
|
||||
|
||||
|
||||
class LBRYcrdAddressRequester(object):
|
||||
|
|
|
@ -1886,22 +1886,6 @@ class Daemon(AuthJSONRPCServer):
|
|||
d.addCallback(lambda r: self._render_response(r, OK_CODE))
|
||||
return d
|
||||
|
||||
def jsonrpc_get_transaction(self, p):
|
||||
"""
|
||||
Get a decoded transaction from a txid
|
||||
|
||||
Args:
|
||||
txid: txid hex string
|
||||
Returns:
|
||||
JSON formatted transaction
|
||||
"""
|
||||
|
||||
|
||||
txid = p['txid']
|
||||
d = self.session.wallet.get_transaction(txid)
|
||||
d.addCallback(lambda r: self._render_response(r, OK_CODE))
|
||||
return d
|
||||
|
||||
@AuthJSONRPCServer.auth_required
|
||||
def jsonrpc_address_is_mine(self, p):
|
||||
"""
|
||||
|
|
Loading…
Reference in a new issue