Я пытаюсь написать простую реализацию дерева Меркле очень на Java.
Я использую значения txids в блок 170 в цепочке биткойнов для справки, поэтому я могу видеть, каким должен быть правильный результат.
txids, соответствующие этому блоку, следующие:
b1fea52486ce0c62bb442b530a3f0132b826c74e473d1f2c220bfa78111c5082
f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16
Насколько я понимаю, реализация биткойн-дерева Меркла работает следующим образом:
С оговоркой:
If there's no additional pairs of txids, concatenate the result of the first pair after double hashing with itself and repeat
Мой код находится в операторе switch, который выглядит так:
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();
}
Ожидаемый результат для корня меркла этих двух txid:
7dac2c5666815c17a3b36427de37bb9d2e2c5ccec3f8633eb91a4205cb4c10ff
Однако в результате я получаю следующее:
3b40cab1157838cc41b08e27641f65d245957ab07b3504d94bc2d355abaed06c
Я не получаю ожидаемого результата, потому что я обманываю, когда выполняю замену байтов, потому что я пропускаю шаг или потому что у меня есть ошибка в моем коде (или какая-то их комбинация)? Любая помощь будет оценена по достоинству!
Вы не понимаете, что такое byte[] в Java. Строки в вашем примере представляют собой «шестнадцатеричные» представления байта []. См. Как инициализировать массив байтов в 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;
}
Инжиниринг — это решение проблем, которые часто не являются «очень простыми». youtube.com/watch?v=82LXZqfABZw В любом случае, не беспокойся, приятель, сойдет.
Вы знаете, что они говорят, каждый день - учебный день. Ценю тебя мужик!
Я знаю, что такое byte[] в java и на любом другом языке, который поддерживает его как тип, если уж на то пошло. Я специально сказал, что я «обманул», используя только строки, и сделал слово очень в утверждении «очень простой» жирным шрифтом намеренно, потому что я новичок и способен только на простые реализации. Спасибо за ваш ответ, так как это именно то, что мне нужно, и явно хорошо продумано, но я бы хотел, чтобы вы были более поощряющими в распространении своих знаний.