From 59424a3e735d0267d136acb195b67b280e73b70d Mon Sep 17 00:00:00 2001
From: Victor Shyba <victor1984@riseup.net>
Date: Tue, 30 Jul 2019 00:34:12 -0300
Subject: [PATCH] add query to get tips summary for granular balances

---
 lbry/lbry/extras/daemon/Daemon.py             | 25 +++++++++++--------
 lbry/lbry/testcase.py                         |  1 +
 lbry/lbry/wallet/account.py                   |  3 +++
 lbry/lbry/wallet/database.py                  | 11 ++++++++
 .../integration/test_transaction_commands.py  | 24 ++++++++++++++----
 5 files changed, 48 insertions(+), 16 deletions(-)

diff --git a/lbry/lbry/extras/daemon/Daemon.py b/lbry/lbry/extras/daemon/Daemon.py
index e12c9772d..07297f908 100644
--- a/lbry/lbry/extras/daemon/Daemon.py
+++ b/lbry/lbry/extras/daemon/Daemon.py
@@ -1033,6 +1033,7 @@ class Daemon(metaclass=JSONRPCServerType):
 
         Usage:
             account_balance [<account_id>] [<address> | --address=<address>]
+                            [<confirmations> | --confirmations=<confirmations>]
 
         Options:
             --account_id=<account_id>       : (str) If provided only the balance for this
@@ -1044,25 +1045,27 @@ class Daemon(metaclass=JSONRPCServerType):
             (decimal) amount of lbry credits in wallet
         """
         account = self.get_account_or_default(account_id)
-        get_total_balance = partial(account.get_balance, confirmations=True, include_claims=True)
+        get_total_balance = partial(account.get_balance, confirmations=confirmations, include_claims=True)
         total = await get_total_balance()
         claims_balance = await get_total_balance(claim_type__or={'is_claim':True, 'is_update': True})
-        supports_balance = await get_total_balance(is_support=True)
-        tips_received, tips_sent = 0, 0
-        for transaction in await account.get_transactions():
-            for support_output in transaction.my_support_outputs:
-                if all([not txi.is_my_account for txi in transaction.inputs]):
-                    tips_received += support_output.amount
-            for support_output in transaction.other_support_outputs:
-                tips_sent += support_output.amount
-        unavailable = claims_balance + supports_balance
+        tips_received, tips_sent, tips_balance, supports_balance = 0, 0, 0, 0
+        for amount, spent, from_me, to_me, height in await account.get_support_summary():
+            tips_sent += amount if from_me and not to_me else 0
+            tips_received += amount if to_me and not from_me else 0
+            if confirmations > 0 and not 0 < height <= self.ledger.headers.height - (confirmations - 1):
+                continue
+            if not spent and to_me:
+                tips_balance += amount if not from_me else 0
+                supports_balance += amount if from_me else 0
+        unavailable = claims_balance + supports_balance + tips_balance
         return {
             'total': dewies_to_lbc(total),
             'available': dewies_to_lbc(total - unavailable),
             'reserved': {
                 'total': dewies_to_lbc(unavailable),
                 'claims': dewies_to_lbc(claims_balance),
-                'supports': dewies_to_lbc(supports_balance)
+                'supports': dewies_to_lbc(supports_balance),
+                'tips': dewies_to_lbc(tips_balance)
             },
             'tips_received': dewies_to_lbc(tips_received),
             'tips_sent': dewies_to_lbc(tips_sent)
diff --git a/lbry/lbry/testcase.py b/lbry/lbry/testcase.py
index 6179bd681..9bf82cfe7 100644
--- a/lbry/lbry/testcase.py
+++ b/lbry/lbry/testcase.py
@@ -156,6 +156,7 @@ class CommandTestCase(IntegrationTestCase):
         await self.on_transaction_id(txid)
         await self.generate(1)
         await self.on_transaction_id(txid)
+        return txid
 
     async def on_transaction_dict(self, tx):
         await self.ledger.wait(
diff --git a/lbry/lbry/wallet/account.py b/lbry/lbry/wallet/account.py
index f29233ba5..899f19437 100644
--- a/lbry/lbry/wallet/account.py
+++ b/lbry/lbry/wallet/account.py
@@ -134,5 +134,8 @@ class Account(BaseAccount):
     def get_support_count(self, **constraints):
         return self.ledger.db.get_support_count(account=self, **constraints)
 
+    def get_support_summary(self):
+        return self.ledger.db.get_supports_summary(account_id=self.id)
+
     async def release_all_outputs(self):
         await self.ledger.db.release_all_outputs(self)
diff --git a/lbry/lbry/wallet/database.py b/lbry/lbry/wallet/database.py
index 4de87d45b..24253aaa4 100644
--- a/lbry/lbry/wallet/database.py
+++ b/lbry/lbry/wallet/database.py
@@ -138,3 +138,14 @@ class WalletDatabase(BaseDatabase):
             "    SELECT address from pubkey_address WHERE account = ?"
             "  )", [account.public_key.address]
         )
+
+    def get_supports_summary(self, account_id):
+        return self.db.execute_fetchall("""
+            select txo.amount, exists(select * from txi where txi.txoid=txo.txoid) as spent,
+                (txo.txid in
+                (select txi.txid from txi join pubkey_address a on txi.address = a.address
+                    where a.account = ?)) as from_me,
+                (txo.address in (select address from pubkey_address where account=?)) as to_me,
+                tx.height
+            from txo join tx using (txid) where is_support=1
+        """, (account_id, account_id))
diff --git a/lbry/tests/integration/test_transaction_commands.py b/lbry/tests/integration/test_transaction_commands.py
index fafd0c722..f931e8018 100644
--- a/lbry/tests/integration/test_transaction_commands.py
+++ b/lbry/tests/integration/test_transaction_commands.py
@@ -44,7 +44,7 @@ class TransactionCommandsTestCase(CommandTestCase):
             'tips_sent': '0.0',
             'total': '10.0',
             'available': '10.0',
-            'reserved': {'total': '0.0', 'claims': '0.0', 'supports': '0.0'}
+            'reserved': {'total': '0.0', 'claims': '0.0', 'supports': '0.0', 'tips': '0.0'}
         }, initial_balance)
         first_claim_id = self.get_claim_id(await self.stream_create('granularity', bid='3.0'))
         await self.stream_update(first_claim_id, data=b'news', bid='1.0')
@@ -52,16 +52,30 @@ class TransactionCommandsTestCase(CommandTestCase):
         second_account_id = (await self.out(self.daemon.jsonrpc_account_create("Tip-er")))['id']
         second_accound_address = await self.daemon.jsonrpc_address_unused(second_account_id)
         await self.confirm_tx((await self.daemon.jsonrpc_account_send('1.0', second_accound_address)).id)
+        self.assertEqual({
+            'tips_received': '0.0',
+            'tips_sent': '0.0',
+            'total': '8.97741',
+            'available': '5.97741',
+            'reserved': {'claims': '1.0', 'supports': '2.0', 'tips': '0.0', 'total': '3.0'}
+        }, await self.daemon.jsonrpc_account_balance())
         second_claim_id = self.get_claim_id(await self.stream_create(
             name='granularity-is-cool', account_id=second_account_id, bid='0.1'))
         await self.daemon.jsonrpc_support_create(second_claim_id, '0.5', tip=True)
-        await self.confirm_tx((await self.daemon.jsonrpc_support_create(
+        first_account_tip_txid = await self.confirm_tx((await self.daemon.jsonrpc_support_create(
             first_claim_id, '0.3', tip=True, account_id=second_account_id)).id)
-        final_balance = await self.daemon.jsonrpc_account_balance()
         self.assertEqual({
             'tips_received': '0.3',
             'tips_sent': '0.5',
             'total': '8.777264',
             'available': '5.477264',
-            'reserved': {'claims': '1.0', 'supports': '2.3', 'total': '3.3'}
-        }, final_balance)
+            'reserved': {'claims': '1.0', 'supports': '2.0', 'tips': '0.3', 'total': '3.3'}
+        }, await self.daemon.jsonrpc_account_balance())
+        await self.confirm_tx((await self.daemon.jsonrpc_support_abandon(txid=first_account_tip_txid, nout=0)).id)
+        self.assertEqual({
+            'tips_received': '0.3',
+            'tips_sent': '0.5',
+            'total': '8.777157',
+            'available': '5.777157',
+            'reserved': {'claims': '1.0', 'supports': '2.0', 'tips': '0.0', 'total': '3.0'}
+        }, await self.daemon.jsonrpc_account_balance())