Better type checking for Merkle root hashes

This commit is contained in:
SChernykh 2023-12-23 10:17:49 +01:00
parent c4153a9a2a
commit 3c510598fa
10 changed files with 42 additions and 24 deletions

View file

@ -201,8 +201,16 @@ struct alignas(uint64_t) hash
friend std::istream& operator>>(std::istream& s, hash& d); friend std::istream& operator>>(std::istream& s, hash& d);
}; };
struct root_hash : public hash
{
FORCEINLINE root_hash() : hash() {}
explicit FORCEINLINE root_hash(const hash& h) : hash(h) {}
};
static_assert(sizeof(hash) == HASH_SIZE, "struct hash has invalid size, check your compiler options"); static_assert(sizeof(hash) == HASH_SIZE, "struct hash has invalid size, check your compiler options");
static_assert(sizeof(root_hash) == HASH_SIZE, "struct root_hash has invalid size, check your compiler options");
static_assert(std::is_standard_layout<hash>::value, "struct hash is not a POD, check your compiler options"); static_assert(std::is_standard_layout<hash>::value, "struct hash is not a POD, check your compiler options");
static_assert(std::is_standard_layout<root_hash>::value, "struct root_hash is not a POD, check your compiler options");
struct struct
#ifdef __GNUC__ #ifdef __GNUC__

View file

@ -23,13 +23,13 @@
namespace p2pool { namespace p2pool {
void merkle_hash(const std::vector<hash>& hashes, hash& root) void merkle_hash(const std::vector<hash>& hashes, root_hash& root)
{ {
const size_t count = hashes.size(); const size_t count = hashes.size();
const uint8_t* h = hashes[0].h; const uint8_t* h = hashes[0].h;
if (count == 1) { if (count == 1) {
root = hashes[0]; root = root_hash(hashes[0]);
} }
else if (count == 2) { else if (count == 2) {
keccak(h, HASH_SIZE * 2, root.h); keccak(h, HASH_SIZE * 2, root.h);
@ -175,21 +175,21 @@ bool get_merkle_proof(const std::vector<std::vector<hash>>& tree, const hash& h,
return true; return true;
} }
hash get_root_from_proof(hash h, const std::vector<hash>& proof, size_t index, size_t count) root_hash get_root_from_proof(hash h, const std::vector<hash>& proof, size_t index, size_t count)
{ {
if (count == 1) { if (count == 1) {
return h; return root_hash(h);
} }
if (index >= count) { if (index >= count) {
return hash(); return root_hash();
} }
hash tmp[2]; hash tmp[2];
if (count == 2) { if (count == 2) {
if (proof.empty()) { if (proof.empty()) {
return hash(); return root_hash();
} }
if (index & 1) { if (index & 1) {
@ -216,7 +216,7 @@ hash get_root_from_proof(hash h, const std::vector<hash>& proof, size_t index, s
index -= k; index -= k;
if (proof.empty()) { if (proof.empty()) {
return hash(); return root_hash();
} }
if (index & 1) { if (index & 1) {
@ -236,7 +236,7 @@ hash get_root_from_proof(hash h, const std::vector<hash>& proof, size_t index, s
for (; cnt >= 2; ++proof_index, index >>= 1, cnt >>= 1) { for (; cnt >= 2; ++proof_index, index >>= 1, cnt >>= 1) {
if (proof_index >= proof.size()) { if (proof_index >= proof.size()) {
return hash(); return root_hash();
} }
if (index & 1) { if (index & 1) {
@ -252,10 +252,10 @@ hash get_root_from_proof(hash h, const std::vector<hash>& proof, size_t index, s
} }
} }
return h; return root_hash(h);
} }
bool verify_merkle_proof(hash h, const std::vector<hash>& proof, size_t index, size_t count, const hash& root) bool verify_merkle_proof(hash h, const std::vector<hash>& proof, size_t index, size_t count, const root_hash& root)
{ {
return get_root_from_proof(h, proof, index, count) == root; return get_root_from_proof(h, proof, index, count) == root;
} }

View file

@ -19,13 +19,13 @@
namespace p2pool { namespace p2pool {
void merkle_hash(const std::vector<hash>& hashes, hash& root); void merkle_hash(const std::vector<hash>& hashes, root_hash& root);
void merkle_hash_full_tree(const std::vector<hash>& hashes, std::vector<std::vector<hash>>& tree); void merkle_hash_full_tree(const std::vector<hash>& hashes, std::vector<std::vector<hash>>& tree);
bool get_merkle_proof(const std::vector<std::vector<hash>>& tree, const hash& h, std::vector<hash>& proof); bool get_merkle_proof(const std::vector<std::vector<hash>>& tree, const hash& h, std::vector<hash>& proof);
hash get_root_from_proof(hash h, const std::vector<hash>& proof, size_t index, size_t count); root_hash get_root_from_proof(hash h, const std::vector<hash>& proof, size_t index, size_t count);
bool verify_merkle_proof(hash h, const std::vector<hash>& proof, size_t index, size_t count, const hash& root); bool verify_merkle_proof(hash h, const std::vector<hash>& proof, size_t index, size_t count, const root_hash& root);
uint32_t get_aux_slot(const hash &id, uint32_t nonce, uint32_t n_aux_chains); uint32_t get_aux_slot(const hash &id, uint32_t nonce, uint32_t n_aux_chains);
bool find_aux_nonce(const std::vector<hash>& aux_id, uint32_t& nonce, uint32_t max_nonce = 0xFFFF); bool find_aux_nonce(const std::vector<hash>& aux_id, uint32_t& nonce, uint32_t max_nonce = 0xFFFF);

View file

@ -456,7 +456,7 @@ void p2pool::handle_chain_main(ChainMain& data, const char* extra)
} }
update_median_timestamp(); update_median_timestamp();
hash merkle_root; root_hash merkle_root;
if (extra) { if (extra) {
const size_t n = strlen(extra); const size_t n = strlen(extra);
if (n >= HASH_SIZE * 2) { if (n >= HASH_SIZE * 2) {
@ -610,7 +610,7 @@ void p2pool::submit_aux_block(const hash& chain_id, uint32_t template_id, uint32
size_t nonce_offset = 0; size_t nonce_offset = 0;
size_t extra_nonce_offset = 0; size_t extra_nonce_offset = 0;
size_t merkle_root_offset = 0; size_t merkle_root_offset = 0;
hash merge_mining_root; root_hash merge_mining_root;
std::vector<uint8_t> blob = m_blockTemplate->get_block_template_blob(template_id, extra_nonce, nonce_offset, extra_nonce_offset, merkle_root_offset, merge_mining_root); std::vector<uint8_t> blob = m_blockTemplate->get_block_template_blob(template_id, extra_nonce, nonce_offset, extra_nonce_offset, merkle_root_offset, merge_mining_root);

View file

@ -337,8 +337,9 @@ bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const
keccak(reinterpret_cast<uint8_t*>(hashes), HASH_SIZE * 3, tmp.h); keccak(reinterpret_cast<uint8_t*>(hashes), HASH_SIZE * 3, tmp.h);
memcpy(h, tmp.h, HASH_SIZE); memcpy(h, tmp.h, HASH_SIZE);
merkle_hash(m_transactions, tmp); root_hash tmp_root;
memcpy(blob + blob_size, tmp.h, HASH_SIZE); merkle_hash(m_transactions, tmp_root);
memcpy(blob + blob_size, tmp_root.h, HASH_SIZE);
} }
blob_size += HASH_SIZE; blob_size += HASH_SIZE;

View file

@ -103,7 +103,7 @@ struct PoolBlock
uint32_t m_merkleTreeDataSize; uint32_t m_merkleTreeDataSize;
uint64_t m_merkleTreeData; uint64_t m_merkleTreeData;
hash m_merkleRoot; root_hash m_merkleRoot;
// All block transaction hashes including the miner transaction hash at index 0 // All block transaction hashes including the miner transaction hash at index 0
std::vector<hash> m_transactions; std::vector<hash> m_transactions;

View file

@ -63,7 +63,7 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
, m_poolName(pool_name ? pool_name : "default") , m_poolName(pool_name ? pool_name : "default")
, m_targetBlockTime(10) , m_targetBlockTime(10)
, m_minDifficulty(MIN_DIFFICULTY, 0) , m_minDifficulty(MIN_DIFFICULTY, 0)
, m_chainWindowSize(2160) , m_chainWindowSize(216)
, m_unclePenalty(20) , m_unclePenalty(20)
, m_precalcFinished(false) , m_precalcFinished(false)
#ifdef DEV_TEST_SYNC #ifdef DEV_TEST_SYNC
@ -735,7 +735,7 @@ PoolBlock* SideChain::find_block(const hash& id) const
return nullptr; return nullptr;
} }
PoolBlock* SideChain::find_block_by_merkle_root(const hash& merkle_root) const PoolBlock* SideChain::find_block_by_merkle_root(const root_hash& merkle_root) const
{ {
ReadLock lock(m_sidechainLock); ReadLock lock(m_sidechainLock);
@ -2120,7 +2120,7 @@ void SideChain::prune_old_blocks()
auto it3 = m_blocksByMerkleRoot.find(block->m_merkleRoot); auto it3 = m_blocksByMerkleRoot.find(block->m_merkleRoot);
if (it3 != m_blocksByMerkleRoot.end()) { if (it3 != m_blocksByMerkleRoot.end()) {
m_blocksByMerkleRoot.erase(it2); m_blocksByMerkleRoot.erase(it3);
} }
else { else {
LOGERR(1, "m_blocksByHeight and m_blocksByMerkleRoot are inconsistent at height " << height << ". Fix the code!"); LOGERR(1, "m_blocksByHeight and m_blocksByMerkleRoot are inconsistent at height " << height << ". Fix the code!");

View file

@ -55,7 +55,7 @@ public:
void get_missing_blocks(unordered_set<hash>& missing_blocks) const; void get_missing_blocks(unordered_set<hash>& missing_blocks) const;
PoolBlock* find_block(const hash& id) const; PoolBlock* find_block(const hash& id) const;
PoolBlock* find_block_by_merkle_root(const hash& merkle_root) const; PoolBlock* find_block_by_merkle_root(const root_hash& merkle_root) const;
void watch_mainchain_block(const ChainMain& data, const hash& possible_merkle_root); void watch_mainchain_block(const ChainMain& data, const hash& possible_merkle_root);
const PoolBlock* get_block_blob(const hash& id, std::vector<uint8_t>& blob) const; const PoolBlock* get_block_blob(const hash& id, std::vector<uint8_t>& blob) const;
@ -118,7 +118,7 @@ private:
std::atomic<PoolBlock*> m_chainTip; std::atomic<PoolBlock*> m_chainTip;
std::map<uint64_t, std::vector<PoolBlock*>> m_blocksByHeight; std::map<uint64_t, std::vector<PoolBlock*>> m_blocksByHeight;
unordered_map<hash, PoolBlock*> m_blocksById; unordered_map<hash, PoolBlock*> m_blocksById;
unordered_map<hash, PoolBlock*> m_blocksByMerkleRoot; unordered_map<root_hash, PoolBlock*> m_blocksByMerkleRoot;
uv_mutex_t m_seenWalletsLock; uv_mutex_t m_seenWalletsLock;
unordered_map<hash, uint64_t> m_seenWallets; unordered_map<hash, uint64_t> m_seenWallets;

View file

@ -378,6 +378,15 @@ struct hash<p2pool::hash>
} }
}; };
template<>
struct hash<p2pool::root_hash>
{
FORCEINLINE size_t operator()(const p2pool::root_hash& value) const noexcept
{
return hash_bytes(value.h, p2pool::HASH_SIZE);
}
};
template<size_t N> template<size_t N>
struct hash<std::array<uint8_t, N>> struct hash<std::array<uint8_t, N>>
{ {

View file

@ -32,7 +32,7 @@ TEST(merkle, tree)
keccak(data, sizeof(data) - 1, input[i].h); keccak(data, sizeof(data) - 1, input[i].h);
} }
hash root; root_hash root;
std::vector<hash> hashes(1, input[0]); std::vector<hash> hashes(1, input[0]);
auto check_full_tree = [&hashes, &root]() { auto check_full_tree = [&hashes, &root]() {