diff --git a/src/side_chain.cpp b/src/side_chain.cpp index b3f247b..1e0a5df 100644 --- a/src/side_chain.cpp +++ b/src/side_chain.cpp @@ -335,9 +335,6 @@ bool SideChain::get_shares(const PoolBlock* tip, std::vector& shares const int L = quiet ? 6 : 3; - shares.clear(); - shares.reserve(m_chainWindowSize * 2); - // Collect shares from each block in the PPLNS window, starting from the "tip" uint64_t block_depth = 0; @@ -363,8 +360,11 @@ bool SideChain::get_shares(const PoolBlock* tip, std::vector& shares const difficulty_type max_pplns_weight = (sidechain_version > 1) ? (mainchain_diff * 2) : diff_max; difficulty_type pplns_weight; + unordered_set shares_set; + shares_set.reserve(m_chainWindowSize * 2); + do { - MinerShare cur_share{ cur->m_difficulty, &cur->m_minerWallet }; + difficulty_type cur_weight = cur->m_difficulty; for (const hash& uncle_id : cur->m_uncles) { auto it = m_blocksById.find(uncle_id); @@ -391,15 +391,21 @@ bool SideChain::get_shares(const PoolBlock* tip, std::vector& shares continue; } - cur_share.m_weight += uncle_penalty; + cur_weight += uncle_penalty; - shares.emplace_back(uncle_weight, &uncle->m_minerWallet); + auto result = shares_set.emplace(uncle_weight, &uncle->m_minerWallet); + if (!result.second) { + result.first->m_weight += uncle_weight; + } pplns_weight = new_pplns_weight; } // Always add non-uncle shares even if PPLNS weight goes above the limit - shares.push_back(cur_share); - pplns_weight += cur_share.m_weight; + auto result = shares_set.emplace(cur_weight, &cur->m_minerWallet); + if (!result.second) { + result.first->m_weight += cur_weight; + } + pplns_weight += cur_weight; // One non-uncle share can go above the limit, but it will also guarantee that "shares" is never empty if (pplns_weight > max_pplns_weight) { @@ -426,34 +432,27 @@ bool SideChain::get_shares(const PoolBlock* tip, std::vector& shares cur = it->second; } while (true); - // Combine shares with the same wallet addresses + shares.assign(shares_set.begin(), shares_set.end()); std::sort(shares.begin(), shares.end(), [](const auto& a, const auto& b) { return *a.m_wallet < *b.m_wallet; }); - size_t k = 0; - for (size_t i = 1, n = shares.size(); i < n; ++i) - { - if (*shares[i].m_wallet == *shares[k].m_wallet) { - shares[k].m_weight += shares[i].m_weight; - } - else { - ++k; - shares[k].m_weight = shares[i].m_weight; - shares[k].m_wallet = shares[i].m_wallet; - } - } - - shares.resize(k + 1); - LOGINFO(6, "get_shares: " << k + 1 << " unique wallets in PPLNS window"); + const uint64_t n = shares.size(); // Shuffle shares - if (sidechain_version > 1) { - std::mt19937_64 rng(*reinterpret_cast(tip->m_txkeySecSeed.h)); + if ((sidechain_version > 1) && (n > 1)) { + hash h; + keccak(tip->m_txkeySecSeed.h, HASH_SIZE, h.h); + + uint64_t seed = *reinterpret_cast(h.h); + if (seed == 0) seed = 1; - for (int64_t i = k; i > 0; --i) { - std::swap(shares[i], shares[rng() % (i + 1)]); + for (uint64_t i = 0, k; i < n - 1; ++i) { + seed = xorshift64star(seed); + umul128(seed, n - i, &k); + std::swap(shares[i], shares[i + k]); } } + LOGINFO(6, "get_shares: " << n << " unique wallets in PPLNS window"); return true; } diff --git a/src/side_chain.h b/src/side_chain.h index c8b84ab..2d2b345 100644 --- a/src/side_chain.h +++ b/src/side_chain.h @@ -32,6 +32,8 @@ struct MinerShare FORCEINLINE MinerShare() : m_weight(), m_wallet(nullptr) {} FORCEINLINE MinerShare(const difficulty_type& w, const Wallet* x) : m_weight(w), m_wallet(x) {} + FORCEINLINE bool operator==(const MinerShare& s) const { return *m_wallet == *s.m_wallet; } + difficulty_type m_weight; const Wallet* m_wallet; }; @@ -157,3 +159,16 @@ private: }; } // namespace p2pool + +namespace robin_hood { + + template<> + struct hash + { + FORCEINLINE size_t operator()(const p2pool::MinerShare& value) const noexcept + { + return hash_bytes(value.m_wallet->spend_public_key().h, p2pool::HASH_SIZE); + } + }; + +} // namespace robin_hood diff --git a/src/util.h b/src/util.h index ee7cd34..1e39d61 100644 --- a/src/util.h +++ b/src/util.h @@ -208,6 +208,14 @@ struct RandomDeviceSeed static RandomDeviceSeed instance; }; +FORCEINLINE uint64_t xorshift64star(uint64_t x) +{ + x ^= x >> 12; + x ^= x << 25; + x ^= x >> 27; + return x * 0x2545F4914F6CDD1DULL; +} + FORCEINLINE uint64_t seconds_since_epoch() { using namespace std::chrono;