区块链学习(3) 计算区块的merkle root

merkle tree
相关的理论资料较多,这里不做过多描述。
注意当一层的节点为奇数个,最后一个节点需要复制一下生成偶数个节点。每一层都要这样做。


以区块181为例,来描述merkle root的生成过程。
Blockchain.com Explorer | BCH | ETH | BCH


区块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; i<txn; i++) {
        memset(tmp, 0x00, sizeof(tmp));
        n = xkey_value_parser(p, "\"hash\":\"", "\",",tmp, sizeof(tmp));
        if (-1 == n) {
            r = -1;
            break;
        }
        p += n;
        printf("hash[%03d]=%s\n", i, tmp);
        hex2bin(tx_info->list[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笔交易是够用了。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值