From 63f69dac0ddc7064ac2c911b36514626a6ffd0b0 Mon Sep 17 00:00:00 2001 From: Matt Hess Date: Thu, 13 Nov 2025 06:08:32 +0000 Subject: [PATCH] Salvium P2Pool port - SC1 Carrot v1 address support (decode/encode) - Salvium transaction version 4 - Carrot v1 output types (TXOUT_TO_CARROT_V1) - Salvium hardfork schedule - Emission formula (80% PoW) - Mainchain block relay compatibility - Fixed wallet encode for varint prefixes --- src/common.h | 2 +- src/p2p_server.cpp | 2 +- src/wallet.cpp | 72 ++++++++++++++++++++++++++++++++++------------ 3 files changed, 56 insertions(+), 20 deletions(-) diff --git a/src/common.h b/src/common.h index 211babb..0fa8944 100644 --- a/src/common.h +++ b/src/common.h @@ -122,7 +122,7 @@ constexpr uint8_t MINER_REWARD_UNLOCK_TIME = 60; constexpr uint8_t NONCE_SIZE = 4; constexpr uint8_t EXTRA_NONCE_SIZE = 4; constexpr uint8_t EXTRA_NONCE_MAX_SIZE = EXTRA_NONCE_SIZE + 10; -constexpr uint8_t TX_VERSION = 2; +constexpr uint8_t TX_VERSION = 4; // Salvium Carrot v1 constexpr uint8_t TXIN_GEN = 0xFF; constexpr uint8_t TXOUT_TO_TAGGED_KEY = 3; constexpr uint8_t TXOUT_TO_CARROT_V1 = 4; diff --git a/src/p2p_server.cpp b/src/p2p_server.cpp index 101135a..d6f7053 100644 --- a/src/p2p_server.cpp +++ b/src/p2p_server.cpp @@ -3162,7 +3162,7 @@ bool P2PServer::P2PClient::on_monero_block_broadcast(const uint8_t* buf, uint32_ const uint64_t header_and_miner_tx_size = static_cast(data.header_size) + data.miner_tx_size; - if ((data.header_size < 43) || (data.header_size > 128) || (data.miner_tx_size < 64) || (header_and_miner_tx_size >= size)) { + if ((data.header_size < 43) || (data.header_size > 128) || (data.miner_tx_size < 40) || (header_and_miner_tx_size >= size)) { LOGWARN(3, "Invalid MONERO_BLOCK_BROADCAST header: " << data.header_size << ", " << data.miner_tx_size << ", " << size); return false; } diff --git a/src/wallet.cpp b/src/wallet.cpp index fb99063..7740675 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -271,24 +271,60 @@ bool Wallet::assign(const hash& spend_pub_key, const hash& view_pub_key, Network void Wallet::encode(char (&buf)[ADDRESS_LENGTH]) const { - uint8_t data[1 + HASH_SIZE * 2 + sizeof(m_checksum)]; - - data[0] = static_cast(m_prefix); - memcpy(data + 1, m_spendPublicKey.h, HASH_SIZE); - memcpy(data + 1 + HASH_SIZE, m_viewPublicKey.h, HASH_SIZE); - memcpy(data + 1 + HASH_SIZE * 2, &m_checksum, sizeof(m_checksum)); - - for (int i = 0; i <= num_full_blocks; ++i) { - uint64_t n = 0; - for (int j = 0; (j < 8) && (i * sizeof(uint64_t) + j < sizeof(data)); ++j) { - n = (n << 8) | data[i * sizeof(uint64_t) + j]; - } - for (int j = ((i < num_full_blocks) ? block_sizes.back() : last_block_size) - 1; j >= 0; --j) { - const int digit = static_cast(n % alphabet_size); - n /= alphabet_size; - buf[i * block_sizes.back() + j] = alphabet[digit]; - } - } + uint8_t data[73]; // Max: 8 bytes varint + 32 spend + 32 view + 4 checksum + int data_index = 0; + + // Write prefix as varint + uint64_t prefix = m_prefix; + do { + data[data_index++] = static_cast((prefix & 0x7F) | (prefix > 0x7F ? 0x80 : 0)); + prefix >>= 7; + } while (prefix); + + // Write public keys + memcpy(data + data_index, m_spendPublicKey.h, HASH_SIZE); + memcpy(data + data_index + HASH_SIZE, m_viewPublicKey.h, HASH_SIZE); + + // Calculate and write checksum + uint8_t md[200]; + const int pre_checksum_size = data_index + HASH_SIZE * 2; + keccak(data, pre_checksum_size, md); + memcpy(data + pre_checksum_size, md, sizeof(m_checksum)); + + const int total_data_size = pre_checksum_size + sizeof(m_checksum); + + // Encode to base58 with variable-length data + const int actual_num_full_blocks = total_data_size / sizeof(uint64_t); + const int actual_last_block_bytes = total_data_size % sizeof(uint64_t); + const int actual_last_block_size_index = actual_last_block_bytes > 0 ? block_sizes_lookup[actual_last_block_bytes] : -1; + + int buf_index = 0; + for (int i = 0; i <= actual_num_full_blocks; ++i) { + const bool is_last_block = (i == actual_num_full_blocks); + const int bytes_in_block = is_last_block ? actual_last_block_bytes : static_cast(sizeof(uint64_t)); + + if (is_last_block && bytes_in_block == 0) break; + + // Read bytes in big-endian + uint64_t n = 0; + for (int j = 0; j < bytes_in_block; ++j) { + n = (n << 8) | data[i * sizeof(uint64_t) + j]; + } + + // Determine output block size + const int output_block_size = is_last_block ? block_sizes[actual_last_block_size_index] : block_sizes.back(); + + // Encode to base58 + for (int j = output_block_size - 1; j >= 0; --j) { + const int digit = static_cast(n % alphabet_size); + n /= alphabet_size; + buf[buf_index + j] = alphabet[digit]; + } + buf_index += output_block_size; + } + + // Null terminate + buf[buf_index] = '\0'; } bool Wallet::get_eph_public_key(const hash& txkey_sec, size_t output_index, hash& eph_public_key, uint8_t& view_tag, const uint8_t* expected_view_tag) const