Saturday, July 6, 2024

issue – Why this ugly wanting system to calculate ‘goal’ from ‘nBits’ in block header: goal = coefficient * 256**(exponent-3)

TL;DR The system comes from turning an algorithm right into a mathematical system. nBits encodes the goal with the primary bytes as the scale of the ultimate goal, adopted by the three most vital bytes of that concentrate on. This may be transformed into that loopy system.


The system is the mathematical illustration of the particular algorithm that was used for the compression. To grasp why the system is as it’s, we have to first check out the unique code that encodes a 256 bit goal as a 4 byte nBits. That is from 0.1.5 within the bitcoin/bitcoin supply tree, however is similar in 0.1.0:

unsigned int GetCompact() const
= (vch[4] << 16);
    if (nSize >= 2) nCompact 

Now the very first thing to take a look at is that this BN_bn2pi operate. Early variations of Bitcoin used OpenSSL’s Bignum module for these calculations. So we have to have a look at the OpenSSL’s docs for this operate. From the docs, we learn:

BN_bn2mpi() and BN_mpi2bn() convert BIGNUMs from and to a format that consists of the quantity’s size in bytes represented as a 4-byte big-endian quantity, and the quantity itself in big-endian format, the place essentially the most vital bit alerts a unfavourable quantity (the illustration of numbers with the MSB set is prefixed with null byte).

BN_bn2mpi() shops the illustration of a at to, the place to should be giant sufficient to carry the outcome. The scale could be decided by calling BN_bn2mpi(a, NULL).

Because of this BN_bn2mpi will place right into a buffer the Bignum within the format

<4 byte measurement> | <variable size quantity>

Calling BN_bn2mpi with NULL because the buffer will return the variety of bytes that that buffer will have to be. That is helpful to know what number of bytes to allocate for the buffer.

So let’s return to the GetCompact operate. We see BN_bn2mpi(this, NULL);. Because of this nSize is now the scale that’s wanted to encode the Bignum. As a result of this encoding additionally contains the scale of the quantity itself, we later see nSize -= 4; which units nSize to be the scale of the particular quantity itself.

BN_bn2mpi(this, &vch[0]); now encodes the Bignum into vch which was set to the scale specified by the primary BN_bn2mpi name. It is very important do not forget that the primary 4 bytes are the size of the quantity, so the precise quantity itself begins at index 4 (vch[4]).

Lastly, the compact quantity itself is constructed. nSize << 24 is simply to set the rightmost byte of nSize to be the leftmost byte of nCompact. Then the operate is setting the remainder of the compact quantity. Every byte is simply being shifted to it is last place within the 4 byte int and OR’d with nCompact to set it. The if statements are for the case that the goal is so low that it’s encoded in fewer bytes than the compact measurement itself.

Taking a look at this operate, we study that the compact encoding is absolutely only one byte indicating the size of the goal, and three bytes for the three most vital bytes in that concentrate on. For those who seemed on the SetCompact operate which takes a compact nBits and converts it right into a Bignum, you’ll see that it’s simply the inverse of GetCompact, so I will not clarify it.

Now the query is, how will we get to the loopy wanting system? How did we go from this code that strictly is simply byte manipulation to a mathematical system?

In your instance, primarily based on the above algorithm, we all know that the ultimate quantity goes to be 0x00ffff0000000000000000000000000000000000000000000000000000. We wish the primary 3 bytes, which we received from the nBits, to be by themselves, so let’s divide this quantity by that:

0x00ffff0000000000000000000000000000000000000000000000000000 / 0x00ffff = 0x010000000000000000000000000000000000000000000000000000

0x010000000000000000000000000000000000000000000000000000 is a 1 adopted by 26 bytes of 0, so it is 256**26 (256 since there are 256 attainable values in a byte). Now how will we get this quantity from the nBits? We will take the primary byte, representing the size of the complete factor, and subtract 3 from it.

256**(0x1d-3)

We will develop this additional as a result of 256 = 2**8 and a few individuals like issues represented as an influence of two. So this may turn into 2**(8*0x1d-3). Thus

0x010000000000000000000000000000000000000000000000000000 = 2**(8*0x1d-3)

Subsequently:

0x00ffff0000000000000000000000000000000000000000000000000000 = 0x010000000000000000000000000000000000000000000000000000 * 0x00ffff = 2**(8*0x1d-3) * 0x00ffff

The ultimate result’s 2**(8*0x1d-3) * 0x00ffff. And, in fact, this generalizes.


A semi-related query then is why does the Bignum encoding have a number one 0x00 byte? An encoding that represents this most goal with a bit of bit extra precision would have been 0x1cffffff so we might keep away from this extraneous 0x00 byte.

That main 0x00 byte is all as a result of the Bignum is signed and the goal is a constructive integer. Essentially the most vital bit signifies whether or not it’s unfavourable. If the goal had been encoded 0xffff...., then decoding this might imply that the goal is unfavourable, which is flawed. So the encoding places a number one 0x00 in order that the quantity stays constructive.

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles