diff --git a/lbrynet/daemon/Daemon.py b/lbrynet/daemon/Daemon.py index 3584c1249..0623102c8 100644 --- a/lbrynet/daemon/Daemon.py +++ b/lbrynet/daemon/Daemon.py @@ -16,6 +16,7 @@ from twisted.internet.task import LoopingCall from twisted.python.failure import Failure from torba.constants import COIN +from torba.baseaccount import SingleKey, HierarchicalDeterministic from lbryschema.claim import ClaimDict from lbryschema.uri import parse_lbry_uri @@ -46,7 +47,7 @@ from lbrynet.core.Peer import Peer from lbrynet.core.SinglePeerDownloader import SinglePeerDownloader from lbrynet.core.client.StandaloneBlobDownloader import StandaloneBlobDownloader from lbrynet.wallet.account import Account as LBCAccount -from torba.baseaccount import SingleKey, HierarchicalDeterministic +from lbrynet.wallet.manager import LbryWalletManager log = logging.getLogger(__name__) requires = AuthJSONRPCServer.requires @@ -226,7 +227,7 @@ class Daemon(AuthJSONRPCServer): # TODO: delete these, get the components where needed self.storage = None self.dht_node = None - self.wallet_manager = None + self.wallet_manager: LbryWalletManager = None self.sd_identifier = None self.file_manager = None self.exchange_rate_manager = None diff --git a/lbrynet/wallet/database.py b/lbrynet/wallet/database.py index 1b945b620..3f8075f3c 100644 --- a/lbrynet/wallet/database.py +++ b/lbrynet/wallet/database.py @@ -64,7 +64,7 @@ class WalletDatabase(BaseDatabase): SELECT tx.txid, txo.position, txo.claim_id FROM txo JOIN tx ON tx.txid=txo.txid WHERE {} AND (is_claim OR is_update) - GROUP BY txo.claim_id ORDER BY tx.height DESC; + GROUP BY txo.claim_id ORDER BY tx.height DESC, tx.position ASC; """.format(filter_sql), (filter_value,) ) @@ -89,23 +89,30 @@ class WalletDatabase(BaseDatabase): filter_sql = "claim_id=?" filter_value = (claim_id,) else: - filter_sql = "txo.txid=? AND position=?" + filter_sql = "txo.txid=? AND txo.position=?" filter_value = (txid, nout) utxos = yield self.db.runQuery( """ - SELECT amount, script, txo.txid, position - FROM txo JOIN tx ON tx.txid=txo.txid - WHERE {} AND (is_claim OR is_update) AND txoid NOT IN (SELECT txoid FROM txi) - ORDER BY tx.height DESC LIMIT 1; + SELECT amount, script, txo.txid, txo.position, account + FROM txo + JOIN tx ON tx.txid=txo.txid + JOIN pubkey_address ON pubkey_address.address=txo.address + WHERE {} + AND (is_claim OR is_update) + AND txoid NOT IN (SELECT txoid FROM txi) + ORDER BY tx.height DESC, tx.position ASC LIMIT 1; """.format(filter_sql), filter_value ) output_class = account.ledger.transaction_class.output_class + account_id = account.public_key.address return [ output_class( values[0], output_class.script_class(values[1]), TXRefImmutable.from_id(values[2]), - position=values[3] + position=values[3], + is_change=False, + is_my_account=values[4] == account_id ) for values in utxos ] @@ -113,11 +120,15 @@ class WalletDatabase(BaseDatabase): def get_claims(self, account): utxos = yield self.db.runQuery( """ - SELECT amount, script, txo.txid, position - FROM txo JOIN tx ON tx.txid=txo.txid - WHERE (is_claim OR is_update) AND txoid NOT IN (SELECT txoid FROM txi) - ORDER BY tx.height DESC; - """ + SELECT amount, script, txo.txid, txo.position + FROM txo + JOIN tx ON tx.txid=txo.txid + JOIN pubkey_address ON pubkey_address.address=txo.address + WHERE (is_claim OR is_update) + AND txoid NOT IN (SELECT txoid FROM txi) + AND account = :account + ORDER BY tx.height DESC, tx.position ASC; + """, {'account': account.public_key.address} ) output_class = account.ledger.transaction_class.output_class return [ @@ -125,6 +136,8 @@ class WalletDatabase(BaseDatabase): values[0], output_class.script_class(values[1]), TXRefImmutable.from_id(values[2]), - position=values[3] + position=values[3], + is_change=False, + is_my_account=True ) for values in utxos ] diff --git a/lbrynet/wallet/manager.py b/lbrynet/wallet/manager.py index 3c28487b3..5b121979b 100644 --- a/lbrynet/wallet/manager.py +++ b/lbrynet/wallet/manager.py @@ -1,6 +1,9 @@ import os import json import logging +from datetime import datetime +from typing import List + from twisted.internet import defer from torba.basemanager import BaseWalletManager @@ -8,7 +11,7 @@ from torba.basemanager import BaseWalletManager from lbryschema.claim import ClaimDict from .ledger import MainNetLedger -from .account import generate_certificate +from .account import BaseAccount, generate_certificate from .transaction import Transaction from .database import WalletDatabase @@ -227,13 +230,62 @@ class LbryWalletManager(BaseWalletManager): return True return False - def get_transaction(self, txid): + def get_transaction(self, txid: str): return self.default_account.ledger.get_transaction(txid) - def get_history(self, account): - return account.get_transactions() + @staticmethod + @defer.inlineCallbacks + def get_history(account: BaseAccount): + headers = account.ledger.headers + txs: List[Transaction] = (yield account.get_transactions()) + history = [] + for tx in txs: + ts = headers[tx.height]['timestamp'] + history.append({ + 'txid': tx.id, + 'timestamp': ts, + 'value': tx.net_account_balance, + 'fee': tx.fee, + 'date': datetime.fromtimestamp(ts).isoformat(' ')[:-3], + 'confirmations': headers.height - tx.height, + 'claim_info': [{ + 'address': txo.get_address(account.ledger), + 'balance_delta': -txo.amount, + 'amount': txo.amount, + 'claim_id': txo.claim_id, + 'claim_name': txo.claim_name, + 'nout': txo.position + } for txo in tx.my_claim_outputs], + 'update_info': [{ + 'address': txo.get_address(account.ledger), + 'balance_delta': -txo.amount, + 'amount': txo.amount, + 'claim_id': txo.claim_id, + 'claim_name': txo.claim_name, + 'nout': txo.position + } for txo in tx.my_update_outputs], + 'support_info': [{ + 'address': txo.get_address(account.ledger), + 'balance_delta': -txo.amount, + 'amount': txo.amount, + 'claim_id': txo.claim_id, + 'claim_name': txo.claim_name, + 'is_tip': False, # TODO: need to add lookup + 'nout': txo.position + } for txo in tx.my_support_outputs], + 'abandon_info': [{ + 'address': txo.get_address(account.ledger), + 'balance_delta': txo.amount, + 'amount': txo.amount, + 'claim_id': txo.claim_id, + 'claim_name': txo.claim_name, + 'nout': txo.position + } for txo in tx.my_abandon_outputs], + }) + return history - def get_utxos(self, account): + @staticmethod + def get_utxos(account: BaseAccount): return account.get_unspent_outputs() @defer.inlineCallbacks diff --git a/lbrynet/wallet/transaction.py b/lbrynet/wallet/transaction.py index 713b1ae01..5512669db 100644 --- a/lbrynet/wallet/transaction.py +++ b/lbrynet/wallet/transaction.py @@ -123,3 +123,34 @@ class Transaction(BaseTransaction): @classmethod def abandon(cls, claims: Iterable[Output], funding_accounts: Iterable[Account], change_account: Account): return cls.create([Input.spend(txo) for txo in claims], [], funding_accounts, change_account) + + def _filter_my_outputs(self, f): + for txo in self.outputs: + if txo.is_my_account and f(txo.script): + yield txo + + @property + def my_claim_outputs(self): + return self._filter_my_outputs(lambda s: s.is_claim_name) + + @property + def my_update_outputs(self): + return self._filter_my_outputs(lambda s: s.is_update_claim) + + @property + def my_support_outputs(self): + return self._filter_my_outputs(lambda s: s.is_support_claim) + + @property + def my_abandon_outputs(self): + for txi in self.inputs: + abandon = txi.txo_ref.txo + if abandon is not None and abandon.is_my_account and abandon.script.is_claim_involved: + is_update = False + if abandon.script.is_claim_name or abandon.script.is_update_claim: + for update in self.my_update_outputs: + if abandon.claim_id == update.claim_id: + is_update = True + break + if not is_update: + yield abandon