美文网首页
MerkleTree - 梅克尔树

MerkleTree - 梅克尔树

作者: 无名樵夫 | 来源:发表于2021-07-20 15:40 被阅读0次

最近打算用 java 实现 bitcoin 协议, 于是就有了实现了一个 梅克尔树 的算法, 网上的有个 Github 的不方便复制黏贴, 所以就自己写了一个, 实现很不 java, 主要来源于 bitcion-core的 merkle.cpp ComputeMerkleRoot, 最早实现了个java 的, 后来重写了, 现在的代码可读性差了点.

梅克尔树

网上都有介绍这里就不啰嗦了, 最终是个 完全二叉树 ,只说一个细节, 如果是奇数的时候会用最后一个数填充

例如:

1, 2, 3, 4, 5, 6, 7, 8, 9, 0, A
11个数(数据是允许重复, 为了说明简单, 设置为不相同), 经过多次填充后与下面的列表计算结果是一致的
1, 2, 3, 4, 5, 6, 7, 8, 9, 0, A, A, 9, 0, A, A
(共16个数, 刚好平分, 真实算法不需要填充, 直接复制结果就行)

  1. 第一轮11是奇数添加一个A 凑够偶数 11 + 1
  2. 第二轮 6 是偶数不需要凑
  3. 第三轮 3 是奇数, 将最后一位凑上 刚好是第一轮 9, 0, A, A 的计算结果, 最终凑为 3 + 1
  4. 第四轮 2 是偶数
  5. 第五轮 1 只有一个结果了 即是梅克尔树的根

java 核心代码

    /**
     * @param <U>     元数据类型
     * @param <T>     hash 后的数据类型
     * @param list    原始数据
     */
    public static <U, T> MerkleTree merkleTree(List<U> list, Function<U, T> mapper, BiFunction<T, T, T> reducer) {
        if (list.isEmpty()) {
            throw new IllegalArgumentException("NOT EMPTY");
        }
        /**
         * 算法说明:
         * 将原数据 封装为 MerkleTree 并添加到列表(trees),
         * len 存储长度, 每次尽可能多(len + 1) / 2)的折半,
         * 遍历, 如果下一个(next = i+1)超出(即len为奇数), 则复制最后一个(next = i)
         * 将计算好的结果组装为新 node, 并添加到 列表(trees) 的前半部分
         * 每次循环都折半, 直到只有一个 len == 1 时候, 即是树根
         *
         * 重复使用 trees 数组, 是根据 bitcoin 源码clone而来, 新建一个数组也是可以的.
         */
        List<MerkleTree<U, T>> trees = list.stream().map(u -> {
            T hash = mapper.apply(u);
            return new MerkleTree<U, T>().setHash(hash).setData(u);
        }).collect(Collectors.toList());
        int len = trees.size();
        while (len > 1) {
            for (int i = 0; i < len; i += 2) {
                int next = i + 1;
                if (next >= len) { // 超长复制最后一个
                    next = i;
                }
                MerkleTree<U, T> node = new MerkleTree();
                node.left = trees.get(i);
                node.right = trees.get(next);
                node.hash = reducer.apply(node.left.hash, node.right.hash);
                trees.set(i / 2, node);
            }
            len = (len + 1) / 2;
        }
        return trees.get(0);
    }

注意:

  1. 大部分区块链浏览器的 交易hash都是小端的, 这个不是指字节的大端小端, 而是C++ 中使用 uint_256 来存储导致的问题
  2. 块链里的交易列表 不是 按照交易在区块中的顺序来排序的, 这个问题花了我一天时间去排查

详细源码

码云 gitee MerkleTree.java

使用

本着复制粘贴方便的目的, 没有添加任何外部依赖, 但使用了java8的函数接口

  1. 第一个参数是要建树的数据
  2. 第二个参数将列表中的值转为 需要 hash 的值
  3. 第三个参数对 两个需要 hash 的值合并为一个值

一般形式如下, 将一个字节数组 hash 为 另一个字节数组 ( 实例中bitcoin需要两次 sha256 )

    List<byte[]> list = ....
    MerkleTree<byte[], byte[]> tree = MerkleTree
        .merkleTree(
            list,
            e -> e,
            (e1, e2) -> ByteUtil.sha256sha256(ByteUtil.concat(e1, e2))
        );

下载源码包 MerkleTreeTest 有详细的测试用例

(完)

相关文章

  • MerkleTree - 梅克尔树

    最近打算用 java 实现 bitcoin 协议, 于是就有了实现了一个 梅克尔树 的算法, 网上的有个 Gith...

  • 梅克尔树

    梅克尔树 默克尔树(又叫哈希树)是一种二叉树,由一个根节点、一组中间节点和一组叶节点组成。最下面的叶节点包含存储数...

  • go区块链公链实战0xa0之MerkleTree

    MerkleTree MerkleTree,通常也被称作Hash Tree,顾名思义,就是存储hash值的一棵树。...

  • 区块链企业面试基本问题

    名词解释 梅克尔树/Merkle Tree 梅克尔树(又叫哈希树)是一种二叉树,是一种高效和安全的组织数据的方法,...

  • 梅克尔树-Merkle Trees

    ❀ 梅克尔树(Merkle Trees)是区块链的基本组成部分。❀ 介绍 梅克尔树是一种二叉树,能快速检查和归纳大...

  • [学习笔记]Merkle Tree go语言实现

    梅克尔树的结构比较简单,其设计思想比较巧妙,它是SPV实现的关键。梅克尔树是二叉树,节点存储哈希指针,叶子节点保存...

  • 答问

    1、名次解释: 梅克尔树:梅克尔树(又叫哈希树)是一种二叉树,是一种高效和安全的组织数据的方法,被用来快速查询验证...

  • 大话Neo系列:Merkle Tree

    这个系列主要结合neo的源码,和大家分享一下区块链的那些事.这篇文章和大家家聊聊梅克尔树. 1. 梅克尔树是区块中...

  • 晓说区块链 | 梅克尔树保障了区块链数据不可篡改,它的机制是怎样

    梅克尔树(Merkle tree)是为了解决多重一次签名中的认证问题而产生的,由于梅克尔树结构具有一次签名大量认证...

  • 什么是梅克尔树(Merkle)

    首先,它可不是一棵梅花树,虽然名字有点像,但是此树非彼树。梅克尔树是区块头中的三巨头之一,我们要知道,区块是区块链...

网友评论

      本文标题:MerkleTree - 梅克尔树

      本文链接:https://www.haomeiwen.com/subject/cvzlmltx.html