account balance can go negative due to fee with "sendfrom" #2079

issue Lohoris opened this issue on December 6, 2012
  1. Lohoris commented at 6:38 PM on December 6, 2012: none
    // Check funds
    int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
    if (nAmount > nBalance)
        throw JSONRPCError(-6, "Account has insufficient funds");
    

    This prevents an account balance from going negative, but it doesn't consider the transaction fee, hence if you send out an entire account balance, you'll get a negative balance if there is a transaction fee.

  2. gavinandresen commented at 7:43 PM on December 6, 2012: contributor

    Yes, that is a 'WONTFIX'.

    A sendall account <address> command that took the account to exactly zero might be a good solution, although you can still run into edge cases like "what if the account contains 0.000001 BTC" (fee to sendall might be greater than that).

  3. Lohoris commented at 7:46 PM on December 6, 2012: none

    But... what's the point of preventing the balance from going negative, if you don't actually do that and it can go negative anyway?

    I really don't get it, either completely remove the check, or check it fully... :(

  4. gavinandresen commented at 12:24 AM on December 7, 2012: contributor

    Removing the check completely might have been the right way to go initially, but it is what it is and changing it now might break existing services that rely on the check to make sure customers can't spend more bitcoins than are in their account.

    Always preventing the account from going negative would also break existing services. I know ClearCoin (my closed escrow service) paid all transaction fees, so the code did:

    sendfrom gettransaction <txid> if fee paid: reimburse account for tx fees using 'move' from an account stocked with coins to pay fees

    Having the sendfrom fail because the customer has 10 BTC in their account and sends exactly 10 BTC would be unacceptable.

    Do you have a use case that doesn't work, or is this just a "but that's UGLY!" issue?

  5. Lohoris commented at 12:31 AM on December 7, 2012: none

    Well, I planned not to pay myself the tx fees, so for me it is not acceptable for any account balance to go negative...

    If I had a way to know in advance the tx fee about to be paid, I could manually check if it had enough balance for the whole operation. Of course, since that wouldn't be an atomic operation anyway, it could be pretty pointless (i.e. risks to be exploitable somehow).

    I've seen plenty of services with fees (internal fees) where if you wanted to send away all your money, you had to deduct their fee. For them the tx fee is not a problem, since they deduct it from the fee that goes to them: since I planned to be able to ask no fee for myself, at least I'd like not to go at a loss...

    ps: I already hacked "movecmd" to check the balance before committing the operation, I could try to modify sendfrom too, of course... https://github.com/Lohoris/bitcoin/commit/4442224ee497260de6800fd09d6c2af993a046ac

  6. luke-jr commented at 12:40 AM on December 7, 2012: member

    Any service operator should be aware that it isn't reasonable to simply pass transaction fees on to their end users: generally the fee has little to do with the transaction being requested itself, and more to do with the particular wallet (which is account-independent) history. A fair method of passing this cost on to end users usually amounts to a fixed fee (on your accounting) possibly coupled with a distribution of excess income across users who made transactions every month or so.

  7. Lohoris commented at 1:49 PM on December 8, 2012: none

    Mh, I see. I'd like to be able to choose my approach here, but since this appears to be not only the most used one, but also the only one the software currently supports, I guess I'll have to bend me to it...

  8. skruger commented at 11:17 PM on September 30, 2013: none

    I just started running into this behavior. When looking for a workaround I hoped to find a prepare step where I could see what the fees would be for a transaction that I could then execute once I have gotten user approval. After looking over the docs I did not immediately find a way to do this.

  9. luke-jr commented at 11:40 PM on September 30, 2013: member

    Fees are semi-random, and might not even be related to the account in question. That is, it's not fair to expect the sending account to be responsible for the transaction fees. Instead, the traditional model for services doing things like this is to simply charge users a flat fee that works out to what you pay on average in transaction fees.

    PR #1645 would make it possible to set a limit on the transaction fee included.

  10. skruger commented at 3:32 AM on October 1, 2013: none

    If that is the case shouldn't it then be possible for sendfrom to have an optional argument for which account the fees should come out of? Currently the fees come out of the from account in a way that would require a reimbursement transaction in order to get to the right balance if the sender's account wasn't actually responsible for fees. This does not seem optimal for maintaining a user's account balance, but not charging them outbound transaction fees.

  11. laanwj commented at 5:57 AM on October 1, 2013: member

    @skruger the only correct way would be what @gavinandresen says: let the account pay the fee (possibly going negative), then reimburse the fee to that account. A "prepare" step will not support concurrency as there is no atomic way to do RPC. A way to limit the paid fee in advance would be useful of course, but is orthogonal to this.

  12. skruger commented at 1:17 PM on October 1, 2013: none

    @laanwj I decided to go with letting the account pay the fee and reimbursing, but I observed something a little bit troubling. When I did my move the account I was reimbursing came up to 0, but the account I was reimbursing from didn't change.

    My accounts

    >>> r.listaccounts()
    [u'', u'testing', u'user_account_6', u'user_account_5']
    

    My move request

    >>> [r.getbalance(x) for x in r.listaccounts()]
    [Decimal('0.00650000'), Decimal('0E-8'), Decimal('0E-8'), Decimal('-0.00050000')]
    >>> r.move('', 'user_account_5', 0.0005)
    True
    >>> [r.getbalance(x) for x in r.listaccounts()]
    [Decimal('0.00650000'), Decimal('0E-8'), Decimal('0E-8'), Decimal('0E-8')]
    

    It is troubling to me that the sum of all accounts after doing the reimbursement move is 0.0005 higher than the sum before doing the move. The interesting thing is that my default account with the balance of 0.0065 before the move is the one that was wrong according to my external transaction log table.

    The move command to reimburse actually created the transactions inside the bitcoind database so that a sum of all transactions in the '' account matched getbalance('') which indicates to me that getbalance('') may not be relying on the transaction tables alone for its calculation.

    Based on this it would appear as if the reimbursement step is required for accounting sanity purposes which makes me wish all the more that there were a way to set an account for transaction fees to be charged against when calling sendfrom so as to avoid reimbursement and the accounting oddities I have noted around that (or the oddities when a reimbursement isn't made).

  13. laanwj closed this on Jan 10, 2014

  14. bananas2 commented at 5:06 AM on March 7, 2014: none

    still no solution for this? many online services fail a trasaction if send + fee is larger than balance, but i don't know how they do it.

  15. philsong commented at 7:50 AM on February 13, 2015: contributor

    2 years passed, still do not resolve this issue?

  16. jonasschnelli commented at 7:56 AM on February 13, 2015: contributor

    @philsong I think this still is a "WONTFIX". See #3816 or merged #5575. Accounting could be removed once and can/should be done within your app and not within bitcoin core.

  17. luke-jr commented at 9:04 AM on February 13, 2015: member

    More like not-a-bug. :)

  18. DrahtBot locked this on Sep 8, 2021

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-04-21 18:16 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me