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
This commit is contained in:
Matt Hess
2025-11-13 06:08:32 +00:00
parent 8fa225b909
commit 63f69dac0d
3 changed files with 56 additions and 20 deletions

View File

@@ -122,7 +122,7 @@ constexpr uint8_t MINER_REWARD_UNLOCK_TIME = 60;
constexpr uint8_t NONCE_SIZE = 4; constexpr uint8_t NONCE_SIZE = 4;
constexpr uint8_t EXTRA_NONCE_SIZE = 4; constexpr uint8_t EXTRA_NONCE_SIZE = 4;
constexpr uint8_t EXTRA_NONCE_MAX_SIZE = EXTRA_NONCE_SIZE + 10; 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 TXIN_GEN = 0xFF;
constexpr uint8_t TXOUT_TO_TAGGED_KEY = 3; constexpr uint8_t TXOUT_TO_TAGGED_KEY = 3;
constexpr uint8_t TXOUT_TO_CARROT_V1 = 4; constexpr uint8_t TXOUT_TO_CARROT_V1 = 4;

View File

@@ -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<uint64_t>(data.header_size) + data.miner_tx_size; const uint64_t header_and_miner_tx_size = static_cast<uint64_t>(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); LOGWARN(3, "Invalid MONERO_BLOCK_BROADCAST header: " << data.header_size << ", " << data.miner_tx_size << ", " << size);
return false; return false;
} }

View File

@@ -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 void Wallet::encode(char (&buf)[ADDRESS_LENGTH]) const
{ {
uint8_t data[1 + HASH_SIZE * 2 + sizeof(m_checksum)]; uint8_t data[73]; // Max: 8 bytes varint + 32 spend + 32 view + 4 checksum
int data_index = 0;
data[0] = static_cast<uint8_t>(m_prefix);
memcpy(data + 1, m_spendPublicKey.h, HASH_SIZE); // Write prefix as varint
memcpy(data + 1 + HASH_SIZE, m_viewPublicKey.h, HASH_SIZE); uint64_t prefix = m_prefix;
memcpy(data + 1 + HASH_SIZE * 2, &m_checksum, sizeof(m_checksum)); do {
data[data_index++] = static_cast<uint8_t>((prefix & 0x7F) | (prefix > 0x7F ? 0x80 : 0));
for (int i = 0; i <= num_full_blocks; ++i) { prefix >>= 7;
uint64_t n = 0; } while (prefix);
for (int j = 0; (j < 8) && (i * sizeof(uint64_t) + j < sizeof(data)); ++j) {
n = (n << 8) | data[i * sizeof(uint64_t) + j]; // Write public keys
} memcpy(data + data_index, m_spendPublicKey.h, HASH_SIZE);
for (int j = ((i < num_full_blocks) ? block_sizes.back() : last_block_size) - 1; j >= 0; --j) { memcpy(data + data_index + HASH_SIZE, m_viewPublicKey.h, HASH_SIZE);
const int digit = static_cast<int>(n % alphabet_size);
n /= alphabet_size; // Calculate and write checksum
buf[i * block_sizes.back() + j] = alphabet[digit]; 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<int>(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<int>(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 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