bug fixes

This commit is contained in:
Lex Berezhny 2019-05-27 22:20:21 -04:00
parent 1848462ad9
commit 9004f240e6
7 changed files with 185 additions and 137 deletions

View file

@ -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)
--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)
--publish_time=<publish_time> : (int) limit by UTC timestamp of containing block (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 = """

View file

@ -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):

View file

@ -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:
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:

View file

@ -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

View file

@ -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')
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(*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()
return self.db.execute(sql, values).fetchall()
except:
self.logger.exception('Failed to execute claim search query:')
print(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
))
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,16 +782,14 @@ 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(self, urls) -> Tuple[List, List]:
result = []
channel_hashes = set()
for raw_url in urls:
def _resolve_one(self, raw_url):
try:
url = URL.parse(raw_url)
except ValueError as e:
result.append(e)
continue
return e
channel = None
if url.has_channel:
query = url.channel.to_dict()
if set(query) == {'name'}:
@ -795,8 +800,8 @@ class SQLDB:
if matches:
channel = matches[0]
else:
result.append(LookupError(f'Could not find channel in "{raw_url}".'))
continue
return LookupError(f'Could not find channel in "{raw_url}".')
if url.has_stream:
query = url.stream.to_dict()
if channel is not None:
@ -808,17 +813,23 @@ class SQLDB:
query['channel_hash'] = channel['claim_hash']
query['is_channel_signature_valid'] = 1
elif set(query) == {'name'}:
query['is_controlling'] = True
query['is_controlling'] = 1
matches = self._search(**query, limit=1)
if matches:
result.append(matches[0])
if matches[0]['channel_hash']:
channel_hashes.add(matches[0]['channel_hash'])
return matches[0]
else:
result.append(LookupError(f'Could not find stream in "{raw_url}".'))
continue
else:
result.append(channel)
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:
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]})

View file

@ -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):

View file

@ -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'