diff --git a/src/base/tools/cryptonote/BlockTemplate.cpp b/src/base/tools/cryptonote/BlockTemplate.cpp index c56c2987..8f207063 100644 --- a/src/base/tools/cryptonote/BlockTemplate.cpp +++ b/src/base/tools/cryptonote/BlockTemplate.cpp @@ -24,6 +24,18 @@ #include "base/tools/cryptonote/BlobReader.h" #include "base/tools/Cvt.h" +namespace +{ + constexpr uint8_t kSalviumProtocolHF = 2; + constexpr uint8_t kSalviumProtocolTxVersionLegacy = 2; + constexpr uint8_t kSalviumProtocolTxVersionCarrot = 4; + constexpr uint8_t kSalviumProtocolTxVersionTokens = 5; + constexpr uint8_t kSalviumCarrotHF = 10; + constexpr uint8_t kSalviumTokensHF = 11; + constexpr uint8_t kTxOutToKey = 2; + constexpr uint8_t kTxOutToTaggedKey = 3; + constexpr uint8_t kTxOutToCarrotV1 = 4; +} void xmrig::BlockTemplate::calculateMinerTxHash(const uint8_t *prefix_begin, const uint8_t *prefix_end, uint8_t *hash) { @@ -67,7 +79,7 @@ void xmrig::BlockTemplate::calculateMerkleTreeHash() { m_minerTxMerkleTreeBranch.clear(); - const uint64_t count = m_numHashes + ((m_coin == Coin::SALVIUM) ? 2 : 1); + const uint64_t count = m_numHashes + baseTransactionCount(); const uint8_t *h = m_hashes.data(); if (count == 1) { @@ -169,7 +181,7 @@ void xmrig::BlockTemplate::generateHashingBlob(Buffer &out) const out.assign(m_blob.begin(), m_blob.begin() + offset(MINER_TX_PREFIX_OFFSET)); out.insert(out.end(), m_rootHash, m_rootHash + kHashSize); - uint64_t k = m_numHashes + ((m_coin == Coin::SALVIUM) ? 2 : 1); + uint64_t k = m_numHashes + baseTransactionCount(); while (k >= 0x80) { out.emplace_back((static_cast(k) & 0x7F) | 0x80); k >>= 7; @@ -181,6 +193,7 @@ void xmrig::BlockTemplate::generateHashingBlob(Buffer &out) const bool xmrig::BlockTemplate::parse(bool hashes) { BlobReader ar(m_blob.data(), m_blob.size()); + m_hasProtocolTx = false; // Block header ar(m_version.first); @@ -191,6 +204,10 @@ bool xmrig::BlockTemplate::parse(bool hashes) setOffset(NONCE_OFFSET, ar.index()); ar.skip(kNonceSize); + if (m_coin == Coin::SALVIUM && m_version.first >= kSalviumProtocolHF) { + m_hasProtocolTx = true; + } + // Wownero block template has miner signature starting from version 18 if (m_coin == Coin::WOWNERO && majorVersion() >= 18) { ar(m_minerSignature, kSignatureSize); @@ -234,6 +251,11 @@ bool xmrig::BlockTemplate::parse(bool hashes) return false; } } + else if (m_coin == Coin::SALVIUM) { + if (m_numOutputs < 1) { + return false; + } + } else if (m_numOutputs != 1) { return false; } @@ -241,8 +263,12 @@ bool xmrig::BlockTemplate::parse(bool hashes) ar(m_amount); ar(m_outputType); - // output type must be txout_to_key (2) or txout_to_tagged_key (3) - if ((m_outputType != 2) && (m_outputType != 3)) { + if (m_coin == Coin::SALVIUM) { + if ((m_outputType != kTxOutToKey) && (m_outputType != kTxOutToTaggedKey) && (m_outputType != kTxOutToCarrotV1)) { + return false; + } + } + else if ((m_outputType != kTxOutToKey) && (m_outputType != kTxOutToTaggedKey)) { return false; } @@ -251,19 +277,11 @@ bool xmrig::BlockTemplate::parse(bool hashes) ar(m_ephPublicKey, kKeySize); if (m_coin == Coin::SALVIUM) { - - uint8_t asset_type_len; - ar(asset_type_len); - ar.skip(asset_type_len); - - uint64_t output_unlock_time; - ar(output_unlock_time); - - if (m_outputType == 3) { - ar(m_viewTag); - } - - } else if (m_coin == Coin::ZEPHYR) { + if (!parseSalviumOutput(ar, m_outputType, true)) { + return false; + } + } + else if (m_coin == Coin::ZEPHYR) { if (m_outputType != 2) { return false; } @@ -297,6 +315,27 @@ bool xmrig::BlockTemplate::parse(bool hashes) ar(m_viewTag); } + if (m_coin == Coin::SALVIUM && m_numOutputs > 1) { + for (uint64_t k = 1; k < m_numOutputs; ++k) { + uint64_t amount2; + ar(amount2); + + uint8_t output_type2; + ar(output_type2); + + if ((output_type2 != kTxOutToKey) && (output_type2 != kTxOutToTaggedKey) && (output_type2 != kTxOutToCarrotV1)) { + return false; + } + + Span key2; + ar(key2, kKeySize); + + if (!parseSalviumOutput(ar, output_type2, false)) { + return false; + } + } + } + if (m_coin == Coin::TOWNFORGE) { ar(m_unlockTime); } @@ -306,6 +345,7 @@ bool xmrig::BlockTemplate::parse(bool hashes) setOffset(TX_EXTRA_OFFSET, ar.index()); BlobReader ar_extra(blob(TX_EXTRA_OFFSET), m_extraSize); + const uint8_t *extra_ptr = blob(TX_EXTRA_OFFSET); ar.skip(m_extraSize); bool pubkey_offset_first = true; @@ -317,6 +357,12 @@ bool xmrig::BlockTemplate::parse(bool hashes) ar_extra(extra_tag); switch (extra_tag) { + case 0x00: // TX_EXTRA_TAG_PADDING + while (ar_extra.index() < m_extraSize && extra_ptr[ar_extra.index()] == 0) { + ar_extra.skip(1); + } + break; + case 0x01: // TX_EXTRA_TAG_PUBKEY if (pubkey_offset_first) { pubkey_offset_first = false; @@ -326,19 +372,36 @@ bool xmrig::BlockTemplate::parse(bool hashes) break; case 0x02: // TX_EXTRA_NONCE - ar_extra(size); + if (!ar_extra(size)) { + return false; + } setOffset(TX_EXTRA_NONCE_OFFSET, offset(TX_EXTRA_OFFSET) + ar_extra.index()); ar_extra(m_txExtraNonce, size); break; case 0x03: // TX_EXTRA_MERGE_MINING_TAG - ar_extra(size); + if (!ar_extra(size)) { + return false; + } setOffset(TX_EXTRA_MERGE_MINING_TAG_OFFSET, offset(TX_EXTRA_OFFSET) + ar_extra.index()); ar_extra(m_txMergeMiningTag, size); break; + case 0x04: // TX_EXTRA_TAG_ADDITIONAL_PUBKEYS + if (!ar_extra(size)) { + return false; + } + ar_extra.skip(kKeySize * size); + break; + default: - return false; // TODO(SChernykh): handle other tags + if (!ar_extra(size)) { + return false; + } + if (size > ar_extra.remaining()) { + return false; + } + ar_extra.skip(static_cast(size)); } } @@ -386,7 +449,7 @@ bool xmrig::BlockTemplate::parse(bool hashes) return false; } - if (m_coin == Coin::SALVIUM) { + if (m_hasProtocolTx) { // Protocol transaction begin // Prefix begin @@ -395,7 +458,10 @@ bool xmrig::BlockTemplate::parse(bool hashes) // Parse/skip the protocol_tx uint64_t protocol_tx_version, protocol_tx_unlock_time, protocol_tx_num_inputs; ar(protocol_tx_version); - if (protocol_tx_version != 2) { + const uint8_t expected_protocol_version = + (majorVersion() >= kSalviumTokensHF) ? kSalviumProtocolTxVersionTokens : + (majorVersion() >= kSalviumCarrotHF) ? kSalviumProtocolTxVersionCarrot : kSalviumProtocolTxVersionLegacy; + if (protocol_tx_version != expected_protocol_version) { return false; } @@ -434,24 +500,16 @@ bool xmrig::BlockTemplate::parse(bool hashes) uint8_t out_type; ar(out_type); - // output type must be txout_to_key (2) or txout_to_tagged_key (3) - if ((out_type != 2) && (out_type != 3)) { + // output type must be txout_to_key (2), txout_to_tagged_key (3) or txout_to_carrot_v1 (4) + if ((out_type != kTxOutToKey) && (out_type != kTxOutToTaggedKey) && (out_type != kTxOutToCarrotV1)) { return false; } Span out_pubkey; ar(out_pubkey, kKeySize); - uint64_t out_at_len; - ar(out_at_len); - ar.skip(out_at_len); - - uint64_t out_unlock; - ar(out_unlock); - - if (out_type == 3) { - uint8_t out_vt; - ar(out_vt); + if (!parseSalviumOutput(ar, out_type, false)) { + return false; } } @@ -490,32 +548,70 @@ bool xmrig::BlockTemplate::parse(bool hashes) ar(m_numHashes); if (hashes) { - if (m_coin == Coin::SALVIUM) { + const uint64_t base_count = baseTransactionCount(); + m_hashes.resize((m_numHashes + base_count) * kHashSize); - m_hashes.resize((m_numHashes + 2) * kHashSize); - // Miner TX - calculateMinerTxHash(blob(MINER_TX_PREFIX_OFFSET), blob(MINER_TX_PREFIX_END_OFFSET), m_hashes.data()); - // Protocol TX - calculateMinerTxHash(blob(PROTOCOL_TX_PREFIX_OFFSET), blob(PROTOCOL_TX_PREFIX_END_OFFSET), m_hashes.data() + kHashSize); + // Miner TX hash always goes first + calculateMinerTxHash(blob(MINER_TX_PREFIX_OFFSET), blob(MINER_TX_PREFIX_END_OFFSET), m_hashes.data()); - for (uint64_t i = 0; i < m_numHashes; ++i) { - Span h; - ar(h, kHashSize); - memcpy(m_hashes.data() + (i+2) * kHashSize, h.data(), kHashSize); - } - } else { - - m_hashes.resize((m_numHashes + 1) * kHashSize); - calculateMinerTxHash(blob(MINER_TX_PREFIX_OFFSET), blob(MINER_TX_PREFIX_END_OFFSET), m_hashes.data()); + uint64_t offset = 1; - for (uint64_t i = 1; i <= m_numHashes; ++i) { - Span h; - ar(h, kHashSize); - memcpy(m_hashes.data() + i * kHashSize, h.data(), kHashSize); - } + if (m_hasProtocolTx) { + calculateMinerTxHash(blob(PROTOCOL_TX_PREFIX_OFFSET), blob(PROTOCOL_TX_PREFIX_END_OFFSET), m_hashes.data() + offset * kHashSize); + ++offset; } + + for (uint64_t i = 0; i < m_numHashes; ++i) { + Span h; + ar(h, kHashSize); + memcpy(m_hashes.data() + (offset + i) * kHashSize, h.data(), kHashSize); + } + calculateMerkleTreeHash(); } return true; } + + +bool xmrig::BlockTemplate::parseSalviumOutput(BlobReader &ar, uint8_t outputType, bool storeExtraData) +{ + uint64_t asset_type_len = 0; + ar(asset_type_len); + + if (asset_type_len > ar.remaining()) { + return false; + } + + if (asset_type_len > 0 && !ar.skip(static_cast(asset_type_len))) { + return false; + } + + if (outputType == kTxOutToKey || outputType == kTxOutToTaggedKey) { + uint64_t unlock_time = 0; + ar(unlock_time); + } + + if (outputType == kTxOutToTaggedKey) { + uint8_t view_tag = 0; + ar(view_tag); + + if (storeExtraData) { + m_viewTag = view_tag; + } + } + else if (outputType == kTxOutToCarrotV1) { + uint8_t carrot_view_tag[kCarrotViewTagSize]; + uint8_t carrot_anchor[kCarrotAnchorSize]; + + ar(carrot_view_tag); + ar(carrot_anchor); + + if (storeExtraData) { + memcpy(m_carrotViewTag, carrot_view_tag, kCarrotViewTagSize); + memcpy(m_carrotEncryptedJanusAnchor, carrot_anchor, kCarrotAnchorSize); + } + } + + return true; +} diff --git a/src/base/tools/cryptonote/BlockTemplate.h b/src/base/tools/cryptonote/BlockTemplate.h index 711732a1..96a22678 100644 --- a/src/base/tools/cryptonote/BlockTemplate.h +++ b/src/base/tools/cryptonote/BlockTemplate.h @@ -25,6 +25,7 @@ #include "3rdparty/rapidjson/fwd.h" #include "base/crypto/Coin.h" #include "base/tools/Buffer.h" +#include "base/tools/cryptonote/BlobReader.h" #include "base/tools/String.h" #include "base/tools/Span.h" @@ -39,6 +40,8 @@ public: static constexpr size_t kKeySize = 32; static constexpr size_t kNonceSize = 4; static constexpr size_t kSignatureSize = 64; + static constexpr size_t kCarrotViewTagSize = 3; + static constexpr size_t kCarrotAnchorSize = 16; # ifdef XMRIG_PROXY_PROJECT static constexpr bool kCalcHashes = true; @@ -96,6 +99,7 @@ public: inline const Buffer &hashes() const { return m_hashes; } inline const Buffer &minerTxMerkleTreeBranch() const { return m_minerTxMerkleTreeBranch; } inline const uint8_t *rootHash() const { return m_rootHash; } + inline bool hasProtocolTransaction() const { return m_hasProtocolTx; } inline Buffer generateHashingBlob() const { @@ -121,6 +125,8 @@ private: inline void setOffset(Offset offset, size_t value) { m_offsets[offset] = static_cast(value); } bool parse(bool hashes); + inline uint64_t baseTransactionCount() const { return (m_coin == Coin::SALVIUM) ? (m_hasProtocolTx ? 2 : 1) : 1; } + bool parseSalviumOutput(BlobReader &ar, uint8_t outputType, bool storeExtraData); Buffer m_blob; Coin m_coin; @@ -150,6 +156,9 @@ private: Buffer m_hashes; Buffer m_minerTxMerkleTreeBranch; uint8_t m_rootHash[kHashSize]{}; + bool m_hasProtocolTx = false; + uint8_t m_carrotViewTag[kCarrotViewTagSize]{}; + uint8_t m_carrotEncryptedJanusAnchor[kCarrotAnchorSize]{}; }; diff --git a/src/base/tools/cryptonote/WalletAddress.cpp b/src/base/tools/cryptonote/WalletAddress.cpp index a66acbe1..37faf492 100644 --- a/src/base/tools/cryptonote/WalletAddress.cpp +++ b/src/base/tools/cryptonote/WalletAddress.cpp @@ -273,6 +273,18 @@ const xmrig::WalletAddress::TagInfo &xmrig::WalletAddress::tagInfo(uint64_t tag) { 0xf343eb318, { Coin::SALVIUM, STAGENET, INTEGRATED, 39081, 39082 } }, { 0x2d47eb318, { Coin::SALVIUM, STAGENET, SUBADDRESS, 39081, 39082 } }, + { 0x180c96, { Coin::SALVIUM, MAINNET, PUBLIC, 19081, 19082 } }, + { 0x2ccc96, { Coin::SALVIUM, MAINNET, INTEGRATED, 19081, 19082 } }, + { 0x314c96, { Coin::SALVIUM, MAINNET, SUBADDRESS, 19081, 19082 } }, + + { 0x254c96, { Coin::SALVIUM, TESTNET, PUBLIC, 29081, 29082 } }, + { 0x1ac50c96, { Coin::SALVIUM, TESTNET, INTEGRATED, 29081, 29082 } }, + { 0x3c54c96, { Coin::SALVIUM, TESTNET, SUBADDRESS, 29081, 29082 } }, + + { 0x24cc96, { Coin::SALVIUM, STAGENET, PUBLIC, 39081, 39082 } }, + { 0x1a848c96, { Coin::SALVIUM, STAGENET, INTEGRATED, 39081, 39082 } }, + { 0x384cc96, { Coin::SALVIUM, STAGENET, SUBADDRESS, 39081, 39082 } }, + }; const auto it = tags.find(tag); diff --git a/src/version.h b/src/version.h index 27f5d5de..ebe535e3 100644 --- a/src/version.h +++ b/src/version.h @@ -19,7 +19,7 @@ #ifndef XMRIG_VERSION_H #define XMRIG_VERSION_H -#define APP_ID "xmrig" +#define APP_ID "xmrig-salvium" #define APP_NAME "XMRig" #define APP_DESC "XMRig miner" #define APP_VERSION "6.21.3"