本文的目的:根據一個區塊的信息算出他的哈希值
區塊信息:
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時間了。