forked from LBRYCommunity/lbry-sdk
add sum_supports api
This commit is contained in:
parent
0215ce6b79
commit
4bb0344e05
8 changed files with 99 additions and 8 deletions
|
@ -36,12 +36,12 @@ def hash160(x):
|
||||||
return ripemd160(sha256(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.
|
""" Convert a big-endian binary hash to displayed hex string.
|
||||||
Display form of a binary hash is reversed and converted to hex. """
|
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. """
|
""" Convert a displayed hex string to a binary hash. """
|
||||||
return reversed(unhexlify(x))
|
return unhexlify(x)[::-1]
|
||||||
|
|
|
@ -288,6 +288,9 @@ class Database:
|
||||||
async def search_supports(self, **constraints) -> Result[Output]:
|
async def search_supports(self, **constraints) -> Result[Output]:
|
||||||
return await self.fetch_result(q.search_supports, **constraints)
|
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]:
|
async def resolve(self, urls, **kwargs) -> Dict[str, Output]:
|
||||||
return await self.run(q.resolve, urls, **kwargs)
|
return await self.run(q.resolve, urls, **kwargs)
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ from decimal import Decimal
|
||||||
from binascii import unhexlify
|
from binascii import unhexlify
|
||||||
from typing import Tuple, List, Optional
|
from typing import Tuple, List, Optional
|
||||||
|
|
||||||
from sqlalchemy import func, case
|
from sqlalchemy import func, case, text
|
||||||
from sqlalchemy.future import select, Select
|
from sqlalchemy.future import select, Select
|
||||||
|
|
||||||
from lbry.schema.tags import clean_tags
|
from lbry.schema.tags import clean_tags
|
||||||
|
@ -62,6 +62,32 @@ def search_supports(**constraints) -> Tuple[List[Output], Optional[int]]:
|
||||||
return txos, total
|
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:
|
def search_support_count(**constraints) -> int:
|
||||||
constraints.pop('offset', None)
|
constraints.pop('offset', None)
|
||||||
constraints.pop('limit', None)
|
constraints.pop('limit', None)
|
||||||
|
|
|
@ -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.blockchain import Transaction, Output, dewies_to_lbc, dict_values_to_lbc
|
||||||
from lbry.stream.managed_stream import ManagedStream
|
from lbry.stream.managed_stream import ManagedStream
|
||||||
from lbry.event import EventController, EventStream
|
from lbry.event import EventController, EventStream
|
||||||
|
from lbry.crypto.hash import hex_str_to_hash
|
||||||
|
|
||||||
from .base import Service
|
from .base import Service
|
||||||
from .json_encoder import Paginated
|
from .json_encoder import Paginated
|
||||||
|
@ -2573,6 +2574,26 @@ class API:
|
||||||
d['total_items'] = result.total
|
d['total_items'] = result.total
|
||||||
return d
|
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(
|
async def support_abandon(
|
||||||
self,
|
self,
|
||||||
keep: str = None, # amount of lbc to keep as support
|
keep: str = None, # amount of lbc to keep as support
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import os
|
import os
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
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 import Database, Result
|
||||||
from lbry.db.constants import TXO_TYPES
|
from lbry.db.constants import TXO_TYPES
|
||||||
|
@ -152,6 +152,9 @@ class Service:
|
||||||
async def search_transactions(self, txids):
|
async def search_transactions(self, txids):
|
||||||
raise NotImplementedError
|
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]):
|
async def announce_addresses(self, address_manager, addresses: List[str]):
|
||||||
await self.ledger.announce_addresses(address_manager, addresses)
|
await self.ledger.announce_addresses(address_manager, addresses)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import logging
|
import logging
|
||||||
from binascii import hexlify, unhexlify
|
from binascii import hexlify, unhexlify
|
||||||
|
from typing import List, Dict
|
||||||
|
|
||||||
from lbry.blockchain.lbrycrd import Lbrycrd
|
from lbry.blockchain.lbrycrd import Lbrycrd
|
||||||
from lbry.blockchain.sync import BlockchainSync
|
from lbry.blockchain.sync import BlockchainSync
|
||||||
|
@ -67,3 +68,6 @@ class FullNode(Service):
|
||||||
|
|
||||||
async def protobuf_resolve(self, urls, **kwargs):
|
async def protobuf_resolve(self, urls, **kwargs):
|
||||||
return await self.db.protobuf_resolve(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)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
|
from typing import List, Dict
|
||||||
|
|
||||||
from lbry.conf import Config
|
from lbry.conf import Config
|
||||||
from lbry.blockchain import Ledger, Transaction
|
from lbry.blockchain import Ledger, Transaction
|
||||||
|
@ -44,3 +45,6 @@ class LightClient(Service):
|
||||||
|
|
||||||
async def search_supports(self, accounts, **kwargs):
|
async def search_supports(self, accounts, **kwargs):
|
||||||
pass
|
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)
|
||||||
|
|
|
@ -191,7 +191,7 @@ class SyncingBlockchainTestCase(BasicBlockchainTestCase):
|
||||||
async def abandon_claim(self, txid: str) -> str:
|
async def abandon_claim(self, txid: str) -> str:
|
||||||
return await self.chain.abandon_claim(txid, self.address)
|
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:
|
if not sign:
|
||||||
response = await self.chain.support_claim(
|
response = await self.chain.support_claim(
|
||||||
txo.claim_name, txo.claim_id, amount
|
txo.claim_name, txo.claim_id, amount
|
||||||
|
@ -202,7 +202,7 @@ class SyncingBlockchainTestCase(BasicBlockchainTestCase):
|
||||||
.add_outputs([
|
.add_outputs([
|
||||||
Output.pay_support_data_pubkey_hash(
|
Output.pay_support_data_pubkey_hash(
|
||||||
lbc_to_dewies(amount), txo.claim_name, txo.claim_id, Support(),
|
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"])
|
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)
|
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):
|
async def test_meta_fields_are_translated_to_protobuf(self):
|
||||||
chan_ab = await self.get_claim(
|
chan_ab = await self.get_claim(
|
||||||
await self.create_claim(claim_id_startswith='ab', is_channel=True))
|
await self.create_claim(claim_id_startswith='ab', is_channel=True))
|
||||||
|
|
Loading…
Reference in a new issue