From 183e3ac1c72fada3c276e3dbbf58522ce102b1b4 Mon Sep 17 00:00:00 2001 From: Lex Berezhny Date: Mon, 25 Mar 2019 22:06:36 -0400 Subject: [PATCH] claim search --- lbrynet/extras/daemon/Daemon.py | 190 ++++------ lbrynet/wallet/resolve.py | 1 + tests/integration/test_chris45.py | 10 +- tests/integration/test_claim_commands.py | 409 +++++++++++----------- tests/integration/test_file_commands.py | 20 +- tests/integration/test_resolve_command.py | 6 +- tests/integration/testcase.py | 27 +- tests/unit/test_cli.py | 2 +- 8 files changed, 308 insertions(+), 357 deletions(-) diff --git a/lbrynet/extras/daemon/Daemon.py b/lbrynet/extras/daemon/Daemon.py index 2e0d71f7f..8b1b26ede 100644 --- a/lbrynet/extras/daemon/Daemon.py +++ b/lbrynet/extras/daemon/Daemon.py @@ -122,7 +122,6 @@ async def maybe_paginate(get_records: Callable, get_record_count: Callable, def sort_claim_results(claims): claims.sort(key=lambda d: (d['height'], d['name'], d['claim_id'], d['txid'], d['nout'])) - return claims DHT_HAS_CONTACTS = "dht_has_contacts" @@ -912,7 +911,7 @@ class Daemon(metaclass=JSONRPCServerType): return {key: cleaned} ACCOUNT_DOC = """ - Account management. + Create, modify and inspect wallet accounts. """ @requires("wallet") @@ -1376,7 +1375,7 @@ class Daemon(metaclass=JSONRPCServerType): } ADDRESS_DOC = """ - Address management. + List, generate and verify addresses. """ @requires(WALLET_COMPONENT) @@ -1623,7 +1622,7 @@ class Daemon(metaclass=JSONRPCServerType): @requires(WALLET_COMPONENT) def jsonrpc_claim_list(self, account_id=None, page=None, page_size=None): """ - List my name claims + List my stream and channel claims. Usage: claim_list [ | --account_id=] @@ -1642,134 +1641,65 @@ class Daemon(metaclass=JSONRPCServerType): ) @requires(WALLET_COMPONENT) - async def jsonrpc_claim_search(self, name, channel_id=None, winning=False): + async def jsonrpc_claim_search( + self, name=None, claim_id=None, txid=None, nout=None, + channel_id=None, winning=False, page=1, page_size=10): """ - Search for claims on the blockchain. + Search for stream and channel claims on the blockchain. + + Use --channel_id= to list all stream claims in a channel. Usage: - claim_search ( | --name=) [--channel_id=] [--winning] + claim_search ([ | --name=] | --claim_id= | --txid= --nout=) + [--channel_id=] [--winning] [--page=] [--page_size=] Options: - --name= : (str) name of the claim to list info about - --channel_id= : (str) limit search to specific channel + --name= : (str) find claims with this name + --claim_id= : (str) find a claim with this claim_id + --txid= : (str) find a claim with this txid:nout + --nout= : (str) find a claim with this txid:nout + --channel_id= : (str) limit search to specific channel (returns stream claims) --winning : (bool) limit to winning claims + --page= : (int) page to return during paginating + --page_size= : (int) number of items on page during pagination """ - response = await self.wallet_manager.ledger.network.get_claims_for_name(name) - resolutions = await self.resolve(*(f"{claim['name']}#{claim['claim_id']}" for claim in response['claims'])) - response['claims'] = [value.get('claim', value.get('certificate')) for value in resolutions.values()] - response['claims'] = sort_claim_results(response['claims']) - return response - """ - Resolve claim info from txid/nout or with claim ID - - Usage: - claim_show [ | --txid=] [ | --nout=] - [ | --claim_id=] - - Options: - --txid= : (str) look for claim with this txid, nout must - also be specified - --nout= : (int) look for claim with this nout, txid must - also be specified - --claim_id= : (str) look for claim with this claim id - """ - if claim_id is not None and txid is None and nout is None: - claim_results = await self.wallet_manager.get_claim_by_claim_id(claim_id) - elif txid is not None and nout is not None and claim_id is None: - claim_results = await self.wallet_manager.get_claim_by_outpoint(txid, int(nout)) + claims = [] + if name is not None: + claims = await self.ledger.network.get_claims_for_name(name) + elif claim_id is not None: + claim = await self.wallet_manager.get_claim_by_claim_id(claim_id) + if claim and claim != 'claim not found': + claims = {'claims': [claim]} + elif txid is not None and nout is not None: + claim = await self.wallet_manager.get_claim_by_outpoint(txid, int(nout)) + if claim and claim != 'claim not found': + claims = {'claims': [claim]} + elif channel_id is not None: + claim = await self.wallet_manager.get_claim_by_claim_id(channel_id) + if claim and claim != 'claim not found': + channel_url = f"{claim['name']}#{claim['claim_id']}" + resolve = await self.resolve(f"{claim['name']}#{claim['claim_id']}", page=page, page_size=page_size) + resolve = resolve.get(channel_url, {}) + claims = resolve.get('claims_in_channel', []) or [] + total_pages = 0 + if claims: + total_pages = int((resolve['total_claims'] + (page_size-1)) / page_size) + #sort_claim_results(claims) + return {"items": claims, "total_pages": total_pages, "page": page, "page_size": page_size} else: - raise Exception("Must specify either txid/nout, or claim_id") - return claim_results - - async def jsonrpc_claim_list_by_channel(self, page=0, page_size=10, uri=None, uris=[]): - """ - Get paginated claims in a channel specified by a channel uri - Usage: - claim_list_by_channel ( | --uri=) [...] [--page=] - [--page_size=] - Options: - --uri= : (str) uri of the channel - --uris= : (list) uris of the channel - --page= : (int) which page of results to return where page 1 is the first - page, defaults to no pages - --page_size= : (int) number of results in a page, default of 10 - Returns: - { - resolved channel uri: { - If there was an error: - 'error': (str) error message - 'claims_in_channel': the total number of results for the channel, - If a page of results was requested: - 'returned_page': page number returned, - 'claims_in_channel': [ - { - 'absolute_channel_position': (int) claim index number in sorted list of - claims which assert to be part of the - channel - 'address': (str) claim address, - 'amount': (float) claim amount, - 'effective_amount': (float) claim amount including supports, - 'claim_id': (str) claim id, - 'decoded_claim': (bool) whether or not the claim value was decoded, - 'height': (int) claim height, - 'depth': (int) claim depth, - 'has_signature': (bool) included if decoded_claim - 'name': (str) claim name, - 'supports: (list) list of supports [{'txid': (str) txid, - 'nout': (int) nout, - 'amount': (float) amount}], - 'txid': (str) claim txid, - 'nout': (str) claim nout, - 'signature_is_valid': (bool), included if has_signature, - 'value': ClaimDict if decoded, otherwise hex string - } - ], - } - } - """ - - uris = tuple(uris) - page = int(page) - page_size = int(page_size) - if uri is not None: - uris += (uri,) - - results = {} - - valid_uris = tuple() - for chan_uri in uris: - try: - parsed = parse_lbry_uri(chan_uri) - if not parsed.contains_channel: - results[chan_uri] = {"error": "%s is not a channel uri" % parsed.name} - elif parsed.path: - results[chan_uri] = {"error": "%s is a claim in a channel" % parsed.path} - else: - valid_uris += (chan_uri,) - except URIParseError: - results[chan_uri] = {"error": "%s is not a valid uri" % chan_uri} - - resolved = await self.resolve(*valid_uris, page=page, page_size=page_size) - if 'error' in resolved: - return {'error': resolved['error']} - for u in resolved: - if 'error' in resolved[u]: - results[u] = resolved[u] - else: - results[u] = { - 'claims_in_channel': resolved[u]['claims_in_channel'] - } - if page: - results[u]['returned_page'] = page - results[u]['claims_in_channel'] = resolved[u].get('claims_in_channel', []) - return results + raise Exception("Must specify either name, claimd_id, or txid:nout.") + if claims: + resolutions = await self.resolve(*(f"{claim['name']}#{claim['claim_id']}" for claim in claims['claims'])) + claims = [value.get('claim', value.get('certificate')) for value in resolutions.values()] + sort_claim_results(claims) + return {"items": claims, "total_pages": 1, "page": 1, "page_size": len(claims)} @requires(WALLET_COMPONENT, conditions=[WALLET_IS_UNLOCKED]) async def jsonrpc_claim_abandon( self, claim_id=None, txid=None, nout=None, account_id=None, preview=False, blocking=True): """ - Abandon a name and reclaim credits from the claim + Abandon one of my channel or stream claims. Usage: claim_abandon [ | --claim_id=] @@ -1787,9 +1717,9 @@ class Daemon(metaclass=JSONRPCServerType): """ account = self.get_account_or_default(account_id) - if txid and nout: + if txid is not None and nout is not None: claims = await account.get_claims(**{'txo.txid': txid, 'txo.position': nout}) - elif claim_id: + elif claim_id is not None: claims = await account.get_claims(claim_id=claim_id) else: raise Exception('Must specify claim_id, or txid and nout') @@ -1812,7 +1742,7 @@ class Daemon(metaclass=JSONRPCServerType): return tx CHANNEL_DOC = """ - Channel management. + Create, update and list your channel claims. """ @deprecated('channel_create') @@ -1823,7 +1753,7 @@ class Daemon(metaclass=JSONRPCServerType): async def jsonrpc_channel_create( self, name, bid, allow_duplicate_name=False, account_id=None, claim_address=None, preview=False, **kwargs): """ - Generate a publisher key and create a new '@' prefixed channel claim. + Create a new channel by generating a channel private key and establishing an '@' prefixed claim. Usage: channel_create ( | --name=) ( | --bid=) @@ -1852,7 +1782,7 @@ class Daemon(metaclass=JSONRPCServerType): --preview : (bool) do not broadcast the transaction """ account = self.get_account_or_default(account_id) - name = self.get_channel_name_or_error(name) + self.valid_channel_name_or_error(name) amount = self.get_dewies_or_error('bid', bid, positive_value=True) claim_address = await self.get_receiving_address(claim_address, account) @@ -1891,7 +1821,7 @@ class Daemon(metaclass=JSONRPCServerType): self, claim_id, bid=None, account_id=None, claim_address=None, new_signing_key=False, preview=False, **kwargs): """ - Update attributes of a channel. + Update an existing channel claim. Usage: channel_update ( | --claim_id=) [ | --bid=] @@ -1970,7 +1900,7 @@ class Daemon(metaclass=JSONRPCServerType): @requires(WALLET_COMPONENT) def jsonrpc_channel_list(self, account_id=None, page=None, page_size=None): """ - Get certificate claim infos for channels that can be published to + List my channel claims. Usage: channel_list [ | --account_id=] @@ -2027,7 +1957,7 @@ class Daemon(metaclass=JSONRPCServerType): return await self.wallet_manager.import_certificate_info(serialized_certificate_info) STREAM_DOC = """ - Create, update, list and inspect stream claims. + Create, update, list and inspect your stream claims. """ @requires(WALLET_COMPONENT, STREAM_MANAGER_COMPONENT, BLOB_COMPONENT, DATABASE_COMPONENT, @@ -2418,7 +2348,7 @@ class Daemon(metaclass=JSONRPCServerType): @requires(WALLET_COMPONENT) def jsonrpc_support_list(self, account_id=None, page=None, page_size=None): """ - List supports and tips. + List supports and tips in my control. Usage: support_list [ | --account_id=] @@ -2441,7 +2371,7 @@ class Daemon(metaclass=JSONRPCServerType): self, claim_id=None, txid=None, nout=None, keep=None, account_id=None, preview=False, blocking=True): """ - Abandon supports, including tips, to a specific claim, optionally + Abandon supports, including tips, of a specific claim, optionally keeping some amount as supports. Usage: @@ -2459,9 +2389,9 @@ class Daemon(metaclass=JSONRPCServerType): """ account = self.get_account_or_default(account_id) - if txid and nout: + if txid is not None and nout is not None: supports = await account.get_supports(**{'txo.txid': txid, 'txo.position': nout}) - elif claim_id: + elif claim_id is not None: supports = await account.get_supports(claim_id=claim_id) else: raise Exception('Must specify claim_id, or txid and nout') diff --git a/lbrynet/wallet/resolve.py b/lbrynet/wallet/resolve.py index 664a3cad4..f7850ae77 100644 --- a/lbrynet/wallet/resolve.py +++ b/lbrynet/wallet/resolve.py @@ -141,6 +141,7 @@ class Resolver: claims_in_channel, upper_bound = channel_info if claims_in_channel: + result['total_claims'] = upper_bound result['claims_in_channel'] = claims_in_channel elif 'error' not in result: return {'error': 'claim not found', 'success': False, 'uri': str(parsed_uri)} diff --git a/tests/integration/test_chris45.py b/tests/integration/test_chris45.py index 9b2c131d4..303979dda 100644 --- a/tests/integration/test_chris45.py +++ b/tests/integration/test_chris45.py @@ -18,7 +18,7 @@ class EpicAdventuresOfChris45(CommandTestCase): # While making the spamdwich he wonders... has anyone on LBRY # registered the @spam channel yet? "I should do that!" he # exclaims and goes back to his computer to do just that! - tx = await self.create_channel('@spam', '1.0') + tx = await self.channel_create('@spam', '1.0') channel_id = tx['outputs'][0]['claim_id'] # Do we have it locally? @@ -50,7 +50,7 @@ class EpicAdventuresOfChris45(CommandTestCase): # And so, many hours later, Chris is finished writing his epic story # about eels driving a hovercraft across the wetlands while eating spam # and decides it's time to publish it to the @spam channel. - tx = await self.create_claim( + tx = await self.stream_create( 'hovercraft', '1.0', data=b'[insert long story about eels driving hovercraft]', channel_id=channel_id @@ -77,7 +77,7 @@ class EpicAdventuresOfChris45(CommandTestCase): # As people start reading his story they discover some typos and notify # Chris who explains in despair "Oh! Noooooos!" but then remembers # "No big deal! I can update my claim." And so he updates his claim. - await self.update_claim(claim_id, data=b'[typo fixing sounds being made]') + await self.stream_update(claim_id, data=b'[typo fixing sounds being made]') # After some soul searching Chris decides that his story needs more # heart and a better ending. He takes down the story and begins the rewrite. @@ -116,7 +116,7 @@ class EpicAdventuresOfChris45(CommandTestCase): # After Chris is done with all the "helping other people" stuff he decides that it's time to # write a new story and publish it to lbry. All he needed was a fresh start and he came up with: - tx = await self.create_claim( + tx = await self.stream_create( 'fresh-start', '1.0', data=b'Amazingly Original First Line', channel_id=channel_id ) claim_id2 = tx['outputs'][0]['claim_id'] @@ -173,7 +173,7 @@ class EpicAdventuresOfChris45(CommandTestCase): # his song, seeing as his novel had smashed all the records, he was the perfect candidate! # ....... # Chris agrees.. 17 hours 43 minutes and 14 seconds later, he makes his publish - tx = await self.create_claim( + tx = await self.stream_create( 'hit-song', '1.0', data=b'The Whale and The Bookmark', channel_id=channel_id ) await self.generate(5) diff --git a/tests/integration/test_claim_commands.py b/tests/integration/test_claim_commands.py index c8c54c81b..e0f986606 100644 --- a/tests/integration/test_claim_commands.py +++ b/tests/integration/test_claim_commands.py @@ -16,40 +16,40 @@ class ChannelCommands(CommandTestCase): async def test_create_channel_names(self): # claim new name - await self.create_channel('@foo') + await self.channel_create('@foo') self.assertEqual(len(await self.daemon.jsonrpc_channel_list()), 1) await self.assertBalance(self.account, '8.991893') # fail to claim duplicate with self.assertRaisesRegex(Exception, "You already have a channel under the name '@foo'."): - await self.create_channel('@foo') + await self.channel_create('@foo') # fail to claim invalid name - with self.assertRaisesRegex(Exception, "Cannot make a new channel for a non channel name"): - await self.create_channel('foo') + with self.assertRaisesRegex(Exception, "Channel names must start with '@' symbol."): + await self.channel_create('foo') # nothing's changed after failed attempts self.assertEqual(len(await self.daemon.jsonrpc_channel_list()), 1) await self.assertBalance(self.account, '8.991893') # succeed overriding duplicate restriction - await self.create_channel('@foo', allow_duplicate_name=True) + await self.channel_create('@foo', allow_duplicate_name=True) self.assertEqual(len(await self.daemon.jsonrpc_channel_list()), 2) await self.assertBalance(self.account, '7.983786') async def test_channel_bids(self): # enough funds - tx = await self.create_channel('@foo', '5.0') + tx = await self.channel_create('@foo', '5.0') claim_id = tx['outputs'][0]['claim_id'] self.assertEqual(len(await self.daemon.jsonrpc_channel_list()), 1) await self.assertBalance(self.account, '4.991893') # bid preserved on update - tx = await self.update_channel(claim_id) + tx = await self.channel_update(claim_id) self.assertEqual(tx['outputs'][0]['amount'], '5.0') # bid changed on update - tx = await self.update_channel(claim_id, bid='4.0') + tx = await self.channel_update(claim_id, bid='4.0') self.assertEqual(tx['outputs'][0]['amount'], '4.0') await self.assertBalance(self.account, '5.991447') @@ -57,12 +57,12 @@ class ChannelCommands(CommandTestCase): # not enough funds with self.assertRaisesRegex( InsufficientFundsError, "Not enough funds to cover this transaction."): - await self.create_channel('@foo2', '9.0') + await self.channel_create('@foo2', '9.0') self.assertEqual(len(await self.daemon.jsonrpc_channel_list()), 1) await self.assertBalance(self.account, '5.991447') # spend exactly amount available, no change - tx = await self.create_channel('@foo3', '5.981266') + tx = await self.channel_create('@foo3', '5.981266') await self.assertBalance(self.account, '0.0') self.assertEqual(len(tx['outputs']), 1) # no change self.assertEqual(len(await self.daemon.jsonrpc_channel_list()), 2) @@ -80,7 +80,7 @@ class ChannelCommands(CommandTestCase): } # create new channel with all fields set - tx = await self.out(self.create_channel('@bigchannel', **values)) + tx = await self.out(self.channel_create('@bigchannel', **values)) txo = tx['outputs'][0] self.assertEqual( txo['value']['channel'], @@ -88,7 +88,7 @@ class ChannelCommands(CommandTestCase): ) # create channel with nothing set - tx = await self.out(self.create_channel('@lightchannel')) + tx = await self.out(self.channel_create('@lightchannel')) txo = tx['outputs'][0] self.assertEqual( txo['value']['channel'], @@ -96,7 +96,7 @@ class ChannelCommands(CommandTestCase): ) # create channel with just some tags - tx = await self.out(self.create_channel('@updatedchannel', tags='blah')) + tx = await self.out(self.channel_create('@updatedchannel', tags='blah')) txo = tx['outputs'][0] claim_id = txo['claim_id'] public_key = txo['value']['channel']['public_key'] @@ -106,7 +106,7 @@ class ChannelCommands(CommandTestCase): ) # update channel setting all fields - tx = await self.out(self.update_channel(claim_id, **values)) + tx = await self.out(self.channel_update(claim_id, **values)) txo = tx['outputs'][0] values['public_key'] = public_key values['tags'].insert(0, 'blah') # existing tag @@ -116,7 +116,7 @@ class ChannelCommands(CommandTestCase): ) # clearing and settings tags - tx = await self.out(self.update_channel(claim_id, tags='single', clear_tags=True)) + tx = await self.out(self.channel_update(claim_id, tags='single', clear_tags=True)) txo = tx['outputs'][0] values['tags'] = ['single'] self.assertEqual( @@ -125,7 +125,7 @@ class ChannelCommands(CommandTestCase): ) # reset signing key - tx = await self.out(self.update_channel(claim_id, new_signing_key=True)) + tx = await self.out(self.channel_update(claim_id, new_signing_key=True)) txo = tx['outputs'][0] self.assertNotEqual( txo['value']['channel']['public_key'], @@ -141,7 +141,7 @@ class ChannelCommands(CommandTestCase): self.assertEqual(len(await self.daemon.jsonrpc_channel_list(account_id=account2_id)), 0) other_address = await account2.receiving.get_or_create_usable_address() - tx = await self.out(self.update_channel(claim_id, claim_address=other_address)) + tx = await self.out(self.channel_update(claim_id, claim_address=other_address)) # after sending self.assertEqual(len(await self.daemon.jsonrpc_channel_list()), 2) @@ -160,122 +160,45 @@ class ChannelCommands(CommandTestCase): self.assertIsNotNone(txo.private_key) -class SupportCommands(CommandTestCase): +class StreamCommands(CommandTestCase): - async def test_regular_supports_and_tip_supports(self): - # account2 will be used to send tips and supports to account1 - account2_id = (await self.daemon.jsonrpc_account_create('second account'))['id'] - account2 = self.daemon.get_account_or_error(account2_id) - - # send account2 5 LBC out of the 10 LBC in account1 - result = await self.out(self.daemon.jsonrpc_account_send( - '5.0', await self.daemon.jsonrpc_address_unused(account2_id) - )) - await self.on_transaction_dict(result) - - # account1 and account2 balances: - await self.assertBalance(self.account, '4.999876') - await self.assertBalance(account2, '5.0') - - # create the claim we'll be tipping and supporting - tx = await self.create_claim() - claim_id = tx['outputs'][0]['claim_id'] - - # account1 and account2 balances: - await self.assertBalance(self.account, '3.979769') - await self.assertBalance(account2, '5.0') - - # send a tip to the claim using account2 - tip = await self.out( - self.daemon.jsonrpc_support_create(claim_id, '1.0', True, account2_id) - ) - await self.on_transaction_dict(tip) - await self.generate(1) - await self.on_transaction_dict(tip) - - # tips don't affect balance so account1 balance is same but account2 balance went down - await self.assertBalance(self.account, '3.979769') - await self.assertBalance(account2, '3.9998585') - - # verify that the incoming tip is marked correctly as is_tip=True in account1 - txs = await self.out(self.daemon.jsonrpc_transaction_list()) - self.assertEqual(len(txs[0]['support_info']), 1) - self.assertEqual(txs[0]['support_info'][0]['balance_delta'], '1.0') - self.assertEqual(txs[0]['support_info'][0]['claim_id'], claim_id) - self.assertEqual(txs[0]['support_info'][0]['is_tip'], True) - self.assertEqual(txs[0]['value'], '1.0') - self.assertEqual(txs[0]['fee'], '0.0') - - # verify that the outgoing tip is marked correctly as is_tip=True in account2 - txs2 = await self.out( - self.daemon.jsonrpc_transaction_list(account2_id) - ) - self.assertEqual(len(txs2[0]['support_info']), 1) - self.assertEqual(txs2[0]['support_info'][0]['balance_delta'], '-1.0') - self.assertEqual(txs2[0]['support_info'][0]['claim_id'], claim_id) - self.assertEqual(txs2[0]['support_info'][0]['is_tip'], True) - self.assertEqual(txs2[0]['value'], '-1.0') - self.assertEqual(txs2[0]['fee'], '-0.0001415') - - # send a support to the claim using account2 - support = await self.out( - self.daemon.jsonrpc_support_create(claim_id, '2.0', False, account2_id) - ) - await self.on_transaction_dict(support) - await self.generate(1) - await self.on_transaction_dict(support) - - # account2 balance went down ~2 - await self.assertBalance(self.account, '3.979769') - await self.assertBalance(account2, '1.999717') - - # verify that the outgoing support is marked correctly as is_tip=False in account2 - txs2 = await self.out(self.daemon.jsonrpc_transaction_list(account2_id)) - self.assertEqual(len(txs2[0]['support_info']), 1) - self.assertEqual(txs2[0]['support_info'][0]['balance_delta'], '-2.0') - self.assertEqual(txs2[0]['support_info'][0]['claim_id'], claim_id) - self.assertEqual(txs2[0]['support_info'][0]['is_tip'], False) - self.assertEqual(txs2[0]['value'], '0.0') - self.assertEqual(txs2[0]['fee'], '-0.0001415') - - -class ClaimCommands(CommandTestCase): - - async def test_create_claim_names(self): + async def test_create_stream_names(self): # claim new name - await self.create_claim('foo') + await self.stream_create('foo') self.assertEqual(len(await self.daemon.jsonrpc_claim_list()), 1) await self.assertBalance(self.account, '8.993893') # fail to claim duplicate - with self.assertRaisesRegex(Exception, "You already have a claim published under the name 'foo'."): - await self.create_claim('foo') + with self.assertRaisesRegex( + Exception, "You already have a stream claim published under the name 'foo'."): + await self.stream_create('foo') # fail claim starting with @ - with self.assertRaisesRegex(Exception, "Claim names cannot start with @ symbol."): - await self.create_claim('@foo') + with self.assertRaisesRegex( + Exception, "Stream names cannot start with '@' symbol."): + await self.stream_create('@foo') self.assertEqual(len(await self.daemon.jsonrpc_claim_list()), 1) await self.assertBalance(self.account, '8.993893') # succeed overriding duplicate restriction - await self.create_claim('foo', allow_duplicate_name=True) + await self.stream_create('foo', allow_duplicate_name=True) self.assertEqual(len(await self.daemon.jsonrpc_claim_list()), 2) await self.assertBalance(self.account, '7.987786') - async def test_bids(self): + async def test_stream_bids(self): # enough funds - tx = await self.create_claim('foo', '2.0') + tx = await self.stream_create('foo', '2.0') claim_id = tx['outputs'][0]['claim_id'] self.assertEqual(len(await self.daemon.jsonrpc_claim_list()), 1) await self.assertBalance(self.account, '7.993893') # bid preserved on update - tx = await self.update_claim(claim_id) + tx = await self.stream_update(claim_id) self.assertEqual(tx['outputs'][0]['amount'], '2.0') # bid changed on update - tx = await self.update_claim(claim_id, bid='3.0') + tx = await self.stream_update(claim_id, bid='3.0') self.assertEqual(tx['outputs'][0]['amount'], '3.0') await self.assertBalance(self.account, '6.993384') @@ -283,12 +206,12 @@ class ClaimCommands(CommandTestCase): # not enough funds with self.assertRaisesRegex( InsufficientFundsError, "Not enough funds to cover this transaction."): - await self.create_claim('foo2', '9.0') + await self.stream_create('foo2', '9.0') self.assertEqual(len(await self.daemon.jsonrpc_claim_list()), 1) await self.assertBalance(self.account, '6.993384') # spend exactly amount available, no change - tx = await self.create_claim('foo3', '6.98527700') + tx = await self.stream_create('foo3', '6.98527700') await self.assertBalance(self.account, '0.0') self.assertEqual(len(tx['outputs']), 1) # no change self.assertEqual(len(await self.daemon.jsonrpc_claim_list()), 2) @@ -298,7 +221,7 @@ class ClaimCommands(CommandTestCase): new_account = await self.daemon.jsonrpc_account_create('second account') account2_id, account2 = new_account['id'], self.daemon.get_account_or_error(new_account['id']) - await self.out(self.create_channel('@spam', '1.0')) + await self.out(self.channel_create('@spam', '1.0')) self.assertEqual('8.989893', await self.daemon.jsonrpc_account_balance()) result = await self.out(self.daemon.jsonrpc_account_send( @@ -309,7 +232,7 @@ class ClaimCommands(CommandTestCase): self.assertEqual('3.989769', await self.daemon.jsonrpc_account_balance()) self.assertEqual('5.0', await self.daemon.jsonrpc_account_balance(account2_id)) - baz_tx = await self.out(self.create_channel('@baz', '1.0', account_id=account2_id)) + baz_tx = await self.out(self.channel_create('@baz', '1.0', account_id=account2_id)) baz_id = baz_tx['outputs'][0]['claim_id'] channels = await self.out(self.daemon.jsonrpc_channel_list(account1_id)) @@ -322,12 +245,12 @@ class ClaimCommands(CommandTestCase): self.assertEqual(channels[0]['name'], '@baz') # defaults to using all accounts to lookup channel - await self.create_claim('hovercraft1', channel_id=baz_id) + await self.stream_create('hovercraft1', channel_id=baz_id) # uses only the specific accounts which contains the channel - await self.create_claim('hovercraft2', channel_id=baz_id, channel_account_id=[account2_id]) + await self.stream_create('hovercraft2', channel_id=baz_id, channel_account_id=[account2_id]) # fails when specifying account which does not contain channel with self.assertRaisesRegex(ValueError, "Couldn't find channel with channel_id"): - await self.create_claim( + await self.stream_create( 'hovercraft3', channel_id=baz_id, channel_account_id=[account1_id] ) @@ -353,7 +276,7 @@ class ClaimCommands(CommandTestCase): } # create new channel with all fields set - tx = await self.out(self.create_claim('big', **values)) + tx = await self.out(self.stream_create('big', **values)) txo = tx['outputs'][0] stream = txo['value']['stream'] fixed_values = values.copy() @@ -373,7 +296,7 @@ class ClaimCommands(CommandTestCase): self.assertEqual(stream, fixed_values) # create channel with nothing set - tx = await self.out(self.create_claim('light')) + tx = await self.out(self.stream_create('light')) txo = tx['outputs'][0] self.assertEqual( txo['value']['stream'], { @@ -384,7 +307,7 @@ class ClaimCommands(CommandTestCase): ) # create channel with just some tags - tx = await self.out(self.create_claim('updated', tags='blah')) + tx = await self.out(self.stream_create('updated', tags='blah')) txo = tx['outputs'][0] claim_id = txo['claim_id'] fixed_values['hash'] = txo['value']['stream']['hash'] @@ -398,13 +321,13 @@ class ClaimCommands(CommandTestCase): ) # update channel setting all fields - tx = await self.out(self.update_claim(claim_id, **values)) + tx = await self.out(self.stream_update(claim_id, **values)) txo = tx['outputs'][0] fixed_values['tags'].insert(0, 'blah') # existing tag self.assertEqual(txo['value']['stream'], fixed_values) # clearing and settings tags - tx = await self.out(self.update_claim(claim_id, tags='single', clear_tags=True)) + tx = await self.out(self.stream_update(claim_id, tags='single', clear_tags=True)) txo = tx['outputs'][0] fixed_values['tags'] = ['single'] self.assertEqual(txo['value']['stream'], fixed_values) @@ -418,7 +341,7 @@ class ClaimCommands(CommandTestCase): self.assertEqual(len(await self.daemon.jsonrpc_claim_list(account_id=account2_id)), 0) other_address = await account2.receiving.get_or_create_usable_address() - tx = await self.out(self.update_claim(claim_id, claim_address=other_address)) + tx = await self.out(self.stream_update(claim_id, claim_address=other_address)) # after sending self.assertEqual(len(await self.daemon.jsonrpc_claim_list()), 2) @@ -427,7 +350,7 @@ class ClaimCommands(CommandTestCase): async def test_create_update_and_abandon_claim(self): await self.assertBalance(self.account, '10.0') - tx = await self.create_claim(bid='2.5') # creates new claim + tx = await self.stream_create(bid='2.5') # creates new claim claim_id = tx['outputs'][0]['claim_id'] txs = await self.out(self.daemon.jsonrpc_transaction_list()) self.assertEqual(len(txs[0]['claim_info']), 1) @@ -438,7 +361,7 @@ class ClaimCommands(CommandTestCase): self.assertEqual(txs[0]['fee'], '-0.020107') await self.assertBalance(self.account, '7.479893') - await self.update_claim(claim_id, bid='1.0') # updates previous claim + await self.stream_update(claim_id, bid='1.0') # updates previous claim txs = await self.out(self.daemon.jsonrpc_transaction_list()) self.assertEqual(len(txs[0]['update_info']), 1) self.assertEqual(txs[0]['update_info'][0]['balance_delta'], '1.5') @@ -447,7 +370,7 @@ class ClaimCommands(CommandTestCase): self.assertEqual(txs[0]['fee'], '-0.000184') await self.assertBalance(self.account, '8.979709') - await self.out(self.daemon.jsonrpc_claim_abandon(claim_id)) + await self.claim_abandon(claim_id) txs = await self.out(self.daemon.jsonrpc_transaction_list()) self.assertEqual(len(txs[0]['abandon_info']), 1) self.assertEqual(txs[0]['abandon_info'][0]['balance_delta'], '1.0') @@ -458,57 +381,88 @@ class ClaimCommands(CommandTestCase): async def test_abandoning_claim_at_loss(self): await self.assertBalance(self.account, '10.0') - tx = await self.create_claim(bid='0.0001') + tx = await self.stream_create(bid='0.0001') await self.assertBalance(self.account, '9.979793') - await self.out(self.daemon.jsonrpc_claim_abandon(tx['outputs'][0]['claim_id'])) + await self.claim_abandon(tx['outputs'][0]['claim_id']) await self.assertBalance(self.account, '9.97968399') - async def test_claim_show(self): - channel = await self.create_channel('@abc', '1.0') - channel_from_claim_show = await self.out( - self.daemon.jsonrpc_claim_show(txid=channel['txid'], nout=0) - ) - self.assertEqual(channel_from_claim_show['value'], channel['outputs'][0]['value']) - channel_from_claim_show = await self.out( - self.daemon.jsonrpc_claim_show(claim_id=channel['outputs'][0]['claim_id']) - ) - self.assertEqual(channel_from_claim_show['value'], channel['outputs'][0]['value']) - - abandon = await self.out(self.daemon.jsonrpc_claim_abandon(txid=channel['txid'], nout=0, blocking=False)) - self.assertEqual(abandon['inputs'][0]['txid'], channel['txid']) - await self.confirm_tx(abandon['txid']) - not_a_claim = await self.out( - self.daemon.jsonrpc_claim_show(txid=abandon['txid'], nout=0) - ) - self.assertEqual(not_a_claim, 'claim not found') - async def test_claim_search(self): - channel = await self.create_channel('@abc', '1.0') - channel_id = channel['outputs'][0]['claim_id'] - claim = await self.create_claim('on-channel-claim', '0.0001', channel_id=channel_id) - unsigned_claim = await self.create_claim('unsigned', '0.0001') + # search for channel claim + channel = await self.channel_create('@abc', '1.0') + channel_id, txid = channel['outputs'][0]['claim_id'], channel['txid'] + value = channel['outputs'][0]['value'] - channel_from_claim_list = await self.out(self.daemon.jsonrpc_claim_search('@abc')) - self.assertEqual(channel_from_claim_list['claims'][0]['value'], channel['outputs'][0]['value']) - signed_claim_from_claim_list = await self.out(self.daemon.jsonrpc_claim_search('on-channel-claim')) - self.assertEqual(signed_claim_from_claim_list['claims'][0]['value'], claim['outputs'][0]['value']) - unsigned_claim_from_claim_list = await self.out(self.daemon.jsonrpc_claim_search('unsigned')) - self.assertEqual(unsigned_claim_from_claim_list['claims'][0]['value'], unsigned_claim['outputs'][0]['value']) + claims = await self.claim_search('@abc') + self.assertEqual(claims[0]['value'], value) - abandon = await self.out(self.daemon.jsonrpc_claim_abandon(txid=channel['txid'], nout=0, blocking=False)) - self.assertTrue(abandon['outputs'][0]['txid'], channel['txid']) - await self.on_transaction_dict(abandon) - await self.generate(1) - await self.on_transaction_dict(abandon) + claims = await self.claim_search(txid=txid, nout=0) + self.assertEqual(claims[0]['value'], value) - empty = await self.out(self.daemon.jsonrpc_claim_search('@abc')) - self.assertEqual(len(empty['claims']), 0) + claims = await self.claim_search(claim_id=channel_id) + self.assertEqual(claims[0]['value'], value) + + await self.claim_abandon(txid=txid, nout=0) + self.assertEqual(len(await self.claim_search(txid=txid, nout=0)), 0) + + # search stream claims + channel = await self.channel_create('@abc', '1.0') + channel_id, txid = channel['outputs'][0]['claim_id'], channel['txid'] + + signed = await self.stream_create('on-channel-claim', '0.0001', channel_id=channel_id) + unsigned = await self.stream_create('unsigned', '0.0001') + + claims = await self.claim_search('on-channel-claim') + self.assertEqual(claims[0]['value'], signed['outputs'][0]['value']) + + claims = await self.claim_search('unsigned') + self.assertEqual(claims[0]['value'], unsigned['outputs'][0]['value']) + + # list streams in a channel + await self.stream_create('on-channel-claim-2', '0.0001', channel_id=channel_id) + await self.stream_create('on-channel-claim-3', '0.0001', channel_id=channel_id) + + claims = await self.claim_search(channel_id=channel_id) + self.assertEqual(len(claims), 3) + + await self.claim_abandon(claim_id=claims[0]['claim_id']) + await self.claim_abandon(claim_id=claims[1]['claim_id']) + await self.claim_abandon(claim_id=claims[2]['claim_id']) + + claims = await self.claim_search(channel_id=channel_id) + self.assertEqual(len(claims), 0) + + tx = await self.daemon.jsonrpc_account_fund(None, None, '0.001', outputs=100, broadcast=True) + await self.confirm_tx(tx.id) + + # 4 claims per block, 3 blocks. Sorted by height (descending) then claim_id (ascending). + claims = [] + for j in range(3): + same_height_claims = [] + for k in range(3): + claim_tx = await self.stream_create(f'c{j}-{k}', '0.000001', channel_id=channel_id, confirm=False) + same_height_claims.append(claim_tx['outputs'][0]['claim_id']) + await self.on_transaction_dict(claim_tx) + claim_tx = await self.stream_create(f'c{j}-4', '0.000001', channel_id=channel_id, confirm=True) + same_height_claims.append(claim_tx['outputs'][0]['claim_id']) + same_height_claims.sort(key=lambda x: int(x, 16)) + claims = same_height_claims + claims + + page = await self.claim_search(page_size=20, channel_id=channel_id) + page_claim_ids = [item['claim_id'] for item in page] + self.assertEqual(page_claim_ids, claims) + + page = await self.claim_search(page_size=6, channel_id=channel_id) + page_claim_ids = [item['claim_id'] for item in page] + self.assertEqual(page_claim_ids, claims[:6]) + + out_of_bounds = await self.claim_search(page=2, page_size=20, channel_id=channel_id) + self.assertEqual(out_of_bounds, []) async def test_abandoned_channel_with_signed_claims(self): - channel = (await self.create_channel('@abc', '1.0'))['outputs'][0] - orphan_claim = await self.create_claim('on-channel-claim', '0.0001', channel_id=channel['claim_id']) - await self.out(self.daemon.jsonrpc_claim_abandon(txid=channel['txid'], nout=0)) - channel = (await self.create_channel('@abc', '1.0'))['outputs'][0] + 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']) + await self.claim_abandon(txid=channel['txid'], nout=0) + channel = (await self.channel_create('@abc', '1.0'))['outputs'][0] orphan_claim_id = orphan_claim['outputs'][0]['claim_id'] # Original channel doesnt exists anymore, so the signature is invalid. For invalid signatures, resolution is @@ -520,17 +474,17 @@ class ClaimCommands(CommandTestCase): direct_uri = 'lbry://on-channel-claim#' + orphan_claim_id response = await self.resolve(direct_uri) self.assertFalse(response[direct_uri]['claim']['signature_is_valid']) - await self.daemon.jsonrpc_claim_abandon(claim_id=orphan_claim_id) + await self.claim_abandon(claim_id=orphan_claim_id) uri = 'lbry://@abc/on-channel-claim' # now, claim something on this channel (it will update the invalid claim, but we save and forcefully restore) - valid_claim = await self.create_claim('on-channel-claim', '0.00000001', channel_id=channel['claim_id']) + valid_claim = await self.stream_create('on-channel-claim', '0.00000001', channel_id=channel['claim_id']) # resolves normally response = await self.resolve(uri) self.assertTrue(response[uri]['claim']['signature_is_valid']) # ooops! claimed a valid conflict! (this happens on the wild, mostly by accident or race condition) - await self.create_claim( + await self.stream_create( 'on-channel-claim', '0.00000001', channel_id=channel['claim_id'], allow_duplicate_name=True ) @@ -538,45 +492,17 @@ class ClaimCommands(CommandTestCase): response = await self.resolve(uri) self.assertTrue(response[uri]['claim']['signature_is_valid']) self.assertEqual(response[uri]['claim']['txid'], valid_claim['txid']) - claims = (await self.daemon.jsonrpc_claim_search('on-channel-claim'))['claims'] + claims = (await self.daemon.jsonrpc_claim_search('on-channel-claim'))['items'] self.assertEqual(2, len(claims)) signer_ids = set([claim['value'].signing_channel_id for claim in claims]) self.assertEqual({channel['claim_id']}, signer_ids) - async def test_claim_list_by_channel(self): - self.maxDiff = None - tx = await self.daemon.jsonrpc_account_fund(None, None, '0.001', outputs=100, broadcast=True) - await self.confirm_tx(tx.id) - channel_id = (await self.create_channel('@abc', "0.0001"))['outputs'][0]['claim_id'] - - # 4 claims per block, 3 blocks. Sorted by height (descending) then claim_id (ascending). - claims = [] - for j in range(3): - same_height_claims = [] - for k in range(3): - claim_tx = await self.create_claim(f'c{j}-{k}', '0.000001', channel_id=channel_id, confirm=False) - same_height_claims.append(claim_tx['outputs'][0]['claim_id']) - await self.on_transaction_dict(claim_tx) - claim_tx = await self.create_claim(f'c{j}-4', '0.000001', channel_id=channel_id, confirm=True) - same_height_claims.append(claim_tx['outputs'][0]['claim_id']) - same_height_claims.sort(key=lambda x: int(x, 16)) - claims = same_height_claims + claims - - page = await self.out(self.daemon.jsonrpc_claim_list_by_channel(1, page_size=20, uri='@abc')) - page_claim_ids = [item['claim_id'] for item in page['@abc']['claims_in_channel']] - self.assertEqual(page_claim_ids, claims) - page = await self.out(self.daemon.jsonrpc_claim_list_by_channel(1, page_size=6, uri='@abc')) - page_claim_ids = [item['claim_id'] for item in page['@abc']['claims_in_channel']] - self.assertEqual(page_claim_ids, claims[:6]) - out_of_bounds = await self.out(self.daemon.jsonrpc_claim_list_by_channel(2, page_size=20, uri='@abc')) - self.assertEqual(out_of_bounds['error'], 'claim 20 greater than max 12') - async def test_normalization_resolution(self): # this test assumes that the lbrycrd forks normalization at height == 250 on regtest - c1 = await self.create_claim('ΣίσυφοςfiÆ', '0.1') - c2 = await self.create_claim('ΣΊΣΥΦΟσFIæ', '0.2') + c1 = await self.stream_create('ΣίσυφοςfiÆ', '0.1') + c2 = await self.stream_create('ΣΊΣΥΦΟσFIæ', '0.2') r1 = await self.daemon.jsonrpc_resolve(urls='lbry://ΣίσυφοςfiÆ') r2 = await self.daemon.jsonrpc_resolve(urls='lbry://ΣΊΣΥΦΟσFIæ') @@ -665,4 +591,85 @@ def generate_signed_legacy(name: str, address: bytes, output: Output): claim.publisherSignature.signatureType = 1 claim.publisherSignature.signature = signature claim.publisherSignature.certificateId = output.claim_hash - return claim \ No newline at end of file + return claim + + +class SupportCommands(CommandTestCase): + + async def test_regular_supports_and_tip_supports(self): + # account2 will be used to send tips and supports to account1 + account2_id = (await self.daemon.jsonrpc_account_create('second account'))['id'] + account2 = self.daemon.get_account_or_error(account2_id) + + # send account2 5 LBC out of the 10 LBC in account1 + result = await self.out(self.daemon.jsonrpc_account_send( + '5.0', await self.daemon.jsonrpc_address_unused(account2_id) + )) + await self.on_transaction_dict(result) + + # account1 and account2 balances: + await self.assertBalance(self.account, '4.999876') + await self.assertBalance(account2, '5.0') + + # create the claim we'll be tipping and supporting + tx = await self.stream_create() + claim_id = tx['outputs'][0]['claim_id'] + + # account1 and account2 balances: + await self.assertBalance(self.account, '3.979769') + await self.assertBalance(account2, '5.0') + + # send a tip to the claim using account2 + tip = await self.out( + self.daemon.jsonrpc_support_create(claim_id, '1.0', True, account2_id) + ) + await self.on_transaction_dict(tip) + await self.generate(1) + await self.on_transaction_dict(tip) + + # tips don't affect balance so account1 balance is same but account2 balance went down + await self.assertBalance(self.account, '3.979769') + await self.assertBalance(account2, '3.9998585') + + # verify that the incoming tip is marked correctly as is_tip=True in account1 + txs = await self.out(self.daemon.jsonrpc_transaction_list()) + self.assertEqual(len(txs[0]['support_info']), 1) + self.assertEqual(txs[0]['support_info'][0]['balance_delta'], '1.0') + self.assertEqual(txs[0]['support_info'][0]['claim_id'], claim_id) + self.assertEqual(txs[0]['support_info'][0]['is_tip'], True) + self.assertEqual(txs[0]['value'], '1.0') + self.assertEqual(txs[0]['fee'], '0.0') + + # verify that the outgoing tip is marked correctly as is_tip=True in account2 + txs2 = await self.out( + self.daemon.jsonrpc_transaction_list(account2_id) + ) + self.assertEqual(len(txs2[0]['support_info']), 1) + self.assertEqual(txs2[0]['support_info'][0]['balance_delta'], '-1.0') + self.assertEqual(txs2[0]['support_info'][0]['claim_id'], claim_id) + self.assertEqual(txs2[0]['support_info'][0]['is_tip'], True) + self.assertEqual(txs2[0]['value'], '-1.0') + self.assertEqual(txs2[0]['fee'], '-0.0001415') + + # send a support to the claim using account2 + support = await self.out( + self.daemon.jsonrpc_support_create(claim_id, '2.0', False, account2_id) + ) + await self.on_transaction_dict(support) + await self.generate(1) + await self.on_transaction_dict(support) + + # account2 balance went down ~2 + await self.assertBalance(self.account, '3.979769') + await self.assertBalance(account2, '1.999717') + + # verify that the outgoing support is marked correctly as is_tip=False in account2 + txs2 = await self.out(self.daemon.jsonrpc_transaction_list(account2_id)) + self.assertEqual(len(txs2[0]['support_info']), 1) + self.assertEqual(txs2[0]['support_info'][0]['balance_delta'], '-2.0') + self.assertEqual(txs2[0]['support_info'][0]['claim_id'], claim_id) + self.assertEqual(txs2[0]['support_info'][0]['is_tip'], False) + self.assertEqual(txs2[0]['value'], '0.0') + self.assertEqual(txs2[0]['fee'], '-0.0001415') + + diff --git a/tests/integration/test_file_commands.py b/tests/integration/test_file_commands.py index 1222ce20f..c4715f37a 100644 --- a/tests/integration/test_file_commands.py +++ b/tests/integration/test_file_commands.py @@ -11,8 +11,8 @@ class FileCommands(CommandTestCase): VERBOSITY = logging.WARN async def test_file_management(self): - await self.create_claim('foo', '0.01') - await self.create_claim('foo2', '0.01') + await self.stream_create('foo', '0.01') + await self.stream_create('foo2', '0.01') file1, file2 = self.daemon.jsonrpc_file_list('claim_name') self.assertEqual(file1['claim_name'], 'foo') @@ -27,7 +27,7 @@ class FileCommands(CommandTestCase): self.assertEqual(len(self.daemon.jsonrpc_file_list()), 1) async def test_download_different_timeouts(self): - tx = await self.create_claim('foo', '0.01') + tx = await self.stream_create('foo', '0.01') sd_hash = tx['outputs'][0]['value']['stream']['hash'] await self.daemon.jsonrpc_file_delete(claim_name='foo') all_except_sd = [ @@ -48,7 +48,7 @@ class FileCommands(CommandTestCase): await asyncio.sleep(0.01) async def test_filename_conflicts_management_on_resume_download(self): - await self.create_claim('foo', '0.01', data=bytes([0]*(1<<23))) + await self.stream_create('foo', '0.01', data=bytes([0] * (1 << 23))) file_info = self.daemon.jsonrpc_file_list()[0] original_path = os.path.join(self.daemon.conf.download_dir, file_info['file_name']) await self.daemon.jsonrpc_file_delete(claim_name='foo') @@ -69,7 +69,7 @@ class FileCommands(CommandTestCase): # this used to be inconsistent, if it becomes again it would create weird bugs, so worth checking async def test_incomplete_downloads_erases_output_file_on_stop(self): - tx = await self.create_claim('foo', '0.01') + tx = await self.stream_create('foo', '0.01') sd_hash = tx['outputs'][0]['value']['stream']['hash'] file_info = self.daemon.jsonrpc_file_list()[0] await self.daemon.jsonrpc_file_delete(claim_name='foo') @@ -88,7 +88,7 @@ class FileCommands(CommandTestCase): self.assertFalse(os.path.isfile(os.path.join(self.daemon.conf.download_dir, file_info['file_name']))) async def test_incomplete_downloads_retry(self): - tx = await self.create_claim('foo', '0.01') + tx = await self.stream_create('foo', '0.01') sd_hash = tx['outputs'][0]['value']['stream']['hash'] await self.daemon.jsonrpc_file_delete(claim_name='foo') blobs = await self.server_storage.get_blobs_for_stream( @@ -128,7 +128,7 @@ class FileCommands(CommandTestCase): async def test_unban_recovers_stream(self): BlobDownloader.BAN_TIME = .5 # fixme: temporary field, will move to connection manager or a conf - tx = await self.create_claim('foo', '0.01', data=bytes([0]*(1<<23))) + tx = await self.stream_create('foo', '0.01', data=bytes([0] * (1 << 23))) sd_hash = tx['outputs'][0]['value']['stream']['hash'] missing_blob_hash = (await self.daemon.jsonrpc_blob_list(sd_hash=sd_hash))[-2] await self.daemon.jsonrpc_file_delete(claim_name='foo') @@ -150,7 +150,7 @@ class FileCommands(CommandTestCase): target_address = await self.blockchain.get_raw_change_address() # FAIL: beyond available balance - await self.create_claim( + await self.stream_create( 'expensive', '0.01', data=b'pay me if you can', fee_currency='LBC', fee_amount='11.0', fee_address=target_address ) @@ -160,7 +160,7 @@ class FileCommands(CommandTestCase): self.assertEqual(len(self.daemon.jsonrpc_file_list()), 0) # FAIL: beyond maximum key fee - await self.create_claim( + await self.stream_create( 'maxkey', '0.01', data=b'no pay me, no', fee_currency='LBC', fee_amount='111.0', fee_address=target_address ) @@ -170,7 +170,7 @@ class FileCommands(CommandTestCase): self.assertEqual(response['error'], 'fee of 111.00000 exceeds max configured to allow of 50.00000') # PASS: purchase is successful - await self.create_claim( + await self.stream_create( 'icanpay', '0.01', data=b'I got the power!', fee_currency='LBC', fee_amount='1.0', fee_address=target_address ) diff --git a/tests/integration/test_resolve_command.py b/tests/integration/test_resolve_command.py index c82496fa7..13b1bccc4 100644 --- a/tests/integration/test_resolve_command.py +++ b/tests/integration/test_resolve_command.py @@ -4,7 +4,7 @@ from integration.testcase import CommandTestCase class ResolveCommand(CommandTestCase): async def test_resolve(self): - tx = await self.create_channel('@abc', '0.01') + tx = await self.channel_create('@abc', '0.01') channel_id = tx['outputs'][0]['claim_id'] # resolving a channel @abc @@ -15,8 +15,8 @@ class ResolveCommand(CommandTestCase): self.assertEqual(response['lbry://@abc']['certificate']['name'], '@abc') self.assertEqual(response['lbry://@abc']['claims_in_channel'], 0) - await self.create_claim('foo', '0.01', channel_id=channel_id) - await self.create_claim('foo2', '0.01', channel_id=channel_id) + await self.stream_create('foo', '0.01', channel_id=channel_id) + await self.stream_create('foo2', '0.01', channel_id=channel_id) # resolving a channel @abc with some claims in it response = await self.resolve('lbry://@abc') diff --git a/tests/integration/testcase.py b/tests/integration/testcase.py index 7f78b2e0b..c163a7d37 100644 --- a/tests/integration/testcase.py +++ b/tests/integration/testcase.py @@ -155,12 +155,12 @@ class CommandTestCase(IntegrationTestCase): to JSON and then back to a dictionary. """ return json.loads(jsonrpc_dumps_pretty(await awaitable, ledger=self.ledger))['result'] - async def create_claim(self, name='hovercraft', bid='1.0', data=b'hi!', confirm=True, **kwargs): + async def stream_create(self, name='hovercraft', bid='1.0', data=b'hi!', confirm=True, **kwargs): with tempfile.NamedTemporaryFile() as file: file.write(data) file.flush() claim = await self.out( - self.daemon.jsonrpc_publish(name, bid, file_path=file.name, **kwargs) + self.daemon.jsonrpc_stream_create(name, bid, file_path=file.name, **kwargs) ) self.assertEqual(claim['outputs'][0]['name'], name) if confirm: @@ -169,16 +169,16 @@ class CommandTestCase(IntegrationTestCase): await self.on_transaction_dict(claim) return claim - async def update_claim(self, claim_id, data=None, confirm=True, **kwargs): + async def stream_update(self, claim_id, data=None, confirm=True, **kwargs): if data: with tempfile.NamedTemporaryFile() as file: file.write(data) file.flush() claim = await self.out( - self.daemon.jsonrpc_claim_update(claim_id, file_path=file.name, **kwargs) + self.daemon.jsonrpc_stream_update(claim_id, file_path=file.name, **kwargs) ) else: - claim = await self.out(self.daemon.jsonrpc_claim_update(claim_id, **kwargs)) + claim = await self.out(self.daemon.jsonrpc_stream_update(claim_id, **kwargs)) self.assertIsNotNone(claim['outputs'][0]['name']) if confirm: await self.on_transaction_dict(claim) @@ -186,7 +186,7 @@ class CommandTestCase(IntegrationTestCase): await self.on_transaction_dict(claim) return claim - async def create_channel(self, name='@arena', bid='1.0', confirm=True, **kwargs): + async def channel_create(self, name='@arena', bid='1.0', confirm=True, **kwargs): channel = await self.out(self.daemon.jsonrpc_channel_create(name, bid, **kwargs)) self.assertEqual(channel['outputs'][0]['name'], name) if confirm: @@ -195,7 +195,7 @@ class CommandTestCase(IntegrationTestCase): await self.on_transaction_dict(channel) return channel - async def update_channel(self, claim_id, confirm=True, **kwargs): + async def channel_update(self, claim_id, confirm=True, **kwargs): channel = await self.out(self.daemon.jsonrpc_channel_update(claim_id, **kwargs)) self.assertTrue(channel['outputs'][0]['name'].startswith('@')) if confirm: @@ -204,5 +204,18 @@ class CommandTestCase(IntegrationTestCase): await self.on_transaction_dict(channel) return channel + async def claim_abandon(self, *args, confirm=True, **kwargs): + if 'blocking' not in kwargs: + kwargs['blocking'] = False + tx = await self.out(self.daemon.jsonrpc_claim_abandon(*args, **kwargs)) + if confirm: + await self.on_transaction_dict(tx) + await self.generate(1) + await self.on_transaction_dict(tx) + return tx + async def resolve(self, uri): return await self.out(self.daemon.jsonrpc_resolve(uri)) + + async def claim_search(self, *args, **kwargs): + return (await self.out(self.daemon.jsonrpc_claim_search(*args, **kwargs)))['items'] diff --git a/tests/unit/test_cli.py b/tests/unit/test_cli.py index 1edcd6590..d7ccc0eeb 100644 --- a/tests/unit/test_cli.py +++ b/tests/unit/test_cli.py @@ -61,7 +61,7 @@ class CLITest(AsyncioTestCase): # publish is ungrouped command, returns usage only implicitly self.assertIn('publish ( | --name=)', self.shell(['publish'])) # publish is ungrouped command, with explicit --help - self.assertIn('Make a new name claim and publish', self.shell(['publish', '--help'])) + self.assertIn('Create or update a stream claim at a given name', self.shell(['publish', '--help'])) # account is a group, returns help implicitly self.assertIn('Return the balance of an account', self.shell(['account'])) # account is a group, with explicit --help