Я работаю над проектом, и, как часть его, мне нужно примерно смоделировать вычисление биткойн-доказательства работы. Это включает в себя итеративное вычисление SHA256 дважды по конкатенации фиксированной строки «BlockHash» и 32-битного одноразового числа типа int, которое увеличивается на каждую итерацию. Если вычисленный хэш меньше строки «TargetHash», мы прерываем цикл и выводим значение nonce.
Я пытаюсь сравнить две последовательные реализации, одна из которых написана с использованием C ++ с использованием реализации SHA256 OpenSSL, а другаяв Java, используя внутреннюю реализацию SHA256 JDK. Я ожидал, что реализация OpenSSL будет намного быстрее, чем JDK, но происходит обратное.
Вот мой код Java:
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class SHA256 {
/**
* convert byte[] to hex string
*
* @param hash
* @return hex string
*/
private static String bytesToHex(byte[] hash) {
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < hash.length; i++) {
String hex = Integer.toHexString(0xff & hash[i]);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
}
/**
* get a sha256 of the input string
*
* @param inputString
* @return resulting hash in hex string
*/
public static String SHA256(String inputString) {
try {
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
return bytesToHex(sha256.digest(inputString.getBytes(StandardCharsets.UTF_8)));
} catch (NoSuchAlgorithmException ex) {
System.err.println(ex.toString());
return null;
}
}
public static void main(String[] args){
String blockHash = SHA256("Some random string to generate a block hash.");
System.out.println("blockHash: " + blockHash);
String targetHash = "000000938023b712892a41e8438e3ff2242a68747105de0395826f60b38d88dc";
String tmp_hash="undefined";
int nonce = 0;
for(nonce=Integer.MIN_VALUE; nonce<=Integer.MAX_VALUE; nonce++) {
tmp_hash = SHA256(SHA256(blockHash+String.valueOf(nonce)));
if(targetHash.compareTo(tmp_hash)>0)
break;
}
System.out.println("Resulting Hash: " + tmp_hash);
System.out.println("Nonce:" + nonce);
}
}
А это моя реализация на C ++:
#include <iostream>
#include <climits>
#include <cstring>
#include <sstream>
#include <string>
#include <iomanip>
#include "format.h"
using namespace std;
#include <openssl/sha.h>
string sha256(const string str)
{
unsigned char hash[SHA256_DIGEST_LENGTH];
SHA256_CTX sha256;
SHA256_Init(&sha256);
SHA256_Update(&sha256, str.c_str(), str.size());
SHA256_Final(hash, &sha256);
stringstream ss;
for(int i = 0; i < SHA256_DIGEST_LENGTH; i++)
{
ss << hex << setw(2) << setfill('0') << (int)hash[i];
}
return ss.str();
}
int main(int argc, char *argv[])
{
string input = "Some random string to generate a block hash.";
string blockHash = sha256(input);
cout << "blockHash: " << blockHash << endl;
string targetHash = "000000938023b712892a41e8438e3ff2242a68747105de0395826f60b38d88dc";
string tmp_hash="undefined";
int nonce = 0;
for(nonce = INT_MIN; nonce <= INT_MAX; nonce++){
tmp_hash = sha256(sha256(fmt::format("{}{}", blockHash, nonce)));
if(strcmp(tmp_hash.c_str(), targetHash.c_str()) < 0)
break;
}
cout<<"Resulting Hash: "<<tmp_hash<<endl;
cout<<"Nonce: "<<nonce<<endl;
return 0;
}
Выводы, использующие утилиту linux 'time' для измерения времени выполнения:
javac SHA256.java
time java SHA256
blockHash: 596143a6a70a23c86e4b218afeb05d151ed45a39e96368e213d17e0a491d894a
Resulting Hash: 0000008ce61c628ffb00b6668687504fd5d44da0a57adb40d6ff59f8e4af0a4a
Nonce:-2135751361
real 0m22.258s
user 0m22.977s
sys 0m0.097s
g++ -O2 -DFMT_HEADER_ONLY main.cpp -lcrypto -lssl
time ./a.out
blockHash: 596143a6a70a23c86e4b218afeb05d151ed45a39e96368e213d17e0a491d894a
Resulting Hash: 0000008ce61c628ffb00b6668687504fd5d44da0a57adb40d6ff59f8e4af0a4a
Nonce: -2135751361
real 0m35.703s
user 0m35.693s
sys 0m0.005s
Это просто для простого TargetHash, для более сложных различие еще больше. Я почти уверен, что реализация openssl sha256 не является узким местом, и что-то еще есть, но, будучи новичком в C ++, я не уверен, что именно. Ранее я использовал to_string (nonce) и s1.compare (s2), которые я заменил на fmt :: format и strcmp, потому что они быстрее, но все же могли получить только несколько секунд. Любые идеи будут по достоинству оценены.