censored resolve responses return appropriate error
This commit is contained in:
parent
b7eec0586c
commit
15abf49211
5 changed files with 52 additions and 15 deletions
|
@ -4,7 +4,13 @@ from typing import List
|
|||
from binascii import hexlify
|
||||
from itertools import chain
|
||||
|
||||
from lbry.error import ResolveCensoredError
|
||||
from lbry.schema.types.v2.result_pb2 import Outputs as OutputsMessage
|
||||
from lbry.schema.types.v2.result_pb2 import Error as ErrorMessage
|
||||
|
||||
INVALID = ErrorMessage.Code.Name(ErrorMessage.INVALID)
|
||||
NOT_FOUND = ErrorMessage.Code.Name(ErrorMessage.NOT_FOUND)
|
||||
BLOCKED = ErrorMessage.Code.Name(ErrorMessage.BLOCKED)
|
||||
|
||||
|
||||
class Censor:
|
||||
|
@ -73,7 +79,12 @@ class Outputs:
|
|||
|
||||
def message_to_txo(self, txo_message, tx_map):
|
||||
if txo_message.WhichOneof('meta') == 'error':
|
||||
return None
|
||||
return {
|
||||
'error': {
|
||||
'name': txo_message.error.Code.Name(txo_message.error.code).lower(),
|
||||
'text': txo_message.error.text,
|
||||
}
|
||||
}
|
||||
txo = tx_map[txo_message.tx_hash].outputs[txo_message.nout]
|
||||
if txo_message.WhichOneof('meta') == 'claim':
|
||||
claim = txo_message.claim
|
||||
|
@ -146,9 +157,11 @@ class Outputs:
|
|||
if isinstance(txo, Exception):
|
||||
txo_message.error.text = txo.args[0]
|
||||
if isinstance(txo, ValueError):
|
||||
txo_message.error.code = txo_message.error.INVALID
|
||||
txo_message.error.code = ErrorMessage.INVALID
|
||||
elif isinstance(txo, LookupError):
|
||||
txo_message.error.code = txo_message.error.NOT_FOUND
|
||||
txo_message.error.code = ErrorMessage.NOT_FOUND
|
||||
elif isinstance(txo, ResolveCensoredError):
|
||||
txo_message.error.code = ErrorMessage.BLOCKED
|
||||
return
|
||||
txo_message.tx_hash = txo['txo_hash'][:32]
|
||||
txo_message.nout, = struct.unpack('<I', txo['txo_hash'][32:])
|
||||
|
|
|
@ -12,7 +12,7 @@ from binascii import hexlify, unhexlify
|
|||
from typing import Dict, Tuple, Type, Iterable, List, Optional, DefaultDict
|
||||
|
||||
import pylru
|
||||
from lbry.schema.result import Outputs
|
||||
from lbry.schema.result import Outputs, INVALID, NOT_FOUND
|
||||
from lbry.schema.url import URL
|
||||
from lbry.crypto.hash import hash160, double_sha256, sha256
|
||||
from lbry.crypto.base58 import Base58
|
||||
|
@ -661,13 +661,13 @@ class Ledger(metaclass=LedgerRegistry):
|
|||
assert len(urls) == len(txos), "Mismatch between urls requested for resolve and responses received."
|
||||
result = {}
|
||||
for url, txo in zip(urls, txos):
|
||||
if txo and URL.parse(url).has_stream_in_channel:
|
||||
if not txo.channel or not txo.is_signed_by(txo.channel, self):
|
||||
txo = None
|
||||
if txo:
|
||||
result[url] = txo
|
||||
if isinstance(txo, Output) and URL.parse(url).has_stream_in_channel:
|
||||
if not txo.channel or not txo.is_signed_by(txo.channel, self):
|
||||
txo = {'error': {'name': INVALID, 'text': f'{url} has invalid channel signature'}}
|
||||
else:
|
||||
result[url] = {'error': f'{url} did not resolve to a claim'}
|
||||
txo = {'error': {'name': NOT_FOUND, 'text': f'{url} did not resolve to a claim'}}
|
||||
result[url] = txo
|
||||
return result
|
||||
|
||||
async def claim_search(self, accounts, **kwargs) -> Tuple[List[Output], dict, int, int]:
|
||||
|
|
|
@ -4,14 +4,14 @@ import apsw
|
|||
import logging
|
||||
from operator import itemgetter
|
||||
from typing import Tuple, List, Dict, Union, Type, Optional
|
||||
from binascii import unhexlify
|
||||
from binascii import unhexlify, hexlify
|
||||
from decimal import Decimal
|
||||
from contextvars import ContextVar
|
||||
from functools import wraps
|
||||
from dataclasses import dataclass
|
||||
|
||||
from lbry.wallet.database import query, interpolate
|
||||
|
||||
from lbry.error import ResolveCensoredError
|
||||
from lbry.schema.url import URL, normalize_name
|
||||
from lbry.schema.tags import clean_tags
|
||||
from lbry.schema.result import Outputs, Censor
|
||||
|
@ -451,6 +451,8 @@ def resolve_url(raw_url):
|
|||
matches = search_claims(censor, **query, limit=1)
|
||||
if matches:
|
||||
channel = matches[0]
|
||||
elif censor.censored:
|
||||
return ResolveCensoredError(raw_url, hexlify(next(iter(censor.censored))[::-1]).decode())
|
||||
else:
|
||||
return LookupError(f'Could not find channel in "{raw_url}".')
|
||||
|
||||
|
@ -469,6 +471,8 @@ def resolve_url(raw_url):
|
|||
matches = search_claims(censor, **query, limit=1)
|
||||
if matches:
|
||||
return matches[0]
|
||||
elif censor.censored:
|
||||
return ResolveCensoredError(raw_url, hexlify(next(iter(censor.censored))[::-1]).decode())
|
||||
else:
|
||||
return LookupError(f'Could not find stream in "{raw_url}".')
|
||||
|
||||
|
|
|
@ -763,29 +763,48 @@ class StreamCommands(ClaimTestCase):
|
|||
bad_content_id = self.get_claim_id(
|
||||
await self.stream_create('bad_content', '1.1', channel_name='@some_channel', tags=['bad'])
|
||||
)
|
||||
blocking_channel_id = self.get_claim_id(
|
||||
filtering_channel_id = self.get_claim_id(
|
||||
await self.channel_create('@filtering', '1.0')
|
||||
)
|
||||
self.conductor.spv_node.server.db.sql.filtering_channel_hashes.add(
|
||||
unhexlify(blocking_channel_id)[::-1]
|
||||
unhexlify(filtering_channel_id)[::-1]
|
||||
)
|
||||
await self.stream_repost(bad_content_id, 'filter1', '1.1', channel_name='@filtering')
|
||||
|
||||
# search for blocked content directly
|
||||
result = await self.out(self.daemon.jsonrpc_claim_search(name='bad_content'))
|
||||
self.assertEqual([], result['items'])
|
||||
self.assertEqual({"channels": {blocking_channel_id: 1}, "total": 1}, result['blocked'])
|
||||
self.assertEqual({"channels": {filtering_channel_id: 1}, "total": 1}, result['blocked'])
|
||||
|
||||
# search channel containing blocked content
|
||||
result = await self.out(self.daemon.jsonrpc_claim_search(channel='@some_channel'))
|
||||
self.assertEqual(1, len(result['items']))
|
||||
self.assertEqual({"channels": {blocking_channel_id: 1}, "total": 1}, result['blocked'])
|
||||
self.assertEqual({"channels": {filtering_channel_id: 1}, "total": 1}, result['blocked'])
|
||||
|
||||
# content was filtered by not_tag before censoring
|
||||
result = await self.out(self.daemon.jsonrpc_claim_search(channel='@some_channel', not_tags=["good", "bad"]))
|
||||
self.assertEqual(0, len(result['items']))
|
||||
self.assertEqual({"channels": {}, "total": 0}, result['blocked'])
|
||||
|
||||
blocking_channel_id = self.get_claim_id(
|
||||
await self.channel_create('@blocking', '1.0')
|
||||
)
|
||||
self.conductor.spv_node.server.db.sql.blocking_channel_hashes.add(
|
||||
unhexlify(blocking_channel_id)[::-1]
|
||||
)
|
||||
|
||||
# filtered content can still be resolved
|
||||
result = await self.out(self.daemon.jsonrpc_resolve('lbry://@some_channel/bad_content'))
|
||||
self.assertEqual(bad_content_id, result['lbry://@some_channel/bad_content']['claim_id'])
|
||||
|
||||
await self.stream_repost(bad_content_id, 'block1', '1.1', channel_name='@blocking')
|
||||
|
||||
# blocked content is not resolveable
|
||||
result = await self.out(self.daemon.jsonrpc_resolve('lbry://@some_channel/bad_content'))
|
||||
error = result['lbry://@some_channel/bad_content']['error']
|
||||
self.assertTrue(error['name'], 'blocked')
|
||||
self.assertTrue(error['text'].startswith("Resolve of 'lbry://@some_channel/bad_content' was censored"))
|
||||
|
||||
async def test_publish_updates_file_list(self):
|
||||
tx = await self.stream_create(title='created')
|
||||
txo = tx['outputs'][0]
|
||||
|
|
|
@ -15,6 +15,7 @@ class BaseResolveTestCase(CommandTestCase):
|
|||
other = (await self.resolve(name))[name]
|
||||
if claim_id is None:
|
||||
self.assertIn('error', other)
|
||||
self.assertEqual(other['error']['name'], 'not_found')
|
||||
else:
|
||||
self.assertEqual(claim_id, other['claim_id'])
|
||||
|
||||
|
|
Loading…
Reference in a new issue