diff --git a/lbrynet/extras/daemon/json_response_encoder.py b/lbrynet/extras/daemon/json_response_encoder.py index c4bc0e4d7..4c415242b 100644 --- a/lbrynet/extras/daemon/json_response_encoder.py +++ b/lbrynet/extras/daemon/json_response_encoder.py @@ -138,7 +138,7 @@ class JSONResponseEncoder(JSONEncoder): 'hex': hexlify(tx.raw).decode(), } - def encode_output(self, txo, check_signature=True, include_meta=True): + def encode_output(self, txo, check_signature=True): tx_height = txo.tx_ref.height best_height = self.ledger.headers.height output = { @@ -171,11 +171,13 @@ class JSONResponseEncoder(JSONEncoder): 'name': txo.claim_name, 'normalized': txo.normalized_name, 'claim_id': txo.claim_id, - 'permanent_url': txo.permanent_url + 'permanent_url': txo.permanent_url, + 'meta': self.encode_claim_meta(txo.meta) }) - if include_meta: - output['meta'] = self.encode_claim_meta(txo.meta) - output['canonical_url'] = output['meta'].pop('canonical_url', None) + if 'short_url' in output['meta']: + output['short_url'] = output['meta'].pop('short_url') + if 'canonical_url' in output['meta']: + output['canonical_url'] = output['meta'].pop('canonical_url') if txo.script.is_claim_name or txo.script.is_update_claim: try: output['value'] = txo.claim @@ -183,7 +185,7 @@ class JSONResponseEncoder(JSONEncoder): if self.include_protobuf: output['protobuf'] = hexlify(txo.claim.to_bytes()) if txo.channel is not None: - output['signing_channel'] = self.encode_output(txo.channel, include_meta=False) + output['signing_channel'] = self.encode_output(txo.channel) if check_signature and txo.claim.is_signed: output['is_channel_signature_valid'] = False if txo.channel: diff --git a/lbrynet/schema/result.py b/lbrynet/schema/result.py index 7b9e3f680..81e48772f 100644 --- a/lbrynet/schema/result.py +++ b/lbrynet/schema/result.py @@ -33,8 +33,8 @@ class Outputs: if txo_message.WhichOneof('meta') == 'claim': claim = txo_message.claim txo.meta = { - 'short_url': claim.short_url, - 'canonical_url': claim.canonical_url or claim.short_url, + 'short_url': f'lbry://{claim.short_url}', + 'canonical_url': f'lbry://{claim.canonical_url or claim.short_url}', 'is_controlling': claim.is_controlling, 'activation_height': claim.activation_height, 'expiration_height': claim.expiration_height, diff --git a/lbrynet/wallet/server/db.py b/lbrynet/wallet/server/db.py index 3a5dd5604..f6e11f0c9 100644 --- a/lbrynet/wallet/server/db.py +++ b/lbrynet/wallet/server/db.py @@ -107,7 +107,9 @@ class SQLDB: trending_global integer not null default 0 ); + 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_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); @@ -663,9 +665,11 @@ class SQLDB: constraints['limit'] = 1 if 'claim_id' in constraints: - constraints['claim.claim_hash'] = sqlite3.Binary( - unhexlify(constraints.pop('claim_id'))[::-1] - ) + claim_id = constraints.pop('claim_id') + if len(claim_id) == 40: + 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')) @@ -737,7 +741,7 @@ class SQLDB: INTEGER_PARAMS = { 'height', 'creation_height', 'activation_height', 'tx_position', - 'release_time', 'timestamp', + 'release_time', 'timestamp', 'is_channel_signature_valid', 'channel_join', 'amount', 'effective_amount', 'support_amount', 'trending_group', 'trending_mixed', 'trending_local', 'trending_global', @@ -785,7 +789,9 @@ class SQLDB: query = url.channel.to_dict() if set(query) == {'name'}: query['is_controlling'] = True - matches = self._search(**query) + else: + query['order_by'] = ['^height'] + matches = self._search(**query, limit=1) if matches: channel = matches[0] else: @@ -794,10 +800,16 @@ class SQLDB: 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'] - if set(query) == {'name'}: + query['is_channel_signature_valid'] = 1 + elif set(query) == {'name'}: query['is_controlling'] = True - matches = self._search(**query) + matches = self._search(**query, limit=1) if matches: result.append(matches[0]) if matches[0]['channel_hash']: diff --git a/tests/integration/test_resolve_command.py b/tests/integration/test_resolve_command.py index 80cbe5e8c..3bf3c0759 100644 --- a/tests/integration/test_resolve_command.py +++ b/tests/integration/test_resolve_command.py @@ -126,6 +126,46 @@ class ResolveCommand(CommandTestCase): await self.assertResolvesToClaimId('foo$3', claim_id1) await self.assertResolvesToClaimId('foo$4', None) + async def test_partial_claim_id_resolve(self): + # add some noise + await self.channel_create('@abc', '0.1', allow_duplicate_name=True) + await self.channel_create('@abc', '0.2', allow_duplicate_name=True) + await self.channel_create('@abc', '1.0', allow_duplicate_name=True) + + channel_id = self.get_claim_id( + await self.channel_create('@abc', '1.1', allow_duplicate_name=True)) + await self.assertResolvesToClaimId(f'@abc', channel_id) + await self.assertResolvesToClaimId(f'@abc#{channel_id[0]}', channel_id) + await self.assertResolvesToClaimId(f'@abc#{channel_id[:10]}', channel_id) + await self.assertResolvesToClaimId(f'@abc#{channel_id}', channel_id) + channel = (await self.claim_search(claim_id=channel_id))[0] + await self.assertResolvesToClaimId(channel['short_url'], channel_id) + await self.assertResolvesToClaimId(channel['canonical_url'], channel_id) + await self.assertResolvesToClaimId(channel['permanent_url'], channel_id) + + # add some noise + await self.stream_create('foo', '0.1', allow_duplicate_name=True, channel_id=channel['claim_id']) + await self.stream_create('foo', '0.2', allow_duplicate_name=True, channel_id=channel['claim_id']) + await self.stream_create('foo', '0.3', allow_duplicate_name=True, channel_id=channel['claim_id']) + + claim_id1 = self.get_claim_id( + await self.stream_create('foo', '0.7', allow_duplicate_name=True, channel_id=channel['claim_id'])) + claim1 = (await self.claim_search(claim_id=claim_id1))[0] + await self.assertResolvesToClaimId('foo', claim_id1) + await self.assertResolvesToClaimId('@abc/foo', claim_id1) + await self.assertResolvesToClaimId(claim1['short_url'], claim_id1) + await self.assertResolvesToClaimId(claim1['canonical_url'], claim_id1) + await self.assertResolvesToClaimId(claim1['permanent_url'], claim_id1) + + claim_id2 = self.get_claim_id( + await self.stream_create('foo', '0.8', allow_duplicate_name=True, channel_id=channel['claim_id'])) + claim2 = (await self.claim_search(claim_id=claim_id2))[0] + await self.assertResolvesToClaimId('foo', claim_id2) + await self.assertResolvesToClaimId('@abc/foo', claim_id2) + await self.assertResolvesToClaimId(claim2['short_url'], claim_id2) + await self.assertResolvesToClaimId(claim2['canonical_url'], claim_id2) + await self.assertResolvesToClaimId(claim2['permanent_url'], claim_id2) + async def test_abandoned_channel_with_signed_claims(self): channel = (await self.channel_create('@abc', '1.0'))['outputs'][0] orphan_claim = await self.stream_create('on-channel-claim', '0.0001', channel_id=channel['claim_id'])