Merkle 树

完整的比特币数据库(也就是区块链)需要超过 140 Gb 的磁盘空间。因为比特币的去中心化特性,网络中的每个节点必须是独立,自给自足的,也就是每个节点必须存储一个区块链的完整副本。随着越来越多的人使用比特币,这条规则变得越来越难以遵守:因为不太可能每个人都去运行一个全节点。并且,由于节点是网络中的完全参与者,它们负有相关责任:节点必须验证交易和区块。另外,要想与其他节点交互和下载新块,也有一定的网络流量需求。

在中本聪的 比特币原始论文 中,对这个问题也有一个解决方案:简易支付验证(Simplified Payment Verification, SPV)。SPV 是一个比特币轻节点,它不需要下载整个区块链,也不需要验证区块和交易。相反,它会在区块链查找交易(为了验证支付),并且需要连接到一个全节点来检索必要的数据。这个机制允许在仅运行一个全节点的情况下有多个轻钱包。

为了实现 SPV,需要有一个方式来检查是否一个区块包含了某笔交易,而无须下载整个区块。这就是 Merkle 树所要完成的事情。

比特币用 Merkle 树来获取交易哈希,哈希被保存在区块头中,并会用于工作量证明系统。到目前为止,我们只是将一个块里面的每笔交易哈希连接了起来,将在上面应用了 SHA-256 算法。虽然这是一个用于获取区块交易唯一表示的一个不错的途径,但是它没有利用到 Merkle 树。

来看一下 Merkle 树:

每个块都会有一个 Merkle 树,它从叶子节点(树的底部)开始,一个叶子节点就是一个交易哈希(比特币使用双 SHA256 哈希)。叶子节点的数量必须是双数,但是并非每个块都包含了双数的交易。因为,如果一个块里面的交易数为单数,那么就将最后一个叶子节点(也就是 Merkle 树的最后一个交易,不是区块的最后一笔交易)复制一份凑成双数。

从下往上,两两成对,连接两个节点哈希,将组合哈希作为新的哈希。新的哈希就成为新的树节点。重复该过程,直到仅有一个节点,也就是树根。根哈希然后就会当做是整个块交易的唯一标示,将它保存到区块头,然后用于工作量证明。

Merkle 树的好处就是一个节点可以在不下载整个块的情况下,验证是否包含某笔交易。并且这些只需要一个交易哈希,一个 Merkle 树根哈希和一个 Merkle 路径。

最后,来写代码:

  1. package main
  2.  
  3. import (
  4. "crypto/sha256"
  5. "fmt"
  6. "encoding/hex"
  7. )
  8.  
  9. type MerkleTree struct{
  10. RootNode * MerkleNode
  11. }
  12.  
  13. type MerkleNode struct{
  14. Left *MerkleNode
  15. Right *MerkleNode
  16. Data []byte
  17. }
  18.  
  19. func NewMerkleTree(data [][]byte) * MerkleTree{
  20. var nodes []MerkleNode
  21. if len(data)% != {
  22. data = append(data,data[len(data)-])
  23. }
  24.  
  25. for _,datum := range data{
  26. node := NewMerkleNode(nil,nil,datum)
  27. nodes = append(nodes,*node)
  28. }
  29.  
  30. for i := ;i < len(data)/;i++{
  31. var newLevel []MerkleNode
  32.  
  33. for j := ;j < len(nodes);j += {
  34. node := NewMerkleNode(&nodes[j],&nodes[j+],nil)
  35. newLevel = append(newLevel,*node)
  36. }
  37. nodes = newLevel
  38. }
  39.  
  40. mTree := MerkleTree{&nodes[]}
  41. return &mTree
  42. }
  43.  
  44. func NewMerkleNode(left,right *MerkleNode,data []byte)*MerkleNode{
  45. mNode := MerkleNode{}
  46.  
  47. if left == nil && right == nil{
  48. hash := sha256.Sum256(data)
  49. mNode.Data = hash[:]
  50. }else{
  51. prevHashes := append(left.Data,right.Data...)
  52. hash := sha256.Sum256(prevHashes)
  53. mNode.Data = hash[:]
  54. }
  55.  
  56. mNode.Left = left
  57. mNode.Right = right
  58.  
  59. return &mNode
  60. }
  61. //==============================================================
  62.  
  63. func testNewMerkleNode1(){
  64. data := [][]byte{
  65. []byte("node1"),
  66. []byte("node2"),
  67. []byte("node3"),
  68. }
  69.  
  70. n1 := NewMerkleNode(nil,nil,data[])
  71. n2 := NewMerkleNode(nil,nil,data[])
  72. n3 := NewMerkleNode(nil,nil,data[])
  73. n4 := NewMerkleNode(nil,nil,data[])
  74.  
  75. n5 := NewMerkleNode(n1,n2,nil)
  76. n6 := NewMerkleNode(n3,n4,nil)
  77.  
  78. n7 := NewMerkleNode(n5,n6,nil)
  79.  
  80. fmt.Println("n5 = ",hex.EncodeToString(n5.Data))
  81. fmt.Println("n6 = ",hex.EncodeToString(n6.Data))
  82. fmt.Println("n7 = ",hex.EncodeToString(n7.Data))
  83. }
  84.  
  85. func testNewMerkleNode2(){
  86. data := [][]byte{
  87. []byte("node1"),
  88. []byte("node2"),
  89. []byte("node3"),
  90. }
  91. // Level 1
  92. n1 := NewMerkleNode(nil, nil, data[])
  93. n2 := NewMerkleNode(nil, nil, data[])
  94. n3 := NewMerkleNode(nil, nil, data[])
  95. n4 := NewMerkleNode(nil, nil, data[])
  96.  
  97. // Level 2
  98. n5 := NewMerkleNode(n1, n2, nil)
  99. n6 := NewMerkleNode(n3, n4, nil)
  100.  
  101. // Level 3
  102. n7 := NewMerkleNode(n5, n6, nil)
  103.  
  104. rootHash := fmt.Sprintf("%x", n7.Data)
  105. mTree := NewMerkleTree(data)
  106.  
  107. fmt.Println("roothash =\t",(rootHash))
  108. fmt.Println("mTree =\t\t",hex.EncodeToString(mTree.RootNode.Data))
  109. }
  110.  
  111. func main() {
  112. testNewMerkleNode1()
  113. testNewMerkleNode2()
  114. }

Merkle_go

c++需要导入之前的sha256  https://www.cnblogs.com/itdef/p/9435218.html

sha256.cpp    sha256.h

  1. // 1111.cpp: 定义控制台应用程序的入口点。
  2. //
  3.  
  4. #include "sha256.h"
  5. #include <string>
  6. #include <vector>
  7. #include <iostream>
  8.  
  9. using namespace std;
  10.  
  11. typedef struct merklenode {
  12. struct merklenode* left;
  13. struct merklenode* right;
  14. string data;
  15. }MerkleNode;
  16.  
  17. typedef struct merkletree {
  18. MerkleNode* merkleNode;
  19. }MerkleTree;
  20.  
  21. MerkleNode* NewMerkleNode(MerkleNode* left, MerkleNode* right, string data) {
  22. MerkleNode* mNode = new MerkleNode{};
  23.  
  24. if (left == nullptr && right == nullptr) {
  25. string hash = sha256(data);
  26. mNode->data = hash;
  27. }
  28. else {
  29. string prevHashes = left->data + right->data;
  30. string hash = sha256(prevHashes);
  31. mNode->data = hash;
  32. }
  33.  
  34. mNode->left = left;
  35. mNode->right = right;
  36.  
  37. return mNode;
  38. }
  39.  
  40. MerkleTree* NewMerkleTree(vector<string> data) {
  41. vector<MerkleNode*> nodes;
  42.  
  43. if ((data.size() % ) != ) {
  44. data.push_back(data[data.size() - ]);
  45. }
  46.  
  47. for (const auto& datum : data) {
  48. MerkleNode* node = NewMerkleNode(nullptr, nullptr, datum);
  49. nodes.push_back(node);
  50. }
  51.  
  52. for (int i = ; i < (data.size() / ); i++) {
  53. vector<MerkleNode*> newLevel;
  54.  
  55. for (int j = ; j < nodes.size(); j += ) {
  56. MerkleNode* node = NewMerkleNode(nodes[j], nodes[j + ], "");
  57. newLevel.push_back(node);
  58. }
  59. nodes = newLevel;
  60. }
  61.  
  62. MerkleTree* mTree = new MerkleTree{ nodes[] };
  63. return mTree;
  64. }
  65.  
  66. void testNewMerkleNode1() {
  67. vector<string> data{ "node1","node2","node3" };
  68.  
  69. MerkleNode* n1 = NewMerkleNode(nullptr, nullptr, data[]);
  70. MerkleNode* n2 = NewMerkleNode(nullptr, nullptr, data[]);
  71. MerkleNode* n3 = NewMerkleNode(nullptr, nullptr, data[]);
  72. MerkleNode* n4 = NewMerkleNode(nullptr, nullptr, data[]);
  73.  
  74. MerkleNode* n5 = NewMerkleNode(n1, n2, "");
  75. MerkleNode* n6 = NewMerkleNode(n3, n4, "");
  76.  
  77. MerkleNode* n7 = NewMerkleNode(n5, n6, "");
  78.  
  79. std::cout << "n5 = " << n5->data << std::endl;
  80. std::cout << "n6 = " << n6->data << std::endl;
  81. std::cout << "n7 = " << n7->data << std::endl;
  82. }
  83.  
  84. void testNewMerkleNode2() {
  85. vector<string> data{ "node1","node2","node3" };
  86.  
  87. MerkleNode* n1 = NewMerkleNode(nullptr, nullptr, data[]);
  88. MerkleNode* n2 = NewMerkleNode(nullptr, nullptr, data[]);
  89. MerkleNode* n3 = NewMerkleNode(nullptr, nullptr, data[]);
  90. MerkleNode* n4 = NewMerkleNode(nullptr, nullptr, data[]);
  91.  
  92. MerkleNode* n5 = NewMerkleNode(n1, n2, "");
  93. MerkleNode* n6 = NewMerkleNode(n3, n4, "");
  94.  
  95. MerkleNode* n7 = NewMerkleNode(n5, n6, "");
  96.  
  97. MerkleTree* mTree = NewMerkleTree(data);
  98.  
  99. std::cout << "roothash = "<< n7->data << std::endl;
  100. std::cout << "mTree = " << mTree->merkleNode->data << std::endl;
  101.  
  102. }
  103.  
  104. int main()
  105. {
  106. testNewMerkleNode1();
  107. testNewMerkleNode2();
  108. return ;
  109. }

Merkle_cpp

参考

https://blog.csdn.net/simple_the_best/article/details/78462129

cpp 区块链模拟示例(七) 补充 Merkle树的更多相关文章

  1. cpp 区块链模拟示例(一)工程建立

    /* 作 者: itdef 欢迎转帖 请保持文本完整并注明出处 技术博客 http://www.cnblogs.com/itdef/ 技术交流群 群号码:432336863欢迎c c++ window ...

  2. cpp 区块链模拟示例(四) 区块链工作量证明

    本文主要在之前的区块链原形上添加了工作量证明,并且为后继的交易功能做好准备. 上一个章节我们已经创建了区块链的基本原形,但是区块的哈希计算和加入太过于简单,如果按照这种速度添加区块那么区块链估计一个小 ...

  3. cpp 区块链模拟示例(二)工程代码解析

    /* 作 者: itdef 欢迎转帖 请保持文本完整并注明出处 技术博客 http://www.cnblogs.com/itdef/ 技术交流群 群号码:432336863欢迎c c++ window ...

  4. cpp 区块链模拟示例(三)新基本原形工程的建立

    /* 作 者: itdef 欢迎转帖 请保持文本完整并注明出处 技术博客 http://www.cnblogs.com/itdef/ 技术交流群 群号码:432336863欢迎c c++ window ...

  5. cpp 区块链模拟示例(五) 序列化

    有了区块和区块链的基本结构,有了工作量证明,我们已经可以开始挖矿了.剩下就是最核心的功能-交易,但是在开始实现交易这一重大功能之前,我们还要预先做一些铺垫,比如数据的序列化和启动命令解析. 根据< ...

  6. cpp 区块链模拟示例(六) 交易

    交易(transaction)是比特币的核心所在,而区块链的唯一目的,也正是为了能够安全可靠地存储交易.在区块链中,交易一旦被创建,就没有任何人能够再去修改或是删除它.在今天的文章中,我们会实现交易的 ...

  7. 区块链开发(七)truffle使用入门汇总

    截止上篇博客,以太坊区块链开发的环境和框架基本上搭建完毕.这一篇博客重点梳理一下基本的流程和操作演示. 前奏 基于前面的安装配置,现在重新梳理一遍,以前博客讲到的就在这里一笔带过. (1)创建一个工作 ...

  8. Merkle tree在区块链中的应用

    上篇博文我们转载了一篇<Merkle Tree(默克尔树)算法解析>,那么大家是不是会有疑问,学习这个算法之后,我们改怎么去应用,区块链中又是如何应用的?今天这篇博客就以Merkle tr ...

  9. [区块链] 共识算法之争(PBFT,Raft,PoW,PoS,DPoS,Ripple)

    近几天对区块链中几种常见的共识机制(PBFT,Raft,PoW,PoS,DPoS,Ripple)进行了总结.尽量使用简单易懂语言,篇幅较大,想了解的可以只读每个算法介绍中前边的原理.本篇文章主要参考& ...

随机推荐

  1. Navicat的外键设置

    “名”:可以不填,你一会保存成功系统会自动生成. “栏位”:这个子表哪个键设置为外键. “参考数据库”:外键关联的数据库. “参考表”:关联的父表 “参考栏位”:父表关联的的字段,一般是id “删除时 ...

  2. Laravel5.1 与 Laypage 结合进行分页

    demo地址:http://lara.ytlwin.top/orm 路由 Route::match(array('get','post'),'/orm','StuController@orm'); 控 ...

  3. Oracle的regexp_instr函数简单用法

    REGEXP_INSTR函数让你搜索一个正则表达式模式字符串.函数使用输入字符集定义的字符进行字符串的计算. 它返回一个整数,指示开始或结束匹配的子位置,这取决于return_option参数的值. ...

  4. sql去除重复语句

    转自芙蓉清秀的BLOG http://blog.sina.com.cn/liurongxiu1211  sql去除重复语句 (2012-06-15 15:00:01) sql 单表/多表查询去除重复记 ...

  5. mysql5.7.20更改root密码

    my.cnf 中在[mysqld]下面增加 skip-grant-tables 使用空密码登录数据库执行下面命令 update mysql.user set authentication_string ...

  6. pta l2-1紧急救援(Dijkstra)

    题目链接:https://pintia.cn/problem-sets/994805046380707840/problems/994805073643683840 题意:给n个城市,m条边,每个城市 ...

  7. centos 6 和centos 7 系统下vnc配置

    一. VNC 服务的大概介绍: VNC (Virtual Network Console)是虚拟网络控制台的缩写.它 是一款优秀的远程控制工具软件,由著名的 AT&T 的欧洲研究实验室开发的. ...

  8. anaconda的安装tensorflow

    在anaconda prompt中我们输入 anaconda search -t conda tensorflow 查看能在哪里安装tensorflow anaconda show dhirschfe ...

  9. Monkey 命令收集相关 --追加Monkey自动化测试开源工具

    .1.环境配置 MONKEY测试使用的是ADB命令,因此只需要配置ADB环境即可. 2.测试准备与执行 在Monkey测试前,必须进行以下准备 Ø  手机屏幕超时设置为30分钟或者永不超时,防止手机进 ...

  10. C#创建cookie读写cookie

    一.创建cookie HttpCookie cookie = new HttpCookie("UserInfo");//创建多值cookie              cookie ...