forked from LBRYCommunity/lbry-sdk
wallet commands progress
This commit is contained in:
parent
d8f5b11158
commit
d459d6a26b
9 changed files with 199 additions and 232 deletions
|
@ -38,7 +38,7 @@ def clean_wallet_account_ids(constraints):
|
|||
async def add_channel_keys_to_txo_results(accounts: List, txos: Iterable[Output]):
|
||||
sub_channels = set()
|
||||
for txo in txos:
|
||||
if txo.claim.is_channel:
|
||||
if txo.is_claim and txo.claim.is_channel:
|
||||
for account in accounts:
|
||||
private_key = await account.get_channel_private_key(
|
||||
txo.claim.channel.public_key_bytes
|
||||
|
|
|
@ -380,8 +380,8 @@ def select_txos(
|
|||
(TXI.c.address.notin_(my_addresses))
|
||||
)
|
||||
joins = TXO.join(TX)
|
||||
#if constraints.get('is_spent', None) is False:
|
||||
# s = s.where((TXO.c.is_spent == False) & (TXO.c.is_reserved == False))
|
||||
if constraints.pop('is_spent', None) is False:
|
||||
s = s.where((TXO.c.spent_height == 0) & (TXO.c.is_reserved == False))
|
||||
if include_is_my_input:
|
||||
joins = joins.join(TXI, (TXI.c.position == 0) & (TXI.c.tx_hash == TXO.c.tx_hash), isouter=True)
|
||||
if claim_id_not_in_claim_table:
|
||||
|
|
|
@ -203,6 +203,7 @@ txi_join_account = TXI.join(AccountAddress, TXI.columns.address == AccountAddres
|
|||
pg_add_txi_constraints_and_indexes = [
|
||||
"ALTER TABLE txi ADD PRIMARY KEY (txo_hash);",
|
||||
"CREATE INDEX txi_height ON txi (height);",
|
||||
"CREATE INDEX txi_first_address ON txi (tx_hash) INCLUDE (address) WHERE position = 0;",
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -859,10 +859,11 @@ class API:
|
|||
{kwargs}
|
||||
|
||||
"""
|
||||
args = transaction(**transaction_kwargs)
|
||||
wallet = self.wallets.get_or_default_for_spending(args['wallet_id'])
|
||||
account = wallet.accounts.get_or_default(args['change_account_id'])
|
||||
accounts = wallet.accounts.get_or_all(args['funding_account_id'])
|
||||
tx_dict, kwargs = pop_kwargs('tx', extract_tx(**tx_kwargs))
|
||||
assert_consumed_kwargs(kwargs)
|
||||
wallet = self.wallets.get_or_default_for_spending(tx_dict.pop('wallet_id'))
|
||||
account = wallet.accounts.get_or_default(tx_dict.pop('change_account_id'))
|
||||
accounts = wallet.accounts.get_or_all(tx_dict.pop('fund_account_id'))
|
||||
amount = self.ledger.get_dewies_or_error("amount", amount)
|
||||
if addresses and not isinstance(addresses, list):
|
||||
addresses = [addresses]
|
||||
|
@ -876,7 +877,7 @@ class API:
|
|||
)
|
||||
tx = await wallet.create_transaction([], outputs, accounts, account)
|
||||
await wallet.sign(tx)
|
||||
await self.service.maybe_broadcast_or_release(tx, args['blocking'], args['preview'])
|
||||
await self.service.maybe_broadcast_or_release(tx, **tx_dict)
|
||||
return tx
|
||||
|
||||
ACCOUNT_DOC = """
|
||||
|
@ -1474,7 +1475,7 @@ class API:
|
|||
tx = await self.wallets.create_purchase_transaction(
|
||||
accounts, txo, self.exchange_rate_manager, override_max_key_fee
|
||||
)
|
||||
await self.service.maybe_broadcast_or_release(tx, blocking, preview)
|
||||
await self.service.maybe_broadcast_or_release(tx, **tx_dict)
|
||||
return tx
|
||||
|
||||
CLAIM_DOC = """
|
||||
|
@ -1635,7 +1636,7 @@ class API:
|
|||
name=name, amount=amount, holding_account=holding_account, funding_accounts=funding_accounts,
|
||||
save_key=not tx_dict['preview'], **remove_nulls(channel_dict)
|
||||
)
|
||||
await self.service.maybe_broadcast_or_release(tx, tx_dict['preview'], tx_dict['no_wait'])
|
||||
await self.service.maybe_broadcast_or_release(tx, **tx_dict)
|
||||
return tx
|
||||
|
||||
async def channel_update(
|
||||
|
@ -1678,7 +1679,7 @@ class API:
|
|||
save_key=not tx_dict['preview'], **remove_nulls(channel_edit_dict)
|
||||
)
|
||||
|
||||
await self.service.maybe_broadcast_or_release(tx, tx_dict['blocking'], tx_dict['preview'])
|
||||
await self.service.maybe_broadcast_or_release(tx, **tx_dict)
|
||||
|
||||
return tx
|
||||
|
||||
|
@ -1719,7 +1720,7 @@ class API:
|
|||
tx = await Transaction.create(
|
||||
[Input.spend(txo) for txo in claims], [], [account], account
|
||||
)
|
||||
await self.service.maybe_broadcast_or_release(tx, blocking, preview)
|
||||
await self.service.maybe_broadcast_or_release(tx, **tx_dict)
|
||||
return tx
|
||||
|
||||
async def channel_list(
|
||||
|
@ -1922,7 +1923,7 @@ class API:
|
|||
new_txo.sign(channel)
|
||||
await tx.sign(funding_accounts)
|
||||
|
||||
await self.service.maybe_broadcast_or_release(tx, blocking, preview)
|
||||
await self.service.maybe_broadcast_or_release(tx, **tx_dict)
|
||||
return tx
|
||||
|
||||
async def stream_create(
|
||||
|
@ -1948,6 +1949,7 @@ class API:
|
|||
amount = self.ledger.get_dewies_or_error('bid', bid, positive_value=True)
|
||||
holding_account = wallet.accounts.get_or_default(stream_dict.pop('account_id'))
|
||||
funding_accounts = wallet.accounts.get_or_all(tx_dict.pop('fund_account_id'))
|
||||
change_account = wallet.accounts.get_or_default(tx_dict.pop('change_account_id'))
|
||||
signing_channel = None
|
||||
channel_id = stream_dict.pop('channel_id')
|
||||
channel_name = stream_dict.pop('channel_name')
|
||||
|
@ -1977,9 +1979,10 @@ class API:
|
|||
name=name, amount=amount, file_path=stream_dict.pop('file_path'),
|
||||
create_file_stream=create_file_stream,
|
||||
holding_address=holding_address, funding_accounts=funding_accounts,
|
||||
signing_channel=signing_channel, **remove_nulls(stream_dict)
|
||||
change_account=change_account, signing_channel=signing_channel,
|
||||
**remove_nulls(stream_dict)
|
||||
)
|
||||
await self.service.maybe_broadcast_or_release(tx, tx_dict['preview'], tx_dict['no_wait'])
|
||||
await self.service.maybe_broadcast_or_release(tx, **tx_dict)
|
||||
return tx
|
||||
|
||||
async def stream_update(
|
||||
|
@ -2002,6 +2005,7 @@ class API:
|
|||
wallet = self.wallets.get_or_default_for_spending(tx_dict.pop('wallet_id'))
|
||||
holding_account = wallet.accounts.get_or_default(stream_dict.pop('account_id'))
|
||||
funding_accounts = wallet.accounts.get_or_all(tx_dict.pop('fund_account_id'))
|
||||
change_account = wallet.accounts.get_or_default(tx_dict.pop('change_account_id'))
|
||||
replace = stream_dict.pop('replace')
|
||||
|
||||
old = await wallet.claims.get(claim_id=claim_id)
|
||||
|
@ -2050,9 +2054,10 @@ class API:
|
|||
old=old, amount=amount, file_path=stream_dict.pop('file_path'),
|
||||
create_file_stream=create_file_stream, replace=replace,
|
||||
holding_address=holding_address, funding_accounts=funding_accounts,
|
||||
signing_channel=signing_channel, **remove_nulls(stream_dict)
|
||||
change_account=change_account, signing_channel=signing_channel,
|
||||
**remove_nulls(stream_dict)
|
||||
)
|
||||
await self.service.maybe_broadcast_or_release(tx, tx_dict['preview'], tx_dict['no_wait'])
|
||||
await self.service.maybe_broadcast_or_release(tx, **tx_dict)
|
||||
return tx
|
||||
|
||||
async def stream_abandon(
|
||||
|
@ -2093,7 +2098,7 @@ class API:
|
|||
[Input.spend(txo) for txo in claims], [], accounts, account
|
||||
)
|
||||
|
||||
await self.service.maybe_broadcast_or_release(tx, blocking, preview)
|
||||
await self.service.maybe_broadcast_or_release(tx, tx_dict)
|
||||
return tx
|
||||
|
||||
async def stream_list(
|
||||
|
@ -2179,7 +2184,7 @@ class API:
|
|||
new_txo.sign(channel)
|
||||
await tx.sign(funding_accounts)
|
||||
|
||||
await self.service.maybe_broadcast_or_release(tx, blocking, preview)
|
||||
await self.service.maybe_broadcast_or_release(tx, tx_dict)
|
||||
return tx
|
||||
|
||||
async def collection_update(
|
||||
|
@ -2259,7 +2264,7 @@ class API:
|
|||
new_txo.sign(channel)
|
||||
await tx.sign(funding_accounts)
|
||||
|
||||
await self.service.maybe_broadcast_or_release(tx, blocking, preview)
|
||||
await self.service.maybe_broadcast_or_release(tx, tx_dict)
|
||||
return tx
|
||||
|
||||
async def collection_abandon(
|
||||
|
@ -2365,16 +2370,22 @@ class API:
|
|||
wallet = self.wallets.get_or_default_for_spending(tx_dict.pop('wallet_id'))
|
||||
amount = self.ledger.get_dewies_or_error('amount', amount, positive_value=True)
|
||||
funding_accounts = wallet.accounts.get_or_all(tx_dict.pop('fund_account_id'))
|
||||
claim = await wallet.claims.get(claim_id=claim_id)
|
||||
change_account = wallet.accounts.get_or_default(tx_dict.pop('change_account_id'))
|
||||
claim = await self.service.get_claim_by_claim_id(wallet.accounts, claim_id)
|
||||
if claim is None:
|
||||
raise Exception(f"Could not find claim with claim_id '{claim_id}'. ")
|
||||
|
||||
claim_address = claim.get_address(self.ledger)
|
||||
if not tip:
|
||||
holding_account = wallet.accounts.get_or_default(account_id)
|
||||
claim_address = await holding_account.receiving.get_or_create_usable_address()
|
||||
|
||||
tx = await wallet.supports.create(
|
||||
claim.claim_name, claim_id, amount, claim_address, funding_accounts, funding_accounts[0]
|
||||
name=claim.claim_name, claim_id=claim_id,
|
||||
amount=amount, holding_address=claim_address,
|
||||
funding_accounts=funding_accounts, change_account=change_account
|
||||
)
|
||||
await self.service.maybe_broadcast_or_release(tx, tx_dict['preview'], tx_dict['no_wait'])
|
||||
await self.service.maybe_broadcast_or_release(tx, **tx_dict)
|
||||
return tx
|
||||
|
||||
async def support_list(
|
||||
|
@ -2499,23 +2510,19 @@ class API:
|
|||
{kwargs}
|
||||
|
||||
"""
|
||||
wallet = self.wallets.get_or_default(wallet_id)
|
||||
assert not wallet.is_locked, "Cannot spend funds with locked wallet, unlock first."
|
||||
if account_id:
|
||||
account = wallet.get_account_or_error(account_id)
|
||||
accounts = [account]
|
||||
else:
|
||||
account = wallet.default_account
|
||||
accounts = wallet.accounts
|
||||
abandon_dict, kwargs = pop_kwargs('abandon', extract_abandon(**abandon_and_tx_kwargs))
|
||||
tx_dict, kwargs = pop_kwargs('tx', extract_tx(**kwargs))
|
||||
assert_consumed_kwargs(kwargs)
|
||||
wallet = self.wallets.get_or_default_for_spending(tx_dict.pop('wallet_id'))
|
||||
funding_accounts = wallet.accounts.get_or_all(tx_dict.pop('fund_account_id'))
|
||||
change_account = wallet.accounts.get_or_default(tx_dict.pop('change_account_id'))
|
||||
|
||||
if txid is not None and nout is not None:
|
||||
supports = await self.ledger.get_supports(
|
||||
wallet=wallet, accounts=accounts, tx_hash=unhexlify(txid)[::-1], position=nout
|
||||
)
|
||||
elif claim_id is not None:
|
||||
supports = await self.ledger.get_supports(
|
||||
wallet=wallet, accounts=accounts, claim_id=claim_id
|
||||
if abandon_dict['txid']:
|
||||
supports = await wallet.supports.list(
|
||||
wallet=wallet, tx_hash=unhexlify(abandon_dict['txid'])[::-1], position=abandon_dict['nout']
|
||||
)
|
||||
elif abandon_dict['claim_id'] is not None:
|
||||
supports = await wallet.supports.list(wallet=wallet, claim_id=abandon_dict['claim_id'])
|
||||
else:
|
||||
raise Exception('Must specify claim_id, or txid and nout')
|
||||
|
||||
|
@ -2523,22 +2530,14 @@ class API:
|
|||
raise Exception('No supports found for the specified claim_id or txid:nout')
|
||||
|
||||
if keep is not None:
|
||||
keep = self.get_dewies_or_error('keep', keep)
|
||||
keep = self.ledger.get_dewies_or_error('keep', keep, positive_value=True)
|
||||
else:
|
||||
keep = 0
|
||||
|
||||
outputs = []
|
||||
if keep > 0:
|
||||
outputs = [
|
||||
Output.pay_support_pubkey_hash(
|
||||
keep, supports[0].claim_name, supports[0].claim_id, supports[0].pubkey_hash
|
||||
tx = await wallet.supports.delete(
|
||||
supports=supports, keep=keep, funding_accounts=funding_accounts, change_account=change_account
|
||||
)
|
||||
]
|
||||
|
||||
tx = await Transaction.create(
|
||||
[Input.spend(txo) for txo in supports], outputs, accounts, account
|
||||
)
|
||||
await self.service.maybe_broadcast_or_release(tx, blocking, preview)
|
||||
await self.service.maybe_broadcast_or_release(tx, **tx_dict)
|
||||
return tx
|
||||
|
||||
BLOCK_DOC = """
|
||||
|
|
|
@ -203,14 +203,14 @@ class Service:
|
|||
async def resolve(self, urls, **kwargs):
|
||||
raise NotImplementedError
|
||||
|
||||
async def search_claims(self, accounts, **kwargs) -> Tuple[List[Output], Optional[int], Censor]:
|
||||
async def search_claims(self, accounts, **kwargs) -> Result[Output]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def search_supports(self, accounts, **kwargs) -> Tuple[List[Output], Optional[int]]:
|
||||
async def search_supports(self, accounts, **kwargs) -> Result[Output]:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_claim_by_claim_id(self, accounts, claim_id, **kwargs) -> Output:
|
||||
for claim in (await self.search_claims(accounts, claim_id=claim_id, **kwargs))[0]:
|
||||
async def get_claim_by_claim_id(self, accounts, claim_id, **kwargs) -> Optional[Output]:
|
||||
for claim in await self.search_claims(accounts, claim_id=claim_id, **kwargs):
|
||||
return claim
|
||||
|
||||
@staticmethod
|
||||
|
|
|
@ -654,6 +654,9 @@ class CommandTestCase(IntegrationTestCase):
|
|||
async def wallet_remove(self, *args, **kwargs):
|
||||
return await self.out(self.api.wallet_remove(*args, **kwargs))
|
||||
|
||||
async def wallet_balance(self, *args, **kwargs):
|
||||
return await self.out(self.api.wallet_balance(*args, **kwargs))
|
||||
|
||||
async def account_list(self, *args, **kwargs):
|
||||
return (await self.out(self.api.account_list(*args, **kwargs)))['items']
|
||||
|
||||
|
@ -669,6 +672,15 @@ class CommandTestCase(IntegrationTestCase):
|
|||
async def account_remove(self, *args, **kwargs):
|
||||
return await self.out(self.api.account_remove(*args, **kwargs))
|
||||
|
||||
async def account_send(self, *args, **kwargs):
|
||||
return await self.out(self.api.account_send(*args, **kwargs))
|
||||
|
||||
async def account_balance(self, *args, **kwargs):
|
||||
return await self.out(self.api.account_balance(*args, **kwargs))
|
||||
|
||||
async def address_unused(self, *args, **kwargs):
|
||||
return await self.out(self.api.address_unused(*args, **kwargs))
|
||||
|
||||
def create_upload_file(self, data, prefix=None, suffix=None):
|
||||
file_path = tempfile.mktemp(
|
||||
prefix=prefix or "tmp", suffix=suffix or "", dir=self.ledger.conf.upload_dir
|
||||
|
@ -762,8 +774,6 @@ class CommandTestCase(IntegrationTestCase):
|
|||
)
|
||||
|
||||
async def support_abandon(self, *args, confirm=True, **kwargs):
|
||||
if 'blocking' not in kwargs:
|
||||
kwargs['blocking'] = False
|
||||
return await self.confirm_and_render(
|
||||
self.api.support_abandon(*args, **kwargs), confirm
|
||||
)
|
||||
|
|
|
@ -665,9 +665,8 @@ class StreamListManager(ClaimListManager):
|
|||
async def create(
|
||||
self, name: str, amount: int, file_path: str,
|
||||
create_file_stream: Callable[[str], Awaitable[ManagedStream]],
|
||||
holding_address: str, funding_accounts: List[Account],
|
||||
signing_channel: Optional[Output] = None,
|
||||
preview=False, **kwargs
|
||||
holding_address: str, funding_accounts: List[Account], change_account: Account,
|
||||
signing_channel: Optional[Output] = None, preview=False, **kwargs
|
||||
) -> Tuple[Transaction, ManagedStream]:
|
||||
|
||||
claim = Claim()
|
||||
|
@ -676,7 +675,7 @@ class StreamListManager(ClaimListManager):
|
|||
# before creating file stream, create TX to ensure we have enough LBC
|
||||
tx = await self._create(
|
||||
name, claim, amount, holding_address,
|
||||
funding_accounts, funding_accounts[0],
|
||||
funding_accounts, change_account,
|
||||
signing_channel
|
||||
)
|
||||
txo = tx.outputs[0]
|
||||
|
@ -705,7 +704,7 @@ class StreamListManager(ClaimListManager):
|
|||
async def update(
|
||||
self, old: Output, amount: int, file_path: str,
|
||||
create_file_stream: Callable[[str], Awaitable[ManagedStream]],
|
||||
holding_address: str, funding_accounts: List[Account],
|
||||
holding_address: str, funding_accounts: List[Account], change_account: Account,
|
||||
signing_channel: Optional[Output] = None,
|
||||
preview=False, replace=False, **kwargs
|
||||
) -> Tuple[Transaction, ManagedStream]:
|
||||
|
@ -726,7 +725,7 @@ class StreamListManager(ClaimListManager):
|
|||
# before creating file stream, create TX to ensure we have enough LBC
|
||||
tx = await super().update(
|
||||
old, claim, amount, holding_address,
|
||||
funding_accounts, funding_accounts[0],
|
||||
funding_accounts, change_account,
|
||||
signing_channel
|
||||
)
|
||||
txo = tx.outputs[0]
|
||||
|
@ -785,9 +784,27 @@ class SupportListManager(BaseListManager):
|
|||
support_output = Output.pay_support_pubkey_hash(
|
||||
amount, name, claim_id, self.wallet.ledger.address_to_hash160(holding_address)
|
||||
)
|
||||
return await self.wallet.create_transaction(
|
||||
tx = await self.wallet.create_transaction(
|
||||
[], [support_output], funding_accounts, change_account
|
||||
)
|
||||
await self.wallet.sign(tx)
|
||||
return tx
|
||||
|
||||
async def delete(self, supports, keep=0, funding_accounts=None, change_account=None):
|
||||
outputs = []
|
||||
if keep > 0:
|
||||
outputs = [
|
||||
Output.pay_support_pubkey_hash(
|
||||
keep, supports[0].claim_name, supports[0].claim_id, supports[0].pubkey_hash
|
||||
)
|
||||
]
|
||||
tx = await self.wallet.create_transaction(
|
||||
[Input.spend(txo) for txo in supports], outputs,
|
||||
funding_accounts or self.wallet._accounts,
|
||||
change_account or self.wallet._accounts[0]
|
||||
)
|
||||
await self.wallet.sign(tx)
|
||||
return tx
|
||||
|
||||
async def list(self, **constraints) -> Result[Output]:
|
||||
return await self.wallet.db.get_supports(**constraints)
|
||||
|
|
|
@ -7,57 +7,63 @@ def extract(d, keys):
|
|||
|
||||
|
||||
class AccountManagement(CommandTestCase):
|
||||
|
||||
async def test_account_list_set_create_remove_add(self):
|
||||
# check initial account
|
||||
accounts = await self.daemon.jsonrpc_account_list()
|
||||
self.assertItemCount(accounts, 1)
|
||||
self.assertEqual(len(await self.account_list()), 1)
|
||||
|
||||
# create another account
|
||||
await self.account_create('second account')
|
||||
accounts = await self.account_list()
|
||||
self.assertEqual(len(accounts), 2)
|
||||
account = accounts[1]
|
||||
self.assertEqual(account['name'], 'second account')
|
||||
self.assertEqual(account['address_generator'], {
|
||||
'name': 'deterministic-chain',
|
||||
'receiving': {'gap': 20, 'maximum_uses_per_address': 1},
|
||||
'change': {'gap': 6, 'maximum_uses_per_address': 1},
|
||||
})
|
||||
|
||||
# change account name and gap
|
||||
account_id = accounts['items'][0]['id']
|
||||
self.daemon.jsonrpc_account_set(
|
||||
account_id=account_id, new_name='test account',
|
||||
await self.account_set(
|
||||
account_id=account['id'], new_name='test account',
|
||||
receiving_gap=95, receiving_max_uses=96,
|
||||
change_gap=97, change_max_uses=98
|
||||
)
|
||||
accounts = (await self.daemon.jsonrpc_account_list())['items'][0]
|
||||
self.assertEqual(accounts['name'], 'test account')
|
||||
self.assertEqual(
|
||||
accounts['address_generator']['receiving'],
|
||||
{'gap': 95, 'maximum_uses_per_address': 96}
|
||||
)
|
||||
self.assertEqual(
|
||||
accounts['address_generator']['change'],
|
||||
{'gap': 97, 'maximum_uses_per_address': 98}
|
||||
)
|
||||
|
||||
# create another account
|
||||
await self.daemon.jsonrpc_account_create('second account')
|
||||
accounts = await self.daemon.jsonrpc_account_list()
|
||||
self.assertItemCount(accounts, 2)
|
||||
self.assertEqual(accounts['items'][1]['name'], 'second account')
|
||||
account_id2 = accounts['items'][1]['id']
|
||||
account = (await self.account_list())[1]
|
||||
self.assertEqual(account['name'], 'test account')
|
||||
self.assertEqual(account['address_generator'], {
|
||||
'name': 'deterministic-chain',
|
||||
'receiving': {'gap': 95, 'maximum_uses_per_address': 96},
|
||||
'change': {'gap': 97, 'maximum_uses_per_address': 98},
|
||||
})
|
||||
|
||||
# make new account the default
|
||||
self.daemon.jsonrpc_account_set(account_id=account_id2, default=True)
|
||||
accounts = await self.daemon.jsonrpc_account_list(show_seed=True)
|
||||
self.assertEqual(accounts['items'][0]['name'], 'second account')
|
||||
await self.account_set(account_id=account['id'], default=True)
|
||||
actual = (await self.account_list())[0]
|
||||
self.assertNotEqual(account['modified_on'], actual['modified_on'])
|
||||
del account['modified_on']
|
||||
del actual['modified_on']
|
||||
self.assertEqual(account, actual)
|
||||
|
||||
account_seed = accounts['items'][1]['seed']
|
||||
account_seed, account_pubkey = account['seed'], account['public_key']
|
||||
|
||||
# remove account
|
||||
self.daemon.jsonrpc_account_remove(accounts['items'][1]['id'])
|
||||
accounts = await self.daemon.jsonrpc_account_list()
|
||||
self.assertItemCount(accounts, 1)
|
||||
await self.account_remove(account['id'])
|
||||
self.assertEqual(len(await self.account_list()), 1)
|
||||
|
||||
# add account
|
||||
await self.daemon.jsonrpc_account_add('recreated account', seed=account_seed)
|
||||
accounts = await self.daemon.jsonrpc_account_list()
|
||||
self.assertItemCount(accounts, 2)
|
||||
self.assertEqual(accounts['items'][1]['name'], 'recreated account')
|
||||
await self.account_add('recreated account', seed=account_seed)
|
||||
accounts = await self.account_list()
|
||||
self.assertEqual(len(accounts), 2)
|
||||
account = accounts[1]
|
||||
self.assertEqual(account['name'], 'recreated account')
|
||||
self.assertEqual(account['public_key'], account_pubkey)
|
||||
|
||||
# list specific account
|
||||
accounts = await self.daemon.jsonrpc_account_list(account_id, include_claims=True)
|
||||
self.assertEqual(accounts['items'][0]['name'], 'recreated account')
|
||||
accounts = await self.account_list(account['id'])
|
||||
self.assertEqual(len(accounts), 1)
|
||||
self.assertEqual(accounts[0]['name'], 'recreated account')
|
||||
|
||||
async def test_wallet_migration(self):
|
||||
# null certificates should get deleted
|
||||
|
|
|
@ -1,50 +1,37 @@
|
|||
import asyncio
|
||||
import json
|
||||
import asyncio
|
||||
from unittest import skip
|
||||
|
||||
from sqlalchemy import event
|
||||
|
||||
from lbry.wallet import ENCRYPT_ON_DISK
|
||||
from lbry.wallet.wallet import ENCRYPT_ON_DISK
|
||||
from lbry.error import InvalidPasswordError
|
||||
from lbry.testcase import CommandTestCase
|
||||
from lbry.wallet.dewies import dict_values_to_lbc
|
||||
from lbry.blockchain.dewies import dict_values_to_lbc
|
||||
|
||||
|
||||
class WalletCommands(CommandTestCase):
|
||||
|
||||
async def test_wallet_create_and_add_subscribe(self):
|
||||
session = next(iter(self.conductor.spv_node.server.session_mgr.sessions))
|
||||
self.assertEqual(len(session.hashX_subs), 27)
|
||||
wallet = await self.daemon.jsonrpc_wallet_create('foo', create_account=True, single_key=True)
|
||||
self.assertEqual(len(session.hashX_subs), 28)
|
||||
await self.daemon.jsonrpc_wallet_remove(wallet.id)
|
||||
self.assertEqual(len(session.hashX_subs), 27)
|
||||
await self.daemon.jsonrpc_wallet_add(wallet.id)
|
||||
self.assertEqual(len(session.hashX_subs), 28)
|
||||
|
||||
async def test_wallet_syncing_status(self):
|
||||
address = await self.daemon.jsonrpc_address_unused()
|
||||
self.assertFalse(self.daemon.jsonrpc_wallet_status()['is_syncing'])
|
||||
await self.blockchain.send_to_address(address, 1)
|
||||
await self.ledger._update_tasks.started.wait()
|
||||
self.assertTrue(self.daemon.jsonrpc_wallet_status()['is_syncing'])
|
||||
await self.ledger._update_tasks.done.wait()
|
||||
self.assertFalse(self.daemon.jsonrpc_wallet_status()['is_syncing'])
|
||||
|
||||
wallet = self.daemon.component_manager.get_actual_component('wallet')
|
||||
wallet_manager = wallet.wallet_manager
|
||||
# when component manager hasn't started yet
|
||||
wallet.wallet_manager = None
|
||||
async def test_list_create_add_and_remove(self):
|
||||
self.assertEqual(await self.wallet_list(), [{'id': 'default_wallet', 'name': 'Wallet'}])
|
||||
self.assertEqual(
|
||||
{'is_encrypted': None, 'is_syncing': None, 'is_locked': None},
|
||||
self.daemon.jsonrpc_wallet_status()
|
||||
)
|
||||
wallet.wallet_manager = wallet_manager
|
||||
self.assertEqual(
|
||||
{'is_encrypted': False, 'is_syncing': False, 'is_locked': False},
|
||||
self.daemon.jsonrpc_wallet_status()
|
||||
await self.wallet_create('another', "Another"),
|
||||
{'id': 'another', 'name': 'Another'}
|
||||
)
|
||||
self.assertEqual(await self.wallet_list(), [
|
||||
{'id': 'default_wallet', 'name': 'Wallet'},
|
||||
{'id': 'another', 'name': 'Another'}
|
||||
])
|
||||
self.assertEqual(await self.wallet_remove('another'), {'id': 'another', 'name': 'Another'})
|
||||
self.assertEqual(await self.wallet_list(), [{'id': 'default_wallet', 'name': 'Wallet'}])
|
||||
self.assertEqual(await self.wallet_add('another'), {'id': 'another', 'name': 'Another'})
|
||||
self.assertEqual(await self.wallet_list(), [
|
||||
{'id': 'default_wallet', 'name': 'Wallet'},
|
||||
{'id': 'another', 'name': 'Another'}
|
||||
])
|
||||
|
||||
async def test_wallet_reconnect(self):
|
||||
@skip
|
||||
async def test_reconnect(self):
|
||||
await self.conductor.spv_node.stop(True)
|
||||
self.conductor.spv_node.port = 54320
|
||||
await self.conductor.spv_node.start(self.conductor.blockchain_node)
|
||||
|
@ -57,160 +44,107 @@ class WalletCommands(CommandTestCase):
|
|||
self.assertEqual(len(status['wallet']['servers']), 1)
|
||||
self.assertEqual(status['wallet']['servers'][0]['port'], 54320)
|
||||
|
||||
async def test_balance_caching(self):
|
||||
account2 = await self.daemon.jsonrpc_account_create("Tip-er")
|
||||
address2 = await self.daemon.jsonrpc_address_unused(account2.id)
|
||||
sendtxid = await self.blockchain.send_to_address(address2, 10)
|
||||
await self.confirm_tx(sendtxid)
|
||||
await self.generate(1)
|
||||
|
||||
wallet_balance = self.daemon.jsonrpc_wallet_balance
|
||||
ledger = self.ledger
|
||||
|
||||
query_count = 0
|
||||
|
||||
def catch_queries(*args, **kwargs):
|
||||
nonlocal query_count
|
||||
query_count += 1
|
||||
|
||||
event.listen(self.ledger.db.engine, "before_cursor_execute", catch_queries)
|
||||
|
||||
expected = {
|
||||
'total': '20.0',
|
||||
'available': '20.0',
|
||||
'reserved': '0.0',
|
||||
'reserved_subtotals': {'claims': '0.0', 'supports': '0.0', 'tips': '0.0'}
|
||||
}
|
||||
self.assertIsNone(ledger._balance_cache.get(self.account.id))
|
||||
|
||||
self.assertEqual(await wallet_balance(), expected)
|
||||
self.assertEqual(query_count, 6)
|
||||
self.assertEqual(dict_values_to_lbc(ledger._balance_cache.get(self.account.id))['total'], '10.0')
|
||||
self.assertEqual(dict_values_to_lbc(ledger._balance_cache.get(account2.id))['total'], '10.0')
|
||||
|
||||
# calling again uses cache
|
||||
self.assertEqual(await wallet_balance(), expected)
|
||||
self.assertEqual(query_count, 6)
|
||||
self.assertEqual(dict_values_to_lbc(ledger._balance_cache.get(self.account.id))['total'], '10.0')
|
||||
self.assertEqual(dict_values_to_lbc(ledger._balance_cache.get(account2.id))['total'], '10.0')
|
||||
|
||||
await self.stream_create()
|
||||
await self.generate(1)
|
||||
|
||||
expected = {
|
||||
'total': '19.979893',
|
||||
'available': '18.979893',
|
||||
'reserved': '1.0',
|
||||
'reserved_subtotals': {'claims': '1.0', 'supports': '0.0', 'tips': '0.0'}
|
||||
}
|
||||
# on_transaction event reset balance cache
|
||||
query_count = 0
|
||||
self.assertEqual(await wallet_balance(), expected)
|
||||
self.assertEqual(dict_values_to_lbc(ledger._balance_cache.get(self.account.id))['total'], '9.979893')
|
||||
self.assertEqual(dict_values_to_lbc(ledger._balance_cache.get(account2.id))['total'], '10.0')
|
||||
self.assertEqual(query_count, 3) # only one of the accounts changed
|
||||
|
||||
async def test_granular_balances(self):
|
||||
account2 = await self.daemon.jsonrpc_account_create("Tip-er")
|
||||
wallet2 = await self.daemon.jsonrpc_wallet_create('foo', create_account=True)
|
||||
account3 = wallet2.default_account
|
||||
address3 = await self.daemon.jsonrpc_address_unused(account3.id, wallet2.id)
|
||||
await self.confirm_tx(await self.blockchain.send_to_address(address3, 1))
|
||||
account2 = (await self.account_create("Tip-er"))["id"]
|
||||
wallet2 = (await self.wallet_create("foo", create_account=True))["id"]
|
||||
account3 = (await self.account_list(wallet_id=wallet2))[0]["id"]
|
||||
address3 = await self.address_unused(account3, wallet2)
|
||||
await self.chain.send_to_address(address3, 1)
|
||||
await self.generate(1)
|
||||
|
||||
account_balance = self.daemon.jsonrpc_account_balance
|
||||
wallet_balance = self.daemon.jsonrpc_wallet_balance
|
||||
|
||||
expected = {
|
||||
'total': '10.0',
|
||||
'available': '10.0',
|
||||
'reserved': '0.0',
|
||||
'reserved_subtotals': {'claims': '0.0', 'supports': '0.0', 'tips': '0.0'}
|
||||
}
|
||||
self.assertEqual(await account_balance(), expected)
|
||||
self.assertEqual(await wallet_balance(), expected)
|
||||
self.assertEqual(await self.account_balance(), expected)
|
||||
self.assertEqual(await self.wallet_balance(), expected)
|
||||
|
||||
# claim with update + supporting our own claim
|
||||
stream1 = await self.stream_create('granularity', '3.0')
|
||||
await self.generate(1)
|
||||
await self.stream_update(self.get_claim_id(stream1), data=b'news', bid='1.0')
|
||||
await self.generate(1)
|
||||
await self.support_create(self.get_claim_id(stream1), '2.0')
|
||||
expected = {
|
||||
'total': '9.977534',
|
||||
'available': '6.977534',
|
||||
'total': '9.977558',
|
||||
'available': '6.977558',
|
||||
'reserved': '3.0',
|
||||
'reserved_subtotals': {'claims': '1.0', 'supports': '2.0', 'tips': '0.0'}
|
||||
}
|
||||
self.assertEqual(await account_balance(), expected)
|
||||
self.assertEqual(await wallet_balance(), expected)
|
||||
self.assertEqual(await self.account_balance(), expected)
|
||||
self.assertEqual(await self.wallet_balance(), expected)
|
||||
|
||||
address2 = await self.daemon.jsonrpc_address_unused(account2.id)
|
||||
address2 = await self.address_unused(account2)
|
||||
|
||||
# send lbc to someone else
|
||||
tx = await self.daemon.jsonrpc_account_send('1.0', address2)
|
||||
await self.confirm_tx(tx.id)
|
||||
self.assertEqual(await account_balance(), {
|
||||
'total': '8.97741',
|
||||
'available': '5.97741',
|
||||
tx = await self.wallet_send('1.0', address2, fund_account_id=self.account.id)
|
||||
await self.generate(1)
|
||||
self.assertEqual(await self.account_balance(), {
|
||||
'total': '8.977434',
|
||||
'available': '5.977434',
|
||||
'reserved': '3.0',
|
||||
'reserved_subtotals': {'claims': '1.0', 'supports': '2.0', 'tips': '0.0'}
|
||||
})
|
||||
self.assertEqual(await wallet_balance(), {
|
||||
'total': '9.97741',
|
||||
'available': '6.97741',
|
||||
self.assertEqual(await self.wallet_balance(), {
|
||||
'total': '9.977434',
|
||||
'available': '6.977434',
|
||||
'reserved': '3.0',
|
||||
'reserved_subtotals': {'claims': '1.0', 'supports': '2.0', 'tips': '0.0'}
|
||||
})
|
||||
|
||||
# tip received
|
||||
support1 = await self.support_create(
|
||||
self.get_claim_id(stream1), '0.3', tip=True, wallet_id=wallet2.id
|
||||
self.get_claim_id(stream1), '0.3', tip=True, wallet_id=wallet2
|
||||
)
|
||||
self.assertEqual(await account_balance(), {
|
||||
'total': '9.27741',
|
||||
'available': '5.97741',
|
||||
self.assertEqual(await self.account_balance(), {
|
||||
'total': '9.277434',
|
||||
'available': '5.977434',
|
||||
'reserved': '3.3',
|
||||
'reserved_subtotals': {'claims': '1.0', 'supports': '2.0', 'tips': '0.3'}
|
||||
})
|
||||
self.assertEqual(await wallet_balance(), {
|
||||
'total': '10.27741',
|
||||
'available': '6.97741',
|
||||
self.assertEqual(await self.wallet_balance(), {
|
||||
'total': '10.277434',
|
||||
'available': '6.977434',
|
||||
'reserved': '3.3',
|
||||
'reserved_subtotals': {'claims': '1.0', 'supports': '2.0', 'tips': '0.3'}
|
||||
})
|
||||
|
||||
# tip claimed
|
||||
tx = await self.daemon.jsonrpc_support_abandon(txid=support1['txid'], nout=0)
|
||||
await self.confirm_tx(tx.id)
|
||||
self.assertEqual(await account_balance(), {
|
||||
'total': '9.277303',
|
||||
'available': '6.277303',
|
||||
tx = await self.support_abandon(txid=support1['txid'])
|
||||
await self.generate(1)
|
||||
self.assertEqual(await self.account_balance(), {
|
||||
'total': '9.277327',
|
||||
'available': '6.277327',
|
||||
'reserved': '3.0',
|
||||
'reserved_subtotals': {'claims': '1.0', 'supports': '2.0', 'tips': '0.0'}
|
||||
})
|
||||
self.assertEqual(await wallet_balance(), {
|
||||
'total': '10.277303',
|
||||
'available': '7.277303',
|
||||
self.assertEqual(await self.wallet_balance(), {
|
||||
'total': '10.277327',
|
||||
'available': '7.277327',
|
||||
'reserved': '3.0',
|
||||
'reserved_subtotals': {'claims': '1.0', 'supports': '2.0', 'tips': '0.0'}
|
||||
})
|
||||
|
||||
stream2 = await self.stream_create(
|
||||
'granularity-is-cool', '0.1', account_id=account2.id, funding_account_ids=[account2.id]
|
||||
'granularity-is-cool', '0.1',
|
||||
account_id=account2, fund_account_id=[account2], change_account_id=account2
|
||||
)
|
||||
|
||||
# tip another claim
|
||||
await self.support_create(
|
||||
self.get_claim_id(stream2), '0.2', tip=True, wallet_id=wallet2.id
|
||||
self.get_claim_id(stream2), '0.2', tip=True, wallet_id=wallet2
|
||||
)
|
||||
self.assertEqual(await account_balance(), {
|
||||
'total': '9.277303',
|
||||
'available': '6.277303',
|
||||
self.assertEqual(await self.account_balance(), {
|
||||
'total': '9.277327',
|
||||
'available': '6.277327',
|
||||
'reserved': '3.0',
|
||||
'reserved_subtotals': {'claims': '1.0', 'supports': '2.0', 'tips': '0.0'}
|
||||
})
|
||||
self.assertEqual(await wallet_balance(), {
|
||||
'total': '10.439196',
|
||||
'available': '7.139196',
|
||||
self.assertEqual(await self.wallet_balance(), {
|
||||
'total': '10.43922',
|
||||
'available': '7.13922',
|
||||
'reserved': '3.3',
|
||||
'reserved_subtotals': {'claims': '1.1', 'supports': '2.0', 'tips': '0.2'}
|
||||
})
|
||||
|
@ -225,7 +159,7 @@ class WalletEncryptionAndSynchronization(CommandTestCase):
|
|||
|
||||
async def asyncSetUp(self):
|
||||
await super().asyncSetUp()
|
||||
self.daemon2 = await self.add_daemon(
|
||||
self.daemon2 = await self.add_full_node(
|
||||
seed="chest sword toast envelope bottom stomach absent "
|
||||
"carbon smart garage balance margin twelve"
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue