most commands now work across all accounts

This commit is contained in:
Lex Berezhny 2019-08-12 00:40:05 -04:00
parent e2d618f472
commit 98d4d00f96
10 changed files with 426 additions and 223 deletions

View file

@ -1703,12 +1703,14 @@ class Daemon(metaclass=JSONRPCServerType):
Returns: {Paginated[Output]} Returns: {Paginated[Output]}
""" """
account = self.get_account_or_default(account_id) if account_id:
return maybe_paginate( account = self.get_account_or_error(account_id)
account.get_claims, claims = account.get_claims
account.get_claim_count, claim_count = account.get_claim_count
page, page_size else:
) claims = self.ledger.get_claims
claim_count = self.ledger.get_claim_count
return maybe_paginate(claims, claim_count, page, page_size)
@requires(WALLET_COMPONENT) @requires(WALLET_COMPONENT)
async def jsonrpc_claim_search(self, **kwargs): async def jsonrpc_claim_search(self, **kwargs):
@ -1859,7 +1861,7 @@ class Daemon(metaclass=JSONRPCServerType):
@requires(WALLET_COMPONENT, conditions=[WALLET_IS_UNLOCKED]) @requires(WALLET_COMPONENT, conditions=[WALLET_IS_UNLOCKED])
async def jsonrpc_channel_create( async def jsonrpc_channel_create(
self, name, bid, allow_duplicate_name=False, account_id=None, claim_address=None, self, name, bid, allow_duplicate_name=False, account_id=None, claim_address=None,
preview=False, blocking=False, **kwargs): funding_account_ids=None, preview=False, blocking=False, **kwargs):
""" """
Create a new channel by generating a channel private key and establishing an '@' prefixed claim. Create a new channel by generating a channel private key and establishing an '@' prefixed claim.
@ -1871,6 +1873,7 @@ class Daemon(metaclass=JSONRPCServerType):
[--tags=<tags>...] [--languages=<languages>...] [--locations=<locations>...] [--tags=<tags>...] [--languages=<languages>...] [--locations=<locations>...]
[--thumbnail_url=<thumbnail_url>] [--cover_url=<cover_url>] [--thumbnail_url=<thumbnail_url>] [--cover_url=<cover_url>]
[--account_id=<account_id>] [--claim_address=<claim_address>] [--account_id=<account_id>] [--claim_address=<claim_address>]
[--funding_account_ids=<funding_account_ids>...]
[--preview] [--blocking] [--preview] [--blocking]
Options: Options:
@ -1924,7 +1927,8 @@ class Daemon(metaclass=JSONRPCServerType):
--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) id of the account to store channel --account_id=<account_id> : (str) account to use for holding the transaction
--funding_account_ids=<funding_account_ids>: (list) ids of accounts to fund this transaction
--claim_address=<claim_address>: (str) address where the channel is sent to, if not specified --claim_address=<claim_address>: (str) address where the channel is sent to, if not specified
it will be determined automatically from the account it will be determined automatically from the account
--preview : (bool) do not broadcast the transaction --preview : (bool) do not broadcast the transaction
@ -1933,6 +1937,7 @@ class Daemon(metaclass=JSONRPCServerType):
Returns: {Transaction} Returns: {Transaction}
""" """
account = self.get_account_or_default(account_id) account = self.get_account_or_default(account_id)
funding_accounts = self.get_accounts_or_all(funding_account_ids)
self.valid_channel_name_or_error(name) self.valid_channel_name_or_error(name)
amount = self.get_dewies_or_error('bid', bid, positive_value=True) amount = self.get_dewies_or_error('bid', bid, positive_value=True)
claim_address = await self.get_receiving_address(claim_address, account) claim_address = await self.get_receiving_address(claim_address, account)
@ -1948,7 +1953,7 @@ class Daemon(metaclass=JSONRPCServerType):
claim = Claim() claim = Claim()
claim.channel.update(**kwargs) claim.channel.update(**kwargs)
tx = await Transaction.claim_create( tx = await Transaction.claim_create(
name, claim, amount, claim_address, [account], account name, claim, amount, claim_address, funding_accounts, funding_accounts[0]
) )
txo = tx.outputs[0] txo = tx.outputs[0]
txo.generate_channel_private_key() txo.generate_channel_private_key()
@ -1970,7 +1975,8 @@ class Daemon(metaclass=JSONRPCServerType):
@requires(WALLET_COMPONENT, conditions=[WALLET_IS_UNLOCKED]) @requires(WALLET_COMPONENT, conditions=[WALLET_IS_UNLOCKED])
async def jsonrpc_channel_update( async def jsonrpc_channel_update(
self, claim_id, bid=None, account_id=None, claim_address=None, self, claim_id, bid=None, account_id=None, claim_address=None,
new_signing_key=False, preview=False, blocking=False, replace=False, **kwargs): funding_account_ids=None, new_signing_key=False, preview=False,
blocking=False, replace=False, **kwargs):
""" """
Update an existing channel claim. Update an existing channel claim.
@ -1984,6 +1990,7 @@ class Daemon(metaclass=JSONRPCServerType):
[--locations=<locations>...] [--clear_locations] [--locations=<locations>...] [--clear_locations]
[--thumbnail_url=<thumbnail_url>] [--cover_url=<cover_url>] [--thumbnail_url=<thumbnail_url>] [--cover_url=<cover_url>]
[--account_id=<account_id>] [--claim_address=<claim_address>] [--new_signing_key] [--account_id=<account_id>] [--claim_address=<claim_address>] [--new_signing_key]
[--funding_account_ids=<funding_account_ids>...]
[--preview] [--blocking] [--replace] [--preview] [--blocking] [--replace]
Options: Options:
@ -2039,7 +2046,8 @@ class Daemon(metaclass=JSONRPCServerType):
--clear_locations : (bool) clear existing locations (prior to adding new ones) --clear_locations : (bool) clear existing locations (prior to adding new ones)
--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) id of the account to store channel --account_id=<account_id> : (str) account to use for holding the transaction
--funding_account_ids=<funding_account_ids>: (list) ids of accounts to fund this transaction
--claim_address=<claim_address>: (str) address where the channel is sent --claim_address=<claim_address>: (str) address where the channel is sent
--new_signing_key : (bool) generate a new signing key, will invalidate all previous publishes --new_signing_key : (bool) generate a new signing key, will invalidate all previous publishes
--preview : (bool) do not broadcast the transaction --preview : (bool) do not broadcast the transaction
@ -2052,6 +2060,7 @@ class Daemon(metaclass=JSONRPCServerType):
Returns: {Transaction} Returns: {Transaction}
""" """
account = self.get_account_or_default(account_id) account = self.get_account_or_default(account_id)
funding_accounts = self.get_accounts_or_all(funding_account_ids)
existing_channels = await account.get_claims(claim_id=claim_id) existing_channels = await account.get_claims(claim_id=claim_id)
if len(existing_channels) != 1: if len(existing_channels) != 1:
@ -2081,7 +2090,7 @@ class Daemon(metaclass=JSONRPCServerType):
claim = Claim.from_bytes(old_txo.claim.to_bytes()) claim = Claim.from_bytes(old_txo.claim.to_bytes())
claim.channel.update(**kwargs) claim.channel.update(**kwargs)
tx = await Transaction.claim_update( tx = await Transaction.claim_update(
old_txo, claim, amount, claim_address, [account], account old_txo, claim, amount, claim_address, funding_accounts, funding_accounts[0]
) )
new_txo = tx.outputs[0] new_txo = tx.outputs[0]
@ -2169,12 +2178,14 @@ class Daemon(metaclass=JSONRPCServerType):
Returns: {Paginated[Output]} Returns: {Paginated[Output]}
""" """
account = self.get_account_or_default(account_id) if account_id:
return maybe_paginate( account = self.get_account_or_error(account_id)
account.get_channels, channels = account.get_channels
account.get_channel_count, channel_count = account.get_channel_count
page, page_size else:
) channels = self.ledger.get_channels
channel_count = self.ledger.get_channel_count
return maybe_paginate(channels, channel_count, page, page_size)
@requires(WALLET_COMPONENT) @requires(WALLET_COMPONENT)
async def jsonrpc_channel_export(self, channel_id=None, channel_name=None, account_id=None): async def jsonrpc_channel_export(self, channel_id=None, channel_name=None, account_id=None):
@ -2261,6 +2272,7 @@ class Daemon(metaclass=JSONRPCServerType):
[--channel_id=<channel_id> | --channel_name=<channel_name>] [--channel_id=<channel_id> | --channel_name=<channel_name>]
[--channel_account_id=<channel_account_id>...] [--channel_account_id=<channel_account_id>...]
[--account_id=<account_id>] [--claim_address=<claim_address>] [--account_id=<account_id>] [--claim_address=<claim_address>]
[--funding_account_ids=<funding_account_ids>...]
[--preview] [--blocking] [--preview] [--blocking]
Options: Options:
@ -2328,7 +2340,8 @@ class Daemon(metaclass=JSONRPCServerType):
--channel_name=<channel_name> : (str) name of publisher channel --channel_name=<channel_name> : (str) name of publisher channel
--channel_account_id=<channel_account_id>: (str) one or more account ids for accounts to look in --channel_account_id=<channel_account_id>: (str) one or more account ids for accounts to look in
for channel certificates, defaults to all accounts. for channel certificates, defaults to all accounts.
--account_id=<account_id> : (str) account to use for funding the transaction --account_id=<account_id> : (str) account to use for holding the transaction
--funding_account_ids=<funding_account_ids>: (list) ids of accounts to fund this transaction
--claim_address=<claim_address>: (str) address where the claim is sent to, if not specified --claim_address=<claim_address>: (str) address where the claim is sent to, if not specified
it will be determined automatically from the account it will be determined automatically from the account
--preview : (bool) do not broadcast the transaction --preview : (bool) do not broadcast the transaction
@ -2359,8 +2372,8 @@ class Daemon(metaclass=JSONRPCServerType):
async def jsonrpc_stream_create( async def jsonrpc_stream_create(
self, name, bid, file_path, allow_duplicate_name=False, self, name, bid, file_path, allow_duplicate_name=False,
channel_id=None, channel_name=None, channel_account_id=None, channel_id=None, channel_name=None, channel_account_id=None,
account_id=None, claim_address=None, preview=False, blocking=False, account_id=None, claim_address=None, funding_account_ids=None,
**kwargs): preview=False, blocking=False, **kwargs):
""" """
Make a new stream claim and announce the associated file to lbrynet. Make a new stream claim and announce the associated file to lbrynet.
@ -2375,6 +2388,7 @@ class Daemon(metaclass=JSONRPCServerType):
[--channel_id=<channel_id> | --channel_name=<channel_name>] [--channel_id=<channel_id> | --channel_name=<channel_name>]
[--channel_account_id=<channel_account_id>...] [--channel_account_id=<channel_account_id>...]
[--account_id=<account_id>] [--claim_address=<claim_address>] [--account_id=<account_id>] [--claim_address=<claim_address>]
[--funding_account_ids=<funding_account_ids>...]
[--preview] [--blocking] [--preview] [--blocking]
Options: Options:
@ -2444,7 +2458,8 @@ class Daemon(metaclass=JSONRPCServerType):
--channel_name=<channel_name> : (str) name of the publisher channel --channel_name=<channel_name> : (str) name of the publisher channel
--channel_account_id=<channel_account_id>: (str) one or more account ids for accounts to look in --channel_account_id=<channel_account_id>: (str) one or more account ids for accounts to look in
for channel certificates, defaults to all accounts. for channel certificates, defaults to all accounts.
--account_id=<account_id> : (str) account to use for funding the transaction --account_id=<account_id> : (str) account to use for holding the transaction
--funding_account_ids=<funding_account_ids>: (list) ids of accounts to fund this transaction
--claim_address=<claim_address>: (str) address where the claim is sent to, if not specified --claim_address=<claim_address>: (str) address where the claim is sent to, if not specified
it will be determined automatically from the account it will be determined automatically from the account
--preview : (bool) do not broadcast the transaction --preview : (bool) do not broadcast the transaction
@ -2454,6 +2469,7 @@ class Daemon(metaclass=JSONRPCServerType):
""" """
self.valid_stream_name_or_error(name) self.valid_stream_name_or_error(name)
account = self.get_account_or_default(account_id) account = self.get_account_or_default(account_id)
funding_accounts = self.get_accounts_or_all(funding_account_ids)
channel = await self.get_channel_or_none(channel_account_id, channel_id, channel_name, for_signing=True) channel = await self.get_channel_or_none(channel_account_id, channel_id, channel_name, for_signing=True)
amount = self.get_dewies_or_error('bid', bid, positive_value=True) amount = self.get_dewies_or_error('bid', bid, positive_value=True)
claim_address = await self.get_receiving_address(claim_address, account) claim_address = await self.get_receiving_address(claim_address, account)
@ -2470,7 +2486,7 @@ class Daemon(metaclass=JSONRPCServerType):
claim = Claim() claim = Claim()
claim.stream.update(file_path=file_path, sd_hash='0'*96, **kwargs) claim.stream.update(file_path=file_path, sd_hash='0'*96, **kwargs)
tx = await Transaction.claim_create( tx = await Transaction.claim_create(
name, claim, amount, claim_address, [account], account, channel name, claim, amount, claim_address, funding_accounts, funding_accounts[0], channel
) )
new_txo = tx.outputs[0] new_txo = tx.outputs[0]
@ -2501,7 +2517,7 @@ class Daemon(metaclass=JSONRPCServerType):
async def jsonrpc_stream_update( async def jsonrpc_stream_update(
self, claim_id, bid=None, file_path=None, self, claim_id, bid=None, file_path=None,
channel_id=None, channel_name=None, channel_account_id=None, clear_channel=False, channel_id=None, channel_name=None, channel_account_id=None, clear_channel=False,
account_id=None, claim_address=None, account_id=None, claim_address=None, funding_account_ids=None,
preview=False, blocking=False, replace=False, **kwargs): preview=False, blocking=False, replace=False, **kwargs):
""" """
Update an existing stream claim and if a new file is provided announce it to lbrynet. Update an existing stream claim and if a new file is provided announce it to lbrynet.
@ -2520,6 +2536,7 @@ class Daemon(metaclass=JSONRPCServerType):
[--channel_id=<channel_id> | --channel_name=<channel_name> | --clear_channel] [--channel_id=<channel_id> | --channel_name=<channel_name> | --clear_channel]
[--channel_account_id=<channel_account_id>...] [--channel_account_id=<channel_account_id>...]
[--account_id=<account_id>] [--claim_address=<claim_address>] [--account_id=<account_id>] [--claim_address=<claim_address>]
[--funding_account_ids=<funding_account_ids>...]
[--preview] [--blocking] [--replace] [--preview] [--blocking] [--replace]
Options: Options:
@ -2595,7 +2612,8 @@ class Daemon(metaclass=JSONRPCServerType):
--clear_channel : (bool) remove channel signature --clear_channel : (bool) remove channel signature
--channel_account_id=<channel_account_id>: (str) one or more account ids for accounts to look in --channel_account_id=<channel_account_id>: (str) one or more account ids for accounts to look in
for channel certificates, defaults to all accounts. for channel certificates, defaults to all accounts.
--account_id=<account_id> : (str) account to use for funding the transaction --account_id=<account_id> : (str) account to use for holding the transaction
--funding_account_ids=<funding_account_ids>: (list) ids of accounts to fund this transaction
--claim_address=<claim_address>: (str) address where the claim is sent to, if not specified --claim_address=<claim_address>: (str) address where the claim is sent to, if not specified
it will be determined automatically from the account it will be determined automatically from the account
--preview : (bool) do not broadcast the transaction --preview : (bool) do not broadcast the transaction
@ -2608,6 +2626,7 @@ class Daemon(metaclass=JSONRPCServerType):
Returns: {Transaction} Returns: {Transaction}
""" """
account = self.get_account_or_default(account_id) account = self.get_account_or_default(account_id)
funding_accounts = self.get_accounts_or_all(funding_account_ids)
existing_claims = await account.get_claims(claim_id=claim_id) existing_claims = await account.get_claims(claim_id=claim_id)
if len(existing_claims) != 1: if len(existing_claims) != 1:
@ -2655,7 +2674,7 @@ class Daemon(metaclass=JSONRPCServerType):
claim = Claim.from_bytes(old_txo.claim.to_bytes()) claim = Claim.from_bytes(old_txo.claim.to_bytes())
claim.stream.update(file_path=file_path, **kwargs) claim.stream.update(file_path=file_path, **kwargs)
tx = await Transaction.claim_update( tx = await Transaction.claim_update(
old_txo, claim, amount, claim_address, [account], account, channel old_txo, claim, amount, claim_address, funding_accounts, funding_accounts[0], channel
) )
new_txo = tx.outputs[0] new_txo = tx.outputs[0]
@ -2753,12 +2772,14 @@ class Daemon(metaclass=JSONRPCServerType):
Returns: {Paginated[Output]} Returns: {Paginated[Output]}
""" """
account = self.get_account_or_default(account_id) if account_id:
return maybe_paginate( account = self.get_account_or_error(account_id)
account.get_streams, streams = account.get_streams
account.get_stream_count, stream_count = account.get_stream_count
page, page_size else:
) streams = self.ledger.get_streams
stream_count = self.ledger.get_stream_count
return maybe_paginate(streams, stream_count, page, page_size)
@requires(WALLET_COMPONENT, EXCHANGE_RATE_MANAGER_COMPONENT, BLOB_COMPONENT, @requires(WALLET_COMPONENT, EXCHANGE_RATE_MANAGER_COMPONENT, BLOB_COMPONENT,
DHT_COMPONENT, DATABASE_COMPONENT, DHT_COMPONENT, DATABASE_COMPONENT,
@ -2785,7 +2806,7 @@ class Daemon(metaclass=JSONRPCServerType):
@requires(WALLET_COMPONENT, conditions=[WALLET_IS_UNLOCKED]) @requires(WALLET_COMPONENT, conditions=[WALLET_IS_UNLOCKED])
async def jsonrpc_support_create( async def jsonrpc_support_create(
self, claim_id, amount, tip=False, account_id=None, self, claim_id, amount, tip=False, account_id=None, funding_account_ids=None,
preview=False, blocking=False): preview=False, blocking=False):
""" """
Create a support or a tip for name claim. Create a support or a tip for name claim.
@ -2793,18 +2814,21 @@ class Daemon(metaclass=JSONRPCServerType):
Usage: Usage:
support_create (<claim_id> | --claim_id=<claim_id>) (<amount> | --amount=<amount>) support_create (<claim_id> | --claim_id=<claim_id>) (<amount> | --amount=<amount>)
[--tip] [--account_id=<account_id>] [--preview] [--blocking] [--tip] [--account_id=<account_id>] [--preview] [--blocking]
[--funding_account_ids=<funding_account_ids>...]
Options: Options:
--claim_id=<claim_id> : (str) claim_id of the claim to support --claim_id=<claim_id> : (str) claim_id of the claim to support
--amount=<amount> : (decimal) amount of support --amount=<amount> : (decimal) amount of support
--tip : (bool) send support to claim owner, default: false. --tip : (bool) send support to claim owner, default: false.
--account_id=<account_id> : (str) id of the account to use --account_id=<account_id> : (str) account to use for holding the transaction
--funding_account_ids=<funding_account_ids>: (list) ids of accounts to fund this transaction
--preview : (bool) do not broadcast the transaction --preview : (bool) do not broadcast the transaction
--blocking : (bool) wait until transaction is in mempool --blocking : (bool) wait until transaction is in mempool
Returns: {Transaction} Returns: {Transaction}
""" """
account = self.get_account_or_default(account_id) account = self.get_account_or_default(account_id)
funding_accounts = self.get_accounts_or_all(funding_account_ids)
amount = self.get_dewies_or_error("amount", amount) amount = self.get_dewies_or_error("amount", amount)
claim = await self.ledger.get_claim_by_claim_id(claim_id) claim = await self.ledger.get_claim_by_claim_id(claim_id)
claim_address = claim.get_address(self.ledger) claim_address = claim.get_address(self.ledger)
@ -2812,7 +2836,7 @@ class Daemon(metaclass=JSONRPCServerType):
claim_address = await account.receiving.get_or_create_usable_address() claim_address = await account.receiving.get_or_create_usable_address()
tx = await Transaction.support( tx = await Transaction.support(
claim.claim_name, claim_id, amount, claim_address, [account], account claim.claim_name, claim_id, amount, claim_address, funding_accounts, funding_accounts[0]
) )
if not preview: if not preview:
@ -2847,12 +2871,14 @@ class Daemon(metaclass=JSONRPCServerType):
Returns: {Paginated[Output]} Returns: {Paginated[Output]}
""" """
account = self.get_account_or_default(account_id) if account_id:
return maybe_paginate( account = self.get_account_or_error(account_id)
account.get_supports, supports = account.get_supports
account.get_support_count, support_count = account.get_support_count
page, page_size else:
) supports = self.ledger.get_supports
support_count = self.ledger.get_support_count
return maybe_paginate(supports, support_count, page, page_size)
@requires(WALLET_COMPONENT, conditions=[WALLET_IS_UNLOCKED]) @requires(WALLET_COMPONENT, conditions=[WALLET_IS_UNLOCKED])
async def jsonrpc_support_abandon( async def jsonrpc_support_abandon(
@ -2978,12 +3004,14 @@ class Daemon(metaclass=JSONRPCServerType):
} }
""" """
account = self.get_account_or_default(account_id) if account_id:
return maybe_paginate( account = self.get_account_or_error(account_id)
self.wallet_manager.get_history, transactions = account.get_transaction_history
self.ledger.db.get_transaction_count, transaction_count = account.get_transaction_history_count
page, page_size, account=account else:
) transactions = self.ledger.get_transaction_history
transaction_count = self.ledger.get_transaction_history_count
return maybe_paginate(transactions, transaction_count, page, page_size)
@requires(WALLET_COMPONENT) @requires(WALLET_COMPONENT)
def jsonrpc_transaction_show(self, txid): def jsonrpc_transaction_show(self, txid):
@ -3020,12 +3048,14 @@ class Daemon(metaclass=JSONRPCServerType):
Returns: {Paginated[Output]} Returns: {Paginated[Output]}
""" """
account = self.get_account_or_default(account_id) if account_id:
return maybe_paginate( account = self.get_account_or_error(account_id)
account.get_utxos, utxos = account.get_utxos
account.get_utxo_count, utxo_count = account.get_utxo_count
page, page_size else:
) utxos = self.ledger.get_utxos
utxo_count = self.ledger.get_utxo_count
return maybe_paginate(utxos, utxo_count, page, page_size)
@requires(WALLET_COMPONENT) @requires(WALLET_COMPONENT)
def jsonrpc_utxo_release(self, account_id=None): def jsonrpc_utxo_release(self, account_id=None):

View file

@ -131,41 +131,35 @@ class Account(BaseAccount):
details['certificates'] = len(self.channel_keys) details['certificates'] = len(self.channel_keys)
return details return details
@staticmethod def get_transaction_history(self, **constraints):
def constraint_spending_utxos(constraints): return self.ledger.get_transaction_history(account=self, **constraints)
constraints.update({'is_claim': 0, 'is_update': 0, 'is_support': 0})
def get_utxos(self, **constraints): def get_transaction_history_count(self, **constraints):
self.constraint_spending_utxos(constraints) return self.ledger.get_transaction_history_count(account=self, **constraints)
return super().get_utxos(**constraints)
def get_utxo_count(self, **constraints):
self.constraint_spending_utxos(constraints)
return super().get_utxo_count(**constraints)
def get_claims(self, **constraints): def get_claims(self, **constraints):
return self.ledger.db.get_claims(account=self, **constraints) return self.ledger.get_claims(account=self, **constraints)
def get_claim_count(self, **constraints): def get_claim_count(self, **constraints):
return self.ledger.db.get_claim_count(account=self, **constraints) return self.ledger.get_claim_count(account=self, **constraints)
def get_streams(self, **constraints): def get_streams(self, **constraints):
return self.ledger.db.get_streams(account=self, **constraints) return self.ledger.get_streams(account=self, **constraints)
def get_stream_count(self, **constraints): def get_stream_count(self, **constraints):
return self.ledger.db.get_stream_count(account=self, **constraints) return self.ledger.get_stream_count(account=self, **constraints)
def get_channels(self, **constraints): def get_channels(self, **constraints):
return self.ledger.db.get_channels(account=self, **constraints) return self.ledger.get_channels(account=self, **constraints)
def get_channel_count(self, **constraints): def get_channel_count(self, **constraints):
return self.ledger.db.get_channel_count(account=self, **constraints) return self.ledger.get_channel_count(account=self, **constraints)
def get_supports(self, **constraints): def get_supports(self, **constraints):
return self.ledger.db.get_supports(account=self, **constraints) return self.ledger.get_supports(account=self, **constraints)
def get_support_count(self, **constraints): def get_support_count(self, **constraints):
return self.ledger.db.get_support_count(account=self, **constraints) return self.ledger.get_support_count(account=self, **constraints)
def get_support_summary(self): def get_support_summary(self):
return self.ledger.db.get_supports_summary(account_id=self.id) return self.ledger.db.get_supports_summary(account_id=self.id)

View file

@ -53,7 +53,7 @@ class WalletDatabase(BaseDatabase):
return row return row
async def get_txos(self, **constraints) -> List[Output]: async def get_txos(self, **constraints) -> List[Output]:
my_account = constraints.get('my_account', constraints.get('account', None)) my_accounts = constraints.get('my_accounts', constraints.get('accounts', []))
txos = await super().get_txos(**constraints) txos = await super().get_txos(**constraints)
@ -62,16 +62,20 @@ class WalletDatabase(BaseDatabase):
if txo.is_claim and txo.can_decode_claim: if txo.is_claim and txo.can_decode_claim:
if txo.claim.is_signed: if txo.claim.is_signed:
channel_ids.add(txo.claim.signing_channel_id) channel_ids.add(txo.claim.signing_channel_id)
if txo.claim.is_channel and my_account is not None: if txo.claim.is_channel and my_accounts:
txo.private_key = my_account.get_channel_private_key( for account in my_accounts:
private_key = account.get_channel_private_key(
txo.claim.channel.public_key_bytes txo.claim.channel.public_key_bytes
) )
if private_key:
txo.private_key = private_key
break
if channel_ids: if channel_ids:
channels = { channels = {
txo.claim_id: txo for txo in txo.claim_id: txo for txo in
(await self.get_claims( (await self.get_claims(
my_account=my_account, my_accounts=my_accounts,
claim_id__in=channel_ids claim_id__in=channel_ids
)) ))
} }

View file

@ -1,7 +1,8 @@
import asyncio import asyncio
import logging import logging
from binascii import unhexlify from binascii import unhexlify
from typing import Tuple, List, Dict from typing import Tuple, List
from datetime import datetime
from torba.client.baseledger import BaseLedger from torba.client.baseledger import BaseLedger
from torba.client.baseaccount import SingleKey from torba.client.baseaccount import SingleKey
@ -74,10 +75,10 @@ class MainNetLedger(BaseLedger):
result[url] = {'error': f'{url} did not resolve to a claim'} result[url] = {'error': f'{url} did not resolve to a claim'}
return result return result
async def claim_search(self, **kwargs) -> Tuple[List, int, int]: async def claim_search(self, **kwargs) -> Tuple[List[Output], int, int]:
return await self._inflate_outputs(self.network.claim_search(**kwargs)) return await self._inflate_outputs(self.network.claim_search(**kwargs))
async def get_claim_by_claim_id(self, claim_id) -> Dict[str, Output]: async def get_claim_by_claim_id(self, claim_id) -> Output:
for claim in (await self.claim_search(claim_id=claim_id))[0]: for claim in (await self.claim_search(claim_id=claim_id))[0]:
return claim return claim
@ -109,6 +110,157 @@ class MainNetLedger(BaseLedger):
'Failed to display wallet state, please file issue ' 'Failed to display wallet state, please file issue '
'for this bug along with the traceback you see below:') 'for this bug along with the traceback you see below:')
def constraint_account_or_all(self, constraints):
account = constraints.pop('account', None)
if account:
constraints['accounts'] = [account]
else:
constraints['accounts'] = self.accounts
def constraint_spending_utxos(self, constraints):
self.constraint_account_or_all(constraints)
constraints.update({'is_claim': 0, 'is_update': 0, 'is_support': 0})
def get_utxos(self, **constraints):
self.constraint_spending_utxos(constraints)
return super().get_utxos(**constraints)
def get_utxo_count(self, **constraints):
self.constraint_spending_utxos(constraints)
return super().get_utxo_count(**constraints)
def get_claims(self, **constraints):
self.constraint_account_or_all(constraints)
return self.db.get_claims(**constraints)
def get_claim_count(self, **constraints):
self.constraint_account_or_all(constraints)
return self.db.get_claim_count(**constraints)
def get_streams(self, **constraints):
self.constraint_account_or_all(constraints)
return self.db.get_streams(**constraints)
def get_stream_count(self, **constraints):
self.constraint_account_or_all(constraints)
return self.db.get_stream_count(**constraints)
def get_channels(self, **constraints):
self.constraint_account_or_all(constraints)
return self.db.get_channels(**constraints)
def get_channel_count(self, **constraints):
self.constraint_account_or_all(constraints)
return self.db.get_channel_count(**constraints)
def get_supports(self, **constraints):
self.constraint_account_or_all(constraints)
return self.db.get_supports(**constraints)
def get_support_count(self, **constraints):
self.constraint_account_or_all(constraints)
return self.db.get_support_count(**constraints)
async def get_transaction_history(self, **constraints):
self.constraint_account_or_all(constraints)
txs = await self.db.get_transactions(**constraints)
headers = self.headers
history = []
for tx in txs:
ts = headers[tx.height]['timestamp'] if tx.height > 0 else None
item = {
'txid': tx.id,
'timestamp': ts,
'date': datetime.fromtimestamp(ts).isoformat(' ')[:-3] if tx.height > 0 else None,
'confirmations': (headers.height+1) - tx.height if tx.height > 0 else 0,
'claim_info': [],
'update_info': [],
'support_info': [],
'abandon_info': []
}
is_my_inputs = all([txi.is_my_account for txi in tx.inputs])
if is_my_inputs:
# fees only matter if we are the ones paying them
item['value'] = dewies_to_lbc(tx.net_account_balance+tx.fee)
item['fee'] = dewies_to_lbc(-tx.fee)
else:
# someone else paid the fees
item['value'] = dewies_to_lbc(tx.net_account_balance)
item['fee'] = '0.0'
for txo in tx.my_claim_outputs:
item['claim_info'].append({
'address': txo.get_address(self),
'balance_delta': dewies_to_lbc(-txo.amount),
'amount': dewies_to_lbc(txo.amount),
'claim_id': txo.claim_id,
'claim_name': txo.claim_name,
'nout': txo.position
})
for txo in tx.my_update_outputs:
if is_my_inputs: # updating my own claim
previous = None
for txi in tx.inputs:
if txi.txo_ref.txo is not None:
other_txo = txi.txo_ref.txo
if (other_txo.is_claim or other_txo.script.is_support_claim) \
and other_txo.claim_id == txo.claim_id:
previous = other_txo
break
if previous is not None:
item['update_info'].append({
'address': txo.get_address(self),
'balance_delta': dewies_to_lbc(previous.amount-txo.amount),
'amount': dewies_to_lbc(txo.amount),
'claim_id': txo.claim_id,
'claim_name': txo.claim_name,
'nout': txo.position
})
else: # someone sent us their claim
item['update_info'].append({
'address': txo.get_address(self),
'balance_delta': dewies_to_lbc(0),
'amount': dewies_to_lbc(txo.amount),
'claim_id': txo.claim_id,
'claim_name': txo.claim_name,
'nout': txo.position
})
for txo in tx.my_support_outputs:
item['support_info'].append({
'address': txo.get_address(self),
'balance_delta': dewies_to_lbc(txo.amount if not is_my_inputs else -txo.amount),
'amount': dewies_to_lbc(txo.amount),
'claim_id': txo.claim_id,
'claim_name': txo.claim_name,
'is_tip': not is_my_inputs,
'nout': txo.position
})
if is_my_inputs:
for txo in tx.other_support_outputs:
item['support_info'].append({
'address': txo.get_address(self),
'balance_delta': dewies_to_lbc(-txo.amount),
'amount': dewies_to_lbc(txo.amount),
'claim_id': txo.claim_id,
'claim_name': txo.claim_name,
'is_tip': is_my_inputs,
'nout': txo.position
})
for txo in tx.my_abandon_outputs:
item['abandon_info'].append({
'address': txo.get_address(self),
'balance_delta': dewies_to_lbc(txo.amount),
'amount': dewies_to_lbc(txo.amount),
'claim_id': txo.claim_id,
'claim_name': txo.claim_name,
'nout': txo.position
})
history.append(item)
return history
def get_transaction_history_count(self, **constraints):
self.constraint_account_or_all(constraints)
return self.db.get_transaction_count(**constraints)
class TestNetLedger(MainNetLedger): class TestNetLedger(MainNetLedger):
network_name = 'testnet' network_name = 'testnet'

View file

@ -3,16 +3,13 @@ import json
import logging import logging
from binascii import unhexlify from binascii import unhexlify
from datetime import datetime
from torba.client.basemanager import BaseWalletManager from torba.client.basemanager import BaseWalletManager
from torba.rpc.jsonrpc import CodeMessageError from torba.rpc.jsonrpc import CodeMessageError
from lbry.wallet.ledger import MainNetLedger from lbry.wallet.ledger import MainNetLedger
from lbry.wallet.account import BaseAccount
from lbry.wallet.transaction import Transaction from lbry.wallet.transaction import Transaction
from lbry.wallet.database import WalletDatabase from lbry.wallet.database import WalletDatabase
from lbry.wallet.dewies import dewies_to_lbc
from lbry.conf import Config from lbry.conf import Config
@ -209,102 +206,6 @@ class LbryWalletManager(BaseWalletManager):
await self.ledger.maybe_verify_transaction(tx, height) await self.ledger.maybe_verify_transaction(tx, height)
return tx return tx
@staticmethod
async def get_history(account: BaseAccount, **constraints):
headers = account.ledger.headers
txs = await account.get_transactions(**constraints)
history = []
for tx in txs:
ts = headers[tx.height]['timestamp'] if tx.height > 0 else None
item = {
'txid': tx.id,
'timestamp': ts,
'date': datetime.fromtimestamp(ts).isoformat(' ')[:-3] if tx.height > 0 else None,
'confirmations': (headers.height+1) - tx.height if tx.height > 0 else 0,
'claim_info': [],
'update_info': [],
'support_info': [],
'abandon_info': []
}
is_my_inputs = all([txi.is_my_account for txi in tx.inputs])
if is_my_inputs:
# fees only matter if we are the ones paying them
item['value'] = dewies_to_lbc(tx.net_account_balance+tx.fee)
item['fee'] = dewies_to_lbc(-tx.fee)
else:
# someone else paid the fees
item['value'] = dewies_to_lbc(tx.net_account_balance)
item['fee'] = '0.0'
for txo in tx.my_claim_outputs:
item['claim_info'].append({
'address': txo.get_address(account.ledger),
'balance_delta': dewies_to_lbc(-txo.amount),
'amount': dewies_to_lbc(txo.amount),
'claim_id': txo.claim_id,
'claim_name': txo.claim_name,
'nout': txo.position
})
for txo in tx.my_update_outputs:
if is_my_inputs: # updating my own claim
previous = None
for txi in tx.inputs:
if txi.txo_ref.txo is not None:
other_txo = txi.txo_ref.txo
if (other_txo.is_claim or other_txo.script.is_support_claim) \
and other_txo.claim_id == txo.claim_id:
previous = other_txo
break
if previous is not None:
item['update_info'].append({
'address': txo.get_address(account.ledger),
'balance_delta': dewies_to_lbc(previous.amount-txo.amount),
'amount': dewies_to_lbc(txo.amount),
'claim_id': txo.claim_id,
'claim_name': txo.claim_name,
'nout': txo.position
})
else: # someone sent us their claim
item['update_info'].append({
'address': txo.get_address(account.ledger),
'balance_delta': dewies_to_lbc(0),
'amount': dewies_to_lbc(txo.amount),
'claim_id': txo.claim_id,
'claim_name': txo.claim_name,
'nout': txo.position
})
for txo in tx.my_support_outputs:
item['support_info'].append({
'address': txo.get_address(account.ledger),
'balance_delta': dewies_to_lbc(txo.amount if not is_my_inputs else -txo.amount),
'amount': dewies_to_lbc(txo.amount),
'claim_id': txo.claim_id,
'claim_name': txo.claim_name,
'is_tip': not is_my_inputs,
'nout': txo.position
})
if is_my_inputs:
for txo in tx.other_support_outputs:
item['support_info'].append({
'address': txo.get_address(account.ledger),
'balance_delta': dewies_to_lbc(-txo.amount),
'amount': dewies_to_lbc(txo.amount),
'claim_id': txo.claim_id,
'claim_name': txo.claim_name,
'is_tip': is_my_inputs,
'nout': txo.position
})
for txo in tx.my_abandon_outputs:
item['abandon_info'].append({
'address': txo.get_address(account.ledger),
'balance_delta': dewies_to_lbc(txo.amount),
'amount': dewies_to_lbc(txo.amount),
'claim_id': txo.claim_id,
'claim_name': txo.claim_name,
'nout': txo.position
})
history.append(item)
return history
def save(self): def save(self):
for wallet in self.wallets: for wallet in self.wallets:
wallet.save() wallet.save()

View file

@ -1,4 +1,9 @@
from lbry.testcase import CommandTestCase from lbry.testcase import CommandTestCase
from lbry.wallet.dewies import dewies_to_lbc
def extract(d, keys):
return dict((k, d[k]) for k in keys)
class AccountManagement(CommandTestCase): class AccountManagement(CommandTestCase):
@ -65,3 +70,101 @@ class AccountManagement(CommandTestCase):
self.account.channel_keys[keys[1]] = "some invalid junk" self.account.channel_keys[keys[1]] = "some invalid junk"
await self.account.maybe_migrate_certificates() await self.account.maybe_migrate_certificates()
self.assertEqual(list(self.account.channel_keys.keys()), [keys[2]]) self.assertEqual(list(self.account.channel_keys.keys()), [keys[2]])
async def assertFindsClaims(self, claim_names, awaitable):
self.assertEqual(claim_names, [txo.claim_name for txo in await awaitable])
async def assertOutputAmount(self, amounts, awaitable):
self.assertEqual(amounts, [dewies_to_lbc(txo.amount) for txo in await awaitable])
async def test_commands_across_accounts(self):
channel_list = self.daemon.jsonrpc_channel_list
stream_list = self.daemon.jsonrpc_stream_list
support_list = self.daemon.jsonrpc_support_list
utxo_list = self.daemon.jsonrpc_utxo_list
default_account = self.daemon.default_account
second_account = await self.daemon.jsonrpc_account_create('second account')
tx = await self.daemon.jsonrpc_account_send(
'0.05', await self.daemon.jsonrpc_address_unused(account_id=second_account.id)
)
await self.confirm_tx(tx.id)
await self.assertOutputAmount(['0.05', '9.949876'], utxo_list())
await self.assertOutputAmount(['0.05'], utxo_list(account_id=second_account.id))
await self.assertOutputAmount(['9.949876'], utxo_list(account_id=default_account.id))
channel1 = await self.channel_create('@channel-in-account1', '0.01')
channel2 = await self.channel_create(
'@channel-in-account2', '0.01', account_id=second_account.id, funding_account_ids=[default_account.id]
)
await self.assertFindsClaims(['@channel-in-account2', '@channel-in-account1'], channel_list())
await self.assertFindsClaims(['@channel-in-account1'], channel_list(account_id=default_account.id))
await self.assertFindsClaims(['@channel-in-account2'], channel_list(account_id=second_account.id))
stream1 = await self.stream_create('stream-in-account1', '0.01', channel_id=self.get_claim_id(channel1))
stream2 = await self.stream_create(
'stream-in-account2', '0.01', channel_id=self.get_claim_id(channel2),
account_id=second_account.id, funding_account_ids=[default_account.id]
)
await self.assertFindsClaims(['stream-in-account2', 'stream-in-account1'], stream_list())
await self.assertFindsClaims(['stream-in-account1'], stream_list(account_id=default_account.id))
await self.assertFindsClaims(['stream-in-account2'], stream_list(account_id=second_account.id))
await self.assertFindsClaims(
['stream-in-account2', 'stream-in-account1', '@channel-in-account2', '@channel-in-account1'],
self.daemon.jsonrpc_claim_list()
)
await self.assertFindsClaims(
['stream-in-account1', '@channel-in-account1'],
self.daemon.jsonrpc_claim_list(account_id=default_account.id)
)
await self.assertFindsClaims(
['stream-in-account2', '@channel-in-account2'],
self.daemon.jsonrpc_claim_list(account_id=second_account.id)
)
support1 = await self.support_create(self.get_claim_id(stream1), '0.01')
support2 = await self.support_create(
self.get_claim_id(stream2), '0.01', account_id=second_account.id, funding_account_ids=[default_account.id]
)
self.assertEqual([support2['txid'], support1['txid']], [txo.tx_ref.id for txo in await support_list()])
self.assertEqual([support1['txid']], [txo.tx_ref.id for txo in await support_list(account_id=default_account.id)])
self.assertEqual([support2['txid']], [txo.tx_ref.id for txo in await support_list(account_id=second_account.id)])
history = await self.daemon.jsonrpc_transaction_list()
self.assertEqual(len(history), 8)
self.assertEqual(extract(history[0]['support_info'][0], ['claim_name', 'is_tip', 'amount', 'balance_delta']), {
'claim_name': 'stream-in-account2',
'is_tip': False,
'amount': '0.01',
'balance_delta': '-0.01'
})
self.assertEqual(extract(history[1]['support_info'][0], ['claim_name', 'is_tip', 'amount', 'balance_delta']), {
'claim_name': 'stream-in-account1',
'is_tip': False,
'amount': '0.01',
'balance_delta': '-0.01'
})
self.assertEqual(extract(history[2]['claim_info'][0], ['claim_name', 'amount', 'balance_delta']), {
'claim_name': 'stream-in-account2',
'amount': '0.01',
'balance_delta': '-0.01'
})
self.assertEqual(extract(history[3]['claim_info'][0], ['claim_name', 'amount', 'balance_delta']), {
'claim_name': 'stream-in-account1',
'amount': '0.01',
'balance_delta': '-0.01'
})
self.assertEqual(extract(history[4]['claim_info'][0], ['claim_name', 'amount', 'balance_delta']), {
'claim_name': '@channel-in-account2',
'amount': '0.01',
'balance_delta': '-0.01'
})
self.assertEqual(extract(history[5]['claim_info'][0], ['claim_name', 'amount', 'balance_delta']), {
'claim_name': '@channel-in-account1',
'amount': '0.01',
'balance_delta': '-0.01'
})
self.assertEqual(history[6]['value'], '0.0')
self.assertEqual(history[7]['value'], '10.0')

View file

@ -478,7 +478,8 @@ class ChannelCommands(CommandTestCase):
tx = await self.out(self.channel_update(claim_id, claim_address=other_address)) tx = await self.out(self.channel_update(claim_id, claim_address=other_address))
# after sending # after sending
self.assertEqual(len(await self.daemon.jsonrpc_channel_list()), 2) self.assertEqual(len(await self.daemon.jsonrpc_channel_list()), 3)
self.assertEqual(len(await self.daemon.jsonrpc_channel_list(account_id=self.account.id)), 2)
self.assertEqual(len(await self.daemon.jsonrpc_channel_list(account_id=account2_id)), 1) self.assertEqual(len(await self.daemon.jsonrpc_channel_list(account_id=account2_id)), 1)
# shoud not have private key # shoud not have private key
@ -618,12 +619,17 @@ class StreamCommands(ClaimTestCase):
channels = await self.out(self.daemon.jsonrpc_channel_list(account1_id)) channels = await self.out(self.daemon.jsonrpc_channel_list(account1_id))
self.assertEqual(len(channels), 1) self.assertEqual(len(channels), 1)
self.assertEqual(channels[0]['name'], '@spam') self.assertEqual(channels[0]['name'], '@spam')
self.assertEqual(channels, await self.out(self.daemon.jsonrpc_channel_list())) self.assertEqual(channels, await self.out(self.daemon.jsonrpc_channel_list(account1_id)))
channels = await self.out(self.daemon.jsonrpc_channel_list(account2_id)) channels = await self.out(self.daemon.jsonrpc_channel_list(account2_id))
self.assertEqual(len(channels), 1) self.assertEqual(len(channels), 1)
self.assertEqual(channels[0]['name'], '@baz') self.assertEqual(channels[0]['name'], '@baz')
channels = await self.out(self.daemon.jsonrpc_channel_list())
self.assertEqual(len(channels), 2)
self.assertEqual(channels[0]['name'], '@baz')
self.assertEqual(channels[1]['name'], '@spam')
# defaults to using all accounts to lookup channel # defaults to using all accounts to lookup channel
await self.stream_create('hovercraft1', '0.1', channel_id=baz_id) await self.stream_create('hovercraft1', '0.1', channel_id=baz_id)
self.assertEqual((await self.claim_search(name='hovercraft1'))[0]['signing_channel']['name'], '@baz') self.assertEqual((await self.claim_search(name='hovercraft1'))[0]['signing_channel']['name'], '@baz')
@ -806,13 +812,15 @@ class StreamCommands(ClaimTestCase):
# before sending # before sending
self.assertEqual(len(await self.daemon.jsonrpc_claim_list()), 4) self.assertEqual(len(await self.daemon.jsonrpc_claim_list()), 4)
self.assertEqual(len(await self.daemon.jsonrpc_claim_list(account_id=self.account.id)), 4)
self.assertEqual(len(await self.daemon.jsonrpc_claim_list(account_id=account2_id)), 0) self.assertEqual(len(await self.daemon.jsonrpc_claim_list(account_id=account2_id)), 0)
other_address = await account2.receiving.get_or_create_usable_address() other_address = await account2.receiving.get_or_create_usable_address()
tx = await self.out(self.stream_update(claim_id, claim_address=other_address)) tx = await self.out(self.stream_update(claim_id, claim_address=other_address))
# after sending # after sending
self.assertEqual(len(await self.daemon.jsonrpc_claim_list()), 3) self.assertEqual(len(await self.daemon.jsonrpc_claim_list()), 4)
self.assertEqual(len(await self.daemon.jsonrpc_claim_list(account_id=self.account.id)), 3)
self.assertEqual(len(await self.daemon.jsonrpc_claim_list(account_id=account2_id)), 1) self.assertEqual(len(await self.daemon.jsonrpc_claim_list(account_id=account2_id)), 1)
async def test_setting_fee_fields(self): async def test_setting_fee_fields(self):
@ -1112,8 +1120,7 @@ class SupportCommands(CommandTestCase):
await self.assertBalance(account2, '5.0') await self.assertBalance(account2, '5.0')
# create the claim we'll be tipping and supporting # create the claim we'll be tipping and supporting
tx = await self.stream_create() claim_id = self.get_claim_id(await self.stream_create())
claim_id = self.get_claim_id(tx)
# account1 and account2 balances: # account1 and account2 balances:
await self.assertBalance(self.account, '3.979769') await self.assertBalance(self.account, '3.979769')
@ -1121,18 +1128,17 @@ class SupportCommands(CommandTestCase):
# send a tip to the claim using account2 # send a tip to the claim using account2
tip = await self.out( tip = await self.out(
self.daemon.jsonrpc_support_create(claim_id, '1.0', True, account2_id) self.daemon.jsonrpc_support_create(
claim_id, '1.0', True, account2_id, funding_account_ids=[account2_id])
) )
await self.on_transaction_dict(tip) await self.confirm_tx(tip['txid'])
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 # 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(self.account, '3.979769')
await self.assertBalance(account2, '3.9998585') await self.assertBalance(account2, '3.9998585')
# verify that the incoming tip is marked correctly as is_tip=True in account1 # verify that the incoming tip is marked correctly as is_tip=True in account1
txs = await self.out(self.daemon.jsonrpc_transaction_list()) txs = await self.out(self.daemon.jsonrpc_transaction_list(self.account.id))
self.assertEqual(len(txs[0]['support_info']), 1) 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]['balance_delta'], '1.0')
self.assertEqual(txs[0]['support_info'][0]['claim_id'], claim_id) self.assertEqual(txs[0]['support_info'][0]['claim_id'], claim_id)
@ -1153,11 +1159,10 @@ class SupportCommands(CommandTestCase):
# send a support to the claim using account2 # send a support to the claim using account2
support = await self.out( support = await self.out(
self.daemon.jsonrpc_support_create(claim_id, '2.0', False, account2_id) self.daemon.jsonrpc_support_create(
claim_id, '2.0', False, account2_id, funding_account_ids=[account2_id])
) )
await self.on_transaction_dict(support) await self.confirm_tx(support['txid'])
await self.generate(1)
await self.on_transaction_dict(support)
# account2 balance went down ~2 # account2 balance went down ~2
await self.assertBalance(self.account, '3.979769') await self.assertBalance(self.account, '3.979769')

View file

@ -50,7 +50,7 @@ class AddressManager:
def _query_addresses(self, **constraints): def _query_addresses(self, **constraints):
return self.account.ledger.db.get_addresses( return self.account.ledger.db.get_addresses(
account=self.account, accounts=[self.account],
chain=self.chain_number, chain=self.chain_number,
**constraints **constraints
) )
@ -406,7 +406,7 @@ class BaseAccount:
if confirmations > 0: if confirmations > 0:
height = self.ledger.headers.height - (confirmations-1) height = self.ledger.headers.height - (confirmations-1)
constraints.update({'height__lte': height, 'height__gt': 0}) constraints.update({'height__lte': height, 'height__gt': 0})
return self.ledger.db.get_balance(account=self, **constraints) return self.ledger.db.get_balance(accounts=[self], **constraints)
async def get_max_gap(self): async def get_max_gap(self):
change_gap = await self.change.get_max_gap() change_gap = await self.change.get_max_gap()
@ -417,16 +417,16 @@ class BaseAccount:
} }
def get_utxos(self, **constraints): def get_utxos(self, **constraints):
return self.ledger.db.get_utxos(account=self, **constraints) return self.ledger.get_utxos(account=self, **constraints)
def get_utxo_count(self, **constraints): def get_utxo_count(self, **constraints):
return self.ledger.db.get_utxo_count(account=self, **constraints) return self.ledger.get_utxo_count(account=self, **constraints)
def get_transactions(self, **constraints): def get_transactions(self, **constraints):
return self.ledger.db.get_transactions(account=self, **constraints) return self.ledger.get_transactions(account=self, **constraints)
def get_transaction_count(self, **constraints): def get_transaction_count(self, **constraints):
return self.ledger.db.get_transaction_count(account=self, **constraints) return self.ledger.get_transaction_count(account=self, **constraints)
async def fund(self, to_account, amount=None, everything=False, async def fund(self, to_account, amount=None, everything=False,
outputs=1, broadcast=False, **constraints): outputs=1, broadcast=False, **constraints):

View file

@ -160,14 +160,10 @@ def query(select, **constraints):
offset = constraints.pop('offset', None) offset = constraints.pop('offset', None)
order_by = constraints.pop('order_by', None) order_by = constraints.pop('order_by', None)
constraints.pop('my_account', None) constraints.pop('my_accounts', None)
account = constraints.pop('account', None) accounts = constraints.pop('accounts', None)
if account is not None: if accounts is not None:
if not isinstance(account, list): constraints['account__in'] = [a.public_key.address for a in accounts]
account = [account]
constraints['account__in'] = [
(a.public_key.address if isinstance(a, BaseAccount) else a) for a in account
]
where, values = constraints_to_sql(constraints) where, values = constraints_to_sql(constraints)
if where: if where:
@ -395,22 +391,26 @@ class BaseDatabase(SQLiteMixin):
# 2. update address histories removing deleted TXs # 2. update address histories removing deleted TXs
return True return True
async def select_transactions(self, cols, account=None, **constraints): async def select_transactions(self, cols, accounts=None, **constraints):
if 'txid' not in constraints and account is not None: if 'txid' not in constraints:
constraints['$account'] = account.public_key.address assert accounts is not None, "'accounts' argument required when no 'txid' constraint"
constraints['txid__in'] = """ constraints.update({
f'$account{i}': a.public_key.address for i, a in enumerate(accounts)
})
account_values = ', '.join([f':$account{i}' for i in range(len(accounts))])
constraints['txid__in'] = f"""
SELECT txo.txid FROM txo SELECT txo.txid FROM txo
JOIN pubkey_address USING (address) WHERE pubkey_address.account = :$account INNER JOIN pubkey_address USING (address) WHERE pubkey_address.account IN ({account_values})
UNION UNION
SELECT txi.txid FROM txi SELECT txi.txid FROM txi
JOIN pubkey_address USING (address) WHERE pubkey_address.account = :$account INNER JOIN pubkey_address USING (address) WHERE pubkey_address.account IN ({account_values})
""" """
return await self.db.execute_fetchall( return await self.db.execute_fetchall(
*query("SELECT {} FROM tx".format(cols), **constraints) *query("SELECT {} FROM tx".format(cols), **constraints)
) )
async def get_transactions(self, my_account=None, **constraints): async def get_transactions(self, **constraints):
my_account = my_account or constraints.get('account', None) accounts = constraints.get('accounts', None)
tx_rows = await self.select_transactions( tx_rows = await self.select_transactions(
'txid, raw, height, position, is_verified', 'txid, raw, height, position, is_verified',
@ -436,7 +436,7 @@ class BaseDatabase(SQLiteMixin):
annotated_txos.update({ annotated_txos.update({
txo.id: txo for txo in txo.id: txo for txo in
(await self.get_txos( (await self.get_txos(
my_account=my_account, my_accounts=accounts,
txid__in=txids[offset:offset+step], txid__in=txids[offset:offset+step],
)) ))
}) })
@ -446,7 +446,7 @@ class BaseDatabase(SQLiteMixin):
referenced_txos.update({ referenced_txos.update({
txo.id: txo for txo in txo.id: txo for txo in
(await self.get_txos( (await self.get_txos(
my_account=my_account, my_accounts=accounts,
txoid__in=txi_txoids[offset:offset+step], txoid__in=txi_txoids[offset:offset+step],
)) ))
}) })
@ -484,12 +484,14 @@ class BaseDatabase(SQLiteMixin):
" JOIN tx USING (txid)".format(cols), **constraints " JOIN tx USING (txid)".format(cols), **constraints
)) ))
async def get_txos(self, my_account=None, no_tx=False, **constraints): async def get_txos(self, my_accounts=None, no_tx=False, **constraints):
my_account = my_account or constraints.get('account', None) my_accounts = [
if isinstance(my_account, BaseAccount): (a.public_key.address if isinstance(a, BaseAccount) else a)
my_account = my_account.public_key.address for a in (my_accounts or constraints.get('accounts', []))
]
if 'order_by' not in constraints: if 'order_by' not in constraints:
constraints['order_by'] = ["tx.height=0 DESC", "tx.height DESC", "tx.position DESC"] constraints['order_by'] = [
"tx.height=0 DESC", "tx.height DESC", "tx.position DESC", "txo.position"]
rows = await self.select_txos( rows = await self.select_txos(
"tx.txid, raw, tx.height, tx.position, tx.is_verified, " "tx.txid, raw, tx.height, tx.position, tx.is_verified, "
"txo.position, chain, account, amount, script", "txo.position, chain, account, amount, script",
@ -513,7 +515,7 @@ class BaseDatabase(SQLiteMixin):
) )
txo = txs[row[0]].outputs[row[5]] txo = txs[row[0]].outputs[row[5]]
txo.is_change = row[6] == 1 txo.is_change = row[6] == 1
txo.is_my_account = row[7] == my_account txo.is_my_account = row[7] in my_accounts
txos.append(txo) txos.append(txo)
return txos return txos

View file

@ -227,6 +227,18 @@ class BaseLedger(metaclass=LedgerRegistry):
def release_tx(self, tx): def release_tx(self, tx):
return self.release_outputs([txi.txo_ref.txo for txi in tx.inputs]) return self.release_outputs([txi.txo_ref.txo for txi in tx.inputs])
def get_utxos(self, **constraints):
return self.db.get_utxos(**constraints)
def get_utxo_count(self, **constraints):
return self.db.get_utxo_count(**constraints)
def get_transactions(self, **constraints):
return self.db.get_transactions(**constraints)
def get_transaction_count(self, **constraints):
return self.db.get_transaction_count(**constraints)
async def get_local_status_and_history(self, address): async def get_local_status_and_history(self, address):
address_details = await self.db.get_address(address=address) address_details = await self.db.get_address(address=address)
history = address_details['history'] or '' history = address_details['history'] or ''