merkle tree
相关的理论资料较多,这里不做过多描述。
注意当一层的节点为奇数个,最后一个节点需要复制一下生成偶数个节点。每一层都要这样做。
以区块181为例,来描述merkle root的生成过程。
https://www.blockchain.com/zh/btc/block/00000000dc55860c8a29c58d45209318fa9e9dc2c1833a7226d86bc465afc6e5
区块181,merkle root生成过程
1、原始的2笔交易
char* tx1_str=8347cee4a1cb5ad1bb0d92e86e6612dbf6cfc7649c9964f210d4069b426e720a
char* tx2_str=a16f3ce4dd5deb92d98ef5cf8afeaf0775ebca408f708b2146c4fb42b41e14be
2、字符串转十六进制
uint8_t tx1_bin=8347cee4a1cb5ad1bb0d92e86e6612dbf6cfc7649c9964f210d4069b426e720a
uint8_t tx2_bin=a16f3ce4dd5deb92d98ef5cf8afeaf0775ebca408f708b2146c4fb42b41e14be
3、按uint32_t反转,并转换字节序
uint8_t tx1_bin=0a726e429b06d410f264999c64c7cff6db12666ee8920dbbd15acba1e4ce4783
uint8_t tx2_bin=0a726e429b06d410f264999c64c7cff6db12666ee8920dbbd15acba1e4ce4783
4、拼装 tx1_bin+tx2_bin
0a726e429b06d410f264999c64c7cff6db12666ee8920dbbd15acba1e4ce4783be141eb442fbc446218b708f40caeb7507affe8acff58ed992eb5ddde43c6fa1
5、2次hash tx1_bin+tx2_bin 得到的就是merkle root
第1次hash
b734b24bba5d201de6c5367966161e24f0569e4772e770e39ec9e25d81def56c
转换字节序
4bb234b71d205dba7936c5e6241e1666479e56f0e370e7725de2c99e6cf5de81
第2次hash
092a6c36b45df01550c050b4ac6571ce3f825fd50a4351ee0b3e998ced92b1db
按uint32_t反转,得到最终得merkle root
ed92b1db0b3e998c0a4351ee3f825fd5ac6571ce50c050b4b45df015092a6c36
完整的代码
// xmain.c
// by maxzero
#include "xutils.h"
#include "xsha256.h"
typedef struct {
int count;
struct _tx_hash{
uint8_t hash[32];
}*list;
}tx_info_t;
static int xdsha256(const uint8_t *src, uint32_t len, uint32_t *hash)
{
uint32_t sha1[8];
uint32_t sha2[8];
int i = 0;
xsha256(src, len, sha1);
for (i=0; i<8; i++) {
sha1[i] = htonl(sha1[i]);
}
/*
printf("sha256-1=");
xdump_byte((uint8_t*)sha1, 32, 32, 0);*/
xsha256((uint8_t*)sha1, sizeof(sha1), sha2);
xreverse_dword(sha2, 8, 0);
/*
printf("sha256-2=");
xdump_byte((uint8_t*)sha2, 32, 32, 0);*/
if (NULL != hash) {
memcpy(hash, sha2, sizeof(sha2));
}
return 0;
}
static int merkle_hash(uint8_t *tx1_bin, uint8_t *tx2_bin, uint8_t *hash)
{
uint8_t temp[64];
uint8_t *p = temp;
memset(temp, 0x00, sizeof(temp));
xreverse_dword((uint32_t*)tx1_bin, 8, 1);
memcpy(p, tx1_bin, 32);
p += 32;
xreverse_dword((uint32_t*)tx2_bin, 8, 1);
memcpy(p, tx2_bin, 32);
xdsha256(temp, 64, (uint32_t*)hash);
return 0;
}
static int transaction_list_get(int block_num, tx_info_t *tx_info)
{
FILE* f = NULL;
char* p = NULL;
int l = 10*1024*1024*sizeof(char);
int i = 0;
int n = 0;
int r = 0;
char url[1024];
char* buf = NULL;
int len = l;
int txn = 0;
char tmp[80];
memset(url, 0x00, sizeof(url));
sprintf(url, "curl -s https://blockchain.info/block-height/%d?format=json", block_num);
f = popen(url, "r");
if (NULL == f) {
xprint_err("popen() failed. url=%s\n", url);
return -1;
}
printf("url=%s\n", url);
buf = (char *)malloc(len);
if (NULL == buf) {
xprint_err("malloc() failed. len=%d\n", len);
pclose(f);
return -1;
}
sleep(1);
memset(buf, 0x00, len);
p = buf;
n = fread(p, 1, l, f);
if (n <= 0) {
xprint_err("fread() failed. n=%d\n", n);
r = -1;
goto xexit;
}
printf("n=%d l=%d\n", n, l);
/*
printf("%s\n\n", buf);*/
memset(tmp, 0x00, sizeof(tmp));
n = xkey_value_parser(buf, "\"n_tx\":", ",",tmp, sizeof(tmp));
if (-1 == n) {
r = -1;
goto xexit;
}
p = buf + n;
txn = atoi(tmp);
printf("block=%06d txn=%d\n", block_num, txn);
tx_info->count = txn;
tx_info->list = (struct _tx_hash*)malloc(sizeof(struct _tx_hash)*(tx_info->count+txn%2));
for (i=0; ilist[i].hash, tmp, strlen(tmp));
}
printf("\n");
xexit:
free(buf);
pclose(f);
return r;
}
static int make_merkle_tree_hash(int block_num)
{
tx_info_t tx_info;
int i = 0;
int n = 0;
/*
char *tx1_str = "d45724bacd1480b0c94d363ebf59f844fb54e60cdfda0cd38ef67154e9d0bc43";
char *tx2_str = "4d6edbeb62735d45ff1565385a8b0045f066055c9425e21540ea7a8060f08bf2";
char *tx3_str = "6bf363548b08aa8761e278be802a2d84b8e40daefe8150f9af7dd7b65a0de49f";
char *tx4_str = "6bf363548b08aa8761e278be802a2d84b8e40daefe8150f9af7dd7b65a0de49f";
printf(" tx1_str=%s\n", tx1_str);
printf(" tx2_str=%s\n", tx2_str);
tx_info.count = 4;
tx_info.list = (struct _tx_hash*)malloc(sizeof(struct _tx_hash)*tx_info.count);
hex2bin(tx_info.list[0].hash, tx1_str, strlen(tx1_str));
hex2bin(tx_info.list[1].hash, tx2_str, strlen(tx2_str));
hex2bin(tx_info.list[2].hash, tx3_str, strlen(tx3_str));
hex2bin(tx_info.list[3].hash, tx4_str, strlen(tx4_str));*/
memset(&tx_info, 0x00, sizeof(tx_info));
if (0 != transaction_list_get(block_num, &tx_info)) {
goto yexit;
}
if (tx_info.count <= 1) {
goto yexit;
}
while (1) {
if (tx_info.count%2) {
memcpy(tx_info.list[tx_info.count].hash, tx_info.list[tx_info.count-1].hash, 32);
}
for (i = 0; i < tx_info.count; i += 2) {
uint8_t hash[32];
merkle_hash(tx_info.list[i].hash, tx_info.list[i+1].hash, hash);
memcpy(tx_info.list[n].hash, hash, sizeof(hash));
printf("hash[%03d]=", n);
xdump_byte(hash, 32, 32, 0);
n ++;
}
if (1 == n) {
break;
}
tx_info.count = n;
n = 0;
printf("\n\n");
}
yexit:
if (NULL != tx_info.list) {
free(tx_info.list);
tx_info.list = NULL;
}
return 0;
}
int main(int argc, char* argv[])
{
int block_num = 20000;
if (argc != 2) {
printf("usage: a.out [block number]\n");
printf(" ex: a.out 100\n");
return -1;
}
block_num = atoi(argv[1]);
make_merkle_tree_hash(block_num);
printf("\n");
return 0;
}
程序编译&运行
xsha256.c xutils.c 在区块链学习(2)中,没有改动。
编译:gcc xmain.c xsha256.c xutils.c
运行:./a.out 181
结果:最后一个hash就是merkle root
[SE root@ubuntu xminer]# ./a.out 181
url=curl -s https://blockchain.info/block-height/181?format=json
block=000181 txn=2
hash[000]=8347cee4a1cb5ad1bb0d92e86e6612dbf6cfc7649c9964f210d4069b426e720a
hash[001]=a16f3ce4dd5deb92d98ef5cf8afeaf0775ebca408f708b2146c4fb42b41e14be
hash[000]=ed92b1db0b3e998c0a4351ee3f825fd5ac6571ce50c050b4b45df015092a6c36
[SE root@ubuntu xminer]#
问题
1、网络不好时
curl -s https://blockchain.info/block-height/181?format=json 这个命令会执行的很慢。
2、读取curl返回结果的缓存可能不够,需要手动调整。
目前10M 3000笔交易是够用了。