From 02405bb8ff7f31ba424587314d85b5341b10b3b1 Mon Sep 17 00:00:00 2001
From: SChernykh <sergey.v.chernykh@gmail.com>
Date: Mon, 10 Oct 2022 14:27:08 +0200
Subject: [PATCH] Workaround for duplicate sidechain IDs

Place transactions in the block template in random order, so two different p2pool nodes mining to the same wallet will get different sidechain IDs with high probability if there's more than 2-3 transactions in mempool.
---
 src/block_template.cpp | 28 ++++++++++++++++++++++------
 src/block_template.h   |  4 ++++
 2 files changed, 26 insertions(+), 6 deletions(-)

diff --git a/src/block_template.cpp b/src/block_template.cpp
index acf5afb..e1096a0 100644
--- a/src/block_template.cpp
+++ b/src/block_template.cpp
@@ -54,7 +54,11 @@ BlockTemplate::BlockTemplate(p2pool* pool)
 	, m_txkeySec{}
 	, m_poolBlockTemplate(new PoolBlock())
 	, m_finalReward(0)
+	, m_rng(RandomDeviceSeed::instance)
 {
+	// Diffuse the initial state in case it has low quality
+	m_rng.discard(10000);
+
 	uv_rwlock_init_checked(&m_lock);
 
 	m_blockHeader.reserve(64);
@@ -133,6 +137,8 @@ BlockTemplate& BlockTemplate::operator=(const BlockTemplate& b)
 	m_mempoolTxsOrder.clear();
 	m_shares.clear();
 
+	m_rng = b.m_rng;
+
 #if TEST_MEMPOOL_PICKING_ALGORITHM
 	m_knapsack.clear();
 #endif
@@ -171,6 +177,14 @@ static FORCEINLINE uint64_t get_block_reward(uint64_t base_reward, uint64_t medi
 	return reward + fees;
 }
 
+void BlockTemplate::shuffle_tx_order()
+{
+	const int64_t n = static_cast<int64_t>(m_mempoolTxsOrder.size());
+	for (int64_t i = n - 1; i > 0; --i) {
+		std::swap(m_mempoolTxsOrder[i], m_mempoolTxsOrder[m_rng() % (i + 1)]);
+	}
+}
+
 void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet* miner_wallet)
 {
 	if (data.major_version > HARDFORK_SUPPORTED_VERSION) {
@@ -315,16 +329,16 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
 
 	// if a block doesn't get into the penalty zone, just pick all transactions
 	if (total_tx_weight + miner_tx_weight <= data.median_weight) {
-		m_numTransactionHashes = 0;
-
 		final_fees = 0;
 		final_weight = miner_tx_weight;
 
-		m_transactionHashes.assign(HASH_SIZE, 0);
-		for (const TxMempoolData& tx : m_mempoolTxs) {
-			m_transactionHashes.insert(m_transactionHashes.end(), tx.id.h, tx.id.h + HASH_SIZE);
-			++m_numTransactionHashes;
+		shuffle_tx_order();
 
+		m_numTransactionHashes = m_mempoolTxsOrder.size();
+		m_transactionHashes.assign(HASH_SIZE, 0);
+		for (size_t i = 0; i < m_mempoolTxsOrder.size(); ++i) {
+			const TxMempoolData& tx = m_mempoolTxs[m_mempoolTxsOrder[i]];
+			m_transactionHashes.insert(m_transactionHashes.end(), tx.id.h, tx.id.h + HASH_SIZE);
 			final_fees += tx.fee;
 			final_weight += tx.weight;
 		}
@@ -398,6 +412,8 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
 		final_fees = 0;
 		final_weight = miner_tx_weight;
 
+		shuffle_tx_order();
+
 		m_numTransactionHashes = m_mempoolTxsOrder.size();
 		m_transactionHashes.assign(HASH_SIZE, 0);
 		for (size_t i = 0; i < m_mempoolTxsOrder.size(); ++i) {
diff --git a/src/block_template.h b/src/block_template.h
index d651330..4a13189 100644
--- a/src/block_template.h
+++ b/src/block_template.h
@@ -106,6 +106,10 @@ private:
 	std::vector<int> m_mempoolTxsOrder;
 	std::vector<MinerShare> m_shares;
 
+	std::mt19937_64 m_rng;
+
+	void shuffle_tx_order();
+
 #if TEST_MEMPOOL_PICKING_ALGORITHM
 	void fill_optimal_knapsack(const MinerData& data, uint64_t base_reward, uint64_t miner_tx_weight, uint64_t& best_reward, uint64_t& final_fees, uint64_t& final_weight);