From 14bbf96feb50e962e6a89433eab74a78b331219f Mon Sep 17 00:00:00 2001 From: SChernykh Date: Tue, 28 Jun 2022 10:32:21 +0200 Subject: [PATCH] Enforce deterministic tx keys starting from v15 --- src/block_template.cpp | 13 +++---- src/block_template.h | 1 - src/crypto.cpp | 71 ++++++++++++++++++++++++++++++++++++--- src/crypto.h | 2 ++ src/miner.cpp | 1 - src/pool_block_parser.inl | 9 +++++ src/stratum_server.cpp | 1 - 7 files changed, 82 insertions(+), 16 deletions(-) diff --git a/src/block_template.cpp b/src/block_template.cpp index 68d1317..1f20bd0 100644 --- a/src/block_template.cpp +++ b/src/block_template.cpp @@ -50,6 +50,8 @@ BlockTemplate::BlockTemplate(p2pool* pool) , m_difficulty{} , m_seedHash{} , m_timestamp(0) + , m_txkeyPub{} + , m_txkeySec{} , m_poolBlockTemplate(new PoolBlock()) , m_finalReward(0) { @@ -73,8 +75,6 @@ BlockTemplate::BlockTemplate(p2pool* pool) #if TEST_MEMPOOL_PICKING_ALGORITHM m_knapsack.reserve(512 * 309375); #endif - - update_tx_keys(); } BlockTemplate::~BlockTemplate() @@ -197,6 +197,8 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet *this = *m_oldTemplates[id % array_size(&BlockTemplate::m_oldTemplates)]; }; + get_tx_keys(m_txkeyPub, m_txkeySec, miner_wallet->spend_public_key(), data.prev_id); + m_height = data.height; m_difficulty = data.difficulty; m_seedHash = data.seed_hash; @@ -1057,13 +1059,6 @@ std::vector BlockTemplate::get_block_template_blob(uint32_t template_id return m_blockTemplateBlob; } -void BlockTemplate::update_tx_keys() -{ - WriteLock lock(m_lock); - - generate_keys(m_txkeyPub, m_txkeySec); -} - void BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce) { WriteLock lock(m_lock); diff --git a/src/block_template.h b/src/block_template.h index 5b02fba..d85b029 100644 --- a/src/block_template.h +++ b/src/block_template.h @@ -47,7 +47,6 @@ public: uint32_t get_hashing_blobs(uint32_t extra_nonce_start, uint32_t count, std::vector& blobs, uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset, uint32_t& template_id) const; std::vector get_block_template_blob(uint32_t template_id, size_t& nonce_offset, size_t& extra_nonce_offset) const; - void update_tx_keys(); FORCEINLINE uint64_t height() const { return m_height; } FORCEINLINE difficulty_type difficulty() const { return m_difficulty; } diff --git a/src/crypto.cpp b/src/crypto.cpp index 0f9b4e1..e2d390a 100644 --- a/src/crypto.cpp +++ b/src/crypto.cpp @@ -24,6 +24,10 @@ extern "C" { #include "crypto-ops.h" } +// l = 2^252 + 27742317777372353535851937790883648493. +// l fits 15 times in 32 bytes (iow, 15 l is the highest multiple of l that fits in 32 bytes) +static constexpr uint8_t limit[32] = { 0xe3, 0x6a, 0x67, 0x72, 0x8b, 0xce, 0x13, 0x29, 0x8f, 0x30, 0x82, 0x8c, 0x0b, 0xa4, 0x10, 0x39, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0 }; + namespace p2pool { namespace { @@ -79,10 +83,6 @@ static FORCEINLINE bool less32(const uint8_t* k0, const uint8_t* k1) // cppcheck-suppress constParameter void generate_keys(hash& pub, hash& sec) { - // l = 2^252 + 27742317777372353535851937790883648493. - // l fits 15 times in 32 bytes (iow, 15 l is the highest multiple of l that fits in 32 bytes) - static constexpr uint8_t limit[32] = { 0xe3, 0x6a, 0x67, 0x72, 0x8b, 0xce, 0x13, 0x29, 0x8f, 0x30, 0x82, 0x8c, 0x0b, 0xa4, 0x10, 0x39, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0 }; - do { do { randomBytes(sec.h); } while (!less32(sec.h, limit)); sc_reduce32(sec.h); @@ -93,6 +93,30 @@ void generate_keys(hash& pub, hash& sec) ge_p3_tobytes(pub.h, &point); } +// cppcheck-suppress constParameter +void generate_keys_deterministic(hash& pub, hash& sec, const uint8_t* entropy, size_t len) +{ + uint32_t counter = 0; + + do { + do { + ++counter; + keccak_custom([entropy, len, counter](int offset) + { + if (offset < static_cast(len)) { + return entropy[offset]; + } + return static_cast(counter >> ((offset - len) * 8)); + }, static_cast(len + sizeof(counter)), sec.h, HASH_SIZE); + } while (!less32(sec.h, limit)); + sc_reduce32(sec.h); + } while (!sc_isnonzero(sec.h)); + + ge_p3 point; + ge_scalarmult_base(&point, sec.h); + ge_p3_tobytes(pub.h, &point); +} + bool check_keys(const hash& pub, const hash& sec) { // From ge_scalarmult_base's comment: "preconditions a[31] <= 127" @@ -226,12 +250,45 @@ public: return true; } + void get_tx_keys(hash& pub, hash& sec, const hash& wallet_spend_key, const hash& monero_block_id) + { + std::array index; + memcpy(index.data(), wallet_spend_key.h, HASH_SIZE); + memcpy(index.data() + HASH_SIZE, monero_block_id.h, HASH_SIZE); + + { + MutexLock lock(m); + auto it = tx_keys.find(index); + if (it != tx_keys.end()) { + pub = it->second.first; + sec = it->second.second; + return; + } + } + + static constexpr char domain[] = "tx_secret_key"; + static constexpr size_t N = sizeof(domain) - 1; + uint8_t entropy[N + HASH_SIZE * 2]; + + memcpy(entropy, domain, N); + memcpy(entropy + N, wallet_spend_key.h, HASH_SIZE); + memcpy(entropy + N + HASH_SIZE, monero_block_id.h, HASH_SIZE); + + generate_keys_deterministic(pub, sec, entropy, sizeof(entropy)); + + { + MutexLock lock(m); + tx_keys.emplace(index, std::pair(pub, sec)); + } + } + void clear() { MutexLock lock(m); derivations.clear(); public_keys.clear(); + tx_keys.clear(); } private: @@ -245,6 +302,7 @@ private: uv_mutex_t m; unordered_map, DerivationEntry> derivations; unordered_map, hash> public_keys; + unordered_map, std::pair> tx_keys; }; static Cache* cache = nullptr; @@ -259,6 +317,11 @@ bool derive_public_key(const hash& derivation, size_t output_index, const hash& return cache->get_public_key(derivation, output_index, base, derived_key); } +void get_tx_keys(hash& pub, hash& sec, const hash& wallet_spend_key, const hash& monero_block_id) +{ + cache->get_tx_keys(pub, sec, wallet_spend_key, monero_block_id); +} + void derive_view_tag(const hash& derivation, size_t output_index, uint8_t& view_tag) { constexpr uint8_t salt[] = "view_tag"; diff --git a/src/crypto.h b/src/crypto.h index 490a33e..d1bd6ba 100644 --- a/src/crypto.h +++ b/src/crypto.h @@ -20,6 +20,8 @@ namespace p2pool { void generate_keys(hash& pub, hash& sec); +void generate_keys_deterministic(hash& pub, hash& sec, const uint8_t* entropy, size_t len); +void get_tx_keys(hash& pub, hash& sec, const hash& wallet_spend_key, const hash& monero_block_id); bool check_keys(const hash& pub, const hash& sec); bool generate_key_derivation(const hash& key1, const hash& key2, size_t output_index, hash& derivation, uint8_t& view_tag); bool derive_public_key(const hash& derivation, size_t output_index, const hash& base, hash& derived_key); diff --git a/src/miner.cpp b/src/miner.cpp index e6974dd..bbd8fdd 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -198,7 +198,6 @@ void Miner::run(WorkerData* data) if (j.m_diff.check_pow(h)) { LOGINFO(0, log::Green() << "worker thread " << data->m_index << '/' << data->m_count << " found a mainchain block, submitting it"); m_pool->submit_block_async(j.m_templateId, j.m_nonce, j.m_extraNonce); - m_pool->block_template().update_tx_keys(); } if (j.m_sidechainDiff.check_pow(h)) { diff --git a/src/pool_block_parser.inl b/src/pool_block_parser.inl index aa4cafa..dae6aa8 100644 --- a/src/pool_block_parser.inl +++ b/src/pool_block_parser.inl @@ -229,6 +229,15 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai return __LINE__; } + // Enforce deterministic tx keys starting from v15 + if (m_majorVersion >= HARDFORK_VIEW_TAGS_VERSION) { + hash pub, sec; + get_tx_keys(pub, sec, spend_pub_key, m_prevId); + if ((pub != m_txkeyPub) || (sec != m_txkeySec)) { + return __LINE__; + } + } + READ_BUF(m_parent.h, HASH_SIZE); uint64_t num_uncles; diff --git a/src/stratum_server.cpp b/src/stratum_server.cpp index f202b67..93b691b 100644 --- a/src/stratum_server.cpp +++ b/src/stratum_server.cpp @@ -374,7 +374,6 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo const char* s = client->m_customUser; LOGINFO(0, log::Green() << "client " << static_cast(client->m_addrString) << (*s ? " user " : "") << s << " found a mainchain block, submitting it"); m_pool->submit_block_async(template_id, nonce, extra_nonce); - block.update_tx_keys(); } SubmittedShare* share;