1、最大難度目標
中本聰規定:
0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
爲最大目標值,區塊要被比特幣網絡接受其哈希值必須要小於最大目標值。
2、難度目標的存儲
以壓縮格式存儲在區塊頭部的nbits字段中,公式如下:
target = coefficient*2^(8*(exponent-3))
例如創世塊的難度目標爲:0x1D00FFFF,根據公式展開爲:
0x00FFFF * 2^(8*(0x1D-3)) = 0x00000000FFFF0000000000000000000000000000000000000000000000000000
可見壓縮後精度是有損失的。不過這個也無關緊要。
(8*(exponent-3))/4 = 2*(exponent-3) 可以得出十六進制難度係數後面有多少個0。
0x1D00FFFF 展開後 係數00FFFF後面可以算出有52個0。
3、難度目標的調整
比特幣設定爲每10分鐘生成一個區塊,每2016個區塊,自動調整一次難度。
難度調整的公式爲:
新難度值 = 舊難度值 * (之前2016個區塊花費時長(單位秒) / 2016*10*60)
不難看出這個公式的意思是:如果當前算力比較大、出塊的速度比較塊,那麼就加大挖礦難度,反之就降低挖礦難度。
4、難度目標的計算
先來一個小程序掃描一下區塊網絡中的難度信息。
通過命令 curl -s https://chain.api.btc.com/v3/block/0 來獲取區塊信息,然後解析出相關的難度信息。
btc.com不太穩定,有時會返回失敗。可以多試幾次。
// xmain.c
// by maxzero
#include "xutils.h"
int grab_block_info(int block_num, char *block_buf, int block_len)
{
FILE* f = NULL;
int n = 0;
int r = 0;
char url[1024];
char* buf = NULL;
int len = 0;
memset(url, 0x00, sizeof(url));
sprintf(url, "curl -s https://chain.api.btc.com/v3/block/%d", block_num);
f = popen(url, "r");
buf = block_buf;
len = block_len;
memset(buf, 0x00, len);
n = fread(buf, 1, len, f);
if (n <= 0) {
xprint_err("fread() failed. n=%d\n", n);
r = -1;
}
pclose(f);
return r;
}
int scan_block_bits(int begin, int end, int interval)
{
int i = 0;
int len = 1*1024*1024*sizeof(char);
char *buf = (char*)malloc(len);
char temp[32];
for (i=begin; i<=end; i+=interval) {
printf("block=%06d ", i);
sleep(1);
if (0 != grab_block_info(i, buf, len)) {
continue;
}
memset(temp, 0x00, sizeof(temp));
if (-1 != xkey_value_parser(buf, "\"timestamp\":", ",", temp, sizeof(temp))) {
printf("timestamp=%s ", temp);
}
memset(temp, 0x00, sizeof(temp));
if (-1 != xkey_value_parser(buf, "\"bits\":", ",", temp, sizeof(temp))) {
uint32_t nbits = atoi(temp);
printf("nbits=%s 0x%x ", temp, nbits);
}
memset(temp, 0x00, sizeof(temp));
if (-1 != xkey_value_parser(buf, "\"difficulty\":", ",", temp, sizeof(temp))) {
printf("difficulty=%s", temp);
}
printf("\n");
}
free(buf);
return 0;
}
int main(int argc, char* argv[])
{
/*掃描block 0-552383,間隔2016*/
scan_block_bits(0, 552383, 2016);
return 0;
}
編譯運行,打印有點多,貼的這部分就夠用了。
gcc xmain.c xutils.c
./a.out
block=000000 timestamp=1231006505 nbits=486604799 0x1d00ffff difficulty=1
block=002016 timestamp=1233063531 nbits=486604799 0x1d00ffff difficulty=1
block=004032 timestamp=1234466190 nbits=486604799 0x1d00ffff difficulty=1
block=006048 timestamp=1235966513 nbits=486604799 0x1d00ffff difficulty=1
block=008064 timestamp=1237508786 nbits=486604799 0x1d00ffff difficulty=1
block=010080 timestamp=1239055463 nbits=486604799 0x1d00ffff difficulty=1
block=012096 timestamp=1240599098 nbits=486604799 0x1d00ffff difficulty=1
block=014112 timestamp=1242098425 nbits=486604799 0x1d00ffff difficulty=1
block=016128 timestamp=1243737085 nbits=486604799 0x1d00ffff difficulty=1
block=018144 timestamp=1246051973 nbits=486604799 0x1d00ffff difficulty=1
block=020160 timestamp=1248481816 nbits=486604799 0x1d00ffff difficulty=1
block=022176 timestamp=1252069298 nbits=486604799 0x1d00ffff difficulty=1
block=024192 timestamp=1254454028 nbits=486604799 0x1d00ffff difficulty=1
block=026208 timestamp=1257002900 nbits=486604799 0x1d00ffff difficulty=1
block=028224 timestamp=1259358667 nbits=486604799 0x1d00ffff difficulty=1
block=030240 timestamp=1261130161 nbits=486604799 0x1d00ffff difficulty=1
block=032256 timestamp=1262153464 nbits=486594666 0x1d00d86a difficulty=1.1828995343128408
block=034272 timestamp=1263250117 nbits=486589480 0x1d00c428 difficulty=1.3050621315915245
block=036288 timestamp=1264424879 nbits=486588017 0x1d00be71 difficulty=1.3442249707710294
...
由打印可見,在block=032256時發生了第一次難度調整,nbits由0x1d00ffff 調整爲 0x1d00d86a。
下面我們就來看一下block=032256的nbits是怎麼算出來的。
將相關參數代入公式:
新難度值 = 舊難度值 * (之前2016個區塊花費時長(單位秒) / 2016*10*60)
block-032256->nbits=block-032255->nbits*((block-032255->timestamp-block-030240->timestamp)/2016*10*60)
block-032255->nbits要先轉換爲256位的整數,計算完後再壓縮位32位的整數,最終得到block-032256->nbits。
這裏涉及到256位大數運算,自己搞還是挺費事的。先偷個懶直接用bitcoin中提供的arith_uint256類來計算了,以後有空了再補上吧。計算代碼如下:
// xmain.cpp
// by maxzero
#include <stdio.h>
#include "arith_uint256.h"
uint32_t difficulty_compute(int64_t time_span, uint32_t nbits_prev)
{
uint32_t nbits = 0;
arith_uint256 diff;
diff.SetCompact(nbits_prev);
diff *= time_span;
diff /= (2016*10*60);
nbits = diff.GetCompact();
printf("0x%x\n", nbits);
printf("%s\n", diff.GetHex().c_str());
return nbits;
}
//block=030240 timestamp=1261130161 nbits=486604799 0x1d00ffff difficulty=1
//block=032255 timestamp=1262152739 nbits=486604799 0x1d00ffff difficulty=1
//block=032256 timestamp=1262153464 nbits=486594666 0x1d00d86a difficulty=1.1828995343128408
int main(int argc, char* argv[])
{
difficulty_compute((1262152739-1261130161), 0x1d00ffff);
return 0;
}
編譯運行
g++ xmain.cpp arith_uint256.cpp uint256.cpp utilstrencodings.cpp -std=gnu++11 -I./
#依賴的文件信息
#這些都可以在bitcoin的源碼找到
#https://github.com/bitcoin/bitcoin/tree/master/src
├── a.out
├── arith_uint256.cpp
├── arith_uint256.h
├── compat
│ ├── byteswap.h
│ └── endian.h
├── crypto
│ ├── common.h
│ └── sha256.h
├── xmain.cpp
├── sha256.cpp
├── tinyformat.h
├── uint256.cpp
├── uint256.h
├── utilstrencodings.cpp
└── utilstrencodings.h
./a.out
0x1d00d86a #壓縮後的nbits
00000000d86a528bc8bc8bc8bc8bc8bc8bc8bc8bc8bc8bc8bc8bc8bc8bc8bc8b #未壓縮的nbits