bug fixes
This commit is contained in:
parent
1848462ad9
commit
9004f240e6
7 changed files with 185 additions and 137 deletions
|
@ -114,6 +114,7 @@ def encode_pagination_doc(items):
|
|||
"page": "Page number of the current items.",
|
||||
"page_size": "Number of items to show on a page.",
|
||||
"total_pages": "Total number of pages.",
|
||||
"total_items": "Total number of items.",
|
||||
"items": [items],
|
||||
}
|
||||
|
||||
|
@ -125,9 +126,11 @@ async def maybe_paginate(get_records: Callable, get_record_count: Callable,
|
|||
"offset": page_size * (page-1),
|
||||
"limit": page_size
|
||||
})
|
||||
total_items = await get_record_count(**constraints)
|
||||
return {
|
||||
"items": await get_records(**constraints),
|
||||
"total_pages": int(((await get_record_count(**constraints)) + (page_size-1)) / page_size),
|
||||
"total_pages": int((total_items + (page_size-1)) / page_size),
|
||||
"total_items": total_items,
|
||||
"page": page, "page_size": page_size
|
||||
}
|
||||
return await get_records(**constraints)
|
||||
|
@ -1684,47 +1687,53 @@ class Daemon(metaclass=JSONRPCServerType):
|
|||
"""
|
||||
Search for stream and channel claims on the blockchain.
|
||||
|
||||
Use --channel_id=<channel_id> to list all stream claims in a channel.
|
||||
|
||||
Arguments marked with "supports equality constraints" allow prepending the
|
||||
value with an equality constraint such as '>', '>=', '<' and '<='
|
||||
eg. --height=">400000" would limit results to only claims above 400k block height.
|
||||
|
||||
Usage:
|
||||
claim_search [<name> | --name=<name>] [--claim_id=<claim_id>] [--txid=<txid> --nout=<nout>]
|
||||
[--channel_id=<channel_id>] [--channel_name=<channel_name>] [--is_controlling]
|
||||
[--order_by=<order_by>...]
|
||||
[--height=<height>] [--publish_time=<publish_time>] [--release_time=<release_time>]
|
||||
claim_search [<name> | --name=<name>] [--claim_id=<claim_id>] [--txid=<txid>] [--nout=<nout>]
|
||||
[--channel=<channel> | --channel_ids=<channel_ids>...] [--is_channel_signature_valid]
|
||||
[--is_controlling] [--release_time=<release_time>]
|
||||
[--timestamp=<timestamp>] [--creation_timestamp=<creation_timestamp>]
|
||||
[--height=<height>] [--creation_height=<creation_height>]
|
||||
[--activation_height=<activation_height>] [--expiration_height=<expiration_height>]
|
||||
[--amount=<amount>] [--effective_amount=<effective_amount>]
|
||||
[--support_amount=<support_amount>] [--trending_group=<trending_group>]
|
||||
[--trending_mixed=<trending_mixed>] [--trending_local=<trending_local>]
|
||||
[--trending_global=<trending_global] [--activation_height=<activation_height>]
|
||||
[--trending_global=<trending_global]
|
||||
[--any_tags=<any_tags>...] [--all_tags=<all_tags>...] [--not_tags=<not_tags>...]
|
||||
[--any_languages=<any_languages>...] [--all_languages=<all_languages>...]
|
||||
[--not_languages=<not_languages>...]
|
||||
[--any_locations=<any_locations>...] [--all_locations=<all_locations>...]
|
||||
[--not_locations=<not_locations>...]
|
||||
[--page=<page>] [--page_size=<page_size>]
|
||||
[--order_by=<order_by>...] [--page=<page>] [--page_size=<page_size>]
|
||||
|
||||
Options:
|
||||
--name=<name> : (str) find claims with this name
|
||||
--claim_id=<claim_id> : (str) find a claim with this claim_id
|
||||
--txid=<txid> : (str) find a claim with this txid:nout
|
||||
--nout=<nout> : (str) find a claim with this txid:nout
|
||||
--channel_id=<channel_id> : (str) limit search to specific channel claim id (returns stream claims)
|
||||
--channel_name=<channel_name> : (str) limit search to specific channel name (returns stream claims)
|
||||
--is_controlling : (bool) limit to controlling claims for their respective name
|
||||
--order_by=<order_by> : (str) field to order by, default is descending order, to do an
|
||||
ascending order prepend ^ to the field name, eg. '^amount'
|
||||
available fields: 'name', 'height', 'release_time',
|
||||
'publish_time', 'amount', 'effective_amount',
|
||||
'support_amount', 'trending_group', 'trending_mixed',
|
||||
'trending_local', 'trending_global', 'activation_height'
|
||||
--height=<height> : (int) limit by block height (supports equality constraints)
|
||||
--activation_height=<activation_height>: (int) height at which claim starts competing for name
|
||||
(supports equality constraints)
|
||||
--publish_time=<publish_time> : (int) limit by UTC timestamp of containing block (supports
|
||||
equality constraints)
|
||||
--name=<name> : (str) claim name (normalized)
|
||||
--claim_id=<claim_id> : (str) full or partial claim id
|
||||
--txid=<txid> : (str) transaction id
|
||||
--nout=<nout> : (str) position in the transaction
|
||||
--channel=<channel> : (str) claims signed by this channel (argument is
|
||||
a URL which automatically gets resolved),
|
||||
see --channel_ids if you need to filter by
|
||||
multiple channels at the same time,
|
||||
includes claims with invalid signatures,
|
||||
use in conjunction with --is_channel_signature_valid
|
||||
--channel_ids=<channel_ids> : (str) claims signed by any of these channels
|
||||
(arguments must be claim ids of the channels),
|
||||
includes claims with invalid signatures,
|
||||
use in conjunction with --is_channel_signature_valid
|
||||
--is_channel_signature_valid : (bool) only return claims with valid channel signatures
|
||||
--is_controlling : (bool) only return winning claims of their respective name
|
||||
--height=<height> : (int) last updated block height (supports equality constraints)
|
||||
--timestamp=<timestamp> : (int) last updated timestamp (supports equality constraints)
|
||||
--creation_height=<creation_height> : (int) created at block height (supports equality constraints)
|
||||
--creation_timestamp=<creation_timestamp>: (int) created at timestamp (supports equality constraints)
|
||||
--activation_height=<activation_height> : (int) height at which claim starts competing for name
|
||||
(supports equality constraints)
|
||||
--expiration_height=<expiration_height> : (int) height at which claim will expire
|
||||
(supports equality constraints)
|
||||
--release_time=<release_time> : (int) limit to claims self-described as having been
|
||||
released to the public on or after this UTC
|
||||
timestamp, when claim does not provide
|
||||
|
@ -1767,6 +1776,12 @@ class Daemon(metaclass=JSONRPCServerType):
|
|||
--not_locations=<not_locations> : (list) find claims not containing any of these locations
|
||||
--page=<page> : (int) page to return during paginating
|
||||
--page_size=<page_size> : (int) number of items on page during pagination
|
||||
--order_by=<order_by> : (str) field to order by, default is descending order, to do an
|
||||
ascending order prepend ^ to the field name, eg. '^amount'
|
||||
available fields: 'name', 'height', 'release_time',
|
||||
'publish_time', 'amount', 'effective_amount',
|
||||
'support_amount', 'trending_group', 'trending_mixed',
|
||||
'trending_local', 'trending_global', 'activation_height'
|
||||
|
||||
Returns: {Paginated[Output]}
|
||||
"""
|
||||
|
@ -1775,7 +1790,8 @@ class Daemon(metaclass=JSONRPCServerType):
|
|||
txos, offset, total = await self.ledger.claim_search(**kwargs)
|
||||
return {
|
||||
"items": txos, "page": page_num, "page_size": page_size,
|
||||
"total_pages": int((total + (page_size-1)) / page_size)
|
||||
"total_pages": int((total + (page_size-1)) / page_size),
|
||||
"total_items": total
|
||||
}
|
||||
|
||||
CHANNEL_DOC = """
|
||||
|
|
|
@ -169,7 +169,7 @@ class JSONResponseEncoder(JSONEncoder):
|
|||
if txo.script.is_claim_involved:
|
||||
output.update({
|
||||
'name': txo.claim_name,
|
||||
'normalized': txo.normalized_name,
|
||||
'normalized_name': txo.normalized_name,
|
||||
'claim_id': txo.claim_id,
|
||||
'permanent_url': txo.permanent_url,
|
||||
'meta': self.encode_claim_meta(txo.meta)
|
||||
|
@ -199,6 +199,8 @@ class JSONResponseEncoder(JSONEncoder):
|
|||
if key.endswith('_amount'):
|
||||
if isinstance(value, int):
|
||||
meta[key] = dewies_to_lbc(value)
|
||||
if meta.get('creation_height', 0) > 0:
|
||||
meta['creation_timestamp'] = self.ledger.headers[meta['creation_height']]['timestamp']
|
||||
return meta
|
||||
|
||||
def encode_input(self, txi):
|
||||
|
|
|
@ -4,8 +4,6 @@ from typing import List
|
|||
from binascii import hexlify
|
||||
from itertools import chain
|
||||
|
||||
from google.protobuf.message import DecodeError
|
||||
|
||||
from lbrynet.schema.types.v2.result_pb2 import Outputs as OutputsMessage
|
||||
|
||||
|
||||
|
@ -36,6 +34,7 @@ class Outputs:
|
|||
'short_url': f'lbry://{claim.short_url}',
|
||||
'canonical_url': f'lbry://{claim.canonical_url or claim.short_url}',
|
||||
'is_controlling': claim.is_controlling,
|
||||
'creation_height': claim.creation_height,
|
||||
'activation_height': claim.activation_height,
|
||||
'expiration_height': claim.expiration_height,
|
||||
'effective_amount': claim.effective_amount,
|
||||
|
@ -47,8 +46,11 @@ class Outputs:
|
|||
}
|
||||
if claim.HasField('channel'):
|
||||
txo.channel = tx_map[claim.channel.tx_hash].outputs[claim.channel.nout]
|
||||
if claim.claims_in_channel is not None:
|
||||
txo.meta['claims_in_channel'] = claim.claims_in_channel
|
||||
try:
|
||||
if txo.claim.is_channel:
|
||||
txo.meta['claims_in_channel'] = claim.claims_in_channel
|
||||
except:
|
||||
pass
|
||||
return txo
|
||||
|
||||
@classmethod
|
||||
|
@ -97,6 +99,7 @@ class Outputs:
|
|||
if txo['canonical_url'] is not None:
|
||||
txo_message.claim.canonical_url = txo['canonical_url']
|
||||
txo_message.claim.is_controlling = bool(txo['is_controlling'])
|
||||
txo_message.claim.creation_height = txo['creation_height']
|
||||
txo_message.claim.activation_height = txo['activation_height']
|
||||
txo_message.claim.expiration_height = txo['expiration_height']
|
||||
if txo['claims_in_channel'] is not None:
|
||||
|
|
|
@ -19,7 +19,7 @@ DESCRIPTOR = _descriptor.FileDescriptor(
|
|||
package='pb',
|
||||
syntax='proto3',
|
||||
serialized_options=None,
|
||||
serialized_pb=_b('\n\x0cresult.proto\x12\x02pb\"b\n\x07Outputs\x12\x18\n\x04txos\x18\x01 \x03(\x0b\x32\n.pb.Output\x12\x1e\n\nextra_txos\x18\x02 \x03(\x0b\x32\n.pb.Output\x12\r\n\x05total\x18\x03 \x01(\r\x12\x0e\n\x06offset\x18\x04 \x01(\r\"{\n\x06Output\x12\x0f\n\x07tx_hash\x18\x01 \x01(\x0c\x12\x0c\n\x04nout\x18\x02 \x01(\r\x12\x0e\n\x06height\x18\x03 \x01(\r\x12\x1e\n\x05\x63laim\x18\x07 \x01(\x0b\x32\r.pb.ClaimMetaH\x00\x12\x1a\n\x05\x65rror\x18\x0f \x01(\x0b\x32\t.pb.ErrorH\x00\x42\x06\n\x04meta\"\xce\x02\n\tClaimMeta\x12\x1b\n\x07\x63hannel\x18\x01 \x01(\x0b\x32\n.pb.Output\x12\x11\n\tshort_url\x18\x02 \x01(\t\x12\x15\n\rcanonical_url\x18\x03 \x01(\t\x12\x16\n\x0eis_controlling\x18\x04 \x01(\x08\x12\x19\n\x11\x61\x63tivation_height\x18\x05 \x01(\r\x12\x19\n\x11\x65xpiration_height\x18\x06 \x01(\r\x12\x19\n\x11\x63laims_in_channel\x18\x07 \x01(\r\x12\x18\n\x10\x65\x66\x66\x65\x63tive_amount\x18\n \x01(\x04\x12\x16\n\x0esupport_amount\x18\x0b \x01(\x04\x12\x16\n\x0etrending_group\x18\x0c \x01(\r\x12\x16\n\x0etrending_mixed\x18\r \x01(\x02\x12\x16\n\x0etrending_local\x18\x0e \x01(\x02\x12\x17\n\x0ftrending_global\x18\x0f \x01(\x02\"i\n\x05\x45rror\x12\x1c\n\x04\x63ode\x18\x01 \x01(\x0e\x32\x0e.pb.Error.Code\x12\x0c\n\x04text\x18\x02 \x01(\t\"4\n\x04\x43ode\x12\x10\n\x0cUNKNOWN_CODE\x10\x00\x12\r\n\tNOT_FOUND\x10\x01\x12\x0b\n\x07INVALID\x10\x02\x62\x06proto3')
|
||||
serialized_pb=_b('\n\x0cresult.proto\x12\x02pb\"b\n\x07Outputs\x12\x18\n\x04txos\x18\x01 \x03(\x0b\x32\n.pb.Output\x12\x1e\n\nextra_txos\x18\x02 \x03(\x0b\x32\n.pb.Output\x12\r\n\x05total\x18\x03 \x01(\r\x12\x0e\n\x06offset\x18\x04 \x01(\r\"{\n\x06Output\x12\x0f\n\x07tx_hash\x18\x01 \x01(\x0c\x12\x0c\n\x04nout\x18\x02 \x01(\r\x12\x0e\n\x06height\x18\x03 \x01(\r\x12\x1e\n\x05\x63laim\x18\x07 \x01(\x0b\x32\r.pb.ClaimMetaH\x00\x12\x1a\n\x05\x65rror\x18\x0f \x01(\x0b\x32\t.pb.ErrorH\x00\x42\x06\n\x04meta\"\xe7\x02\n\tClaimMeta\x12\x1b\n\x07\x63hannel\x18\x01 \x01(\x0b\x32\n.pb.Output\x12\x11\n\tshort_url\x18\x02 \x01(\t\x12\x15\n\rcanonical_url\x18\x03 \x01(\t\x12\x16\n\x0eis_controlling\x18\x04 \x01(\x08\x12\x17\n\x0f\x63reation_height\x18\x05 \x01(\r\x12\x19\n\x11\x61\x63tivation_height\x18\x06 \x01(\r\x12\x19\n\x11\x65xpiration_height\x18\x07 \x01(\r\x12\x19\n\x11\x63laims_in_channel\x18\x08 \x01(\r\x12\x18\n\x10\x65\x66\x66\x65\x63tive_amount\x18\n \x01(\x04\x12\x16\n\x0esupport_amount\x18\x0b \x01(\x04\x12\x16\n\x0etrending_group\x18\x0c \x01(\r\x12\x16\n\x0etrending_mixed\x18\r \x01(\x02\x12\x16\n\x0etrending_local\x18\x0e \x01(\x02\x12\x17\n\x0ftrending_global\x18\x0f \x01(\x02\"i\n\x05\x45rror\x12\x1c\n\x04\x63ode\x18\x01 \x01(\x0e\x32\x0e.pb.Error.Code\x12\x0c\n\x04text\x18\x02 \x01(\t\"4\n\x04\x43ode\x12\x10\n\x0cUNKNOWN_CODE\x10\x00\x12\r\n\tNOT_FOUND\x10\x01\x12\x0b\n\x07INVALID\x10\x02\x62\x06proto3')
|
||||
)
|
||||
|
||||
|
||||
|
@ -45,8 +45,8 @@ _ERROR_CODE = _descriptor.EnumDescriptor(
|
|||
],
|
||||
containing_type=None,
|
||||
serialized_options=None,
|
||||
serialized_start=635,
|
||||
serialized_end=687,
|
||||
serialized_start=660,
|
||||
serialized_end=712,
|
||||
)
|
||||
_sym_db.RegisterEnumDescriptor(_ERROR_CODE)
|
||||
|
||||
|
@ -201,63 +201,70 @@ _CLAIMMETA = _descriptor.Descriptor(
|
|||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='activation_height', full_name='pb.ClaimMeta.activation_height', index=4,
|
||||
name='creation_height', full_name='pb.ClaimMeta.creation_height', index=4,
|
||||
number=5, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='expiration_height', full_name='pb.ClaimMeta.expiration_height', index=5,
|
||||
name='activation_height', full_name='pb.ClaimMeta.activation_height', index=5,
|
||||
number=6, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='claims_in_channel', full_name='pb.ClaimMeta.claims_in_channel', index=6,
|
||||
name='expiration_height', full_name='pb.ClaimMeta.expiration_height', index=6,
|
||||
number=7, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='effective_amount', full_name='pb.ClaimMeta.effective_amount', index=7,
|
||||
name='claims_in_channel', full_name='pb.ClaimMeta.claims_in_channel', index=7,
|
||||
number=8, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='effective_amount', full_name='pb.ClaimMeta.effective_amount', index=8,
|
||||
number=10, type=4, cpp_type=4, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='support_amount', full_name='pb.ClaimMeta.support_amount', index=8,
|
||||
name='support_amount', full_name='pb.ClaimMeta.support_amount', index=9,
|
||||
number=11, type=4, cpp_type=4, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='trending_group', full_name='pb.ClaimMeta.trending_group', index=9,
|
||||
name='trending_group', full_name='pb.ClaimMeta.trending_group', index=10,
|
||||
number=12, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='trending_mixed', full_name='pb.ClaimMeta.trending_mixed', index=10,
|
||||
name='trending_mixed', full_name='pb.ClaimMeta.trending_mixed', index=11,
|
||||
number=13, type=2, cpp_type=6, label=1,
|
||||
has_default_value=False, default_value=float(0),
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='trending_local', full_name='pb.ClaimMeta.trending_local', index=11,
|
||||
name='trending_local', full_name='pb.ClaimMeta.trending_local', index=12,
|
||||
number=14, type=2, cpp_type=6, label=1,
|
||||
has_default_value=False, default_value=float(0),
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='trending_global', full_name='pb.ClaimMeta.trending_global', index=12,
|
||||
name='trending_global', full_name='pb.ClaimMeta.trending_global', index=13,
|
||||
number=15, type=2, cpp_type=6, label=1,
|
||||
has_default_value=False, default_value=float(0),
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
|
@ -276,7 +283,7 @@ _CLAIMMETA = _descriptor.Descriptor(
|
|||
oneofs=[
|
||||
],
|
||||
serialized_start=246,
|
||||
serialized_end=580,
|
||||
serialized_end=605,
|
||||
)
|
||||
|
||||
|
||||
|
@ -314,8 +321,8 @@ _ERROR = _descriptor.Descriptor(
|
|||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=582,
|
||||
serialized_end=687,
|
||||
serialized_start=607,
|
||||
serialized_end=712,
|
||||
)
|
||||
|
||||
_OUTPUTS.fields_by_name['txos'].message_type = _OUTPUT
|
||||
|
|
|
@ -109,7 +109,7 @@ class SQLDB:
|
|||
|
||||
create index if not exists claim_id_idx on claim (claim_id);
|
||||
create index if not exists claim_normalized_idx on claim (normalized);
|
||||
create index if not exists claim_search_idx on claim (normalized, claim_id);
|
||||
create index if not exists claim_resolve_idx on claim (normalized, claim_id);
|
||||
create index if not exists claim_txo_hash_idx on claim (txo_hash);
|
||||
create index if not exists claim_channel_hash_idx on claim (channel_hash);
|
||||
create index if not exists claim_release_time_idx on claim (release_time);
|
||||
|
@ -352,8 +352,8 @@ class SQLDB:
|
|||
'support', {'txo_hash__in': [sqlite3.Binary(txo_hash) for txo_hash in txo_hashes]}
|
||||
))
|
||||
|
||||
def validate_channel_signatures(self, height, new_claims, updated_claims):
|
||||
if not new_claims and not updated_claims:
|
||||
def validate_channel_signatures(self, height, new_claims, updated_claims, spent_claims):
|
||||
if not new_claims and not updated_claims and not spent_claims:
|
||||
return
|
||||
|
||||
channels, new_channel_keys, signables = {}, {}, {}
|
||||
|
@ -456,6 +456,14 @@ class SQLDB:
|
|||
WHERE claim_hash=:claim_hash;
|
||||
""", claim_updates)
|
||||
|
||||
if spent_claims:
|
||||
self.execute(
|
||||
f"""
|
||||
UPDATE claim SET is_channel_signature_valid=0, channel_join=NULL, canonical_url=NULL
|
||||
WHERE channel_hash IN ({','.join('?' for _ in spent_claims)})
|
||||
""", [sqlite3.Binary(cid) for cid in spent_claims]
|
||||
)
|
||||
|
||||
if channels:
|
||||
self.db.executemany(
|
||||
"UPDATE claim SET public_key_bytes=:public_key_bytes WHERE claim_hash=:claim_hash", [{
|
||||
|
@ -620,7 +628,7 @@ class SQLDB:
|
|||
r(self.delete_supports, delete_support_txo_hashes)
|
||||
r(self.insert_claims, insert_claims, header)
|
||||
r(self.update_claims, update_claims, header)
|
||||
r(self.validate_channel_signatures, height, insert_claims, update_claims)
|
||||
r(self.validate_channel_signatures, height, insert_claims, update_claims, delete_claim_hashes)
|
||||
r(self.insert_supports, insert_supports)
|
||||
r(self.update_claimtrie, height, recalculate_claim_hashes, deleted_claim_names, forward_timer=True)
|
||||
r(calculate_trending, self.db, height, self.main.first_sync, daemon_height)
|
||||
|
@ -670,21 +678,25 @@ class SQLDB:
|
|||
constraints['claim.claim_id'] = claim_id
|
||||
else:
|
||||
constraints['claim.claim_id__like'] = f'{claim_id[:40]}%'
|
||||
|
||||
if 'name' in constraints:
|
||||
constraints['claim.normalized'] = normalize_name(constraints.pop('name'))
|
||||
|
||||
if 'channel' in constraints:
|
||||
url = URL.parse(constraints.pop('channel'))
|
||||
if url.channel.claim_id:
|
||||
constraints['channel_id'] = url.channel.claim_id
|
||||
channel_url = constraints.pop('channel')
|
||||
match = self._resolve_one(channel_url)
|
||||
if isinstance(match, sqlite3.Row):
|
||||
constraints['channel_hash'] = match['claim_hash']
|
||||
else:
|
||||
constraints['channel_name'] = url.channel.name
|
||||
if 'channel_id' in constraints:
|
||||
constraints['channel_hash'] = unhexlify(constraints.pop('channel_id'))[::-1]
|
||||
raise LookupError(f'Could not resolve channel "{channel_url}".')
|
||||
if 'channel_hash' in constraints:
|
||||
constraints['channel.claim_hash'] = sqlite3.Binary(constraints.pop('channel_hash'))
|
||||
if 'channel_name' in constraints:
|
||||
constraints['channel.normalized'] = normalize_name(constraints.pop('channel_name'))
|
||||
constraints['claim.channel_hash'] = sqlite3.Binary(constraints.pop('channel_hash'))
|
||||
if 'channel_ids' in constraints:
|
||||
channel_ids = constraints.pop('channel_ids')
|
||||
if channel_ids:
|
||||
constraints['claim.channel_hash__in'] = [
|
||||
sqlite3.Binary(unhexlify(cid)[::-1]) for cid in channel_ids
|
||||
]
|
||||
|
||||
if 'txid' in constraints:
|
||||
tx_hash = unhexlify(constraints.pop('txid'))[::-1]
|
||||
|
@ -697,23 +709,17 @@ class SQLDB:
|
|||
_apply_constraints_for_array_attributes(constraints, 'language')
|
||||
_apply_constraints_for_array_attributes(constraints, 'location')
|
||||
|
||||
try:
|
||||
return self.db.execute(*query(
|
||||
f"""
|
||||
SELECT {cols} FROM claim
|
||||
LEFT JOIN claimtrie USING (claim_hash)
|
||||
LEFT JOIN claim as channel ON (claim.channel_hash=channel.claim_hash)
|
||||
""", **constraints
|
||||
)).fetchall()
|
||||
except:
|
||||
self.logger.exception('Failed to execute claim search query:')
|
||||
print(query(
|
||||
f"""
|
||||
sql, values = query(
|
||||
f"""
|
||||
SELECT {cols} FROM claim
|
||||
LEFT JOIN claimtrie USING (claim_hash)
|
||||
LEFT JOIN claim as channel ON (claim.channel_hash=channel.claim_hash)
|
||||
""", **constraints
|
||||
))
|
||||
)
|
||||
try:
|
||||
return self.db.execute(sql, values).fetchall()
|
||||
except:
|
||||
self.logger.exception(f'Failed to execute claim search query: {sql}')
|
||||
raise
|
||||
|
||||
def get_claims_count(self, **constraints):
|
||||
|
@ -727,8 +733,9 @@ class SQLDB:
|
|||
return self.get_claims(
|
||||
"""
|
||||
claimtrie.claim_hash as is_controlling,
|
||||
claim.claim_hash, claim.txo_hash, claim.height,
|
||||
claim.claim_hash, claim.txo_hash,
|
||||
claim.is_channel, claim.claims_in_channel,
|
||||
claim.height, claim.creation_height,
|
||||
claim.activation_height, claim.expiration_height,
|
||||
claim.effective_amount, claim.support_amount,
|
||||
claim.trending_group, claim.trending_mixed,
|
||||
|
@ -740,16 +747,16 @@ class SQLDB:
|
|||
)
|
||||
|
||||
INTEGER_PARAMS = {
|
||||
'height', 'creation_height', 'activation_height', 'tx_position',
|
||||
'release_time', 'timestamp', 'is_channel_signature_valid', 'channel_join',
|
||||
'height', 'creation_height', 'activation_height', 'expiration_height',
|
||||
'timestamp', 'creation_timestamp', 'release_time',
|
||||
'tx_position', 'channel_join', 'is_channel_signature_valid',
|
||||
'amount', 'effective_amount', 'support_amount',
|
||||
'trending_group', 'trending_mixed',
|
||||
'trending_local', 'trending_global',
|
||||
}
|
||||
|
||||
SEARCH_PARAMS = {
|
||||
'name', 'claim_id', 'txid', 'nout',
|
||||
'channel', 'channel_id', 'channel_name',
|
||||
'name', 'claim_id', 'txid', 'nout', 'channel', 'channel_ids',
|
||||
'any_tags', 'all_tags', 'not_tags',
|
||||
'any_locations', 'all_locations', 'not_locations',
|
||||
'any_languages', 'all_languages', 'not_languages',
|
||||
|
@ -775,50 +782,54 @@ class SQLDB:
|
|||
extra_txo_rows = self._search(**{'claim.claim_hash__in': [sqlite3.Binary(h) for h in channel_hashes]})
|
||||
return txo_rows, extra_txo_rows, constraints['offset'], total
|
||||
|
||||
def _resolve_one(self, raw_url):
|
||||
try:
|
||||
url = URL.parse(raw_url)
|
||||
except ValueError as e:
|
||||
return e
|
||||
|
||||
channel = None
|
||||
|
||||
if url.has_channel:
|
||||
query = url.channel.to_dict()
|
||||
if set(query) == {'name'}:
|
||||
query['is_controlling'] = True
|
||||
else:
|
||||
query['order_by'] = ['^height']
|
||||
matches = self._search(**query, limit=1)
|
||||
if matches:
|
||||
channel = matches[0]
|
||||
else:
|
||||
return LookupError(f'Could not find channel in "{raw_url}".')
|
||||
|
||||
if url.has_stream:
|
||||
query = url.stream.to_dict()
|
||||
if channel is not None:
|
||||
if set(query) == {'name'}:
|
||||
# temporarily emulate is_controlling for claims in channel
|
||||
query['order_by'] = ['effective_amount']
|
||||
else:
|
||||
query['order_by'] = ['^channel_join']
|
||||
query['channel_hash'] = channel['claim_hash']
|
||||
query['is_channel_signature_valid'] = 1
|
||||
elif set(query) == {'name'}:
|
||||
query['is_controlling'] = 1
|
||||
matches = self._search(**query, limit=1)
|
||||
if matches:
|
||||
return matches[0]
|
||||
else:
|
||||
return LookupError(f'Could not find stream in "{raw_url}".')
|
||||
|
||||
return channel
|
||||
|
||||
def resolve(self, urls) -> Tuple[List, List]:
|
||||
result = []
|
||||
channel_hashes = set()
|
||||
for raw_url in urls:
|
||||
try:
|
||||
url = URL.parse(raw_url)
|
||||
except ValueError as e:
|
||||
result.append(e)
|
||||
continue
|
||||
channel = None
|
||||
if url.has_channel:
|
||||
query = url.channel.to_dict()
|
||||
if set(query) == {'name'}:
|
||||
query['is_controlling'] = True
|
||||
else:
|
||||
query['order_by'] = ['^height']
|
||||
matches = self._search(**query, limit=1)
|
||||
if matches:
|
||||
channel = matches[0]
|
||||
else:
|
||||
result.append(LookupError(f'Could not find channel in "{raw_url}".'))
|
||||
continue
|
||||
if url.has_stream:
|
||||
query = url.stream.to_dict()
|
||||
if channel is not None:
|
||||
if set(query) == {'name'}:
|
||||
# temporarily emulate is_controlling for claims in channel
|
||||
query['order_by'] = ['effective_amount']
|
||||
else:
|
||||
query['order_by'] = ['^channel_join']
|
||||
query['channel_hash'] = channel['claim_hash']
|
||||
query['is_channel_signature_valid'] = 1
|
||||
elif set(query) == {'name'}:
|
||||
query['is_controlling'] = True
|
||||
matches = self._search(**query, limit=1)
|
||||
if matches:
|
||||
result.append(matches[0])
|
||||
if matches[0]['channel_hash']:
|
||||
channel_hashes.add(matches[0]['channel_hash'])
|
||||
else:
|
||||
result.append(LookupError(f'Could not find stream in "{raw_url}".'))
|
||||
continue
|
||||
else:
|
||||
result.append(channel)
|
||||
match = self._resolve_one(raw_url)
|
||||
result.append(match)
|
||||
if isinstance(match, sqlite3.Row) and match['channel_hash']:
|
||||
channel_hashes.add(match['channel_hash'])
|
||||
extra_txo_rows = []
|
||||
if channel_hashes:
|
||||
extra_txo_rows = self._search(**{'claim.claim_hash__in': [sqlite3.Binary(h) for h in channel_hashes]})
|
||||
|
|
|
@ -68,8 +68,9 @@ class ClaimSearchCommand(CommandTestCase):
|
|||
|
||||
# finding claims with and without a channel
|
||||
await self.assertFindsClaims([signed2, signed], name='on-channel-claim')
|
||||
await self.assertFindsClaim(signed, name='on-channel-claim', channel_id=self.channel_id)
|
||||
await self.assertFindsClaim(signed2, name='on-channel-claim', channel_id=channel_id2)
|
||||
await self.assertFindsClaims([signed2, signed], channel_ids=[self.channel_id, channel_id2])
|
||||
await self.assertFindsClaim(signed, name='on-channel-claim', channel_ids=[self.channel_id])
|
||||
await self.assertFindsClaim(signed2, name='on-channel-claim', channel_ids=[channel_id2])
|
||||
await self.assertFindsClaim(unsigned, name='unsigned')
|
||||
await self.assertFindsClaim(unsigned, txid=unsigned['txid'], nout=0)
|
||||
await self.assertFindsClaim(unsigned, claim_id=unsigned['outputs'][0]['claim_id'])
|
||||
|
@ -79,37 +80,36 @@ class ClaimSearchCommand(CommandTestCase):
|
|||
|
||||
# three streams in channel, zero streams in abandoned channel
|
||||
claims = [three, two, signed]
|
||||
await self.assertFindsClaims(claims, channel_id=self.channel_id)
|
||||
await self.assertFindsClaims([three, two, signed2, signed], channel_name="@abc")
|
||||
await self.assertFindsClaims(claims, channel_name="@abc", channel_id=self.channel_id)
|
||||
await self.assertFindsClaims(claims, channel_ids=[self.channel_id])
|
||||
await self.assertFindsClaims(claims, channel=f"@abc#{self.channel_id}")
|
||||
await self.assertFindsClaims([three, two, signed2, signed], channel_ids=[channel_id2, self.channel_id])
|
||||
await self.channel_abandon(claim_id=self.channel_id)
|
||||
await self.assertFindsClaims([], channel_id=self.channel_id)
|
||||
await self.assertFindsClaims([signed2], channel_name="@abc")
|
||||
await self.assertFindsClaims([], channel_name="@abc", channel_id=self.channel_id)
|
||||
await self.assertFindsClaims([], channel=f"@abc#{self.channel_id}")
|
||||
await self.assertFindsClaims([], channel_ids=[self.channel_id], is_channel_signature_valid=True)
|
||||
await self.assertFindsClaims([signed2], channel_ids=[channel_id2], is_channel_signature_valid=True)
|
||||
await self.assertFindsClaims([signed2], channel_ids=[channel_id2, self.channel_id],
|
||||
is_channel_signature_valid=True)
|
||||
|
||||
# abandoned stream won't show up for streams in channel search
|
||||
await self.stream_abandon(txid=signed2['txid'], nout=0)
|
||||
await self.assertFindsClaims([], channel_name="@abc")
|
||||
await self.assertFindsClaims([], channel_ids=[channel_id2])
|
||||
|
||||
async def test_pagination(self):
|
||||
await self.create_channel()
|
||||
await self.create_lots_of_streams()
|
||||
|
||||
page = await self.claim_search(page_size=20, channel_id=self.channel_id)
|
||||
page = await self.claim_search(page_size=20, channel='@abc')
|
||||
page_claim_ids = [item['name'] for item in page]
|
||||
self.assertEqual(page_claim_ids, self.streams)
|
||||
|
||||
page = await self.claim_search(page_size=6, channel_id=self.channel_id)
|
||||
page = await self.claim_search(page_size=6, channel='@abc')
|
||||
page_claim_ids = [item['name'] for item in page]
|
||||
self.assertEqual(page_claim_ids, self.streams[:6])
|
||||
|
||||
page = await self.claim_search(page=2, page_size=6, channel_id=self.channel_id)
|
||||
page = await self.claim_search(page=2, page_size=6, channel='@abc')
|
||||
page_claim_ids = [item['name'] for item in page]
|
||||
self.assertEqual(page_claim_ids, self.streams[6:])
|
||||
|
||||
out_of_bounds = await self.claim_search(page=2, page_size=20, channel_id=self.channel_id)
|
||||
out_of_bounds = await self.claim_search(page=2, page_size=20, channel='@abc')
|
||||
self.assertEqual(out_of_bounds, [])
|
||||
|
||||
async def test_tag_search(self):
|
||||
|
|
|
@ -94,7 +94,7 @@ class TestSQLDB(unittest.TestCase):
|
|||
Input.spend(claim)
|
||||
)
|
||||
|
||||
def get_stream_abandon(self, tx):
|
||||
def get_abandon(self, tx):
|
||||
claim = Transaction(tx[0].serialize()).outputs[0]
|
||||
return self._make_tx(
|
||||
Output.pay_pubkey_hash(claim.amount, b'abc'),
|
||||
|
@ -264,7 +264,7 @@ class TestSQLDB(unittest.TestCase):
|
|||
active=[('Claim A', 10*COIN, 10*COIN, 13)],
|
||||
accepted=[]
|
||||
)
|
||||
advance(14, [self.get_stream_abandon(stream2)])
|
||||
advance(14, [self.get_abandon(stream2)])
|
||||
state(
|
||||
controlling=('Claim A', 10*COIN, 10*COIN, 13),
|
||||
active=[],
|
||||
|
@ -281,7 +281,7 @@ class TestSQLDB(unittest.TestCase):
|
|||
active=[('Claim A', 10*COIN, 10*COIN, 13)],
|
||||
accepted=[]
|
||||
)
|
||||
advance(15, [self.get_stream_abandon(stream2), self.get_stream('Claim C', 12*COIN)])
|
||||
advance(15, [self.get_abandon(stream2), self.get_stream('Claim C', 12*COIN)])
|
||||
state(
|
||||
controlling=('Claim C', 12*COIN, 12*COIN, 15),
|
||||
active=[('Claim A', 10*COIN, 10*COIN, 13)],
|
||||
|
@ -380,7 +380,8 @@ class TestSQLDB(unittest.TestCase):
|
|||
self.assertEqual(0, self.sql._search(claim_id=txo_chan_a.claim_id, limit=1)[0]['claims_in_channel'])
|
||||
|
||||
# reinstate previous channel public key (previous stream claim signatures become valid again)
|
||||
advance(9, [self.get_channel_update(txo_chan_a, COIN, key=b'c')])
|
||||
channel_update = self.get_channel_update(txo_chan_a, COIN, key=b'c')
|
||||
advance(9, [channel_update])
|
||||
r_ab2, r_a2 = self.sql._search(order_by=['creation_height'], limit=2)
|
||||
self.assertEqual(f"foo#{a2_claim_id[:2]}", r_a2['short_url'])
|
||||
self.assertEqual(f"foo#{ab2_claim_id[:4]}", r_ab2['short_url'])
|
||||
|
@ -388,6 +389,14 @@ class TestSQLDB(unittest.TestCase):
|
|||
self.assertEqual("@foo#a/foo#ab", r_ab2['canonical_url'])
|
||||
self.assertEqual(2, self.sql._search(claim_id=txo_chan_a.claim_id, limit=1)[0]['claims_in_channel'])
|
||||
|
||||
# delete channel, invaliding stream claim signatures
|
||||
advance(10, [self.get_abandon(channel_update)])
|
||||
r_ab2, r_a2 = self.sql._search(order_by=['creation_height'], limit=2)
|
||||
self.assertEqual(f"foo#{a2_claim_id[:2]}", r_a2['short_url'])
|
||||
self.assertEqual(f"foo#{ab2_claim_id[:4]}", r_ab2['short_url'])
|
||||
self.assertIsNone(r_a2['canonical_url'])
|
||||
self.assertIsNone(r_ab2['canonical_url'])
|
||||
|
||||
def test_canonical_find_shortest_id(self):
|
||||
new_hash = 'abcdef0123456789beef'
|
||||
other0 = '1bcdef0123456789beef'
|
||||
|
|
Loading…
Reference in a new issue