fixed for Salvium Carrot/Token support

This commit is contained in:
Some Random Crypto Guy
2026-03-10 16:55:32 +00:00
parent 303f15d6bd
commit 347d0a1b1c
4 changed files with 172 additions and 55 deletions
+150 -54
View File
@@ -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<uint8_t>(k) & 0x7F) | 0x80);
k >>= 7;
@@ -181,6 +193,7 @@ void xmrig::BlockTemplate::generateHashingBlob(Buffer &out) const
bool xmrig::BlockTemplate::parse(bool hashes)
{
BlobReader<true> 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<true> 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_t>(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<true> &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<size_t>(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;
}