学习算法,编程之路

MerKle Tree的建立

merkle tree 的基本概念:Merkle Tree 是由计算机科学家 Ralph Merkle 在很多年前提出的,并以他本人的名字来命名。不过,Merkle Tree 确实涉及到了很多有意思的实际应用。最近几年才有的一个例子是,比特币钱包服务用 Merkle Tree 的机制来作”百分百准备金证明“ ( http://blog.bifubao.com/2014/03/16/proof-of-reserves/ )。不过今天, Merkle Tree多出现在数据的“完整性校验”。

要在文件的基础上构建MerkleTree,首先是对文件进行分块,然后对每个文件分块进行hash计算,得到各块的hash value, 所有的hash value都为merkle tree的叶子节点,这些叶子节点两两成对(连接),再次hash计算,得到其父亲节点,其父亲节点也按照同样的方法,得到它的父亲节点,如此进行直到获得根节点。

图示:叶子节点数为4时,构造的merkleTree
这里写图片描述

以下是本人用C编写的构建文件MerkleTree的基本思路:
1.打开指定的文件;
2.获得文件长度,按照每块64Bytes大小对文件分块,块数n=len/64(这里测试的文件,其大小均能被64Bytes整除,eg1KB)
3.定义数据结构Path

typedef struct Path{
int * node;
}Path;

用于构建并保存Merkle Tree,图示
这里写图片描述
从底向上的建树过程,第i层对应path[i],第i层的第j个节点对应path[i].node[j];其子节点为path[i-1].node[j*2],path[i-1].node[j*2+1];
父亲节点是左右孩子节点的hashvalue连接后计算的hash值
测试代码:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

typedef struct Path{
    int * node;

}Path;
unsigned int  SDBMHash(char *str)
{
    unsigned int hash = 0;

    while (*str)
    {
        // equivalent to: hash = 65599*hash + (*str++);
        hash = (*str++) + (hash << 6) + (hash << 16) - hash;
    }

    return (hash & 0x7FFFFFFF);
}

int merkle_root(Path path[],int num){
    int node_num=num,depth=0,i,j,k;
    unsigned char temp[8];
    char *p;
    while(node_num/2!=0){
        node_num/=2;
        path[++depth].node=(int *)malloc(node_num*sizeof(int));
        i=0;
        while(i<node_num){
            p=(char *)&path[depth-1].node[i*2];
            for(j=0;j<8;j++){
                temp[j]=*(p++);
            }
        /*  for(k=0;k<8;k++){
                printf("%02x ",temp[k]);
            }
            printf("\n");
                */
            path[depth].node[i]=SDBMHash(temp);

            i++;
        }
    }
    printf("root=%x,%d\n",path[depth].node[0],path[depth].node[0]);
    return depth;
}

    /*在文件的基础上建立MKT*/
int merkle_overFile(char * filename){
FILE *fp;
int n,len,i,depth,k;
// filename[20];
unsigned char buffer[64];
Path path[30];
if((fp=fopen(filename,"rb"))==NULL){
    printf("the file %s cannot be opened!\n",filename);
}

fseek(fp,0,SEEK_END);
if((len=ftell(fp))==-1L){
    printf("Sorry! Can not calculate files which larger than 2GB!\n");
    fclose(fp);
}
rewind(fp);
printf("len=%d\n",len);
n=len/64;
/*文件按64bytes大小分块,叶子节点初始化*/
path[0].node=(int *)malloc(n*sizeof(int));
i=0;
while(i<n){

    fread(buffer,1,64,fp);
    path[0].node[i]=SDBMHash(buffer);
    i++;
}
fclose(fp);

depth=merkle_root(path,n);
return depth;
}

//测试输入文件file_1k.txt
int main(){


char filename[20];
int depth;
printf("please input the file:");
scanf("%s",filename);
depth=merkle_overFile(filename);
printf("depth=%d\n",depth);
return 0;
}


需要注意的地方:
windows下生成指定大小的文件,进入命令窗口,输入命令:
fsutil file createnew test.txt 104857600
则瞬间生成一个100MB大小的文件
其中104857600=100*1024*1024字节(Byte);

在C语言中用printf()函数打印字符型变量时,如果想采用”%x”的格式将字符型变量值以十六进制形式打印出来,会出现一个小问题

     char buf[10] = {0};
     buf[0] = 0xbf;
     printf("%2x\n\n\n", buf[0]);            /*在终端将会显示成:ffffffbf*/

     buf[1] = 0x7f;
     printf("%2x\n\n\n", buf[1]);            /*在终端将会显示成:7f*/

研究发现,只要字符型变量值的二进制第一位是1,就会如buf[0]所显示的那样,出现6个f。
而格式输出函数printf中会对所输出的变量做有符号/无符号型的判断。如果是有符号型变量,且该值二进制首位为1(如我们定义的是char buf[100],且buf[0]中为0xbf),则会按照补码形式前面全置为1,也就是全为f。(因为终端显示的是8个十六机制数字;另外,我用Tobor C编译,因为Tobor C中int是2bytes,终端显示就成4个十六进制数字了:ffbf,因此我猜测,printf函数显示之前先将有符号型值转换成了一个4bytes的int。当然,这是函数内部的处理,跟我们所说的话题关系不大。)。
按照上面述说的,如果把buf声明为unsigned char型,则显示会变成:bf
而且,如果程序需要讲一个有符号型的变量以十六进制形式输出,且只显示两位,可以强制类型转换为无符号型变量,如上面的buf[0],可以转换成:(unsigned char)buf[0]
(这个是借鉴前人的经验,在测试中出现了类似问题,特写此处方便自己查看)

运行截图

这里写图片描述
如果有不妥之处,还请指教!(测试文件大小1KB,root是树根,前面是十六进制输出,后面是十进制输出结果,树深度为4)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值