forked from LBRYCommunity/lbry-sdk
claim search
This commit is contained in:
parent
8087b457c6
commit
183e3ac1c7
8 changed files with 308 additions and 357 deletions
|
@ -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> | --account_id=<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=<channel_id> to list all stream claims in a channel.
|
||||
|
||||
Usage:
|
||||
claim_search (<name> | --name=<name>) [--channel_id=<channel_id>] [--winning]
|
||||
claim_search ([<name> | --name=<name>] | --claim_id=<claim_id> | --txid=<txid> --nout=<nout>)
|
||||
[--channel_id=<channel_id>] [--winning] [--page=<page>] [--page_size=<page_size>]
|
||||
|
||||
Options:
|
||||
--name=<name> : (str) name of the claim to list info about
|
||||
--channel_id=<channel_id> : (str) limit search to specific channel
|
||||
--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 (returns stream claims)
|
||||
--winning : (bool) limit to winning claims
|
||||
--page=<page> : (int) page to return during paginating
|
||||
--page_size=<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> | --txid=<txid>] [<nout> | --nout=<nout>]
|
||||
[<claim_id> | --claim_id=<claim_id>]
|
||||
|
||||
Options:
|
||||
--txid=<txid> : (str) look for claim with this txid, nout must
|
||||
also be specified
|
||||
--nout=<nout> : (int) look for claim with this nout, txid must
|
||||
also be specified
|
||||
--claim_id=<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> | --uri=<uri>) [<uris>...] [--page=<page>]
|
||||
[--page_size=<page_size>]
|
||||
Options:
|
||||
--uri=<uri> : (str) uri of the channel
|
||||
--uris=<uris> : (list) uris of the channel
|
||||
--page=<page> : (int) which page of results to return where page 1 is the first
|
||||
page, defaults to no pages
|
||||
--page_size=<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> | --claim_id=<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> | --name=<name>) (<bid> | --bid=<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> | --claim_id=<claim_id>) [<bid> | --bid=<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> | --account_id=<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> | --account_id=<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')
|
||||
|
|
|
@ -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)}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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æ')
|
||||
|
@ -666,3 +592,84 @@ def generate_signed_legacy(name: str, address: bytes, output: Output):
|
|||
claim.publisherSignature.signature = signature
|
||||
claim.publisherSignature.certificateId = output.claim_hash
|
||||
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')
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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']
|
||||
|
|
|
@ -61,7 +61,7 @@ class CLITest(AsyncioTestCase):
|
|||
# publish is ungrouped command, returns usage only implicitly
|
||||
self.assertIn('publish (<name> | --name=<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
|
||||
|
|
Loading…
Reference in a new issue