add sum_supports api

This commit is contained in:
Alex Grintsvayg 2020-10-13 14:34:19 -04:00 committed by Lex Berezhny
parent 0215ce6b79
commit 4bb0344e05
8 changed files with 99 additions and 8 deletions

View file

@ -36,12 +36,12 @@ def hash160(x):
return ripemd160(sha256(x))
def hash_to_hex_str(x):
def hash_to_hex_str(x: bytes) -> str:
""" Convert a big-endian binary hash to displayed hex string.
Display form of a binary hash is reversed and converted to hex. """
return hexlify(reversed(x))
return hexlify(x[::-1])
def hex_str_to_hash(x):
def hex_str_to_hash(x: str) -> bytes:
""" Convert a displayed hex string to a binary hash. """
return reversed(unhexlify(x))
return unhexlify(x)[::-1]

View file

@ -288,6 +288,9 @@ class Database:
async def search_supports(self, **constraints) -> Result[Output]:
return await self.fetch_result(q.search_supports, **constraints)
async def sum_supports(self, claim_hash, include_channel_content=False) -> List[Dict]:
return await self.run(q.sum_supports, claim_hash, include_channel_content)
async def resolve(self, urls, **kwargs) -> Dict[str, Output]:
return await self.run(q.resolve, urls, **kwargs)

View file

@ -4,7 +4,7 @@ from decimal import Decimal
from binascii import unhexlify
from typing import Tuple, List, Optional
from sqlalchemy import func, case
from sqlalchemy import func, case, text
from sqlalchemy.future import select, Select
from lbry.schema.tags import clean_tags
@ -62,6 +62,32 @@ def search_supports(**constraints) -> Tuple[List[Output], Optional[int]]:
return txos, total
def sum_supports(claim_hash, include_channel_content = False) -> Tuple[List[Output], Optional[int]]:
supporter = Claim.alias("supporter")
content = Claim.alias("content")
where_condition = (content.c.claim_hash == claim_hash)
if include_channel_content:
where_condition |= (content.c.channel_hash == claim_hash)
q = select(
supporter.c.claim_name.label("supporter"),
func.sum(TXO.c.amount).label("staked"),
).select_from(
TXO
.join(content, TXO.c.claim_hash == content.c.claim_hash)
.join(supporter, TXO.c.channel_hash == supporter.c.claim_hash)
).where(
where_condition &
(TXO.c.txo_type == TXO_TYPES["support"]) &
((TXO.c.address == content.c.address) | ((TXO.c.address != content.c.address) & (TXO.c.spent_height == 0)))
).group_by(
supporter.c.claim_name
).order_by(
text("staked DESC")
)
return context().fetchall(q)
def search_support_count(**constraints) -> int:
constraints.pop('offset', None)
constraints.pop('limit', None)

View file

@ -18,6 +18,7 @@ from lbry.wallet import Wallet, Account, SingleKey, HierarchicalDeterministic
from lbry.blockchain import Transaction, Output, dewies_to_lbc, dict_values_to_lbc
from lbry.stream.managed_stream import ManagedStream
from lbry.event import EventController, EventStream
from lbry.crypto.hash import hex_str_to_hash
from .base import Service
from .json_encoder import Paginated
@ -2573,6 +2574,26 @@ class API:
d['total_items'] = result.total
return d
async def support_sum(
self,
claim_id: str, # id of claim to calculate support stats for
include_channel_content: bool = False, # if claim_id is for a channel, include supports for
# claims in that channel
**pagination_kwargs
) -> Paginated[Dict]: # supports grouped by channel
# TODO: add unsigned supports to the output so the numbers add up. just a left join on identity
"""
List total staked supports for a claim, grouped by the channel that signed the support.
If claim_id is a channel claim, you can use --include_channel_content to also include supports for
content claims in the channel.
Usage:
support sum <claim_id> [--inculde_channel_content]
{kwargs}
"""
return await self.service.sum_supports(hex_str_to_hash(claim_id), include_channel_content)
async def support_abandon(
self,
keep: str = None, # amount of lbc to keep as support

View file

@ -1,7 +1,7 @@
import os
import asyncio
import logging
from typing import List, Optional, Tuple, NamedTuple
from typing import List, Optional, Tuple, NamedTuple, Dict
from lbry.db import Database, Result
from lbry.db.constants import TXO_TYPES
@ -152,6 +152,9 @@ class Service:
async def search_transactions(self, txids):
raise NotImplementedError
async def sum_supports(self, claim_hash: bytes, include_channel_content=False) -> List[Dict]:
raise NotImplementedError
async def announce_addresses(self, address_manager, addresses: List[str]):
await self.ledger.announce_addresses(address_manager, addresses)

View file

@ -1,5 +1,6 @@
import logging
from binascii import hexlify, unhexlify
from typing import List, Dict
from lbry.blockchain.lbrycrd import Lbrycrd
from lbry.blockchain.sync import BlockchainSync
@ -67,3 +68,6 @@ class FullNode(Service):
async def protobuf_resolve(self, urls, **kwargs):
return await self.db.protobuf_resolve(urls, **kwargs)
async def sum_supports(self, claim_hash: bytes, include_channel_content=False) -> List[Dict]:
return await self.db.sum_supports(claim_hash, include_channel_content)

View file

@ -1,4 +1,5 @@
import logging
from typing import List, Dict
from lbry.conf import Config
from lbry.blockchain import Ledger, Transaction
@ -44,3 +45,6 @@ class LightClient(Service):
async def search_supports(self, accounts, **kwargs):
pass
async def sum_supports(self, claim_hash: bytes, include_channel_content=False) -> List[Dict]:
return await self.client.sum_supports(claim_hash, include_channel_content)

View file

@ -191,7 +191,7 @@ class SyncingBlockchainTestCase(BasicBlockchainTestCase):
async def abandon_claim(self, txid: str) -> str:
return await self.chain.abandon_claim(txid, self.address)
async def support_claim(self, txo: Output, amount='0.01', sign=None) -> str:
async def support_claim(self, txo: Output, amount='0.01', sign=None, address=None) -> str:
if not sign:
response = await self.chain.support_claim(
txo.claim_name, txo.claim_id, amount
@ -202,7 +202,7 @@ class SyncingBlockchainTestCase(BasicBlockchainTestCase):
.add_outputs([
Output.pay_support_data_pubkey_hash(
lbc_to_dewies(amount), txo.claim_name, txo.claim_id, Support(),
self.chain.ledger.address_to_hash160(self.address)
self.chain.ledger.address_to_hash160(address if address else self.address)
)
])
)
@ -944,6 +944,36 @@ class TestGeneralBlockchainSync(SyncingBlockchainTestCase):
results = await self.db.search_claims(effective_amount=42000000, amount_order=1, order_by=["effective_amount"])
self.assertEqual(claim.claim_id, results[0].claim_id)
async def test_claim_search_sum(self):
await self.generate(100)
channel_a = await self.get_claim(await self.create_claim(name="@A", is_channel=True))
channel_b = await self.get_claim(await self.create_claim(name="@B", is_channel=True))
channel_c = await self.get_claim(await self.create_claim(name="@C", is_channel=True))
await self.support_claim(channel_a, '10.0', sign=channel_b)
await self.support_claim(channel_a, '4.0', sign=channel_c)
await self.support_claim(channel_a, '2.0', sign=channel_c)
await self.generate(1)
results = await self.db.sum_supports(channel_a.claim_hash)
self.assertEqual(results, [{'supporter': '@B', 'staked': 1000000000}, {'supporter': '@C', 'staked': 600000000}])
claim_a = await self.get_claim(await self.create_claim(name="bob", amount='2.0', sign=channel_a))
await self.support_claim(claim_a, '1.0', sign=channel_b)
await self.generate(1)
results = await self.db.sum_supports(channel_a.claim_hash)
self.assertEqual(results, [{'supporter': '@B', 'staked': 1000000000}, {'supporter': '@C', 'staked': 600000000}])
results = await self.db.sum_supports(channel_a.claim_hash, True)
self.assertEqual(results, [{'supporter': '@B', 'staked': 1100000000}, {'supporter': '@C', 'staked': 600000000}])
results = await self.db.sum_supports(claim_a.claim_hash, False)
self.assertEqual(results, [{'supporter': '@B', 'staked': 100000000}])
results = await self.db.sum_supports(claim_a.claim_hash, True)
self.assertEqual(results, [{'supporter': '@B', 'staked': 100000000}])
async def test_meta_fields_are_translated_to_protobuf(self):
chan_ab = await self.get_claim(
await self.create_claim(claim_id_startswith='ab', is_channel=True))