From f23aea99517c3d00761078e84f1eb935a28f000e Mon Sep 17 00:00:00 2001 From: Lex Berezhny Date: Tue, 4 Jun 2019 14:16:11 -0400 Subject: [PATCH] better handling for claims in abandoned channels --- lbrynet/extras/daemon/Daemon.py | 5 +-- .../extras/daemon/json_response_encoder.py | 11 +++--- lbrynet/schema/claim.py | 5 +-- lbrynet/wallet/server/db.py | 37 +++++++++++-------- tests/integration/test_claim_commands.py | 11 ++++++ 5 files changed, 42 insertions(+), 27 deletions(-) diff --git a/lbrynet/extras/daemon/Daemon.py b/lbrynet/extras/daemon/Daemon.py index a8d82a48f..1111baae3 100644 --- a/lbrynet/extras/daemon/Daemon.py +++ b/lbrynet/extras/daemon/Daemon.py @@ -51,7 +51,6 @@ if typing.TYPE_CHECKING: from lbrynet.wallet.manager import LbryWalletManager from lbrynet.wallet.ledger import MainNetLedger from lbrynet.stream.stream_manager import StreamManager - from lbrynet.stream.managed_stream import ManagedStream log = logging.getLogger(__name__) @@ -1808,9 +1807,9 @@ class Daemon(metaclass=JSONRPCServerType): Returns: {Paginated[Output]} """ if kwargs.pop('valid_channel_signatures', False): - kwargs['is_channel_signature_valid'] = 1 + kwargs['signature_valid'] = 1 if kwargs.pop('invalid_channel_signatures', False): - kwargs['is_channel_signature_valid'] = 0 + kwargs['signature_valid'] = 0 page_num, page_size = abs(kwargs.pop('page', 1)), min(abs(kwargs.pop('page_size', 10)), 50) kwargs.update({'offset': page_size * (page_num-1), 'limit': page_size}) txos, offset, total = await self.ledger.claim_search(**kwargs) diff --git a/lbrynet/extras/daemon/json_response_encoder.py b/lbrynet/extras/daemon/json_response_encoder.py index 423f99914..b9c634db0 100644 --- a/lbrynet/extras/daemon/json_response_encoder.py +++ b/lbrynet/extras/daemon/json_response_encoder.py @@ -184,12 +184,13 @@ class JSONResponseEncoder(JSONEncoder): output['value_type'] = txo.claim.claim_type 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) - if check_signature and txo.claim.is_signed: + if check_signature and txo.claim.is_signed: + if txo.channel is not None: + output['signing_channel'] = self.encode_output(txo.channel) + output['is_channel_signature_valid'] = txo.is_signed_by(txo.channel, self.ledger) + else: + output['signing_channel'] = {'channel_id': txo.claim.signing_channel_id} output['is_channel_signature_valid'] = False - if txo.channel: - output['is_channel_signature_valid'] = txo.is_signed_by(txo.channel, self.ledger) except DecodeError: pass return output diff --git a/lbrynet/schema/claim.py b/lbrynet/schema/claim.py index c59e84269..9fa304f37 100644 --- a/lbrynet/schema/claim.py +++ b/lbrynet/schema/claim.py @@ -195,14 +195,13 @@ class Stream(BaseClaim): claim['source']['hash'] = self.source.file_hash if 'sd_hash' in claim['source']: claim['source']['sd_hash'] = self.source.sd_hash + if 'media_type' in claim['source']: + claim['stream_type'] = guess_stream_type(claim['source']['media_type']) fee = claim.get('fee', {}) if 'address' in fee: fee['address'] = self.fee.address if 'amount' in fee: fee['amount'] = str(self.fee.amount) - stream_type = self.message.WhichOneof('type') - if stream_type: - claim['stream_type'] = stream_type return claim def update(self, file_path=None, height=None, width=None, duration=None, **kwargs): diff --git a/lbrynet/wallet/server/db.py b/lbrynet/wallet/server/db.py index 7f35cba0b..76af0024b 100644 --- a/lbrynet/wallet/server/db.py +++ b/lbrynet/wallet/server/db.py @@ -116,7 +116,7 @@ class SQLDB: channel_join integer, -- height at which claim got valid signature / joined channel signature bytes, signature_digest bytes, - is_channel_signature_valid bool not null default false, + signature_valid bool, effective_amount integer not null default 0, support_amount integer not null default 0, @@ -141,6 +141,8 @@ class SQLDB: create index if not exists claim_stream_type_idx on claim (stream_type); create index if not exists claim_media_type_idx on claim (media_type); + create index if not exists claim_signature_valid_idx on claim (signature_valid); + create index if not exists claim_effective_amount_idx on claim (effective_amount); create index if not exists claim_trending_group_idx on claim (trending_group); create index if not exists claim_trending_mixed_idx on claim (trending_mixed); @@ -431,13 +433,14 @@ class SQLDB: 'channel_hash': None, 'signature': None, 'signature_digest': None, - 'is_channel_signature_valid': False + 'signature_valid': None } if claim.is_signed: update.update({ 'channel_hash': sqlite3.Binary(claim.signing_channel_hash), 'signature': sqlite3.Binary(txo.get_encoded_signature()), - 'signature_digest': sqlite3.Binary(txo.get_signature_digest(self.ledger)) + 'signature_digest': sqlite3.Binary(txo.get_signature_digest(self.ledger)), + 'signature_valid': 0 }) claim_updates.append(update) @@ -454,13 +457,13 @@ class SQLDB: 'channel_hash': sqlite3.Binary(affected_claim['channel_hash']), 'signature': sqlite3.Binary(affected_claim['signature']), 'signature_digest': sqlite3.Binary(affected_claim['signature_digest']), - 'is_channel_signature_valid': False + 'signature_valid': 0 }) for update in claim_updates: channel_pub_key = all_channel_keys.get(update['channel_hash']) if channel_pub_key and update['signature']: - update['is_channel_signature_valid'] = Output.is_signature_valid( + update['signature_valid'] = Output.is_signature_valid( bytes(update['signature']), bytes(update['signature_digest']), channel_pub_key ) @@ -468,20 +471,20 @@ class SQLDB: self.db.executemany(f""" UPDATE claim SET channel_hash=:channel_hash, signature=:signature, signature_digest=:signature_digest, - is_channel_signature_valid=:is_channel_signature_valid, + signature_valid=:signature_valid, channel_join=CASE - WHEN is_channel_signature_valid AND :is_channel_signature_valid THEN channel_join - WHEN :is_channel_signature_valid THEN {height} + WHEN signature_valid=1 AND :signature_valid=1 THEN channel_join + WHEN :signature_valid=1 THEN {height} END, canonical_url=CASE - WHEN is_channel_signature_valid AND :is_channel_signature_valid THEN canonical_url - WHEN :is_channel_signature_valid THEN + WHEN signature_valid=1 AND :signature_valid=1 THEN canonical_url + WHEN :signature_valid=1 THEN (SELECT short_url FROM claim WHERE claim_hash=:channel_hash)||'/'|| claim_name||COALESCE( (SELECT shortest_id(other_claim.claim_id, claim.claim_id) FROM claim AS other_claim WHERE other_claim.normalized = claim.normalized AND other_claim.channel_hash = :channel_hash AND - other_claim.is_channel_signature_valid = 1), + other_claim.signature_valid = 1), '#'||substr(claim_id, 1, 1) ) END @@ -491,7 +494,9 @@ class SQLDB: if spent_claims: self.execute( f""" - UPDATE claim SET is_channel_signature_valid=0, channel_join=NULL, canonical_url=NULL + UPDATE claim SET + signature_valid=CASE WHEN signature IS NOT NULL THEN 0 END, + channel_join=NULL, canonical_url=NULL WHERE channel_hash IN ({','.join('?' for _ in spent_claims)}) """, [sqlite3.Binary(cid) for cid in spent_claims] ) @@ -517,7 +522,7 @@ class SQLDB: claims_in_channel=( SELECT COUNT(*) FROM claim AS claim_in_channel WHERE claim_in_channel.channel_hash=claim.claim_hash AND - claim_in_channel.is_channel_signature_valid + claim_in_channel.signature_valid=1 ) WHERE claim_hash = ? """, [(sqlite3.Binary(channel_hash),) for channel_hash in all_channel_keys.keys()]) @@ -800,14 +805,14 @@ class SQLDB: claim.trending_local, claim.trending_global, claim.short_url, claim.canonical_url, claim.channel_hash, channel.txo_hash AS channel_txo_hash, - channel.height AS channel_height, claim.is_channel_signature_valid + channel.height AS channel_height, claim.signature_valid """, **constraints ) INTEGER_PARAMS = { 'height', 'creation_height', 'activation_height', 'expiration_height', 'timestamp', 'creation_timestamp', 'release_time', - 'tx_position', 'channel_join', 'is_channel_signature_valid', + 'tx_position', 'channel_join', 'signature_valid', 'amount', 'effective_amount', 'support_amount', 'trending_group', 'trending_mixed', 'trending_local', 'trending_global', @@ -870,7 +875,7 @@ class SQLDB: else: query['order_by'] = ['^channel_join'] query['channel_hash'] = channel['claim_hash'] - query['is_channel_signature_valid'] = 1 + query['signature_valid'] = 1 elif set(query) == {'name'}: query['is_controlling'] = 1 matches = self._search(**query, limit=1) diff --git a/tests/integration/test_claim_commands.py b/tests/integration/test_claim_commands.py index 1b2f925dd..5933781e1 100644 --- a/tests/integration/test_claim_commands.py +++ b/tests/integration/test_claim_commands.py @@ -144,6 +144,17 @@ class ClaimSearchCommand(ClaimTestCase): # pass `invalid_channel_signatures=False` to catch a bug in argument processing await self.assertFindsClaims([signed2], channel_ids=[channel_id2, self.channel_id], valid_channel_signatures=True, invalid_channel_signatures=False) + # invalid signature still returns channel_id + self.ledger._tx_cache.clear() + invalid_claims = await self.claim_search(invalid_channel_signatures=True) + self.assertEqual(3, len(invalid_claims)) + self.assertTrue(all([not c['is_channel_signature_valid'] for c in invalid_claims])) + self.assertEqual({'channel_id': self.channel_id}, invalid_claims[0]['signing_channel']) + + valid_claims = await self.claim_search(valid_channel_signatures=True) + self.assertEqual(1, len(valid_claims)) + self.assertTrue(all([c['is_channel_signature_valid'] for c in valid_claims])) + self.assertEqual('@abc', valid_claims[0]['signing_channel']['name']) # abandoned stream won't show up for streams in channel search await self.stream_abandon(txid=signed2['txid'], nout=0)