added txo_spend command to support liquidating large number of txos (eg. tips)

This commit is contained in:
Lex Berezhny 2020-03-26 22:16:05 -04:00
parent 6494754ab9
commit 48d2497eb2
4 changed files with 76 additions and 1 deletions

View file

@ -4248,6 +4248,58 @@ class Daemon(metaclass=JSONRPCServerType):
self._constrain_txo_from_kwargs(constraints, **kwargs) self._constrain_txo_from_kwargs(constraints, **kwargs)
return paginate_rows(claims, None if no_totals else claim_count, page, page_size, **constraints) return paginate_rows(claims, None if no_totals else claim_count, page, page_size, **constraints)
@requires(WALLET_COMPONENT)
async def jsonrpc_txo_spend(
self, account_id=None, wallet_id=None, batch_size=1000,
preview=False, blocking=False, **kwargs):
"""
Spend transaction outputs, batching into multiple transactions as necessary.
Usage:
txo_spend [--account_id=<account_id>] [--type=<type>...] [--txid=<txid>...]
[--claim_id=<claim_id>...] [--channel_id=<channel_id>...] [--name=<name>...]
[--is_my_input | --is_not_my_input]
[--exclude_internal_transfers] [--wallet_id=<wallet_id>]
[--batch_size=<batch_size>]
Options:
--type=<type> : (str or list) claim type: stream, channel, support,
purchase, collection, repost, other
--txid=<txid> : (str or list) transaction id of outputs
--claim_id=<claim_id> : (str or list) claim id
--channel_id=<channel_id> : (str or list) claims in this channel
--name=<name> : (str or list) claim name
--is_my_input : (bool) show outputs created by you
--is_not_my_input : (bool) show outputs not created by you
--exclude_internal_transfers: (bool) excludes any outputs that are exactly this combination:
"--is_my_input --is_my_output --type=other"
this allows to exclude "change" payments, this
flag can be used in combination with any of the other flags
--account_id=<account_id> : (str) id of the account to query
--wallet_id=<wallet_id> : (str) restrict results to specific wallet
--batch_size=<batch_size> : (int) number of txos to spend per transactions
Returns: {List[Transaction]}
"""
wallet = self.wallet_manager.get_wallet_or_default(wallet_id)
accounts = [wallet.get_account_or_error(account_id)] if account_id else wallet.accounts
txos = await self.ledger.get_txos(
wallet=wallet, accounts=accounts, read_only=True,
**self._constrain_txo_from_kwargs({}, unspent=True, is_my_output=True, **kwargs)
)
txs = []
while txos:
txs.append(
await Transaction.create(
[Input.spend(txos.pop()) for _ in range(min(len(txos), batch_size))],
[], accounts, accounts[0]
)
)
if not preview:
for tx in txs:
await self.broadcast_or_release(tx, blocking)
return txs
@requires(WALLET_COMPONENT) @requires(WALLET_COMPONENT)
def jsonrpc_txo_sum(self, account_id=None, wallet_id=None, **kwargs): def jsonrpc_txo_sum(self, account_id=None, wallet_id=None, **kwargs):
""" """

View file

@ -565,6 +565,14 @@ class CommandTestCase(IntegrationTestCase):
self.daemon.jsonrpc_wallet_send(*args, **kwargs), confirm self.daemon.jsonrpc_wallet_send(*args, **kwargs), confirm
) )
async def txo_spend(self, *args, confirm=True, **kwargs):
txs = await self.daemon.jsonrpc_txo_spend(*args, **kwargs)
if confirm:
await asyncio.wait([self.ledger.wait(tx) for tx in txs])
await self.generate(1)
await asyncio.wait([self.ledger.wait(tx, self.blockchain.block_expected) for tx in txs])
return self.sout(txs)
async def resolve(self, uri, **kwargs): async def resolve(self, uri, **kwargs):
return (await self.out(self.daemon.jsonrpc_resolve(uri, **kwargs)))[uri] return (await self.out(self.daemon.jsonrpc_resolve(uri, **kwargs)))[uri]

View file

@ -266,7 +266,7 @@ class Ledger(metaclass=LedgerRegistry):
self.constraint_spending_utxos(constraints) self.constraint_spending_utxos(constraints)
return self.db.get_utxo_count(**constraints) return self.db.get_utxo_count(**constraints)
async def get_txos(self, resolve=False, **constraints): async def get_txos(self, resolve=False, **constraints) -> List[Output]:
txos = await self.db.get_txos(**constraints) txos = await self.db.get_txos(**constraints)
if resolve: if resolve:
return await self._resolve_for_local_results(constraints.get('accounts', []), txos) return await self._resolve_for_local_results(constraints.get('accounts', []), txos)

View file

@ -610,6 +610,21 @@ class TransactionOutputCommands(ClaimTestCase):
{'day': '2016-06-25', 'total': '0.6'}, {'day': '2016-06-25', 'total': '0.6'},
], plot) ], plot)
async def test_txo_spend(self):
stream_id = self.get_claim_id(await self.stream_create())
for _ in range(10):
await self.support_create(stream_id, '0.1')
await self.assertBalance(self.account, '7.978478')
self.assertEqual('1.0', lbc(await self.txo_sum(type='support', unspent=True)))
txs = await self.txo_spend(type='support', batch_size=3)
self.assertEqual(4, len(txs))
self.assertEqual(3, len(txs[0]['inputs']))
self.assertEqual(3, len(txs[1]['inputs']))
self.assertEqual(3, len(txs[2]['inputs']))
self.assertEqual(1, len(txs[3]['inputs']))
self.assertEqual('0.0', lbc(await self.txo_sum(type='support', unspent=True)))
await self.assertBalance(self.account, '8.977606')
class ClaimCommands(ClaimTestCase): class ClaimCommands(ClaimTestCase):