Quite a few program are out there that use Bitcoin's RPC interface to send vast amounts of money. As far as I can tell, this involves POSTing a JSON object over HTTP to Bitcoind and receiving JSON in return. The problem I've noticed is due to the fact that we're using JSON's double-precision floating point format (and also floats in the client) instead of encoding the number as a string or in a 64 bit integer like Bitcoin does.
For example, let's say I want to send 0.00002 to "address".
Using Jeff Garzik's Python library I code something like this:
x.sendtoaddress("address", 0.00002)
Notice first that the library seems to only accept floating point numbers. Why is that? Why are we using floats for delicate financial operations?
The above code produces this:
Send -> {"method": "sendtoaddress", "params": ["address", 2e-05], "id": 1, "version": "1.1"}
Now notice the Python float has been converted to its equivalent exponential E notation, which is valid in JSON. But it still doesn't change the fact that we aren't talking precision here, or are we? Can anyone explain why this is safe to do? For example, are there any situations where either Bitcoin or the JSON-RPC Python client doesn't receive the actual amount of coins due to rounding / floating point conversion errors? Is this a precise why to handle money?
It seems a shame to even be having this discussion given that Satoshi himself was aware of this problem which is precisely (pun somewhat intended) why he used integers. I have no clue why we're talking about floating point, rounding, and truncation when the reference implementation is internally perfect. It just seems to me that the interfaces to talk to Bitcoin isn't, unless someone can explain to me why this is safe. The Wiki for proper money also mentions that rounding is required. Is proper rounding done by python-bitcoinrpc? These issues aren't discussed any where. The Wiki also goes on to say that "You, or the JSON library you are using, should convert amounts to either a fixed-point Decimal representation (with 8 digits after the decimal point) or ideally a 64-bit integer representation." Yeah, no kidding. Now any sample code to actually do that or is it currently impossible with JSON double-precision types?
def JSONtoAmount(value):
return long(round(value * 1e8))
def AmountToJSON(amount):
return float(amount / 1e8)
Context?
If any of these questions seem stupid then I apologize. But people are always complaining about incompetent developers and poorly secure software and Bitcoin doesn't even have proper documentation for something as important as precise handling of money. Which, fair enough, it's still a new project. But I'm starting to think that only the core developers have any clue how any of this works and it's very hard to learn when the only proper documentation is literally a 10,000 line+ C++ project.
Thanks for reading.