diff --git a/src/block_template.cpp b/src/block_template.cpp index d35cdcd..fb1f691 100644 --- a/src/block_template.cpp +++ b/src/block_template.cpp @@ -268,19 +268,10 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet const int sidechain_version = m_poolBlockTemplate->get_sidechain_version(); - if (sidechain_version > 1) { - static_assert(decltype(m_rng)::word_size == 64, "m_rng must be 64-bit"); - - uint64_t* p = reinterpret_cast(m_poolBlockTemplate->m_txkeySecSeed.h); - for (size_t i = 0; i < HASH_SIZE / sizeof(uint64_t); ++i) { - p[i] = m_rng(); - } - + if (sidechain_version <= 1) { + m_poolBlockTemplate->m_txkeySecSeed = miner_wallet->spend_public_key(); get_tx_keys(m_poolBlockTemplate->m_txkeyPub, m_poolBlockTemplate->m_txkeySec, m_poolBlockTemplate->m_txkeySecSeed, data.prev_id); } - else { - get_tx_keys(m_poolBlockTemplate->m_txkeyPub, m_poolBlockTemplate->m_txkeySec, miner_wallet->spend_public_key(), data.prev_id); - } m_poolBlockTemplate->m_minerWallet = *miner_wallet; diff --git a/src/pool_block.cpp b/src/pool_block.cpp index 07bc35e..18e3fa0 100644 --- a/src/pool_block.cpp +++ b/src/pool_block.cpp @@ -122,13 +122,13 @@ PoolBlock::~PoolBlock() uv_mutex_destroy(&m_lock); } -std::vector PoolBlock::serialize_mainchain_data(size_t* header_size, size_t* miner_tx_size, int* outputs_offset, int* outputs_blob_size) const +std::vector PoolBlock::serialize_mainchain_data(size_t* header_size, size_t* miner_tx_size, int* outputs_offset, int* outputs_blob_size, const uint32_t* nonce, const uint32_t* extra_nonce) const { MutexLock lock(m_lock); - return serialize_mainchain_data_nolock(header_size, miner_tx_size, outputs_offset, outputs_blob_size); + return serialize_mainchain_data_nolock(header_size, miner_tx_size, outputs_offset, outputs_blob_size, nonce, extra_nonce); } -std::vector PoolBlock::serialize_mainchain_data_nolock(size_t* header_size, size_t* miner_tx_size, int* outputs_offset, int* outputs_blob_size) const +std::vector PoolBlock::serialize_mainchain_data_nolock(size_t* header_size, size_t* miner_tx_size, int* outputs_offset, int* outputs_blob_size, const uint32_t* nonce, const uint32_t* extra_nonce) const { std::vector data; data.reserve(128 + m_outputs.size() * 39 + m_transactions.size() * HASH_SIZE); @@ -138,7 +138,11 @@ std::vector PoolBlock::serialize_mainchain_data_nolock(size_t* header_s data.push_back(m_minorVersion); writeVarint(m_timestamp, data); data.insert(data.end(), m_prevId.h, m_prevId.h + HASH_SIZE); - data.insert(data.end(), reinterpret_cast(&m_nonce), reinterpret_cast(&m_nonce) + NONCE_SIZE); + + if (!nonce) { + nonce = &m_nonce; + } + data.insert(data.end(), reinterpret_cast(nonce), reinterpret_cast(nonce) + NONCE_SIZE); const size_t header_size0 = data.size(); if (header_size) { @@ -191,7 +195,10 @@ std::vector PoolBlock::serialize_mainchain_data_nolock(size_t* header_s *(p++) = TX_EXTRA_NONCE; *(p++) = static_cast(extra_nonce_size); - memcpy(p, &m_extraNonce, EXTRA_NONCE_SIZE); + if (!extra_nonce) { + extra_nonce = &m_extraNonce; + } + memcpy(p, extra_nonce, EXTRA_NONCE_SIZE); p += EXTRA_NONCE_SIZE; if (extra_nonce_size > EXTRA_NONCE_SIZE) { memset(p, 0, extra_nonce_size - EXTRA_NONCE_SIZE); @@ -232,7 +239,7 @@ std::vector PoolBlock::serialize_sidechain_data() const MutexLock lock(m_lock); - data.reserve((m_uncles.size() + 4) * HASH_SIZE + 20); + data.reserve((m_uncles.size() + 4) * HASH_SIZE + 36); const hash& spend = m_minerWallet.spend_public_key(); const hash& view = m_minerWallet.view_public_key(); @@ -317,7 +324,7 @@ bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const MutexLock lock(m_lock); size_t header_size, miner_tx_size; - const std::vector mainchain_data = serialize_mainchain_data_nolock(&header_size, &miner_tx_size, nullptr, nullptr); + const std::vector mainchain_data = serialize_mainchain_data_nolock(&header_size, &miner_tx_size, nullptr, nullptr, nullptr, nullptr); if (!header_size || !miner_tx_size || (mainchain_data.size() < header_size + miner_tx_size)) { LOGERR(1, "tried to calculate PoW of uninitialized block"); @@ -405,4 +412,28 @@ uint64_t PoolBlock::get_payout(const Wallet& w) const return 0; } +hash PoolBlock::calculate_tx_key_seed() const +{ + const char domain[] = "tx_key_seed"; + const uint32_t zero = 0; + + const std::vector mainchain_data = serialize_mainchain_data(nullptr, nullptr, nullptr, nullptr, &zero, &zero); + const std::vector sidechain_data = serialize_sidechain_data(); + + hash result; + keccak_custom([&domain, &mainchain_data, &sidechain_data](int offset) -> uint8_t { + size_t k = offset; + + if (k < sizeof(domain)) return domain[k]; + k -= sizeof(domain); + + if (k < mainchain_data.size()) return mainchain_data[k]; + k -= mainchain_data.size(); + + return sidechain_data[k]; + }, static_cast(sizeof(domain) + mainchain_data.size() + sidechain_data.size()), result.h, HASH_SIZE); + + return result; +} + } // namespace p2pool diff --git a/src/pool_block.h b/src/pool_block.h index 2e2bc4a..343909b 100644 --- a/src/pool_block.h +++ b/src/pool_block.h @@ -137,8 +137,8 @@ struct PoolBlock uint64_t m_localTimestamp; - std::vector serialize_mainchain_data(size_t* header_size = nullptr, size_t* miner_tx_size = nullptr, int* outputs_offset = nullptr, int* outputs_blob_size = nullptr) const; - std::vector serialize_mainchain_data_nolock(size_t* header_size, size_t* miner_tx_size, int* outputs_offset, int* outputs_blob_size) const; + std::vector serialize_mainchain_data(size_t* header_size = nullptr, size_t* miner_tx_size = nullptr, int* outputs_offset = nullptr, int* outputs_blob_size = nullptr, const uint32_t* nonce = nullptr, const uint32_t* extra_nonce = nullptr) const; + std::vector serialize_mainchain_data_nolock(size_t* header_size, size_t* miner_tx_size, int* outputs_offset, int* outputs_blob_size, const uint32_t* nonce, const uint32_t* extra_nonce) const; std::vector serialize_sidechain_data() const; int deserialize(const uint8_t* data, size_t size, const SideChain& sidechain, uv_loop_t* loop, bool compact); @@ -171,6 +171,8 @@ struct PoolBlock memcpy(p + HASH_SIZE + NONCE_SIZE, &m_extraNonce, EXTRA_NONCE_SIZE); return key; } + + hash calculate_tx_key_seed() const; }; } // namespace p2pool diff --git a/src/side_chain.cpp b/src/side_chain.cpp index a17982f..75d92c0 100644 --- a/src/side_chain.cpp +++ b/src/side_chain.cpp @@ -203,6 +203,7 @@ SideChain::~SideChain() void SideChain::fill_sidechain_data(PoolBlock& block, std::vector& shares) const { + const int sidechain_version = block.get_sidechain_version(); block.m_uncles.clear(); ReadLock lock(m_sidechainLock); @@ -215,10 +216,20 @@ void SideChain::fill_sidechain_data(PoolBlock& block, std::vector& s block.m_difficulty = m_minDifficulty; block.m_cumulativeDifficulty = m_minDifficulty; + if (sidechain_version > 1) { + block.m_txkeySecSeed = {}; + get_tx_keys(block.m_txkeyPub, block.m_txkeySec, block.m_txkeySecSeed, block.m_prevId); + } + get_shares(&block, shares); return; } + if (sidechain_version > 1) { + block.m_txkeySecSeed = (block.m_prevId == tip->m_prevId) ? tip->m_txkeySecSeed : tip->calculate_tx_key_seed(); + get_tx_keys(block.m_txkeyPub, block.m_txkeySec, block.m_txkeySecSeed, block.m_prevId); + } + block.m_parent = tip->m_sidechainId; block.m_sidechainHeight = tip->m_sidechainHeight + 1; @@ -1331,12 +1342,15 @@ void SideChain::verify_loop(PoolBlock* block) void SideChain::verify(PoolBlock* block) { + const int sidechain_version = block->get_sidechain_version(); + // Genesis block if (block->m_sidechainHeight == 0) { if (!block->m_parent.empty() || !block->m_uncles.empty() || (block->m_difficulty != m_minDifficulty) || - (block->m_cumulativeDifficulty != m_minDifficulty)) + (block->m_cumulativeDifficulty != m_minDifficulty) || + ((sidechain_version > 1) && !block->m_txkeySecSeed.empty())) { block->m_invalid = true; } @@ -1382,12 +1396,24 @@ void SideChain::verify(PoolBlock* block) return; } + if (sidechain_version > 1) { + // Check m_txkeySecSeed + const hash h = (block->m_prevId == parent->m_prevId) ? parent->m_txkeySecSeed : parent->calculate_tx_key_seed(); + if (block->m_txkeySecSeed != h) { + LOGWARN(3, "block " << block->m_sidechainId << " has invalid tx key seed: expected " << h << ", got " << block->m_txkeySecSeed); + block->m_verified = true; + block->m_invalid = true; + return; + } + } + const uint64_t expectedHeight = parent->m_sidechainHeight + 1; if (block->m_sidechainHeight != expectedHeight) { LOGWARN(3, "block at height = " << block->m_sidechainHeight << ", id = " << block->m_sidechainId << ", mainchain height = " << block->m_txinGenHeight << " has wrong height: expected " << expectedHeight); + block->m_verified = true; block->m_invalid = true; return; } diff --git a/tests/src/block_template_tests.cpp b/tests/src/block_template_tests.cpp index a42195e..37c5d93 100644 --- a/tests/src/block_template_tests.cpp +++ b/tests/src/block_template_tests.cpp @@ -60,7 +60,7 @@ TEST(block_template, update) tpl.update(data, mempool, &wallet); const PoolBlock* b = tpl.pool_block_template(); - ASSERT_EQ(b->m_sidechainId, H("e6fa454c10374439dcc9eee3198f6ba17a7a3764510569c3b963b31510b74780")); + ASSERT_EQ(b->m_sidechainId, H("3085dc0425d94fb2752e21e5d5a4fbad42105a189f9678cadabb69cf55a321ee")); std::vector blobs; uint64_t height; @@ -79,7 +79,7 @@ TEST(block_template, update) hash blobs_hash; keccak(blobs.data(), static_cast(blobs.size()), blobs_hash.h); - ASSERT_EQ(blobs_hash, H("fd0079d39d1739ded6e710192228276cbaa55eaabde7a26c38f8986709a030e4")); + ASSERT_EQ(blobs_hash, H("c1211e083d27ddb81eddb125af4f10b6df16efd418cf684f6d0e8e49a5ffaf3f")); // Test 2: mempool with high fee and low fee transactions, it must choose high fee transactions for (uint64_t i = 0; i < 512; ++i) {