/** * mining_work.cpp */ #include #include "nlohmann/json.hpp" #include "ee382n_bitcoin/mining_work.hpp" #include "ee382n_bitcoin/sha256.h" #include "ee382n_bitcoin/bitcoin_block_header.hpp" namespace stratum_v1 { MiningWork::MiningWork(json ¶ms, const std::string& xnonce1, size_t xnonce2_size) { if(!params.is_array() && params.size() != 9) { std::cerr << "[MiningWork::MiningWork] Unexpected input JSON!" << std::endl; return; } _job_id = params[0]; _prev_hash = params[1]; _coin_base1 = params[2]; _coin_base2 = params[3]; _merkle_branch = params[4]; _version = params[5]; _nbits = params[6]; _ntime = params[7]; _clean_jobs = params[8]; _xnonce1 = xnonce1; _xnonce1_size = _xnonce1.length() / 2; _xnonce2_size = xnonce2_size; _xnonce2.resize(_xnonce2_size, 0); std::cout << "[ctor] Version: " << _version << std::endl; std::cout << "[ctor] Coinbase: " << generate_coinbase() << std::endl; std::cout << "[ctor] Merkle root: " << generate_merkle_root() << std::endl; // std::cout << "Header: " << generate_block_header(0) << std::endl; } /** * generate_coinbase creates a big-endian hex string representation of the coin base generation transaction * @return coin_base */ std::string MiningWork::generate_coinbase() { std::string xnonce2_str = bin2hex(_xnonce2.data(), _xnonce2_size); // std::string coinbase = _coin_base1 + _xnonce1 + xnonce2_str + _coin_base2; std::string coinbase = _coin_base2 + xnonce2_str + _xnonce1 + _coin_base1; // std::cout << "coinbase (" << coinbase.length()/2 << " bytes): " << coinbase << std::endl; return coinbase; } /** * Increment Extra Nonce in generation transaction after exhausting standard nonce+version search space. * This requires regenerating the merkle root from coinbase + merkle branch. * Ref: https://github.com/pooler/cpuminer/blob/5f02105940edb61144c09a7eb960bba04a10d5b7/cpu-miner.c#L1093 */ void MiningWork::increment_xnonce2() { for (size_t i = 0; i < _xnonce2_size && !++_xnonce2[i]; i++); } /** * Generate merkle root for this work using merkle branch provided by Stratum server * Ref: https://github.com/pooler/cpuminer/blob/5f02105940edb61144c09a7eb960bba04a10d5b7/cpu-miner.c#L1086 * * @return hex-formatted merkle root string */ std::string MiningWork::generate_merkle_root() { std::string cb_str = generate_coinbase(); std::vector cb(cb_str.length()/2); hex2bin(cb_str, cb.data()); // size_t cb_size = cb_str.length()/2; // std::vector cb(cb_size); // for(int i = 0; i < cb_str.length(); i+=2) { // std::string byte_str = cb_str.substr(i, 2); // cb[cb_size - i/2 - 1] = (uint8_t) strtol(byte_str.c_str(), NULL, 16); // } std::vector merkle_root(64); sha256_double(cb.data(), cb.size(), merkle_root.data()); for(int i = 0; i < _merkle_branch.size(); i++) { hex2bin(_merkle_branch[i], merkle_root.data() + 32); sha256_double(merkle_root.data(), 64, merkle_root.data()); } return bin2hex(merkle_root.data(), 32); } ///** // * // * @param header // * @param nonce // */ //void MiningWork::generate_block_header(uint8_t header[80], const uint32_t nonce) { // BitcoinBlockHeader h; // h.set_version(hex2bin32(_version)); // h.set_prev_block(_prev_hash); // h.set_merkle_root(generate_merkle_root()); // h.set_ntime(hex2bin32(_ntime)); // h.set_nbits(hex2bin32(_nbits)); // h.set_nonce(nonce); // // std::cout << "[MiningWork::generate_block_header] header: " << h << std::endl; // // memcpy(header, h.header_bytes(), 80); // TODO: Make this less dumb by returning a BitcoinBlockHeader directly //} /** * Get header template without nonce. The miner needs to iterate over the nonce. * @return BitcoinBlockHeader */ BitcoinBlockHeader MiningWork::generate_header_template() { BitcoinBlockHeader h; h.set_version(hex2bin32(_version)); h.set_prev_block(_prev_hash); h.set_merkle_root(generate_merkle_root()); h.set_ntime(hex2bin32(_ntime)); h.set_nbits(hex2bin32(_nbits)); h.set_nonce(0); std::cout << "[MiningWork::generate_block_header] header: " << h << std::endl; return h; } /** * Convert "big endian" hex-formatted string to correct little endian byte array * TODO: Make this safer - possibly add param to check size of output? * @param hex_str * @param out */ void MiningWork::hex2bin(const std::string &hex_str, uint8_t *out) { size_t size_bytes = hex_str.length()/2; for(int i = 0; i < hex_str.length(); i+=2) { std::string byte_str = hex_str.substr(i, 2); out[size_bytes - i/2 - 1] = (uint8_t) strtol(byte_str.c_str(), NULL, 16); } } /** * * @return */ uint32_t MiningWork::hex2bin32(const std::string& hex) { // TODO: Make this not dumb assert(hex.length() == 8); uint32_t out; hex2bin(hex, (uint8_t*)&out); return out; } /** * Convert "little endian" byte array into "big endian" hex-formatted string * @param bin Input * @param len Length of input in bytes * @return */ std::string MiningWork::bin2hex(const uint8_t *bin, size_t len) { std::stringstream stream; for(int i = len - 1; i >= 0 ; i--) { stream << std::setfill('0') << std::setw(2) << std::hex << static_cast(bin[i]); } return stream.str(); } bool MiningWork::get_clean_jobs() const { return _clean_jobs; } std::string MiningWork::get_job_id() const { return _job_id; } std::string MiningWork::get_prev_hash() const { return _prev_hash; } std::string MiningWork::get_version() const { return _version; } std::string MiningWork::get_nbits() const { return _nbits; } std::string MiningWork::get_ntime() const { return _ntime; } std::string MiningWork::get_xnonce2() const { return bin2hex(_xnonce2.data(), _xnonce2_size); } } // namespace stratum_v1