Hi 2112. I appreciate your taking the time to reply in depth.
That said, it seems that whenever the subject of fee calculation via API comes up, there is a lot of negativity shooting the messenger and very little contructive effort to actually come up with a solution. I'm hoping to see more of the latter this time around.
anyway, I will address your points.
1) The coin-selection is a non-trivial problem that is NP-complete in a general case (I've already mentioned that the current solution is a stochastic approximation, which is non-monotonic and therefore breaks the code like yours)
a) How coin-selection works is an implementation detail of bitcoin core. It is not something that API clients should need to know or care about. Getting hit with a surprise fee that is 20%, 50%, or larger of the send amount IS something that API clients should and DO care about.
b) I do not see how this "stochastic approximation" breaks the sample application code I pasted. You see, with my proposed modification, we can ignore what goes on under the hood with coin-selection. When sendtoaddress is called, it works its' coin-selection black magic and determines a fee. If that fee is higher than the max_fee it received from the API caller, then sendtoaddress returns with an "insufficient fee" error code. Otherwise it sends. So that is simple, right?
Now, in addition to that, I proposed as a nice-to-have, an additional API calcsendfee that would take the same address and amount args as sendtoaddress. It would call the same black magic code to select inputs and return a fee amount to the caller. The caller could then decide whether to accept or reject that fee amount. If it accepts it, it could then pass that fee amount as a LIMIT to sendtoaddress. So sendtoaddress may come up with a new fee based on pseudo-randomness or input changes, and if the new fee is higher than the limit, then the send will fail. If it is lower, then the lower fee will be used. Everybody is happy.
Probably calcsendfee should be called estimatesendfee instead, since that is technically more accurate.
The main purpose of estimatesendfee is to provide an "order of magnitude" type of estimate to the caller, so it doesn't have to waste cycles starting with a very low limit (eg 0) and working upwards calling sendtoaddress. This API could also be useful to determine how much fee variation is taking place from the pseudo-random behavior.
2) Bitcoin wallet isn't static while your code snippets are running, new blocks can appear randomly and change the coin selection both by adding and removing coins from it. (This is the most common mistake in the various "enterprise" patches that I've seen: mishandling of orphan blocks)
Yes indeed. I'm well aware and my proposed API change accounts for that. See 1b above.
Nevertheless I would not be at all surprised if in the majority of cases, the fee calculated by sendtoaddress would be the same as the one returned by estimatesendfee. Anyway, that would be interesting to watch.
3) After you correctly solve the first two problems there is an issue of repeated greedy local optimization leaving wallet full of near-dust coins, so the optimization needs to be upgraded to some non-local, near-global or at least allow for optimizing coin selection for a set of transactions.
I'm not sure what you mean by "solve the first two problems". Rather than "solve" them, I just observe that those are implementation details the caller doesn't need to know about, and can be worked around with the proposed fee limit.
4) After you correctly solve problem (3) you need to provide a solution for the common operational requirement of keeping 2 wallets: hot and cold, and manage the coin transfers between the two of them that fulfill the non-scalar optimization goal of maximizing security and minimizing fees.
yeah, that's one of the use cases this proposal is aimed at. See original description. If one is able to define a max fee, then one can enforce a business rule that we do not pay above 1% fees (for example) when transferring to cold-storage. If the fee would be higher, one can then choose to wait until a later time, or retry and maybe get lucky with a new coin selection, or create a raw transaction.
I hope that the above list helps you understand the true "enterprise" requirements and why your proposal is nothing more than a short-sighted temporary hack.
I don't see that this list added anything to the discussion that I had not already considered and/or discussed already in this thread. Nevertheless I thank you for the opportunity to more fully elaborate. I also don't think that calling it a "short-sighted temporary hack" is justified or polite.
If you have a far-sighted, non-hackish proposal in mind, please do share.
surmise whatever you wish. I'm not here out of ego. I just want something implemented that is useful.
I have no objection to a 2 phase commit approach. That's what I meant by locking/state in an earlier comment. However I think that perfection is often the enemy of the good, and right now we have neither. I believe my proposal is at least workable and should be simple enough to implement that even someone with relatively little experience with bitcoind code like myself could do it. While I would not trust myself to implement 2 phase commit correctly.
Again: if you have something better in mind, please post it here. I would very much like to see the actual API calls you propose and pseudo-code for using them. That can lead to more productive discussion. Otherwise, it seems you prefer to just cast stones...
I'm still hoping we can get some type of consensus that either:
a) that my proposed API changes make sense and will be accepted if implemented well. - OR -
b) a core dev will implement some type of 2 phase commit for a true calcsendfee API
So what technical reason prevents that capability from being exposed at the RPC API level?
The technical reason is called "inversion of control" (i.e. server calling client to make a decision). Again this was discussed on this forum many times, at least since the wx->Qt UI transition. Use the search function.
This is getting off into the woods a bit. I agree it is messy for the server to call the client. Still I don't see what prevents something like: lockallunspent( true ); fee = calcsendfee(); sendtoaddress( ..., fee ); localallunspent( false ); Anyway, I don't wish to debate about this, because my proposal handles it in another way without requiring locking.
If it is really such a "sharp pain point" then you are better off basing your work on a different code that Bitcoin Core and submit raw transactions to be reference implementation.
I went down that route. Unfortunately the required fees keep changing from release to release, so properly estimating them is tedious and error-prone. Another factor for me personally (and for some others) is that my code is designed to work with altcoins as well as bitcoin, so it is much cleaner if the wallet software itself lets me know what an appropriate fee is and/or enforces a limit.