forked from LBRYCommunity/lbry-sdk
tests
This commit is contained in:
parent
246f055145
commit
25a91f89fd
13 changed files with 482 additions and 34 deletions
|
@ -3271,6 +3271,337 @@ class Daemon(metaclass=JSONRPCServerType):
|
||||||
"""
|
"""
|
||||||
return self.get_est_cost_from_uri(uri)
|
return self.get_est_cost_from_uri(uri)
|
||||||
|
|
||||||
|
COLLECTION_DOC = """
|
||||||
|
Create, list and abandon collections.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@requires(WALLET_COMPONENT)
|
||||||
|
async def jsonrpc_collection_create(
|
||||||
|
self, name, bid, claims, allow_duplicate_name=False,
|
||||||
|
channel_id=None, channel_name=None, channel_account_id=None,
|
||||||
|
account_id=None, wallet_id=None, claim_address=None, funding_account_ids=None,
|
||||||
|
preview=False, blocking=False, **kwargs):
|
||||||
|
"""
|
||||||
|
Create a new collection ....
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
collection_create (<name> | --name=<name>) (<bid> | --bid=<bid>)
|
||||||
|
(<claims>... | --claims=<claims>...)
|
||||||
|
[--allow_duplicate_name]
|
||||||
|
[--title=<title>] [--description=<description>]
|
||||||
|
[--tags=<tags>...] [--languages=<languages>...] [--locations=<locations>...]
|
||||||
|
[--thumbnail_url=<thumbnail_url>]
|
||||||
|
[--account_id=<account_id>] [--wallet_id=<wallet_id>]
|
||||||
|
[--claim_address=<claim_address>] [--funding_account_ids=<funding_account_ids>...]
|
||||||
|
[--preview] [--blocking]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--name=<name> : (str) name of the collection
|
||||||
|
--bid=<bid> : (decimal) amount to back the claim
|
||||||
|
--claims=<claims> : (list) claim ids to be included in the collection
|
||||||
|
--allow_duplicate_name : (bool) create new collection even if one already exists with
|
||||||
|
given name. default: false.
|
||||||
|
--title=<title> : (str) title of the collection
|
||||||
|
--description=<description> : (str) description of the collection
|
||||||
|
--clear_languages : (bool) clear existing languages (prior to adding new ones)
|
||||||
|
--tags=<tags> : (list) content tags
|
||||||
|
--clear_languages : (bool) clear existing languages (prior to adding new ones)
|
||||||
|
--languages=<languages> : (list) languages used by the collection,
|
||||||
|
using RFC 5646 format, eg:
|
||||||
|
for English `--languages=en`
|
||||||
|
for Spanish (Spain) `--languages=es-ES`
|
||||||
|
for Spanish (Mexican) `--languages=es-MX`
|
||||||
|
for Chinese (Simplified) `--languages=zh-Hans`
|
||||||
|
for Chinese (Traditional) `--languages=zh-Hant`
|
||||||
|
--locations=<locations> : (list) locations of the collection, consisting of 2 letter
|
||||||
|
`country` code and a `state`, `city` and a postal
|
||||||
|
`code` along with a `latitude` and `longitude`.
|
||||||
|
for JSON RPC: pass a dictionary with aforementioned
|
||||||
|
attributes as keys, eg:
|
||||||
|
...
|
||||||
|
"locations": [{'country': 'US', 'state': 'NH'}]
|
||||||
|
...
|
||||||
|
for command line: pass a colon delimited list
|
||||||
|
with values in the following order:
|
||||||
|
|
||||||
|
"COUNTRY:STATE:CITY:CODE:LATITUDE:LONGITUDE"
|
||||||
|
|
||||||
|
making sure to include colon for blank values, for
|
||||||
|
example to provide only the city:
|
||||||
|
|
||||||
|
... --locations="::Manchester"
|
||||||
|
|
||||||
|
with all values set:
|
||||||
|
|
||||||
|
... --locations="US:NH:Manchester:03101:42.990605:-71.460989"
|
||||||
|
|
||||||
|
optionally, you can just pass the "LATITUDE:LONGITUDE":
|
||||||
|
|
||||||
|
... --locations="42.990605:-71.460989"
|
||||||
|
|
||||||
|
finally, you can also pass JSON string of dictionary
|
||||||
|
on the command line as you would via JSON RPC
|
||||||
|
|
||||||
|
... --locations="{'country': 'US', 'state': 'NH'}"
|
||||||
|
|
||||||
|
--thumbnail_url=<thumbnail_url>: (str) thumbnail url
|
||||||
|
--account_id=<account_id> : (str) account to use for holding the transaction
|
||||||
|
--wallet_id=<wallet_id> : (str) restrict operation to specific wallet
|
||||||
|
--funding_account_ids=<funding_account_ids>: (list) ids of accounts to fund this transaction
|
||||||
|
--claim_address=<claim_address>: (str) address where the collection is sent to, if not specified
|
||||||
|
it will be determined automatically from the account
|
||||||
|
--preview : (bool) do not broadcast the transaction
|
||||||
|
--blocking : (bool) wait until transaction is in mempool
|
||||||
|
|
||||||
|
Returns: {Transaction}
|
||||||
|
"""
|
||||||
|
wallet = self.wallet_manager.get_wallet_or_default(wallet_id)
|
||||||
|
account = wallet.get_account_or_default(account_id)
|
||||||
|
funding_accounts = wallet.get_accounts_or_all(funding_account_ids)
|
||||||
|
self.valid_collection_name_or_error(name)
|
||||||
|
channel = await self.get_channel_or_none(wallet, channel_account_id, channel_id, channel_name, for_signing=True)
|
||||||
|
amount = self.get_dewies_or_error('bid', bid, positive_value=True)
|
||||||
|
claim_address = await self.get_receiving_address(claim_address, account)
|
||||||
|
|
||||||
|
existing_collections = await self.ledger.get_collections(accounts=wallet.accounts, claim_name=name)
|
||||||
|
if len(existing_collections) > 0:
|
||||||
|
if not allow_duplicate_name:
|
||||||
|
raise Exception(
|
||||||
|
f"You already have a collection under the name '{name}'. "
|
||||||
|
f"Use --allow-duplicate-name flag to override."
|
||||||
|
)
|
||||||
|
|
||||||
|
claim = Claim()
|
||||||
|
claim.collection.update(claims=claims, **kwargs) # maybe specify claims=[] # here
|
||||||
|
tx = await Transaction.claim_create(
|
||||||
|
name, claim, amount, claim_address, funding_accounts, funding_accounts[0], channel
|
||||||
|
)
|
||||||
|
new_txo = tx.outputs[0]
|
||||||
|
|
||||||
|
if channel:
|
||||||
|
new_txo.sign(channel)
|
||||||
|
await tx.sign(funding_accounts)
|
||||||
|
if not preview:
|
||||||
|
await self.broadcast_or_release(tx, blocking)
|
||||||
|
await self.storage.save_claims([self._old_get_temp_claim_info(
|
||||||
|
tx, new_txo, claim_address, claim, name, dewies_to_lbc(amount)
|
||||||
|
)])
|
||||||
|
# await self.analytics_manager.send_new_channel()
|
||||||
|
else:
|
||||||
|
await account.ledger.release_tx(tx)
|
||||||
|
|
||||||
|
return tx
|
||||||
|
|
||||||
|
@requires(WALLET_COMPONENT)
|
||||||
|
async def jsonrpc_collection_update(
|
||||||
|
self, claim_id, bid=None, claim=None, allow_duplicate_name=False,
|
||||||
|
channel_id=None, channel_name=None, channel_account_id=None, clear_channel=False,
|
||||||
|
account_id=None, wallet_id=None, claim_address=None, funding_account_ids=None,
|
||||||
|
preview=False, blocking=False, replace=False, **kwargs):
|
||||||
|
"""
|
||||||
|
Update an existing collection claim.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
collection_update (<claim_id> | --claim_id=<claim_id>) [--bid=<bid>]
|
||||||
|
[--claims=<claims>...] [--clear_claim_ids]
|
||||||
|
[--title=<title>] [--description=<description>]
|
||||||
|
[--tags=<tags>...] [--clear_tags]
|
||||||
|
[--languages=<languages>...] [--clear_languages]
|
||||||
|
[--locations=<locations>...] [--clear_locations]
|
||||||
|
[--thumbnail_url=<thumbnail_url>] [--cover_url=<cover_url>]
|
||||||
|
[--account_id=<account_id>] [--wallet_id=<wallet_id>]
|
||||||
|
[--claim_address=<claim_address>] [--new_signing_key]
|
||||||
|
[--funding_account_ids=<funding_account_ids>...]
|
||||||
|
[--preview] [--blocking] [--replace]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--claim_id=<claim_id> : (str) claim_id of the collection to update
|
||||||
|
--bid=<bid> : (decimal) amount to back the claim
|
||||||
|
--claims=<claim_ids> : (list) claim ids
|
||||||
|
--clear_claims : (bool) clear existing claim references (prior to adding new ones)
|
||||||
|
--title=<title> : (str) title of the collection
|
||||||
|
--description=<description> : (str) description of the collection
|
||||||
|
--tags=<tags> : (list) add content tags
|
||||||
|
--clear_tags : (bool) clear existing tags (prior to adding new ones)
|
||||||
|
--languages=<languages> : (list) languages used by the collection,
|
||||||
|
using RFC 5646 format, eg:
|
||||||
|
for English `--languages=en`
|
||||||
|
for Spanish (Spain) `--languages=es-ES`
|
||||||
|
for Spanish (Mexican) `--languages=es-MX`
|
||||||
|
for Chinese (Simplified) `--languages=zh-Hans`
|
||||||
|
for Chinese (Traditional) `--languages=zh-Hant`
|
||||||
|
--clear_languages : (bool) clear existing languages (prior to adding new ones)
|
||||||
|
--locations=<locations> : (list) locations of the collection, consisting of 2 letter
|
||||||
|
`country` code and a `state`, `city` and a postal
|
||||||
|
`code` along with a `latitude` and `longitude`.
|
||||||
|
for JSON RPC: pass a dictionary with aforementioned
|
||||||
|
attributes as keys, eg:
|
||||||
|
...
|
||||||
|
"locations": [{'country': 'US', 'state': 'NH'}]
|
||||||
|
...
|
||||||
|
for command line: pass a colon delimited list
|
||||||
|
with values in the following order:
|
||||||
|
|
||||||
|
"COUNTRY:STATE:CITY:CODE:LATITUDE:LONGITUDE"
|
||||||
|
|
||||||
|
making sure to include colon for blank values, for
|
||||||
|
example to provide only the city:
|
||||||
|
|
||||||
|
... --locations="::Manchester"
|
||||||
|
|
||||||
|
with all values set:
|
||||||
|
|
||||||
|
... --locations="US:NH:Manchester:03101:42.990605:-71.460989"
|
||||||
|
|
||||||
|
optionally, you can just pass the "LATITUDE:LONGITUDE":
|
||||||
|
|
||||||
|
... --locations="42.990605:-71.460989"
|
||||||
|
|
||||||
|
finally, you can also pass JSON string of dictionary
|
||||||
|
on the command line as you would via JSON RPC
|
||||||
|
|
||||||
|
... --locations="{'country': 'US', 'state': 'NH'}"
|
||||||
|
|
||||||
|
--clear_locations : (bool) clear existing locations (prior to adding new ones)
|
||||||
|
--thumbnail_url=<thumbnail_url>: (str) thumbnail url
|
||||||
|
#--cover_url=<cover_url> : (str) url of cover image
|
||||||
|
--account_id=<account_id> : (str) account in which to look for collection (default: all)
|
||||||
|
--wallet_id=<wallet_id> : (str) restrict operation to specific wallet
|
||||||
|
--funding_account_ids=<funding_account_ids>: (list) ids of accounts to fund this transaction
|
||||||
|
--claim_address=<claim_address>: (str) address where the collection is sent
|
||||||
|
--new_signing_key : (bool) generate a new signing key, will invalidate all previous publishes
|
||||||
|
--preview : (bool) do not broadcast the transaction
|
||||||
|
--blocking : (bool) wait until transaction is in mempool
|
||||||
|
--replace : (bool) instead of modifying specific values on
|
||||||
|
the collection, this will clear all existing values
|
||||||
|
and only save passed in values, useful for form
|
||||||
|
submissions where all values are always set
|
||||||
|
|
||||||
|
Returns: {Transaction}
|
||||||
|
"""
|
||||||
|
wallet = self.wallet_manager.get_wallet_or_default(wallet_id)
|
||||||
|
funding_accounts = wallet.get_accounts_or_all(funding_account_ids)
|
||||||
|
if account_id:
|
||||||
|
account = wallet.get_account_or_error(account_id)
|
||||||
|
accounts = [account]
|
||||||
|
else:
|
||||||
|
account = wallet.default_account
|
||||||
|
accounts = wallet.accounts
|
||||||
|
|
||||||
|
existing_collections = await self.ledger.get_collections(
|
||||||
|
wallet=wallet, accounts=accounts, claim_id=claim_id
|
||||||
|
)
|
||||||
|
if len(existing_collections) != 1:
|
||||||
|
account_ids = ', '.join(f"'{account.id}'" for account in accounts)
|
||||||
|
raise Exception(
|
||||||
|
f"Can't find the collection '{claim_id}' in account(s) {account_ids}."
|
||||||
|
)
|
||||||
|
# Here we might have a problem of replacing a stream with a collection
|
||||||
|
old_txo = existing_collections[0]
|
||||||
|
if not old_txo.claim.is_collection: # as we're only checking @ or not, this is not definitive
|
||||||
|
raise Exception(
|
||||||
|
f"A claim with id '{claim_id}' was found but it is not a stream or collection."
|
||||||
|
)
|
||||||
|
|
||||||
|
if bid is not None:
|
||||||
|
amount = self.get_dewies_or_error('bid', bid, positive_value=True)
|
||||||
|
else:
|
||||||
|
amount = old_txo.amount
|
||||||
|
|
||||||
|
if claim_address is not None:
|
||||||
|
self.valid_address_or_error(claim_address)
|
||||||
|
else:
|
||||||
|
claim_address = old_txo.get_address(account.ledger)
|
||||||
|
|
||||||
|
channel = None
|
||||||
|
if channel_id or channel_name:
|
||||||
|
channel = await self.get_channel_or_error(
|
||||||
|
wallet, channel_account_id, channel_id, channel_name, for_signing=True)
|
||||||
|
elif old_txo.claim.is_signed and not clear_channel and not replace:
|
||||||
|
channel = old_txo.channel
|
||||||
|
|
||||||
|
if replace:
|
||||||
|
claim = Claim()
|
||||||
|
claim.collection.message.source.CopyFrom(
|
||||||
|
old_txo.claim.collection.message.source
|
||||||
|
)
|
||||||
|
|
||||||
|
claim.collection.update(**kwargs)
|
||||||
|
else:
|
||||||
|
claim = Claim.from_bytes(old_txo.claim.to_bytes())
|
||||||
|
claim.collection.update(**kwargs)
|
||||||
|
tx = await Transaction.claim_update(
|
||||||
|
old_txo, claim, amount, claim_address, funding_accounts, funding_accounts[0], channel
|
||||||
|
)
|
||||||
|
new_txo = tx.outputs[0]
|
||||||
|
|
||||||
|
new_txo.script.generate()
|
||||||
|
|
||||||
|
if channel:
|
||||||
|
new_txo.sign(channel)
|
||||||
|
await tx.sign(funding_accounts)
|
||||||
|
|
||||||
|
if not preview:
|
||||||
|
await self.broadcast_or_release(tx, blocking)
|
||||||
|
await self.analytics_manager.send_claim_action('publish')
|
||||||
|
else:
|
||||||
|
await account.ledger.release_tx(tx)
|
||||||
|
|
||||||
|
return tx
|
||||||
|
|
||||||
|
@requires(WALLET_COMPONENT)
|
||||||
|
async def jsonrpc_collection_abandon(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Abandon one of my collection claims.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
collection_abandon [<claim_id> | --claim_id=<claim_id>]
|
||||||
|
[<txid> | --txid=<txid>] [<nout> | --nout=<nout>]
|
||||||
|
[--account_id=<account_id>] [--wallet_id=<wallet_id>]
|
||||||
|
[--preview] [--blocking]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--claim_id=<claim_id> : (str) claim_id of the claim to abandon
|
||||||
|
--txid=<txid> : (str) txid of the claim to abandon
|
||||||
|
--nout=<nout> : (int) nout of the claim to abandon
|
||||||
|
--account_id=<account_id> : (str) id of the account to use
|
||||||
|
--wallet_id=<wallet_id> : (str) restrict operation to specific wallet
|
||||||
|
--preview : (bool) do not broadcast the transaction
|
||||||
|
--blocking : (bool) wait until abandon is in mempool
|
||||||
|
|
||||||
|
Returns: {Transaction}
|
||||||
|
"""
|
||||||
|
return await self.jsonrpc_stream_abandon(*args, **kwargs)
|
||||||
|
|
||||||
|
@requires(WALLET_COMPONENT)
|
||||||
|
def jsonrpc_collection_list(self, resolve_claims=False, account_id=None, wallet_id=None, page=None, page_size=None):
|
||||||
|
"""
|
||||||
|
List my collection claims.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
collection_list [--resolve_claims] [<account_id> | --account_id=<account_id>] [--wallet_id=<wallet_id>]
|
||||||
|
[--page=<page>] [--page_size=<page_size>]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--resolve_claims : (bool) resolve every claim
|
||||||
|
--account_id=<account_id> : (str) id of the account to use
|
||||||
|
--wallet_id=<wallet_id> : (str) restrict results to specific wallet
|
||||||
|
--page=<page> : (int) page to return during paginating
|
||||||
|
--page_size=<page_size> : (int) number of items on page during pagination
|
||||||
|
|
||||||
|
Returns: {Paginated[Output]}
|
||||||
|
"""
|
||||||
|
wallet = self.wallet_manager.get_wallet_or_default(wallet_id)
|
||||||
|
if account_id:
|
||||||
|
account: LBCAccount = wallet.get_account_or_error(account_id)
|
||||||
|
collections = account.get_collections
|
||||||
|
collection_count = account.get_collection_count
|
||||||
|
else:
|
||||||
|
collections = partial(self.ledger.get_collections, wallet=wallet, accounts=wallet.accounts)
|
||||||
|
collection_count = partial(self.ledger.get_collection_count, wallet=wallet, accounts=wallet.accounts)
|
||||||
|
return paginate_rows(collections, collection_count, page, page_size, resolve=resolve_claims)
|
||||||
|
|
||||||
|
|
||||||
SUPPORT_DOC = """
|
SUPPORT_DOC = """
|
||||||
Create, list and abandon all types of supports.
|
Create, list and abandon all types of supports.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -9,6 +9,7 @@ async def jsonrpc_collection_create(
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
collection_create (<name> | --name=<name>) (<bid> | --bid=<bid>)
|
collection_create (<name> | --name=<name>) (<bid> | --bid=<bid>)
|
||||||
|
( --claims=<claimIds> )
|
||||||
[--allow_duplicate_name=<allow_duplicate_name>]
|
[--allow_duplicate_name=<allow_duplicate_name>]
|
||||||
[--title=<title>] [--description=<description>]
|
[--title=<title>] [--description=<description>]
|
||||||
[--tags=<tags>...] [--languages=<languages>...] [--locations=<locations>...]
|
[--tags=<tags>...] [--languages=<languages>...] [--locations=<locations>...]
|
||||||
|
@ -20,12 +21,14 @@ async def jsonrpc_collection_create(
|
||||||
Options:
|
Options:
|
||||||
--name=<name> : (str) name of the collection
|
--name=<name> : (str) name of the collection
|
||||||
--bid=<bid> : (decimal) amount to back the claim
|
--bid=<bid> : (decimal) amount to back the claim
|
||||||
--allow_duplicate_name=<allow_duplicate_name> : (bool) create new collection even if one already exists with
|
--allow_duplicate_name=<allow_duplicate_name> : (bool) create new collection even if one already exists with
|
||||||
given name. default: false.
|
given name. default: false.
|
||||||
--claims=<claims> : (list) claim ids
|
--claims=<claimIds> : (list) claim ids
|
||||||
--title=<title> : (str) title of the publication
|
--title=<title> : (str) title of the collection
|
||||||
--description=<description> : (str) description of the publication
|
--description=<description> : (str) description of the collection
|
||||||
|
--clear_languages : (bool) clear existing languages (prior to adding new ones)
|
||||||
--tags=<tags> : (list) content tags
|
--tags=<tags> : (list) content tags
|
||||||
|
--clear_languages : (bool) clear existing languages (prior to adding new ones)
|
||||||
--languages=<languages> : (list) languages used by the collection,
|
--languages=<languages> : (list) languages used by the collection,
|
||||||
using RFC 5646 format, eg:
|
using RFC 5646 format, eg:
|
||||||
for English `--languages=en`
|
for English `--languages=en`
|
||||||
|
@ -65,7 +68,7 @@ async def jsonrpc_collection_create(
|
||||||
... --locations="{'country': 'US', 'state': 'NH'}"
|
... --locations="{'country': 'US', 'state': 'NH'}"
|
||||||
|
|
||||||
--thumbnail_url=<thumbnail_url>: (str) thumbnail url
|
--thumbnail_url=<thumbnail_url>: (str) thumbnail url
|
||||||
# --cover_url=<cover_url> : (str) url of cover image
|
# --cover_url=<cover_url> : (str) url of cover image
|
||||||
--account_id=<account_id> : (str) account to use for holding the transaction
|
--account_id=<account_id> : (str) account to use for holding the transaction
|
||||||
--wallet_id=<wallet_id> : (str) restrict operation to specific wallet
|
--wallet_id=<wallet_id> : (str) restrict operation to specific wallet
|
||||||
--funding_account_ids=<funding_account_ids>: (list) ids of accounts to fund this transaction
|
--funding_account_ids=<funding_account_ids>: (list) ids of accounts to fund this transaction
|
||||||
|
@ -94,7 +97,7 @@ async def jsonrpc_collection_create(
|
||||||
)
|
)
|
||||||
|
|
||||||
claim = Claim()
|
claim = Claim()
|
||||||
claim.collection.update(**kwargs) #maybe specify claims=[]
|
claim.collection.update(**kwargs) #maybe specify claims=[] # here
|
||||||
tx = await Transaction.claim_create(
|
tx = await Transaction.claim_create(
|
||||||
name, claim, amount, claim_address, funding_accounts, funding_accounts[0], channel
|
name, claim, amount, claim_address, funding_accounts, funding_accounts[0], channel
|
||||||
)
|
)
|
||||||
|
@ -138,8 +141,10 @@ async def jsonrpc_collection_update(
|
||||||
Options:
|
Options:
|
||||||
--claim_id=<claim_id> : (str) claim_id of the collection to update
|
--claim_id=<claim_id> : (str) claim_id of the collection to update
|
||||||
--bid=<bid> : (decimal) amount to back the claim
|
--bid=<bid> : (decimal) amount to back the claim
|
||||||
--title=<title> : (str) title of the publication
|
--claims=<claim_ids> : (list) claim ids
|
||||||
--description=<description> : (str) description of the publication
|
--clear_claims : (bool) clear existing claim references (prior to adding new ones)
|
||||||
|
--title=<title> : (str) title of the collection
|
||||||
|
--description=<description> : (str) description of the collection
|
||||||
--tags=<tags> : (list) add content tags
|
--tags=<tags> : (list) add content tags
|
||||||
--clear_tags : (bool) clear existing tags (prior to adding new ones)
|
--clear_tags : (bool) clear existing tags (prior to adding new ones)
|
||||||
--languages=<languages> : (list) languages used by the collection,
|
--languages=<languages> : (list) languages used by the collection,
|
||||||
|
|
|
@ -201,6 +201,8 @@ class JSONResponseEncoder(JSONEncoder):
|
||||||
output['short_url'] = output['meta'].pop('short_url')
|
output['short_url'] = output['meta'].pop('short_url')
|
||||||
if 'canonical_url' in output['meta']:
|
if 'canonical_url' in output['meta']:
|
||||||
output['canonical_url'] = output['meta'].pop('canonical_url')
|
output['canonical_url'] = output['meta'].pop('canonical_url')
|
||||||
|
if txo.claims is not None:
|
||||||
|
output['claims'] = [self.encode_output(o) for o in txo.claims]
|
||||||
if txo.script.is_claim_name or txo.script.is_update_claim:
|
if txo.script.is_claim_name or txo.script.is_update_claim:
|
||||||
try:
|
try:
|
||||||
output['value'] = txo.claim
|
output['value'] = txo.claim
|
||||||
|
|
|
@ -347,10 +347,6 @@ class ClaimList(BaseMessageList[ClaimReference]):
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
item_class = ClaimReference
|
item_class = ClaimReference
|
||||||
|
|
||||||
@property
|
|
||||||
def _message(self):
|
|
||||||
return self.message.claim_references
|
|
||||||
|
|
||||||
def append(self, value):
|
def append(self, value):
|
||||||
self.add().claim_id = value
|
self.add().claim_id = value
|
||||||
|
|
||||||
|
|
|
@ -392,9 +392,10 @@ class Collection(BaseClaim):
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
claim = super().to_dict()
|
claim = super().to_dict()
|
||||||
if 'claim_references' in claim:
|
if 'claim_references' in claim:
|
||||||
claim['claim_references'] = self.claims.ids
|
claim['claims'] = self.claims.ids
|
||||||
|
del claim['claim_references']
|
||||||
return claim
|
return claim
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def claims(self) -> ClaimList:
|
def claims(self) -> ClaimList:
|
||||||
return ClaimList(self.message)
|
return ClaimList(self.message.claim_references)
|
||||||
|
|
|
@ -267,10 +267,25 @@ class CommandTestCase(IntegrationTestCase):
|
||||||
return await self.confirm_and_render(
|
return await self.confirm_and_render(
|
||||||
self.daemon.jsonrpc_channel_abandon(*args, **kwargs), confirm
|
self.daemon.jsonrpc_channel_abandon(*args, **kwargs), confirm
|
||||||
)
|
)
|
||||||
|
# ClaimIDs = ....
|
||||||
|
async def collection_create(
|
||||||
|
self, name='firstcollection', bid='1.0', confirm=True, **kwargs):
|
||||||
|
return await self.confirm_and_render(
|
||||||
|
self.daemon.jsonrpc_collection_create(name, bid, **kwargs), confirm
|
||||||
|
)
|
||||||
|
# ClaimIDs = ....
|
||||||
|
async def collection_update(
|
||||||
|
self, claim_id, confirm=True, **kwargs):
|
||||||
|
return await self.confirm_and_render(
|
||||||
|
self.daemon.jsonrpc_collection_update(claim_id, **kwargs), confirm
|
||||||
|
)
|
||||||
|
|
||||||
# async def collection_create
|
async def collection_abandon(self, *args, confirm=True, **kwargs):
|
||||||
# async def collection_update
|
if 'blocking' not in kwargs:
|
||||||
# async def collection_abandon
|
kwargs['blocking'] = False
|
||||||
|
return await self.confirm_and_render(
|
||||||
|
self.daemon.jsonrpc_stream_abandon(*args, **kwargs), confirm
|
||||||
|
)
|
||||||
|
|
||||||
async def support_create(self, claim_id, bid='1.0', confirm=True, **kwargs):
|
async def support_create(self, claim_id, bid='1.0', confirm=True, **kwargs):
|
||||||
return await self.confirm_and_render(
|
return await self.confirm_and_render(
|
||||||
|
|
|
@ -5,7 +5,7 @@ from hashlib import sha256
|
||||||
from string import hexdigits
|
from string import hexdigits
|
||||||
|
|
||||||
import ecdsa
|
import ecdsa
|
||||||
from lbry.wallet.constants import TXO_TYPES
|
from lbry.wallet.constants import CLAIM_TYPES
|
||||||
|
|
||||||
from torba.client.baseaccount import BaseAccount, HierarchicalDeterministic
|
from torba.client.baseaccount import BaseAccount, HierarchicalDeterministic
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ class Account(BaseAccount):
|
||||||
get_total_balance = partial(self.get_balance, confirmations=confirmations, include_claims=True)
|
get_total_balance = partial(self.get_balance, confirmations=confirmations, include_claims=True)
|
||||||
total = await get_total_balance()
|
total = await get_total_balance()
|
||||||
if reserved_subtotals:
|
if reserved_subtotals:
|
||||||
claims_balance = await get_total_balance(txo_type__in=[TXO_TYPES['stream'], TXO_TYPES['channel']])
|
claims_balance = await get_total_balance(txo_type__in=CLAIM_TYPES)
|
||||||
for amount, spent, from_me, to_me, height in await self.get_support_summary():
|
for amount, spent, from_me, to_me, height in await self.get_support_summary():
|
||||||
if confirmations > 0 and not 0 < height <= self.ledger.headers.height - (confirmations - 1):
|
if confirmations > 0 and not 0 < height <= self.ledger.headers.height - (confirmations - 1):
|
||||||
continue
|
continue
|
||||||
|
@ -163,6 +163,12 @@ class Account(BaseAccount):
|
||||||
def get_channel_count(self, **constraints):
|
def get_channel_count(self, **constraints):
|
||||||
return self.ledger.get_channel_count(wallet=self.wallet, accounts=[self], **constraints)
|
return self.ledger.get_channel_count(wallet=self.wallet, accounts=[self], **constraints)
|
||||||
|
|
||||||
|
def get_collections(self, **constraints):
|
||||||
|
return self.ledger.get_collections(wallet=self.wallet, accounts=[self], **constraints)
|
||||||
|
|
||||||
|
def get_collection_count(self, **constraints):
|
||||||
|
return self.ledger.get_collection_count(wallet=self.wallet, accounts=[self], **constraints)
|
||||||
|
|
||||||
def get_supports(self, **constraints):
|
def get_supports(self, **constraints):
|
||||||
return self.ledger.get_supports(wallet=self.wallet, accounts=[self], **constraints)
|
return self.ledger.get_supports(wallet=self.wallet, accounts=[self], **constraints)
|
||||||
|
|
||||||
|
|
|
@ -5,3 +5,9 @@ TXO_TYPES = {
|
||||||
"purchase": 4,
|
"purchase": 4,
|
||||||
"collection": 5
|
"collection": 5
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CLAIM_TYPES = [
|
||||||
|
TXO_TYPES['stream'],
|
||||||
|
TXO_TYPES['channel'],
|
||||||
|
TXO_TYPES['collection'],
|
||||||
|
]
|
||||||
|
|
|
@ -3,7 +3,7 @@ from typing import List
|
||||||
from torba.client.basedatabase import BaseDatabase
|
from torba.client.basedatabase import BaseDatabase
|
||||||
|
|
||||||
from lbry.wallet.transaction import Output
|
from lbry.wallet.transaction import Output
|
||||||
from lbry.wallet.constants import TXO_TYPES
|
from lbry.wallet.constants import TXO_TYPES, CLAIM_TYPES
|
||||||
|
|
||||||
|
|
||||||
class WalletDatabase(BaseDatabase):
|
class WalletDatabase(BaseDatabase):
|
||||||
|
@ -140,14 +140,7 @@ class WalletDatabase(BaseDatabase):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def constrain_claims(constraints):
|
def constrain_claims(constraints):
|
||||||
constraints['txo_type__in'] = [
|
constraints['txo_type__in'] = CLAIM_TYPES
|
||||||
TXO_TYPES['stream'], TXO_TYPES['channel']
|
|
||||||
]
|
|
||||||
|
|
||||||
def constrain_claims(constraints):
|
|
||||||
constraints['txo_type__in'] = [
|
|
||||||
TXO_TYPES['stream'], TXO_TYPES['channel']
|
|
||||||
]
|
|
||||||
|
|
||||||
async def get_claims(self, **constraints) -> List[Output]:
|
async def get_claims(self, **constraints) -> List[Output]:
|
||||||
self.constrain_claims(constraints)
|
self.constrain_claims(constraints)
|
||||||
|
@ -198,11 +191,11 @@ class WalletDatabase(BaseDatabase):
|
||||||
constraints['txo_type'] = TXO_TYPES['collection']
|
constraints['txo_type'] = TXO_TYPES['collection']
|
||||||
|
|
||||||
def get_collections(self, **constraints):
|
def get_collections(self, **constraints):
|
||||||
self.constrain_supports(constraints)
|
self.constrain_collections(constraints)
|
||||||
return self.get_utxos(**constraints)
|
return self.get_utxos(**constraints)
|
||||||
|
|
||||||
def get_collection_count(self, **constraints):
|
def get_collection_count(self, **constraints):
|
||||||
self.constrain_supports(constraints)
|
self.constrain_collections(constraints)
|
||||||
return self.get_utxo_count(**constraints)
|
return self.get_utxo_count(**constraints)
|
||||||
|
|
||||||
async def release_all_outputs(self, account):
|
async def release_all_outputs(self, account):
|
||||||
|
|
|
@ -186,10 +186,29 @@ class MainNetLedger(BaseLedger):
|
||||||
def get_channel_count(self, **constraints):
|
def get_channel_count(self, **constraints):
|
||||||
return self.db.get_channel_count(**constraints)
|
return self.db.get_channel_count(**constraints)
|
||||||
|
|
||||||
def get_collections(self, **constraints):
|
async def get_collections(self, resolve=False, **constraints):
|
||||||
return self.db.get_collections(**constraints)
|
collections = await self.db.get_collections(**constraints)
|
||||||
|
if resolve:
|
||||||
|
for collection in collections:
|
||||||
|
claim_ids = collection.claim.collection.claims.ids;
|
||||||
|
try:
|
||||||
|
resolve_results, _, _ = await self.claim_search([], claim_ids=collection.claim.collection.claims.ids)
|
||||||
|
except:
|
||||||
|
log.exception("Resolve failed while looking up collection claim ids:")
|
||||||
|
claims = []
|
||||||
|
for claim_id in claim_ids:
|
||||||
|
found = False
|
||||||
|
for txo in resolve_results:
|
||||||
|
if txo.claim_id == claim_id:
|
||||||
|
claims.append(txo)
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
if not found:
|
||||||
|
claims.append(None)
|
||||||
|
collection.claims = claims
|
||||||
|
return collections
|
||||||
|
|
||||||
def get_collection_count(self, **constraints):
|
def get_collection_count(self, resolve=False, **constraints):
|
||||||
return self.db.get_collection_count(**constraints)
|
return self.db.get_collection_count(**constraints)
|
||||||
|
|
||||||
def get_supports(self, **constraints):
|
def get_supports(self, **constraints):
|
||||||
|
|
|
@ -32,6 +32,7 @@ class Output(BaseOutput):
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
'channel', 'private_key', 'meta',
|
'channel', 'private_key', 'meta',
|
||||||
'purchase', 'purchased_claim', 'purchase_receipt',
|
'purchase', 'purchased_claim', 'purchase_receipt',
|
||||||
|
'claims',
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, *args, channel: Optional['Output'] = None,
|
def __init__(self, *args, channel: Optional['Output'] = None,
|
||||||
|
@ -42,6 +43,7 @@ class Output(BaseOutput):
|
||||||
self.purchase: 'Output' = None # txo containing purchase metadata
|
self.purchase: 'Output' = None # txo containing purchase metadata
|
||||||
self.purchased_claim: 'Output' = None # resolved claim pointed to by purchase
|
self.purchased_claim: 'Output' = None # resolved claim pointed to by purchase
|
||||||
self.purchase_receipt: 'Output' = None # txo representing purchase receipt for this claim
|
self.purchase_receipt: 'Output' = None # txo representing purchase receipt for this claim
|
||||||
|
self.claims: List['Output'] = None # resolved claims for collection
|
||||||
self.meta = {}
|
self.meta = {}
|
||||||
|
|
||||||
def update_annotations(self, annotated):
|
def update_annotations(self, annotated):
|
||||||
|
|
|
@ -1257,3 +1257,55 @@ class SupportCommands(CommandTestCase):
|
||||||
self.assertFalse(txs2[0]['support_info'][0]['is_tip'])
|
self.assertFalse(txs2[0]['support_info'][0]['is_tip'])
|
||||||
self.assertEqual(txs2[0]['value'], '0.0')
|
self.assertEqual(txs2[0]['value'], '0.0')
|
||||||
self.assertEqual(txs2[0]['fee'], '-0.0001415')
|
self.assertEqual(txs2[0]['fee'], '-0.0001415')
|
||||||
|
|
||||||
|
|
||||||
|
class CollectionCommands(CommandTestCase):
|
||||||
|
|
||||||
|
async def test_collections(self):
|
||||||
|
claim_ids = [
|
||||||
|
self.get_claim_id(tx) for tx in [
|
||||||
|
await self.stream_create('stream-one'),
|
||||||
|
await self.stream_create('stream-two')
|
||||||
|
]
|
||||||
|
]
|
||||||
|
claim_ids.append(claim_ids[0])
|
||||||
|
claim_ids.append('beef')
|
||||||
|
tx = await self.collection_create('radjingles', claims=claim_ids, title="boring title")
|
||||||
|
claim_id = self.get_claim_id(tx)
|
||||||
|
collections = await self.out(self.daemon.jsonrpc_collection_list())
|
||||||
|
self.assertEqual(collections['items'][0]['value']['title'], 'boring title')
|
||||||
|
self.assertEqual(collections['items'][0]['value']['claims'], claim_ids)
|
||||||
|
|
||||||
|
self.assertItemCount(collections, 1)
|
||||||
|
await self.assertBalance(self.account, '6.939679')
|
||||||
|
|
||||||
|
with self.assertRaisesRegex(Exception, "You already have a collection under the name 'radjingles'."):
|
||||||
|
await self.collection_create('radjingles', claims=claim_ids)
|
||||||
|
|
||||||
|
self.assertItemCount(await self.daemon.jsonrpc_collection_list(), 1)
|
||||||
|
await self.assertBalance(self.account, '6.939679')
|
||||||
|
|
||||||
|
collections = await self.out(self.daemon.jsonrpc_collection_list())
|
||||||
|
self.assertEqual(collections['items'][0]['value']['title'], 'boring title')
|
||||||
|
await self.collection_update(claim_id, title='fancy title')
|
||||||
|
collections = await self.out(self.daemon.jsonrpc_collection_list())
|
||||||
|
self.assertEqual(collections['items'][0]['value']['title'], 'fancy title')
|
||||||
|
self.assertEqual(collections['items'][0]['value']['claims'], claim_ids)
|
||||||
|
self.assertNotIn('claims', collections['items'][0])
|
||||||
|
|
||||||
|
await self.collection_create('radjingles', claims=claim_ids, allow_duplicate_name=True)
|
||||||
|
self.assertItemCount(await self.daemon.jsonrpc_collection_list(), 2)
|
||||||
|
|
||||||
|
await self.collection_abandon(claim_id)
|
||||||
|
self.assertItemCount(await self.daemon.jsonrpc_collection_list(), 1)
|
||||||
|
|
||||||
|
collections = await self.out(self.daemon.jsonrpc_collection_list(resolve_claims=True))
|
||||||
|
self.assertEqual(collections['items'][0]['claims'][0]['name'], 'stream-one')
|
||||||
|
self.assertEqual(collections['items'][0]['claims'][1]['name'], 'stream-two')
|
||||||
|
self.assertEqual(collections['items'][0]['claims'][2]['name'], 'stream-one')
|
||||||
|
self.assertIsNone(collections['items'][0]['claims'][3])
|
||||||
|
|
||||||
|
claims = await self.out(self.daemon.jsonrpc_claim_list())
|
||||||
|
self.assertEqual(claims['items'][0]['name'], 'radjingles')
|
||||||
|
self.assertEqual(claims['items'][1]['name'], 'stream-two')
|
||||||
|
self.assertEqual(claims['items'][2]['name'], 'stream-one')
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
from lbry.schema.claim import Claim, Stream
|
from lbry.schema.claim import Claim, Stream, Collection
|
||||||
|
|
||||||
|
|
||||||
class TestClaimContainerAwareness(TestCase):
|
class TestClaimContainerAwareness(TestCase):
|
||||||
|
@ -118,6 +118,26 @@ class TestTags(TestCase):
|
||||||
self.assertEqual(claim.channel.tags, ['anime'])
|
self.assertEqual(claim.channel.tags, ['anime'])
|
||||||
|
|
||||||
|
|
||||||
|
class TestCollection(TestCase):
|
||||||
|
|
||||||
|
def test_collection(self):
|
||||||
|
collection = Collection()
|
||||||
|
|
||||||
|
collection.update(claims=['abc123', 'def123'])
|
||||||
|
self.assertListEqual(collection.claims.ids, ['abc123', 'def123'])
|
||||||
|
|
||||||
|
collection.update(claims=['abc123', 'bbb123'])
|
||||||
|
self.assertListEqual(collection.claims.ids, ['abc123', 'def123', 'abc123', 'bbb123'])
|
||||||
|
|
||||||
|
collection.update(clear_claims=True, claims=['bbb987', 'bb'])
|
||||||
|
self.assertListEqual(collection.claims.ids, ['bbb987', 'bb'])
|
||||||
|
|
||||||
|
self.assertEqual(collection.to_dict(), {'claims': ['bbb987', 'bb']})
|
||||||
|
|
||||||
|
collection.update(clear_claims=True)
|
||||||
|
self.assertListEqual(collection.claims.ids, [])
|
||||||
|
|
||||||
|
|
||||||
class TestLocations(TestCase):
|
class TestLocations(TestCase):
|
||||||
|
|
||||||
def test_location_successful_parsing(self):
|
def test_location_successful_parsing(self):
|
||||||
|
|
Loading…
Reference in a new issue