diff --git a/src/base/net/stratum/Client.cpp b/src/base/net/stratum/Client.cpp index 3cf50e21b..2f5865b16 100644 --- a/src/base/net/stratum/Client.cpp +++ b/src/base/net/stratum/Client.cpp @@ -236,6 +236,10 @@ int64_t xmrig::Client::submit(const JobResult &result) if (result.commitment()) { params.AddMember("commitment", StringRef(commitment), allocator); } +# else + if (result.commitment) { + params.AddMember("commitment", StringRef(result.commitment), allocator); + } # endif if (has() && result.algorithm.isValid()) { diff --git a/src/base/net/stratum/DaemonClient.cpp b/src/base/net/stratum/DaemonClient.cpp index 37f352bc1..f8e2bd6df 100644 --- a/src/base/net/stratum/DaemonClient.cpp +++ b/src/base/net/stratum/DaemonClient.cpp @@ -410,6 +410,7 @@ bool xmrig::DaemonClient::parseJob(const rapidjson::Value ¶ms, int *code) m_blocktemplate.offset(BlockTemplate::TX_EXTRA_NONCE_OFFSET) - k, m_blocktemplate.txExtraNonce().size(), m_blocktemplate.minerTxMerkleTreeBranch(), + m_blocktemplate.minerTxMerkleTreePath(), m_blocktemplate.outputType() == 3 ); # endif diff --git a/src/base/net/stratum/Job.cpp b/src/base/net/stratum/Job.cpp index 1a7cfbfe1..c15276109 100644 --- a/src/base/net/stratum/Job.cpp +++ b/src/base/net/stratum/Job.cpp @@ -269,6 +269,7 @@ void xmrig::Job::copy(const Job &other) m_minerTxExtraNonceOffset = other.m_minerTxExtraNonceOffset; m_minerTxExtraNonceSize = other.m_minerTxExtraNonceSize; m_minerTxMerkleTreeBranch = other.m_minerTxMerkleTreeBranch; + m_minerTxMerkleTreePath = other.m_minerTxMerkleTreePath; m_hasViewTag = other.m_hasViewTag; # else memcpy(m_ephPublicKey, other.m_ephPublicKey, sizeof(m_ephPublicKey)); @@ -325,6 +326,7 @@ void xmrig::Job::move(Job &&other) m_minerTxExtraNonceOffset = other.m_minerTxExtraNonceOffset; m_minerTxExtraNonceSize = other.m_minerTxExtraNonceSize; m_minerTxMerkleTreeBranch = std::move(other.m_minerTxMerkleTreeBranch); + m_minerTxMerkleTreePath = other.m_minerTxMerkleTreePath; m_hasViewTag = other.m_hasViewTag; # else memcpy(m_ephPublicKey, other.m_ephPublicKey, sizeof(m_ephPublicKey)); @@ -349,7 +351,7 @@ void xmrig::Job::setSpendSecretKey(const uint8_t *key) } -void xmrig::Job::setMinerTx(const uint8_t *begin, const uint8_t *end, size_t minerTxEphPubKeyOffset, size_t minerTxPubKeyOffset, size_t minerTxExtraNonceOffset, size_t minerTxExtraNonceSize, const Buffer &minerTxMerkleTreeBranch, bool hasViewTag) +void xmrig::Job::setMinerTx(const uint8_t *begin, const uint8_t *end, size_t minerTxEphPubKeyOffset, size_t minerTxPubKeyOffset, size_t minerTxExtraNonceOffset, size_t minerTxExtraNonceSize, const Buffer &minerTxMerkleTreeBranch, uint32_t minerTxMerkleTreePath, bool hasViewTag) { m_minerTxPrefix.assign(begin, end); m_minerTxEphPubKeyOffset = minerTxEphPubKeyOffset; @@ -357,6 +359,7 @@ void xmrig::Job::setMinerTx(const uint8_t *begin, const uint8_t *end, size_t min m_minerTxExtraNonceOffset = minerTxExtraNonceOffset; m_minerTxExtraNonceSize = minerTxExtraNonceSize; m_minerTxMerkleTreeBranch = minerTxMerkleTreeBranch; + m_minerTxMerkleTreePath = minerTxMerkleTreePath; m_hasViewTag = hasViewTag; } @@ -401,7 +404,7 @@ void xmrig::Job::generateHashingBlob(String &blob) const { uint8_t root_hash[32]; const uint8_t* p = m_minerTxPrefix.data(); - BlockTemplate::calculateRootHash(p, p + m_minerTxPrefix.size(), m_minerTxMerkleTreeBranch, root_hash); + BlockTemplate::calculateRootHash(p, p + m_minerTxPrefix.size(), m_minerTxMerkleTreeBranch, m_minerTxMerkleTreePath, root_hash); uint64_t root_hash_offset = nonceOffset() + nonceSize(); diff --git a/src/base/net/stratum/Job.h b/src/base/net/stratum/Job.h index 8e28c1bf1..d5967c7a7 100644 --- a/src/base/net/stratum/Job.h +++ b/src/base/net/stratum/Job.h @@ -121,7 +121,7 @@ public: inline bool hasViewTag() const { return m_hasViewTag; } void setSpendSecretKey(const uint8_t* key); - void setMinerTx(const uint8_t* begin, const uint8_t* end, size_t minerTxEphPubKeyOffset, size_t minerTxPubKeyOffset, size_t minerTxExtraNonceOffset, size_t minerTxExtraNonceSize, const Buffer& minerTxMerkleTreeBranch, bool hasViewTag); + void setMinerTx(const uint8_t* begin, const uint8_t* end, size_t minerTxEphPubKeyOffset, size_t minerTxPubKeyOffset, size_t minerTxExtraNonceOffset, size_t minerTxExtraNonceSize, const Buffer& minerTxMerkleTreeBranch, uint32_t minerTxMerkleTreePath, bool hasViewTag); void setViewTagInMinerTx(uint8_t view_tag); void setExtraNonceInMinerTx(uint32_t extra_nonce); void generateSignatureData(String& signatureData, uint8_t& view_tag) const; @@ -179,6 +179,7 @@ private: size_t m_minerTxExtraNonceOffset = 0; size_t m_minerTxExtraNonceSize = 0; Buffer m_minerTxMerkleTreeBranch; + uint32_t m_minerTxMerkleTreePath = 0; bool m_hasViewTag = false; # else // Miner signatures diff --git a/src/base/tools/cryptonote/BlockTemplate.cpp b/src/base/tools/cryptonote/BlockTemplate.cpp index 1b64f2ee5..54933365d 100644 --- a/src/base/tools/cryptonote/BlockTemplate.cpp +++ b/src/base/tools/cryptonote/BlockTemplate.cpp @@ -48,69 +48,98 @@ void xmrig::BlockTemplate::calculateMinerTxHash(const uint8_t *prefix_begin, con } -void xmrig::BlockTemplate::calculateRootHash(const uint8_t *prefix_begin, const uint8_t *prefix_end, const Buffer &miner_tx_merkle_tree_branch, uint8_t *root_hash) +void xmrig::BlockTemplate::calculateRootHash(const uint8_t *prefix_begin, const uint8_t *prefix_end, const Buffer &miner_tx_merkle_tree_branch, uint32_t miner_tx_merkle_tree_path, uint8_t *root_hash) { calculateMinerTxHash(prefix_begin, prefix_end, root_hash); - for (size_t i = 0; i < miner_tx_merkle_tree_branch.size(); i += kHashSize) { + const size_t depth = miner_tx_merkle_tree_branch.size() / kHashSize; + + for (size_t d = 0; d < depth; ++d) { uint8_t h[kHashSize * 2]; - memcpy(h, root_hash, kHashSize); - memcpy(h + kHashSize, miner_tx_merkle_tree_branch.data() + i, kHashSize); + const uint32_t t = (miner_tx_merkle_tree_path >> (depth - d - 1)) & 1; + + memcpy(h + kHashSize * t, root_hash, kHashSize); + memcpy(h + kHashSize * (t ^ 1), miner_tx_merkle_tree_branch.data() + d * kHashSize, kHashSize); keccak(h, kHashSize * 2, root_hash, kHashSize); } } -void xmrig::BlockTemplate::calculateMerkleTreeHash() +void xmrig::BlockTemplate::calculateMerkleTreeHash(uint32_t index) { m_minerTxMerkleTreeBranch.clear(); + m_minerTxMerkleTreePath = 0; - const uint64_t count = m_numHashes + 1; + const size_t count = m_hashes.size() / kHashSize; const uint8_t *h = m_hashes.data(); - if (count == 1) { + if (count == 1) { memcpy(m_rootHash, h, kHashSize); - } - else if (count == 2) { - m_minerTxMerkleTreeBranch.insert(m_minerTxMerkleTreeBranch.end(), h + kHashSize, h + kHashSize * 2); + } + else if (count == 2) { keccak(h, kHashSize * 2, m_rootHash, kHashSize); - } - else { - size_t i = 0; - size_t j = 0; - size_t cnt = 0; - for (i = 0, cnt = 1; cnt <= count; ++i, cnt <<= 1) {} + m_minerTxMerkleTreeBranch.reserve(1); + m_minerTxMerkleTreeBranch.insert(m_minerTxMerkleTreeBranch.end(), h + kHashSize * (index ^ 1), h + kHashSize * ((index ^ 1) + 1)); + m_minerTxMerkleTreePath = static_cast(index); + } + else { + uint8_t h2[kHashSize]; + memcpy(h2, h + kHashSize * index, kHashSize); - cnt >>= 1; + size_t cnt = 1, proof_max_size = 0; + do { + cnt <<= 1; + ++proof_max_size; + } while (cnt <= count); + cnt >>= 1; - m_minerTxMerkleTreeBranch.reserve(kHashSize * (i - 1)); + m_minerTxMerkleTreeBranch.reserve(proof_max_size); Buffer ints(cnt * kHashSize); - memcpy(ints.data(), h, (cnt * 2 - count) * kHashSize); - for (i = cnt * 2 - count, j = cnt * 2 - count; j < cnt; i += 2, ++j) { - if (i == 0) { - m_minerTxMerkleTreeBranch.insert(m_minerTxMerkleTreeBranch.end(), h + kHashSize, h + kHashSize * 2); - } - keccak(h + i * kHashSize, kHashSize * 2, ints.data() + j * kHashSize, kHashSize); - } + const size_t k = cnt * 2 - count; + memcpy(ints.data(), h, k * kHashSize); - while (cnt > 2) { - cnt >>= 1; - for (i = 0, j = 0; j < cnt; i += 2, ++j) { - if (i == 0) { - m_minerTxMerkleTreeBranch.insert(m_minerTxMerkleTreeBranch.end(), ints.data() + kHashSize, ints.data() + kHashSize * 2); - } - keccak(ints.data() + i * kHashSize, kHashSize * 2, ints.data() + j * kHashSize, kHashSize); - } - } + for (size_t i = k, j = k; j < cnt; i += 2, ++j) { + keccak(h + i * kHashSize, kHashSize * 2, ints.data() + j * kHashSize, kHashSize); - m_minerTxMerkleTreeBranch.insert(m_minerTxMerkleTreeBranch.end(), ints.data() + kHashSize, ints.data() + kHashSize * 2); - keccak(ints.data(), kHashSize * 2, m_rootHash, kHashSize); - } + if (memcmp(h + i * kHashSize, h2, kHashSize) == 0) { + m_minerTxMerkleTreeBranch.insert(m_minerTxMerkleTreeBranch.end(), h + kHashSize * (i + 1), h + kHashSize * (i + 2)); + memcpy(h2, ints.data() + j * kHashSize, kHashSize); + } + else if (memcmp(h + (i + 1) * kHashSize, h2, kHashSize) == 0) { + m_minerTxMerkleTreeBranch.insert(m_minerTxMerkleTreeBranch.end(), h + kHashSize * i, h + kHashSize * (i + 1)); + memcpy(h2, ints.data() + j * kHashSize, kHashSize); + m_minerTxMerkleTreePath = 1; + } + } + + while (cnt >= 2) { + cnt >>= 1; + for (size_t i = 0, j = 0; j < cnt; i += 2, ++j) { + uint8_t tmp[kHashSize]; + keccak(ints.data() + i * kHashSize, kHashSize * 2, tmp, kHashSize); + + if (memcmp(ints.data() + i * kHashSize, h2, kHashSize) == 0) { + m_minerTxMerkleTreeBranch.insert(m_minerTxMerkleTreeBranch.end(), ints.data() + kHashSize * (i + 1), ints.data() + kHashSize * (i + 2)); + memcpy(h2, tmp, kHashSize); + m_minerTxMerkleTreePath <<= 1; + } + else if (memcmp(ints.data() + (i + 1) * kHashSize, h2, kHashSize) == 0) { + m_minerTxMerkleTreeBranch.insert(m_minerTxMerkleTreeBranch.end(), ints.data() + kHashSize * i, ints.data() + kHashSize * (i + 1)); + memcpy(h2, tmp, kHashSize); + m_minerTxMerkleTreePath = (m_minerTxMerkleTreePath << 1) | 1; + } + + memcpy(ints.data() + j * kHashSize, tmp, kHashSize); + } + } + + memcpy(m_rootHash, ints.data(), kHashSize); + } } @@ -375,16 +404,42 @@ bool xmrig::BlockTemplate::parse(bool hashes) ar(m_numHashes); if (hashes) { - m_hashes.resize((m_numHashes + 1) * kHashSize); - calculateMinerTxHash(blob(MINER_TX_PREFIX_OFFSET), blob(MINER_TX_PREFIX_END_OFFSET), m_hashes.data()); + // FCMP++ layout: + // + // index 0 fcmp_pp_n_tree_layers + 31 zero bytes + // index 1 fcmp_pp_tree_root + // index 2 coinbase transaction hash + // index 3+ other transaction hashes + // + // pre-FCMP++ layout: + // + // index 0 coinbase transaction hash + // index 1+ other transaction hashes + // + const uint32_t coinbase_tx_index = is_fcmp_pp ? 2 : 0; + + m_hashes.clear(); + m_hashes.resize((coinbase_tx_index + m_numHashes + 1) * kHashSize); + + uint8_t* data = m_hashes.data() + coinbase_tx_index * kHashSize; + + calculateMinerTxHash(blob(MINER_TX_PREFIX_OFFSET), blob(MINER_TX_PREFIX_END_OFFSET), data); for (uint64_t i = 1; i <= m_numHashes; ++i) { Span h; ar(h, kHashSize); - memcpy(m_hashes.data() + i * kHashSize, h.data(), kHashSize); + memcpy(data + i * kHashSize, h.data(), kHashSize); } - calculateMerkleTreeHash(); + if (is_fcmp_pp) { + ar(m_FCMPTreeLayers); + ar(m_FCMPTreeRoot); + + m_hashes[0] = m_FCMPTreeLayers; + memcpy(m_hashes.data() + kHashSize, m_FCMPTreeRoot, kHashSize); + } + + calculateMerkleTreeHash(coinbase_tx_index); } return true; diff --git a/src/base/tools/cryptonote/BlockTemplate.h b/src/base/tools/cryptonote/BlockTemplate.h index a4e75f3ff..ebfcc1328 100644 --- a/src/base/tools/cryptonote/BlockTemplate.h +++ b/src/base/tools/cryptonote/BlockTemplate.h @@ -93,6 +93,7 @@ public: inline uint64_t numHashes() const { return m_numHashes; } inline const Buffer &hashes() const { return m_hashes; } inline const Buffer &minerTxMerkleTreeBranch() const { return m_minerTxMerkleTreeBranch; } + inline uint32_t minerTxMerkleTreePath() const { return m_minerTxMerkleTreePath; } inline const uint8_t *rootHash() const { return m_rootHash; } inline Buffer generateHashingBlob() const @@ -104,13 +105,13 @@ public: } static void calculateMinerTxHash(const uint8_t *prefix_begin, const uint8_t *prefix_end, uint8_t *hash); - static void calculateRootHash(const uint8_t *prefix_begin, const uint8_t *prefix_end, const Buffer &miner_tx_merkle_tree_branch, uint8_t *root_hash); + static void calculateRootHash(const uint8_t *prefix_begin, const uint8_t *prefix_end, const Buffer &miner_tx_merkle_tree_branch, uint32_t miner_tx_merkle_tree_path, uint8_t *root_hash); bool parse(const Buffer &blocktemplate, const Coin &coin, bool hashes = kCalcHashes); bool parse(const char *blocktemplate, size_t size, const Coin &coin, bool hashes); bool parse(const rapidjson::Value &blocktemplate, const Coin &coin, bool hashes = kCalcHashes); bool parse(const String &blocktemplate, const Coin &coin, bool hashes = kCalcHashes); - void calculateMerkleTreeHash(); + void calculateMerkleTreeHash(uint32_t index); void generateHashingBlob(Buffer &out) const; private: @@ -147,9 +148,12 @@ private: uint64_t m_numHashes = 0; Buffer m_hashes; Buffer m_minerTxMerkleTreeBranch; + uint32_t m_minerTxMerkleTreePath = 0; uint8_t m_rootHash[kHashSize]{}; uint8_t m_carrotViewTag[3]{}; uint8_t m_janusAnchor[16]{}; + uint8_t m_FCMPTreeLayers = 0; + uint8_t m_FCMPTreeRoot[kHashSize]{}; };