From 2d1158af64c22def50775582be2265703fa1dfce Mon Sep 17 00:00:00 2001
From: SChernykh <sergey.v.chernykh@gmail.com>
Date: Wed, 25 Oct 2023 15:49:25 +0200
Subject: [PATCH] Store Merkle tree root in the merge mining tag

---
 src/block_template.cpp    | 29 ++++++++++++++++++++---------
 src/pool_block.cpp        | 14 +++++++++-----
 src/pool_block.h          | 12 ++++++++++++
 src/pool_block_parser.inl | 23 ++++++++---------------
 4 files changed, 49 insertions(+), 29 deletions(-)

diff --git a/src/block_template.cpp b/src/block_template.cpp
index 53f1913..860ec77 100644
--- a/src/block_template.cpp
+++ b/src/block_template.cpp
@@ -662,6 +662,11 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, const
 	m_poolBlockTemplate->m_extraNonce = 0;
 	m_poolBlockTemplate->m_sidechainId = {};
 
+	// TODO: fill in merkle tree data here
+	m_poolBlockTemplate->m_merkleTreeDataSize = 1;
+	m_poolBlockTemplate->m_merkleTreeData = 0;
+	m_poolBlockTemplate->m_merkleRoot = {};
+
 	const std::vector<uint8_t> sidechain_data = m_poolBlockTemplate->serialize_sidechain_data();
 	const std::vector<uint8_t>& consensus_id = m_sidechain->consensus_id();
 
@@ -693,9 +698,13 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, const
 
 	m_poolBlockTemplate->m_sidechainId = calc_sidechain_hash(0);
 
+	// TODO: fill in merkle tree data here
+	m_poolBlockTemplate->m_merkleTreeDataSize = 1;
+	m_poolBlockTemplate->m_merkleTreeData = 0;
+	m_poolBlockTemplate->m_merkleRoot = m_poolBlockTemplate->m_sidechainId;
+
 	if (pool_block_debug()) {
-		// TODO: fix it, it will change depending on mm_data varint size
-		const size_t sidechain_hash_offset = m_extraNonceOffsetInTemplate + m_poolBlockTemplate->m_extraNonceSize + 3;
+		const size_t sidechain_hash_offset = m_extraNonceOffsetInTemplate + m_poolBlockTemplate->m_extraNonceSize + 2 + m_poolBlockTemplate->m_merkleTreeDataSize;
 
 		memcpy(m_blockTemplateBlob.data() + sidechain_hash_offset, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE);
 		memcpy(m_fullDataBlob.data() + sidechain_hash_offset, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE);
@@ -934,11 +943,9 @@ int BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<Mine
 
 	m_poolBlockTemplate->m_extraNonceSize = corrected_extra_nonce_size;
 
-	// Valid for tree size = 1 (no other merge mined chains)
-	// TODO: insert mm_data and merkle root here
 	m_minerTxExtra.push_back(TX_EXTRA_MERGE_MINING_TAG);
-	m_minerTxExtra.push_back(1 + HASH_SIZE);
-	m_minerTxExtra.push_back(0);
+	m_minerTxExtra.push_back(static_cast<uint8_t>(m_poolBlockTemplate->m_merkleTreeDataSize + HASH_SIZE));
+	writeVarint(m_poolBlockTemplate->m_merkleTreeData, m_minerTxExtra);
 	m_minerTxExtra.insert(m_minerTxExtra.end(), HASH_SIZE, 0);
 	// TX_EXTRA end
 
@@ -1024,8 +1031,7 @@ hash BlockTemplate::calc_miner_tx_hash(uint32_t extra_nonce) const
 
 	// Calculate sidechain id with this extra_nonce
 	const hash sidechain_id = calc_sidechain_hash(extra_nonce);
-	// TODO: fix it, it will change depending on mm_data varint size
-	const size_t sidechain_hash_offset = extra_nonce_offset + m_poolBlockTemplate->m_extraNonceSize + 3;
+	const size_t sidechain_hash_offset = extra_nonce_offset + m_poolBlockTemplate->m_extraNonceSize + 2 + m_poolBlockTemplate->m_merkleTreeDataSize;
 
 	// 1. Prefix (everything except vin_rct_type byte in the end)
 	// Apply extra_nonce in-place because we can't write to the block template here
@@ -1291,7 +1297,7 @@ std::vector<uint8_t> BlockTemplate::get_block_template_blob(uint32_t template_id
 
 	nonce_offset = m_nonceOffset;
 	extra_nonce_offset = m_extraNonceOffsetInTemplate;
-	sidechain_id_offset = m_extraNonceOffsetInTemplate + m_poolBlockTemplate->m_extraNonceSize + 3;
+	sidechain_id_offset = m_extraNonceOffsetInTemplate + m_poolBlockTemplate->m_extraNonceSize + 2 + m_poolBlockTemplate->m_merkleTreeDataSize;
 	sidechain_id = calc_sidechain_hash(sidechain_extra_nonce);
 	return m_blockTemplateBlob;
 }
@@ -1306,6 +1312,11 @@ bool BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce,
 		m_poolBlockTemplate->m_sidechainId = calc_sidechain_hash(extra_nonce);
 		m_poolBlockTemplate->m_sidechainExtraBuf[3] = extra_nonce;
 
+		// TODO: fill in merkle tree data here
+		m_poolBlockTemplate->m_merkleTreeDataSize = 1;
+		m_poolBlockTemplate->m_merkleTreeData = 0;
+		m_poolBlockTemplate->m_merkleRoot = m_poolBlockTemplate->m_sidechainId;
+
 		if (pool_block_debug()) {
 			std::vector<uint8_t> buf = m_poolBlockTemplate->serialize_mainchain_data();
 			const std::vector<uint8_t> sidechain_data = m_poolBlockTemplate->serialize_sidechain_data();
diff --git a/src/pool_block.cpp b/src/pool_block.cpp
index f1bbf4e..83d9386 100644
--- a/src/pool_block.cpp
+++ b/src/pool_block.cpp
@@ -39,6 +39,9 @@ PoolBlock::PoolBlock()
 	, m_txkeyPub{}
 	, m_extraNonceSize(0)
 	, m_extraNonce(0)
+	, m_merkleTreeDataSize(1)
+	, m_merkleTreeData(0)
+	, m_merkleRoot{}
 	, m_txkeySecSeed{}
 	, m_txkeySec{}
 	, m_parent{}
@@ -85,6 +88,9 @@ PoolBlock& PoolBlock::operator=(const PoolBlock& b)
 	m_txkeyPub = b.m_txkeyPub;
 	m_extraNonceSize = b.m_extraNonceSize;
 	m_extraNonce = b.m_extraNonce;
+	m_merkleTreeDataSize = b.m_merkleTreeDataSize;
+	m_merkleTreeData = b.m_merkleTreeData;
+	m_merkleRoot = b.m_merkleRoot;
 	m_transactions = b.m_transactions;
 	m_minerWallet = b.m_minerWallet;
 	m_txkeySecSeed = b.m_txkeySecSeed;
@@ -187,12 +193,10 @@ std::vector<uint8_t> PoolBlock::serialize_mainchain_data(size_t* header_size, si
 		p += extra_nonce_size - EXTRA_NONCE_SIZE;
 	}
 
-	// Valid for tree size = 1 (no other merge mined chains)
-	// TODO: insert mm_data and merkle root here
 	*(p++) = TX_EXTRA_MERGE_MINING_TAG;
-	*(p++) = 1 + HASH_SIZE;
-	*(p++) = 0;
-	memcpy(p, m_sidechainId.h, HASH_SIZE);
+	*(p++) = static_cast<uint8_t>(m_merkleTreeDataSize + HASH_SIZE);
+	writeVarint(m_merkleTreeData, [&p](const uint8_t b) { *(p++) = b; });
+	memcpy(p, m_merkleRoot.h, HASH_SIZE);
 	p += HASH_SIZE;
 
 	writeVarint(static_cast<size_t>(p - tx_extra), data);
diff --git a/src/pool_block.h b/src/pool_block.h
index ec493eb..ce8ac20 100644
--- a/src/pool_block.h
+++ b/src/pool_block.h
@@ -101,6 +101,10 @@ struct PoolBlock
 	uint64_t m_extraNonceSize;
 	uint32_t m_extraNonce;
 
+	uint32_t m_merkleTreeDataSize;
+	uint64_t m_merkleTreeData;
+	hash m_merkleRoot;
+
 	// All block transaction hashes including the miner transaction hash at index 0
 	std::vector<hash> m_transactions;
 
@@ -171,6 +175,14 @@ struct PoolBlock
 	}
 
 	hash calculate_tx_key_seed() const;
+
+	FORCEINLINE void decode_merkle_tree_data(uint32_t& mm_n_aux_chains, uint32_t& mm_nonce) const
+	{
+		const uint32_t k = static_cast<uint32_t>(m_merkleTreeData);
+		const uint32_t n = 1U + (k & 7U);
+		mm_n_aux_chains = 1U + ((k >> 3U) & ((1U << n) - 1U));
+		mm_nonce = static_cast<uint32_t>(m_merkleTreeData >> (3U + n));
+	}
 };
 
 } // namespace p2pool
diff --git a/src/pool_block_parser.inl b/src/pool_block_parser.inl
index 044d86f..ea3f03a 100644
--- a/src/pool_block_parser.inl
+++ b/src/pool_block_parser.inl
@@ -188,26 +188,19 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
 		uint64_t mm_field_size;
 		READ_VARINT(mm_field_size);
 
-		const int mm_field_begin = static_cast<int>(data - data_begin);
+		const uint8_t* const mm_field_begin = data;
 
-		uint64_t mm_data;
-		READ_VARINT(mm_data);
+		READ_VARINT(m_merkleTreeData);
 
-		if (mm_data > std::numeric_limits<uint32_t>::max()) {
-			return __LINE__;
-		}
+		m_merkleTreeDataSize = static_cast<uint32_t>(data - mm_field_begin);
 
-		const uint32_t mm_n_bits = 1 + (mm_data & 7);
-		const uint32_t mm_n_aux_chains = 1 + ((mm_data >> 3) & ((1 << mm_n_bits) - 1));
-		const uint32_t mm_nonce = static_cast<uint32_t>(mm_data >> (3 + mm_n_bits));
+		uint32_t mm_n_aux_chains, mm_nonce;
+		decode_merkle_tree_data(mm_n_aux_chains, mm_nonce);
 
 		const int mm_root_hash_offset = static_cast<int>((data - data_begin) + outputs_blob_size_diff);
-		hash mm_root;
-		READ_BUF(mm_root.h, HASH_SIZE);
+		READ_BUF(m_merkleRoot.h, HASH_SIZE);
 
-		const int mm_field_end = static_cast<int>(data - data_begin);
-
-		if (static_cast<uint64_t>(mm_field_end - mm_field_begin) != mm_field_size) {
+		if (static_cast<uint64_t>(data - mm_field_begin) != mm_field_size) {
 			return __LINE__;
 		}
 
@@ -432,7 +425,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
 
 		const uint32_t mm_aux_slot = get_aux_slot(sidechain.consensus_hash(), mm_nonce, mm_n_aux_chains);
 
-		if (!verify_merkle_proof(check, m_merkleProof, mm_aux_slot, mm_n_aux_chains, mm_root)) {
+		if (!verify_merkle_proof(check, m_merkleProof, mm_aux_slot, mm_n_aux_chains, m_merkleRoot)) {
 			return __LINE__;
 		}