-
Notifications
You must be signed in to change notification settings - Fork 489
Support symbolic amount/bid=everything which spends all funds from selected account/wallets. #3605
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 15 commits
9cf28f0
7bafce0
262c30e
508acb3
1fb3d83
6f12ea3
42708fc
0fce4f8
c1fe4ca
f7dfc66
4a1b1da
07e8ab3
cfefe99
8b2c284
665c12b
6cfcb6e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -2,7 +2,7 @@ | |||
| import logging | ||||
| import typing | ||||
| from binascii import hexlify, unhexlify | ||||
| from typing import List, Iterable, Optional, Tuple | ||||
| from typing import List, Iterable, Optional, Tuple, Union | ||||
|
|
||||
| from lbry.error import InsufficientFundsError | ||||
| from lbry.crypto.hash import hash160, sha256 | ||||
|
|
@@ -12,6 +12,7 @@ | |||
| from lbry.schema.base import Signable | ||||
| from lbry.schema.purchase import Purchase | ||||
| from lbry.schema.support import Support | ||||
| from lbry.wallet.dewies import amount_to_dewies | ||||
|
|
||||
| from .script import InputScript, OutputScript | ||||
| from .constants import COIN, DUST, NULL_HASH32 | ||||
|
|
@@ -793,7 +794,8 @@ def ensure_all_have_same_ledger_and_wallet( | |||
| @classmethod | ||||
| async def create(cls, inputs: Iterable[Input], outputs: Iterable[Output], | ||||
| funding_accounts: Iterable['Account'], change_account: 'Account', | ||||
| sign: bool = True): | ||||
| sign: bool = True, | ||||
| *, everything: bool = False): | ||||
| """ Find optimal set of inputs when only outputs are provided; add change | ||||
| outputs if only inputs are provided or if inputs are greater than outputs. """ | ||||
|
|
||||
|
|
@@ -803,6 +805,20 @@ async def create(cls, inputs: Iterable[Input], outputs: Iterable[Output], | |||
|
|
||||
| ledger, _ = cls.ensure_all_have_same_ledger_and_wallet(funding_accounts, change_account) | ||||
|
|
||||
| if everything and not any(map(lambda txi: not txi.txo_ref.txo.is_claim, tx._inputs)): | ||||
| # Spend "everything" requested, but inputs not specified. | ||||
| # Make a set of inputs from all funding accounts. | ||||
| all_utxos = [] | ||||
| for acct in funding_accounts: | ||||
| # TODO: Constraints for get_utxos()? | ||||
| utxos = await acct.get_utxos() | ||||
|
||||
| def constraint_spending_utxos(constraints): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TXO_TYPES = {
"other": 0,
"stream": 1,
"channel": 2,
"support": 3,
"purchase": 4,
"collection": 5,
"repost": 6,
}
def constraint_spending_utxos(constraints):
constraints['txo_type__in'] = (0, TXO_TYPES['purchase'])
More context:
7d333ef
I take it "other" is a generic txo carrying an amount of LBC created by wallet_send, and "purchase" is a purchase receipt txo created by purchase_create. It is unclear there is any non-zero amount associated with a purchase receipt txo. Is this correct?
The intention of the original bug was to send only "other" funds, NOT purchase receipts (and NOT the claim types as you say). Transferring both "other" and "purchase" inside jsonrpc_account_fund() kind of makes sense because in that context both from/to accounts are in the same wallet.
I will work to exclude "purchase" txos from funds transferred by support_create, channel_create, stream_create, etc.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1125,23 +1125,31 @@ async def test_channel_bids(self): | |
| tx = await self.channel_update(claim_id) | ||
| self.assertEqual(tx['outputs'][0]['amount'], '5.0') | ||
|
|
||
| # bid changed on update | ||
| # spend exactly amount available, no change | ||
| tx = await self.channel_update(claim_id, bid_everything=True) | ||
| await self.assertBalance(self.account, '0.0') | ||
| self.assertEqual(len(tx['outputs']), 1) # no change | ||
| self.assertEqual(tx['outputs'][0]['amount'], '9.991457') | ||
| self.assertItemCount(await self.daemon.jsonrpc_channel_list(), 1) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you move this to the end of the test, this way you don't have to change the values of the other asserts. |
||
|
|
||
| # bid reduced on update | ||
| tx = await self.channel_update(claim_id, bid='4.0') | ||
| self.assertEqual(tx['outputs'][0]['amount'], '4.0') | ||
|
|
||
| await self.assertBalance(self.account, '5.991503') | ||
| await self.assertBalance(self.account, '5.991299') | ||
|
|
||
| # not enough funds | ||
| with self.assertRaisesRegex( | ||
| InsufficientFundsError, "Not enough funds to cover this transaction."): | ||
| await self.channel_create('@foo2', '9.0') | ||
| self.assertItemCount(await self.daemon.jsonrpc_channel_list(), 1) | ||
| await self.assertBalance(self.account, '5.991503') | ||
| await self.assertBalance(self.account, '5.991299') | ||
|
|
||
| # spend exactly amount available, no change | ||
| tx = await self.channel_create('@foo3', '5.981322') | ||
| tx = await self.channel_create('@foo3', bid=None, bid_everything=True) | ||
| await self.assertBalance(self.account, '0.0') | ||
| self.assertEqual(len(tx['outputs']), 1) # no change | ||
| self.assertEqual(tx['outputs'][0]['amount'], '5.98122') | ||
| self.assertItemCount(await self.daemon.jsonrpc_channel_list(), 2) | ||
|
|
||
| async def test_setting_channel_fields(self): | ||
|
|
@@ -1337,23 +1345,31 @@ async def test_stream_bids(self): | |
| tx = await self.stream_update(claim_id) | ||
| self.assertEqual(tx['outputs'][0]['amount'], '2.0') | ||
|
|
||
| # bid changed on update | ||
| # spend exactly amount available, no change | ||
| tx = await self.stream_update(claim_id, bid_everything=True) | ||
| await self.assertBalance(self.account, '0.0') | ||
| self.assertEqual(len(tx['outputs']), 1) # no change | ||
| self.assertEqual(tx['outputs'][0]['amount'], '9.993347') | ||
| self.assertItemCount(await self.daemon.jsonrpc_claim_list(), 1) | ||
|
|
||
| # bid reduced on update | ||
| tx = await self.stream_update(claim_id, bid='3.0') | ||
| self.assertEqual(tx['outputs'][0]['amount'], '3.0') | ||
|
|
||
| await self.assertBalance(self.account, '6.993319') | ||
| await self.assertBalance(self.account, '6.993134') | ||
|
|
||
| # not enough funds | ||
| with self.assertRaisesRegex( | ||
| InsufficientFundsError, "Not enough funds to cover this transaction."): | ||
| await self.stream_create('foo2', '9.0') | ||
| self.assertItemCount(await self.daemon.jsonrpc_claim_list(), 1) | ||
| await self.assertBalance(self.account, '6.993319') | ||
| await self.assertBalance(self.account, '6.993134') | ||
|
|
||
| # spend exactly amount available, no change | ||
| tx = await self.stream_create('foo3', '6.98523') | ||
| tx = await self.stream_create('foo3', bid=None, bid_everything=True) | ||
| await self.assertBalance(self.account, '0.0') | ||
| self.assertEqual(len(tx['outputs']), 1) # no change | ||
| self.assertEqual(tx['outputs'][0]['amount'], '6.985055') | ||
| self.assertItemCount(await self.daemon.jsonrpc_claim_list(), 2) | ||
|
|
||
| async def test_stream_update_and_abandon_across_accounts(self): | ||
|
|
@@ -2113,7 +2129,7 @@ async def test_abandoning_stream_at_loss(self): | |
| async def test_publish(self): | ||
|
|
||
| # errors on missing arguments to create a stream | ||
| with self.assertRaisesRegex(Exception, "'bid' is a required argument for new publishes."): | ||
| with self.assertRaisesRegex(Exception, "None or null is not valid value for argument 'bid'."): | ||
| await self.daemon.jsonrpc_publish('foo') | ||
|
|
||
| # successfully create stream | ||
|
|
@@ -2271,9 +2287,32 @@ async def test_regular_supports_and_tip_supports(self): | |
| self.assertEqual(txs2[0]['value'], '0.0') | ||
| self.assertEqual(txs2[0]['fee'], '-0.0001415') | ||
|
|
||
| # send all remaining funds to support the claim using account2 | ||
| support = await self.out( | ||
| self.daemon.jsonrpc_support_create( | ||
| claim_id, amount=None, tip=False, account_id=account2.id, wallet_id='wallet2', | ||
| funding_account_ids=[account2.id], amount_everything=True, blocking=True) | ||
| ) | ||
| await self.confirm_tx(support['txid']) | ||
|
|
||
| # account2 balance went down to 0.0 | ||
| await self.assertBalance(self.account, '3.979769') | ||
| await self.assertBalance(account2, '0.0') | ||
|
|
||
| # verify that the outgoing support is marked correctly as is_tip=False in account2 | ||
| txs2 = await self.transaction_list(wallet_id='wallet2') | ||
| self.assertEqual(len(txs2[0]['support_info']), 1) | ||
| self.assertEqual(txs2[0]['support_info'][0]['balance_delta'], '-1.9996035') | ||
| self.assertEqual(txs2[0]['support_info'][0]['claim_id'], claim_id) | ||
| self.assertFalse(txs2[0]['support_info'][0]['is_tip']) | ||
| self.assertFalse(txs2[0]['support_info'][0]['is_spent']) | ||
| self.assertEqual(txs2[0]['value'], '0.0') | ||
| self.assertEqual(txs2[0]['fee'], '-0.0001135') | ||
|
|
||
| # abandoning the tip increases balance and shows tip as spent | ||
| await self.support_abandon(claim_id) | ||
| await self.assertBalance(self.account, '4.979662') | ||
| await self.assertBalance(account2, '0.0') | ||
| txs = await self.transaction_list(account_id=self.account.id) | ||
| self.assertEqual(len(txs[0]['abandon_info']), 1) | ||
| self.assertEqual(len(txs[1]['support_info']), 1) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All caps variable already means this is a constant, doesn't need a comment above it saying the same.