I was browsing through some articles and stumbled upon a question that's super easy to answer: "How many satoshis are in a bitcoin?" I could probably answer that in my sleep, but I realized I haven't really looked into why Bitcoin is measured in satoshis in the first place.
So, I took a fast dive into the source code, and I found a pretty brief piece of code, not more than 30 lines if you count the comments. I had to cut out some parts because the confusion stems from just two lines.
I think the first line of code shows the amount of Bitcoin in its smallest unit (satoshis), which the comment suggests, but it also has this odd statement that says it can be negative.
Then when you check the next line, there’s a boolean statement. If nValue is more than zero but less than MAX, it returns True. Otherwise, it returns False.
So, here’s my question:
1. How can satoshis be negative if any nValue that doesn't meet the True condition must return False?
I’m totally open to corrections since I'm still learning. If I messed up anywhere, just let me know and feel free to share your thoughts. And yeah, maybe consider doing your own research after this.
Can you have negative satoshis?
7 replies 356 views
The amount value can not be negative, we just have to check it to reject invalid amounts.
When you represent a number using a signed value type (in this case signed 64-bit integer called int64_t) you have to check for negative values. The condition can not just be <= max because for example negative one which is 0xFFFFFFFFFFFFFFFF is also smaller than max (0x000775f05a074000) and can only be rejected if you check the sign.
So when we check something like -1 we get to the first condition nValue >= 0 and since that is [false], and [false && whatever] is still false, then false is returned.
P.S. This conditional can be simplified by simply using an unsigned value type instead!
Although apparently since core uses the CAmount in wallet too and needs to represent negative values, they went with a signed value type.
paul.stakeHero Member
Posts: 651 · Reputation: 3798
#3Jul 26, 2019, 04:53 AM
Because in 64 bits, you can represent anything from 1 satoshi, to 21 quadrillion satoshi, whereas if you used "Bitcoin" as a unit, you would need to make use of floating points, which complicates things for no reason. Floating points are useful, if we want to go beyond 21 million, or less than 0.00000001, and if precision does not matter, which is certainly not the case with a currency.
I think his question boils down to "why does it use 'signed' if we do not use negative numbers?".
It's a good question, with no apparent answer AFAICS. Founder does not reply however.
The response and the topic you shared is related to timestamps that can not handle year 2038 if signed int32 is used. It is not related to the signed int64 used for amounts.
The only thing we have about that is the following from bitcoin core dev Pieter Wuille:
For example, you can make a transaction with negative fee:
Then, you sign it with SIGHASH_SINGLE | SIGHASH_ANYONECANPAY. If you check fees for this specific partially signed transaction, you should see a negative value of -1.00 BTC. And then, Bob can claim it, by putting at least 1 BTC in another input. Example: https://bitcointalk.org/index.php?topic=5502111
Someone completed it on testnet3: https://mempool.space/testnet/tx/70f8a2d9e01f8603cc0301fe5b3b809c5a9acfc975a5f122393b1c900b977cba
so basically, you are saying that we can't have a negative value, but the implementation of a signed 64-bit gives room to check for both positive values that are below the MAX, and negative values( which returns False if happens to be checked for) ?. I'm actually finding this very confusing, because from the explanation given below(using a tx as an example), it sounds theoretical to have a negative value(which comes in a form of fx fee), or does it mean that Alice's input will continue to return False, until Bob makes his own combined input alongside the tx fee, using a SIGHASH_SINGLE | SIGHASH_ANYONECANPAY ?.
And maybe due to flexibility i guess, because i doubt if an unsigned-64bit can hold negative values unlike the signed-64bits that can hold both positive and negative values.
I think you are misinterpreting the the code.
The variable type CAmount is a signed integer which means that variables of this type can hold both positive and negative numbers. That does not mean that a CAmount with a negative value is valid in every context. However, it allows for the possibility, and the comment (as I interpret it) is a warning that negative values are a possibility.
There can be instances where a negative value is reasonable. For example, when displaying the history of a wallet's balance, a transaction sending bitcoins from the wallet would show the change in the balance as a negative number of satoshis.
Finally, if you want to get into some C/C++ programming nitty-gritty ...
In C/C++, signed and unsigned integers are assigned to each other without conversion, so an unsigned integer variable can be assigned and can hold a negative value. A programmer might make the mistake of thinking that using an unsigned integer will somehow protect them from negative values, but it won't
A programmer may use an unsigned integer type to indicate that a variable is never expected to be negative, but that creates a false sense of security. In fact, it prevents them from verifying that value is not negative because the the value of an unsigned integer variable is always interpreted to be >= 0.
Consider that (unsigned)-2 >= 0, (unsigned)0 - (unsigned)2 == (unsigned)-2, and (unsigned)-2 + (unsigned)2 == 0 are all true.
So for those reasons, I never use unsigned integers for any variables involved in any mathematical operations, even if I never expect the values to ever be negative.
There is only one way to ensure that a variable is not negative and that is to allow it to be negative and then check its value, which is what the code you referenced does.
You still can't have a negative "amount" in your output. What is negative here is just the fee which is calculated on a higher level (usually for UI). But that can also be interpreted using unsigned integers as in "sum(output amounts >= sum(input amounts)".
If you really want to go down that rabbit hole, it may help if you think of numbers as binary values (as in zeros and ones), just like the computer does. There will no longer be any signed or unsigned values that way.
In fixed length numbers like (U)Int32 and (U)Int64 all the arithmetic is modular. So when you say -2 as a 32-bit number, that is congruent to 4294967294 and both of them are 0b11111111_11111111_11111111_11111110 in binary because the arithmetic here is modulo 4294967296 (=232).
That means the only reason why -2+2 is zero is because it overflows and we discard that extra 1.
0b11111111_11111111_11111111_11111110 [=-2]
0b00000000_00000000_00000000_00000010 [=2] +
---------------------------------------------
0b1_00000000_00000000_00000000_00000000 [=4294967296 % 4294967296 =0]
It may be easier to understand as 8-bit integers (1 byte) which can be from 0 (0x00) to 255 (0xff) and the arithmetic is modulo 256 (=28).
0b11111110 [-2 ≡ 254 (mod 256)]
0b00000010 [2] +
--------------------------
0b1_00000000 [254 + 2 ≡ 256 ≡ 0 (mod 256)]
So it really doesn't matter what value type you use to represent your numbers, the computer interprets them all the same. You just need have to be mindful of the type you used in your code.
Related topics
- Erlay seems to have some issues here’s a better proposal for a bitcoin protocol without invites 3
- Bitcoin Core displaying coins (UTXOs) in a new tab 13
- Are you in favor of BIP-110? Let's get a Bitcoin poll going. 0
- New Optional Hourglass Implementation is Live 3
- Ways to earn some sats by contributing to bitcoin core development 5
- Exploring the Potential and Challenges of a Kardashev-Scale Bitcoin Network 3