当前位置: 首页 > 知识库问答 >
问题:

java中一个简单的merkle-Tree实现

章光华
2023-03-14

我正试图用Java编写一个非常简单的merkle树实现。

我使用比特币区块链上方框170中的TXID值作为参考,因此我可以看到正确的结果。

与该块对应的TXID如下所示:

b1fea52486ce0c62bb442b530a3f0132b826c74e473d1f2c220bfa78111c5082
f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16

据我了解,比特币的merkle树实现方式如下:

  1. 将块中的事务拆分为成对的事务

有一个警告是:

If there's no additional pairs of txids, concatenate the result of the first pair after double hashing with itself and repeat

我的代码在一个开关语句中,它看起来像这样:

case "test merkle root": {
    // txid A
    String A = "b1fea52486ce0c62bb442b530a3f0132b826c74e473d1f2c220bfa78111c5082";

    // txid A byte-swapped
    String A_little = MainChain.swapEndianness(A);

    // txid B
    String B = "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16";

    // txid B byte-swapped
    String B_little = MainChain.swapEndianness(B);

    // txid A + B concatenated
    String AB_little = A_little + B_little;

    // double hash of byte-swapped concatenated A+B
    String ABdoubleHash =  SHA256.generateSHA256Hash(SHA256.generateSHA256Hash(AB_little));

    // double hash concatenated with itself
    String ABAB_little = ABdoubleHash + ABdoubleHash;

    // double hash of self-concatenated double-hashed txid
    String merkleRootLittleEndian = SHA256.generateSHA256Hash(SHA256.generateSHA256Hash(ABAB_little));

    // print result byte-swapped back to big-endian
    System.out.println("Merkle root: " + MainChain.swapEndianness(merkleRootLittleEndian));
}

我编写的swapEndianness方法不是真正的“字节级”交换,而是更改字符串的顺序,如下所示:

public static String swapEndianness(String hash) {
        char[] hashAsCharArray = hash.toCharArray();
        StringBuilder sb = new StringBuilder();
        for (int i = hash.length() - 1; i > 0; i-=2) {
            sb.append(hashAsCharArray[i - 1]);
            sb.append(hashAsCharArray[i]);
        }
        return sb.toString();
    }

这两种TXD的merkle根的预期结果为:

7dac2c5666815c17a3b36427de37bb9d2e2c5ccec3f8633eb91a4205cb4c10ff

然而,我最终得到的结果是:

3b40cab1157838cc41b08e27641f65d245957ab07b3504d94bc2d355abaed06c

我没有得到我期望的结果,是因为我在进行字节交换时作弊,还是因为我错过了一个步骤,或者是因为我的代码中有错误(或者这些错误的组合)?任何帮助都将不胜感激!

共有1个答案

凌伟泽
2023-03-14

您不了解Java中的byte[]是什么。示例中的字符串是字节[]的十六进制表示。请参见如何在Java中初始化字节数组

public class MerkleTree {
    static MessageDigest digest;
    public static void main(String[] args) throws NoSuchAlgorithmException {
        digest = MessageDigest.getInstance("SHA-256");
        new MerkleTree().run();
    }
    private void run() {
        // txid A
        byte[] A = hexStringToByteArray("b1fea52486ce0c62bb442b530a3f0132b826c74e473d1f2c220bfa78111c5082");
        System.out.println(Arrays.toString(A));
        // txid A byte-swapped
        byte[] A_little = swapEndianness(A);
        System.out.println(Arrays.toString(A_little));

        // txid B
        byte[] B = hexStringToByteArray("f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16");
        System.out.println(Arrays.toString(B));

        // txid B byte-swapped
        byte[] B_little = swapEndianness(B);
        System.out.println(Arrays.toString(B_little));

        // txid A + B concatenated
        byte[] AB_little = Arrays.copyOf(A_little, A_little.length + B_little.length);
        System.arraycopy(B_little, 0, AB_little, A_little.length, B_little.length);
        System.out.println(Arrays.toString(AB_little));

        // double hash of byte-swapped concatenated A+B
        byte[] ABdoubleHash = SHA256(SHA256(AB_little));
        System.out.println(Arrays.toString(ABdoubleHash));

        // print result byte-swapped back to big-endian
        byte[] result = swapEndianness(ABdoubleHash);
        System.out.println(Arrays.toString(result));
        System.out.println(getHex(result));         
    }
    byte[] swapEndianness(byte[] hash) {
        byte[] result = new byte[hash.length];
        for (int i = 0; i < hash.length; i++) {
            result[i] = hash[hash.length-i-1];
        }
        return result;
    }
    byte[] SHA256(byte[] obytes) {
        return digest.digest(obytes);
    }
    byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                                 + Character.digit(s.charAt(i+1), 16));
        }
        return data;
    }
    private static final String    HEXES    = "0123456789abcdef";
    String getHex(byte[] raw) {
        final StringBuilder hex = new StringBuilder(2 * raw.length);
        for (final byte b : raw) {
            hex.append(HEXES.charAt((b & 0xF0) >> 4)).append(HEXES.charAt((b & 0x0F)));
        }
        return hex.toString();
    }
} 

最后,参考Java代码将字节转换为十六进制

编辑:这将是一个更好的资源,因为你经常想做这类事情很多。

    // txid A byte-swapped
    byte[] A = swapEndianness(
            hexStringToByteArray("b1fea52486ce0c62bb442b530a3f0132b826c74e473d1f2c220bfa78111c5082")
        );
    // txid B byte-swapped
    byte[] B = swapEndianness(
            hexStringToByteArray("f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16")
        );
    // txid A + B concatenated
    byte[] AB = Arrays.copyOf(A, A.length + B.length);
    System.arraycopy(B, 0, AB, A.length, B.length);

    // print result byte-swapped back to big-endian
    String result = getHex(swapEndianness(SHA256(SHA256(AB))));
    System.out.println(result);         
    }
    byte[] swapEndianness(byte[] hash) {
        for (int i = 0; i < hash.length/2; i++) {
            byte t = hash[hash.length-i-1];
            hash[hash.length-i-1] = hash[i]; 
            hash[i] = t; 
        }
        return hash;
    }
 类似资料:
  • 问题内容: 我目前正在尝试在Go中实现Merkle- tree数据结构。基本上,我的最终目标是存储一小组结构化数据(最大10MB),并使该“数据库”可以轻松地与网络上分布的其他节点同步(请参阅参考资料)。由于没有类型检查,因此我已经在Node中有效地实现了这一点。这就是Go的问题,我想利用Go的编译时类型检查,尽管我也想拥有一个可以与任何提供的树一起使用的库。 简而言之,我想将结构用作merkle

  • 本文向大家介绍如何实现Java中一个简单的LinkedList,包括了如何实现Java中一个简单的LinkedList的使用技巧和注意事项,需要的朋友参考一下 LinkedList与ArrayList都是List接口的具体实现类。LinkedList与ArrayList在功能上也是大体一致,但是因为两者具体的实现方式不一致,所以在进行一些相同操作的时候,其效率也是有差别的。 对于抽象的数据结构——

  • 问题内容: 我应该如何实现简单的文件下载servlet? 这个想法是,通过请求,用户可以下载例如。从文件中读取,文件会将文件上传给用户。 我可以获取文件,但是如何实现文件下载? 问题答案: 那要看。如果你可以通过HTTP服务器或Servlet容器公开访问该文件,则只需将其重定向到via即可。 如果不是,则需要手动将其复制到响应输出流: 当然,你需要处理适当的异常。

  • 本文向大家介绍如何简单的实现一个promise?相关面试题,主要包含被问及如何简单的实现一个promise?时的应答技巧和注意事项,需要的朋友参考一下 参考回答: 首先明确什么是promiseA+规范,参考规范的地址:primise A+规范 如何实现一个promise,参考我的文章: 实现一个完美符合Promise/A+规范的Promise 一般不会问的很详细,只要能写出上述文章中的v1.0版本

  • 我是使用Dagger2的新手(我一直使用Koin),我正在尝试实现一个简单的示例,但我真的不知道我缺少了什么。这就是我目前得到的。 app.gradle: 应用模块。kt: AppComponent。kt: TestClass。千吨 pp.kt: MainActivity.kt: 错误:testClass==null

  • 本文向大家介绍Java中反射的一个简单使用,包括了Java中反射的一个简单使用的使用技巧和注意事项,需要的朋友参考一下 简介 首先介绍一些不太实用的解释:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。 简单使用 反射,在java中是非常常见和好用