區塊鏈學習(4) 難度目標調整

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章