區塊鏈學習(2) 計算一個區塊的哈希值

本文的目的:根據一個區塊的信息算出他的哈希值


區塊信息:
https://www.blockchain.com/zh/btc/block-height/20000


區塊頭格式:
共80個字節,上圖中都是字符串,需要轉換爲二進制的區塊頭格式。

字段 大小 描述
version 04字節 版本號
previous block hash 32字節 前一個區塊的哈希
merkle root 32字節 該區塊中交易的merkle樹根的哈希值
time 04字節 該區塊的創建時間戳 utc時間
bits 04字節 該區塊鏈工作量證明難度目標
nonce 04字節 用於證明工作量的計算隨機數

生成二進制的區塊頭
前提條件 非常重要
1、修改系統時間爲utc時間,因爲區塊頭中的時間戳爲utc時間,爲了避免轉換的麻煩,直接將系統改爲utc時間。
      ubuntu下的修改命令爲: dpkg-reconfigure tzdata -> None of the above -> utc
2、確定系統的字節序爲小端
生成區塊頭的代碼:

typedef struct _block_header_str {
    char *ver;       /*版本號         10進制*/
    char *hash_prev; /*前一區塊hash值 16進制*/
    char *hash_mkrt; /*merkle root    16進制*/
    char *timex;     /*utc時間              */
    char *bits;      /*難度目標       10進制*/
    char *nonce;     /*隨機數         10進制*/
}block_header_str_t;

#if 0
/*Block #20000
00000000770ebe897270ca5f6d539d8afb4ea4f4e757761a34ca82e17207d886*/
block_header_str.ver       = "1";
block_header_str.hash_prev = "00000000ba36eb929dc90170a96ee3efb76cbebee0e0e5c4da9eb0b6e74d9124";
block_header_str.hash_mkrt = "01f8255cad4f0060170f5cca22a5e0ca99fa62b3d26b89a34ac3100d876d14a4";
block_header_str.timex     = "2009-07-22 19:42:19";
block_header_str.bits      = "486604799";
block_header_str.nonce     = "2406314246";
#endif

static int make_block_header(block_header_str_t *block_header_str, uint8_t *block_header_bin)
{
    uint8_t  *p = block_header_bin; 
    uint8_t   temp[128];
    struct tm time_info;

    uint32_t ver   = 0;
    uint32_t bits  = 0;
    uint32_t nonce = 0;
    time_t   timex = 0;
    
    /*寫入版本號*/
    ver = atoi(block_header_str->ver);
    memcpy(p, &ver, 4);
    p += 4;

    /*寫入前一個區塊的哈希*/
    memset(temp, 0x00, sizeof(temp));
    hex2bin(temp, block_header_str->hash_prev, strlen(block_header_str->hash_prev));
    xreverse_dword((uint32_t*)temp, 8, 1);
    memcpy(p, temp, 32);
    p += 32;
    
    /*寫入默克樹根的哈希*/
    memset(temp, 0x00, sizeof(temp));
    hex2bin(temp, block_header_str->hash_mkrt, strlen(block_header_str->hash_mkrt));
    xreverse_dword((uint32_t*)temp, 8, 1);
    memcpy(p, temp, 32);
    p += 32;
    
    /*寫入時間戳*/
    memset(&time_info, 0x00, sizeof(time_info));
    sscanf(block_header_str->timex, "%d-%d-%d %d:%d:%d", \
           &time_info.tm_year, &time_info.tm_mon, &time_info.tm_mday, \
           &time_info.tm_hour, &time_info.tm_min, &time_info.tm_sec);
    time_info.tm_year = time_info.tm_year - 1900;
    time_info.tm_mon  = time_info.tm_mon - 1;
    timex = mktime(&time_info);
    memcpy(p, &timex, 4);
    p += 4;
     
    /*寫入難度目標*/
    bits = atoi(block_header_str->bits);
    memcpy(p, &bits, 4);
    p += 4;

    /*寫入隨機數*/
    nonce = atoi(block_header_str->nonce);
    memcpy(p, &nonce, 4);

    /*按照上面的輸入參數最終生成
    0100000024914de7b6b09edac4e5e0e0bebe6cb7efe36ea97001c99d92eb36ba00000000a4146d870d10c34aa3896bd2b362fa99cae0a522ca5c0f1760004fad5c25f8019b6b674affff001d06716d8f
    */
    xdump_byte(block_header_bin, 80, 80, 1);
    return 0;
}

計算區塊頭的哈希
區塊頭最終要計算2次哈希

static int hash_block_header(void)
{
    block_header_str_t block_header_str;
    uint8_t  block_header_bin[80];
    uint32_t sha1[8];
    uint32_t sha2[8];
    int i = 0;

    /*Block #20000
    00000000770ebe897270ca5f6d539d8afb4ea4f4e757761a34ca82e17207d886*/
    block_header_str.ver       = "1";
    block_header_str.hash_prev = "00000000ba36eb929dc90170a96ee3efb76cbebee0e0e5c4da9eb0b6e74d9124";
    block_header_str.hash_mkrt = "01f8255cad4f0060170f5cca22a5e0ca99fa62b3d26b89a34ac3100d876d14a4";
    block_header_str.timex     = "2009-07-22 19:42:19";
    block_header_str.bits      = "486604799";
    block_header_str.nonce     = "2406314246";
    
    memset(block_header_bin, 0x00, sizeof(block_header_bin));
    make_block_header(&block_header_str, block_header_bin);
    
    /*第一次哈希*/
    xsha256(block_header_bin, 80, sha1);
    printf("sha256-1=");
    for (i=0; i<8; i++) {
        printf("%08x", sha1[i]);
        sha1[i] = htonl(sha1[i]);
    }
    printf("\n");

    /*第二次哈希*/
    xsha256((uint8_t*)sha1, sizeof(sha1), sha2);
    xreverse_dword(sha2, 8, 1);
    printf("sha256-2=");
    for (i=0; i<8; i++) {
        printf("%08x", sha2[i]);
    }
    printf("\n");

    return 0;
}

完整的代碼

// xmain.c
// by maxzero

#include "xutils.h"
#include "xsha256.h"

/*
**小端系統
**dpkg-reconfigure tzdata -> None of the above -> utc  修改UTC時間
*/
typedef struct _block_header_str {
    char *ver;       /*版本號         10進制*/
    char *hash_prev; /*前一區塊hash值 16進制*/
    char *hash_mkrt; /*merkle root    16進制*/
    char *timex;     /*utc時間              */
    char *bits;      /*難度目標       10進制*/
    char *nonce;     /*隨機數         10進制*/
}block_header_str_t;

static int make_block_header(block_header_str_t *block_header_str, uint8_t *block_header_bin)
{
    uint8_t  *p = block_header_bin; 
    uint8_t   temp[128];
    struct tm time_info;

    uint32_t ver   = 0;
    uint32_t bits  = 0;
    uint32_t nonce = 0;
    time_t   timex = 0;
    
    ver = atoi(block_header_str->ver);
    memcpy(p, &ver, 4);
    p += 4;

    memset(temp, 0x00, sizeof(temp));
    hex2bin(temp, block_header_str->hash_prev, strlen(block_header_str->hash_prev));
    xreverse_dword((uint32_t*)temp, 8, 1);
    memcpy(p, temp, 32);
    p += 32;

    memset(temp, 0x00, sizeof(temp));
    hex2bin(temp, block_header_str->hash_mkrt, strlen(block_header_str->hash_mkrt));
    xreverse_dword((uint32_t*)temp, 8, 1);
    memcpy(p, temp, 32);
    p += 32;

    memset(&time_info, 0x00, sizeof(time_info));
    sscanf(block_header_str->timex, "%d-%d-%d %d:%d:%d", \
           &time_info.tm_year, &time_info.tm_mon, &time_info.tm_mday, \
           &time_info.tm_hour, &time_info.tm_min, &time_info.tm_sec);
    time_info.tm_year = time_info.tm_year - 1900;
    time_info.tm_mon  = time_info.tm_mon - 1;
    timex = mktime(&time_info);
    memcpy(p, &timex, 4);
    p += 4;

    bits = atoi(block_header_str->bits);
    memcpy(p, &bits, 4);
    p += 4;

    nonce = atoi(block_header_str->nonce);
    memcpy(p, &nonce, 4);

    xdump_byte(block_header_bin, 80, 80, 1);
    return 0;
}

static int hash_block_header(void)
{
    block_header_str_t block_header_str;
    uint8_t  block_header_bin[80];
    uint32_t sha1[8];
    uint32_t sha2[8];
    int i = 0;

    /*Block #20000
    00000000770ebe897270ca5f6d539d8afb4ea4f4e757761a34ca82e17207d886*/
    block_header_str.ver       = "1";
    block_header_str.hash_prev = "00000000ba36eb929dc90170a96ee3efb76cbebee0e0e5c4da9eb0b6e74d9124";
    block_header_str.hash_mkrt = "01f8255cad4f0060170f5cca22a5e0ca99fa62b3d26b89a34ac3100d876d14a4";
    block_header_str.timex     = "2009-07-22 19:42:19";
    block_header_str.bits      = "486604799";
    block_header_str.nonce     = "2406314246";
    
    memset(block_header_bin, 0x00, sizeof(block_header_bin));
    make_block_header(&block_header_str, block_header_bin);

    xsha256(block_header_bin, 80, sha1);
    printf("sha256-1=");
    for (i=0; i<8; i++) {
        printf("%08x", sha1[i]);
        sha1[i] = htonl(sha1[i]);
    }
    printf("\n");

    xsha256((uint8_t*)sha1, sizeof(sha1), sha2);
    xreverse_dword(sha2, 8, 1);
    printf("sha256-2=");
    for (i=0; i<8; i++) {
        printf("%08x", sha2[i]);
    }
    printf("\n");

    return 0;
}

int main(int argc, char* argv[])
{
    hash_block_header();

    return 0;
}

// xsha256.h
// by maxzero

#ifndef _M_XSHA256_H
#define _M_XSHA256_H

#include "xutils.h"

#ifdef  __cplusplus  
extern "C" {  
#endif 

int xsha256(const uint8_t *src, uint32_t len, uint32_t *hash);

#ifdef  __cplusplus  
}
#endif 
#endif

////////////////////////////////////////////////////////////////////////////////

// xsha256.c
// by maxzero

#include "xsha256.h"

#define XSHA_DBG 0
#define SHFR(x, n) (((x) >> (n)))
#define ROTR(x, n) (((x) >> (n)) | ((x) << ((sizeof(x) << 3) - (n))))
#define ROTL(x, n) (((x) << (n)) | ((x) >> ((sizeof(x) << 3) - (n))))

#define CHX(x, y, z) (((x) &  (y)) ^ (~(x) & (z)))
#define MAJ(x, y, z) (((x) &  (y)) ^ ( (x) & (z)) ^ ((y) & (z)))

#define BSIG0(x) (ROTR(x,  2) ^ ROTR(x, 13) ^ ROTR(x, 22))
#define BSIG1(x) (ROTR(x,  6) ^ ROTR(x, 11) ^ ROTR(x, 25))
#define SSIG0(x) (ROTR(x,  7) ^ ROTR(x, 18) ^ SHFR(x,  3))
#define SSIG1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHFR(x, 10))

#define SHA256_BLOCK_SIZE (512/8)
#define SHA256_COVER_SIZE (SHA256_BLOCK_SIZE*2)


static uint32_t k[64] = {
    0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
    0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
    0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
    0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
    0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
    0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
    0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
    0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};


static int xtransform(const uint8_t *msg, uint32_t *h)
{
    uint32_t w[64];
    uint32_t a0, b1, c2, d3, e4, f5, g6, h7;
    uint32_t t1, t2;

    int i = 0;
    int j = 0;

    for (i=0; i<16; i++) {
        w[i] = msg[j]<<24 | msg[j+1]<<16 | msg[j+2]<<8 | msg[j+3];
        j += 4;
    }

    for(i=16; i<64; i++){
        w[i] = SSIG1(w[i-2])+w[i-7]+SSIG0(w[i-15])+w[i-16];
    }

#if XSHA_DBG
    xdump_byte((uint8_t*)w, 64*4, 32);
#endif

    a0 = h[0];
    b1 = h[1];
    c2 = h[2];
    d3 = h[3];
    e4 = h[4];
    f5 = h[5];
    g6 = h[6];
    h7 = h[7];

    for (i= 0; i<64; i++) {
        t1 = h7 + BSIG1(e4) + CHX(e4, f5, g6) + k[i] + w[i];
        t2 = BSIG0(a0) + MAJ(a0, b1, c2);

        h7 = g6;
        g6 = f5;
        f5 = e4;
        e4 = d3 + t1;
        d3 = c2;
        c2 = b1;
        b1 = a0;
        a0 = t1 + t2;
    }

    h[0] += a0;
    h[1] += b1;
    h[2] += c2;
    h[3] += d3;
    h[4] += e4;
    h[5] += f5;
    h[6] += g6;
    h[7] += h7;

    return 0;
}

int xsha256(const uint8_t *src, uint32_t len, uint32_t *hash)
{
    uint8_t *tmp = (uint8_t*)src;
    uint8_t  cover_data[SHA256_COVER_SIZE];
    uint32_t cover_size = 0;
    
    uint32_t i = 0;
    uint32_t n = 0;
    uint32_t m = 0;
    uint32_t h[8];

    h[0] = 0x6a09e667;
    h[1] = 0xbb67ae85;
    h[2] = 0x3c6ef372;
    h[3] = 0xa54ff53a;
    h[4] = 0x510e527f;
    h[5] = 0x9b05688c;
    h[6] = 0x1f83d9ab;
    h[7] = 0x5be0cd19;

    memset(cover_data, 0x00, sizeof(uint8_t)*SHA256_COVER_SIZE);

    n = len / SHA256_BLOCK_SIZE;
    m = len % SHA256_BLOCK_SIZE;

    if (m < 56 ) {
        cover_size = SHA256_BLOCK_SIZE;
    }else {
        cover_size = SHA256_BLOCK_SIZE*2;
        
    }

    if (m !=0) {
        memcpy(cover_data, tmp + (n * SHA256_BLOCK_SIZE), m);
    }
    cover_data[m] = 0x80;
    cover_data[cover_size-4]  = ((len*8)&0xff000000) >> 24;
    cover_data[cover_size-3]  = ((len*8)&0x00ff0000) >> 16;
    cover_data[cover_size-2]  = ((len*8)&0x0000ff00) >> 8;
    cover_data[cover_size-1]  = ((len*8)&0x000000ff);

#if XSHA_DBG
    xdump_hex(tmp, len-m, 32);
    xdump_hex(cover_data, cover_size, 32);
#endif

    for (i=0; i<n; i++) {
        xtransform(tmp, h);
        tmp += SHA256_BLOCK_SIZE;
    }

    tmp = cover_data;
    n = cover_size / SHA256_BLOCK_SIZE;
    for (i=0; i<n; i++) {
        xtransform(tmp, h);
        tmp += SHA256_BLOCK_SIZE;
    }
    
    if (NULL != hash) {
        memcpy(hash, h, sizeof(uint32_t)*8);
    }
    return 0;
}

// xutils.h
// by maxzero

#ifndef _M_XUTILS_H
#define _M_XUTILS_H

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <memory.h>
#include <string.h>
#include <time.h>
#include <arpa/inet.h>
#include <stdbool.h>

#ifdef  __cplusplus  
extern "C" {  
#endif 

#define xdbg 1

#if xdbg
#define xprint_log(fmt, ...) \
    printf("[%s:%04d] %s() "      fmt, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)
#define xprint_err(fmt, ...) \
    printf("[%s:%04d] %s() err: " fmt, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)
#else
#define xprint_log(fmt, ...)
#define xprint_err(fmt, ...)
#endif

int xdump_byte(const uint8_t *data, int size, int line, int flag);
uint8_t crc5(uint8_t *data, uint8_t size);
bool hex2bin(unsigned char *binstr, const char *hexstr, size_t len);
void xreverse_byte(unsigned char *s, size_t n);
void xreverse_dword(uint32_t *s, size_t n, int f);
#ifdef  __cplusplus  
}
#endif 

#endif

/////////////////////////////////////////////////////////////////////////////////

// xutils.c
// by maxzero

#include "xutils.h"

static const int hex2bin_tbl[256] = {
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
     0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1,
    -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
};

int xdump_byte(const uint8_t *data, int size, int line, int flag)
{
    int i;
    int l = line;
    
    if (size <= 0) { return 0; }

    for (i = 0; i < size; i++) {
        if((i%l) == 0) {
            printf( "[%02x] ", i/l );
        }
        printf( "%02x", data[i] );
        if (0 == flag) {
            printf(" ");
        }
        if(((i+1)%l) == 0) {
            printf( "\n" );
        }
    }
    return 0;
}

uint8_t crc5(uint8_t *data, uint8_t size)
{
    uint8_t i, j, k;
    uint8_t crc = 0x1f;
    uint8_t crcin [5] = {1, 1, 1, 1, 1};
    uint8_t crcout[5] = {1, 1, 1, 1, 1};
    uint8_t din = 0;

    j = 0x80;
    k = 0;
    for (i=0; i<size; i++) {
        if (*data & j) {
            din = 1;
        } else {
            din = 0;
        }
        crcout[0] = crcin[4] ^ din;
        crcout[1] = crcin[0];
        crcout[2] = crcin[1] ^ crcin[4] ^ din;
        crcout[3] = crcin[2];
        crcout[4] = crcin[3];

        j = j >> 1;
        k++;
        if (k == 8) {
            j = 0x80;
            k = 0;
            data++;
        }
        memcpy(crcin, crcout, 5);
    }

    crc = 0;
    if(crcin[4]) crc |= 0x10;
    if(crcin[3]) crc |= 0x08;
    if(crcin[2]) crc |= 0x04;
    if(crcin[1]) crc |= 0x02;
    if(crcin[0]) crc |= 0x01;
    return crc;
}

bool hex2bin(unsigned char *bin, const char *hexstr, size_t len)
{
    int nibble1, nibble2;
    unsigned char idx;
    bool ret = false;

    while (*hexstr && len) {
        if (!hexstr[1]) {
            xprint_err("hex string truncated.\n");
            return ret;
        }

        idx = *hexstr++;
        nibble1 = hex2bin_tbl[idx];
        idx = *hexstr++;
        nibble2 = hex2bin_tbl[idx];

        if ((nibble1<0) || (nibble2<0)) {
            xprint_err("hex string scan failed.\n");
            return ret;
        }

        *bin++ = (((unsigned char)nibble1)<<4) | ((unsigned char)nibble2);
        --len;
    }

    if (len == 0 && *hexstr == 0) {
        ret = true;
    }

    return ret;
}

void xreverse_byte(unsigned char *s, size_t n)
{
    size_t i = 0;
    size_t j = 0;

    for (i=0, j=n-1; i<j; i++, j--) {
        s[i] = s[i] ^ s[j];
        s[j] = s[j] ^ s[i];
        s[i] = s[i] ^ s[j];
    }
    return ;
}

void xreverse_dword(uint32_t *s, size_t n, int f)
{
    size_t i = 0;
    size_t j = 0;

    for (i=0, j=n-1; i<j; i++, j--) {
        s[i] = s[i] ^ s[j];
        s[j] = s[j] ^ s[i];
        s[i] = s[i] ^ s[j];
        if (1 == f) {
            s[i] = htonl(s[i]);
            s[j] = htonl(s[j]);
        }
    }

    return ;
}

編譯運行
ubuntu 14.04 gcc 5.5.0 

[root@ubuntu:src]# gcc xmain.c xsha256.c xutils.c
[root@ubuntu:src]# ./a.out 
[00] 0100000024914de7b6b09edac4e5e0e0bebe6cb7efe36ea97001c99d92eb36ba00000000a4146d870d10c34aa3896bd2b362fa99cae0a522ca5c0f1760004fad5c25f8019b6b674affff001d06716d8f
sha256-1=1545b82c6fe89804068d7c5c5ebc057501d487702a236834bd301c48555b57cd
sha256-2=00000000770ebe897270ca5f6d539d8afb4ea4f4e757761a34ca82e17207d886
[root@ubuntu:src]# 

總結一下,區塊哈希值的計算核心算法就是一個哈希256,然後就是各種字節序的轉換,再一個大坑就是utc時間,爲了省事我直接將系統時間設置爲utc時間了。

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