mirror of
https://github.com/SChernykh/p2pool.git
synced 2024-12-22 19:39:22 +00:00
Dynamic PPLNS window
This commit is contained in:
parent
ccc5117172
commit
4f34c4466a
6 changed files with 79 additions and 66 deletions
|
@ -185,8 +185,8 @@ struct
|
||||||
#endif
|
#endif
|
||||||
difficulty_type
|
difficulty_type
|
||||||
{
|
{
|
||||||
FORCEINLINE difficulty_type() : lo(0), hi(0) {}
|
FORCEINLINE constexpr difficulty_type() : lo(0), hi(0) {}
|
||||||
FORCEINLINE difficulty_type(uint64_t a, uint64_t b) : lo(a), hi(b) {}
|
FORCEINLINE constexpr difficulty_type(uint64_t a, uint64_t b) : lo(a), hi(b) {}
|
||||||
|
|
||||||
uint64_t lo;
|
uint64_t lo;
|
||||||
uint64_t hi;
|
uint64_t hi;
|
||||||
|
@ -254,7 +254,10 @@ struct
|
||||||
return (lo < other.lo);
|
return (lo < other.lo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FORCEINLINE bool operator>(const difficulty_type& other) const { return other.operator<(*this); }
|
||||||
|
|
||||||
FORCEINLINE bool operator>=(const difficulty_type& other) const { return !operator<(other); }
|
FORCEINLINE bool operator>=(const difficulty_type& other) const { return !operator<(other); }
|
||||||
|
FORCEINLINE bool operator<=(const difficulty_type& other) const { return !operator>(other); }
|
||||||
|
|
||||||
FORCEINLINE bool operator==(const difficulty_type& other) const { return (lo == other.lo) && (hi == other.hi); }
|
FORCEINLINE bool operator==(const difficulty_type& other) const { return (lo == other.lo) && (hi == other.hi); }
|
||||||
FORCEINLINE bool operator!=(const difficulty_type& other) const { return (lo != other.lo) || (hi != other.hi); }
|
FORCEINLINE bool operator!=(const difficulty_type& other) const { return (lo != other.lo) || (hi != other.hi); }
|
||||||
|
@ -294,6 +297,8 @@ struct
|
||||||
static_assert(sizeof(difficulty_type) == sizeof(uint64_t) * 2, "struct difficulty_type has invalid size, check your compiler options");
|
static_assert(sizeof(difficulty_type) == sizeof(uint64_t) * 2, "struct difficulty_type has invalid size, check your compiler options");
|
||||||
static_assert(std::is_standard_layout<difficulty_type>::value, "struct difficulty_type is not a POD, check your compiler options");
|
static_assert(std::is_standard_layout<difficulty_type>::value, "struct difficulty_type is not a POD, check your compiler options");
|
||||||
|
|
||||||
|
static constexpr difficulty_type diff_max = { std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max() };
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
FORCEINLINE difficulty_type operator+(const difficulty_type& a, const T& b)
|
FORCEINLINE difficulty_type operator+(const difficulty_type& a, const T& b)
|
||||||
{
|
{
|
||||||
|
|
|
@ -492,7 +492,7 @@ struct DummyStream
|
||||||
MSVC_PRAGMA(warning(suppress:26444)) \
|
MSVC_PRAGMA(warning(suppress:26444)) \
|
||||||
[=]() { \
|
[=]() { \
|
||||||
log::DummyStream x; \
|
log::DummyStream x; \
|
||||||
x << level << __VA_ARGS__; \
|
x << (level) << __VA_ARGS__; \
|
||||||
}; \
|
}; \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
@ -508,7 +508,7 @@ struct DummyStream
|
||||||
#define LOG(level, severity, ...) \
|
#define LOG(level, severity, ...) \
|
||||||
do { \
|
do { \
|
||||||
SIDE_EFFECT_CHECK(level, __VA_ARGS__); \
|
SIDE_EFFECT_CHECK(level, __VA_ARGS__); \
|
||||||
if (level <= log::GLOBAL_LOG_LEVEL) { \
|
if ((level) <= log::GLOBAL_LOG_LEVEL) { \
|
||||||
log::Writer CONCAT(log_wrapper_, __LINE__)(severity); \
|
log::Writer CONCAT(log_wrapper_, __LINE__)(severity); \
|
||||||
CONCAT(log_wrapper_, __LINE__) << log::Gray() << log_category_prefix; \
|
CONCAT(log_wrapper_, __LINE__) << log::Gray() << log_category_prefix; \
|
||||||
log::apply_severity<severity>(CONCAT(log_wrapper_, __LINE__)); \
|
log::apply_severity<severity>(CONCAT(log_wrapper_, __LINE__)); \
|
||||||
|
|
|
@ -146,6 +146,14 @@ struct PoolBlock
|
||||||
// but P2Pool can switch to using only TXOUT_TO_TAGGED_KEY for miner payouts starting from v15
|
// but P2Pool can switch to using only TXOUT_TO_TAGGED_KEY for miner payouts starting from v15
|
||||||
FORCEINLINE uint8_t get_tx_type() const { return (m_majorVersion < HARDFORK_VIEW_TAGS_VERSION) ? TXOUT_TO_KEY : TXOUT_TO_TAGGED_KEY; }
|
FORCEINLINE uint8_t get_tx_type() const { return (m_majorVersion < HARDFORK_VIEW_TAGS_VERSION) ? TXOUT_TO_KEY : TXOUT_TO_TAGGED_KEY; }
|
||||||
|
|
||||||
|
FORCEINLINE uint8_t get_sidechain_version() const
|
||||||
|
{
|
||||||
|
// P2Pool forks to v2 at 2023-03-18 21:00 UTC
|
||||||
|
// Different miners can have different timestamps,
|
||||||
|
// so a temporary mix of v1 and v2 blocks is allowed
|
||||||
|
return (m_timestamp >= 1679173200) ? 2 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
typedef std::array<uint8_t, HASH_SIZE + NONCE_SIZE + EXTRA_NONCE_SIZE> full_id;
|
typedef std::array<uint8_t, HASH_SIZE + NONCE_SIZE + EXTRA_NONCE_SIZE> full_id;
|
||||||
|
|
||||||
FORCEINLINE full_id get_full_id() const
|
FORCEINLINE full_id get_full_id() const
|
||||||
|
|
|
@ -316,8 +316,10 @@ P2PServer* SideChain::p2pServer() const
|
||||||
return m_pool ? m_pool->p2p_server() : nullptr;
|
return m_pool ? m_pool->p2p_server() : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SideChain::get_shares(const PoolBlock* tip, std::vector<MinerShare>& shares) const
|
bool SideChain::get_shares(const PoolBlock* tip, std::vector<MinerShare>& shares, bool quiet) const
|
||||||
{
|
{
|
||||||
|
const int L = quiet ? 6 : 3;
|
||||||
|
|
||||||
shares.clear();
|
shares.clear();
|
||||||
shares.reserve(m_chainWindowSize * 2);
|
shares.reserve(m_chainWindowSize * 2);
|
||||||
|
|
||||||
|
@ -325,14 +327,34 @@ bool SideChain::get_shares(const PoolBlock* tip, std::vector<MinerShare>& shares
|
||||||
|
|
||||||
uint64_t block_depth = 0;
|
uint64_t block_depth = 0;
|
||||||
const PoolBlock* cur = tip;
|
const PoolBlock* cur = tip;
|
||||||
|
|
||||||
|
difficulty_type mainchain_diff
|
||||||
|
#ifdef P2POOL_UNIT_TESTS
|
||||||
|
= m_testMainChainDiff
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
|
||||||
|
if (m_pool && !tip->m_parent.empty()) {
|
||||||
|
const uint64_t h = p2pool::get_seed_height(tip->m_txinGenHeight);
|
||||||
|
if (!m_pool->get_difficulty_at_height(h, mainchain_diff)) {
|
||||||
|
LOGWARN(L, "get_shares: couldn't get mainchain difficulty for height = " << h);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dynamic PPLNS window starting from v2
|
||||||
|
// Limit PPLNS weight to 2x of the Monero difficulty (max 2 blocks per PPLNS window on average)
|
||||||
|
const difficulty_type max_pplns_weight = (tip->get_sidechain_version() > 1) ? (mainchain_diff * 2) : diff_max;
|
||||||
|
difficulty_type pplns_weight;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
MinerShare cur_share{ cur->m_difficulty, &cur->m_minerWallet };
|
MinerShare cur_share{ cur->m_difficulty, &cur->m_minerWallet };
|
||||||
|
|
||||||
for (const hash& uncle_id : cur->m_uncles) {
|
for (const hash& uncle_id : cur->m_uncles) {
|
||||||
auto it = m_blocksById.find(uncle_id);
|
auto it = m_blocksById.find(uncle_id);
|
||||||
if (it == m_blocksById.end()) {
|
if (it == m_blocksById.end()) {
|
||||||
LOGWARN(3, "get_shares: can't find uncle block at height = " << cur->m_sidechainHeight << ", id = " << uncle_id);
|
LOGWARN(L, "get_shares: can't find uncle block at height = " << cur->m_sidechainHeight << ", id = " << uncle_id);
|
||||||
LOGWARN(3, "get_shares: can't calculate shares for block at height = " << tip->m_sidechainHeight << ", id = " << tip->m_sidechainId << ", mainchain height = " << tip->m_txinGenHeight);
|
LOGWARN(L, "get_shares: can't calculate shares for block at height = " << tip->m_sidechainHeight << ", id = " << tip->m_sidechainId << ", mainchain height = " << tip->m_txinGenHeight);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,11 +367,28 @@ bool SideChain::get_shares(const PoolBlock* tip, std::vector<MinerShare>& shares
|
||||||
|
|
||||||
// Take some % of uncle's weight into this share
|
// Take some % of uncle's weight into this share
|
||||||
const difficulty_type uncle_penalty = uncle->m_difficulty * m_unclePenalty / 100;
|
const difficulty_type uncle_penalty = uncle->m_difficulty * m_unclePenalty / 100;
|
||||||
cur_share.m_weight += uncle_penalty;
|
const difficulty_type uncle_weight = uncle->m_difficulty - uncle_penalty;
|
||||||
shares.emplace_back(uncle->m_difficulty - uncle_penalty, &uncle->m_minerWallet);
|
const difficulty_type new_pplns_weight = pplns_weight + uncle_weight;
|
||||||
|
|
||||||
|
// Skip uncles that push PPLNS weight above the limit
|
||||||
|
if (new_pplns_weight > max_pplns_weight) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cur_share.m_weight += uncle_penalty;
|
||||||
|
|
||||||
|
shares.emplace_back(uncle_weight, &uncle->m_minerWallet);
|
||||||
|
pplns_weight = new_pplns_weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always add non-uncle shares even if PPLNS weight goes above the limit
|
||||||
shares.push_back(cur_share);
|
shares.push_back(cur_share);
|
||||||
|
pplns_weight += cur_share.m_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) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
++block_depth;
|
++block_depth;
|
||||||
if (block_depth >= m_chainWindowSize) {
|
if (block_depth >= m_chainWindowSize) {
|
||||||
|
@ -363,8 +402,8 @@ bool SideChain::get_shares(const PoolBlock* tip, std::vector<MinerShare>& shares
|
||||||
|
|
||||||
auto it = m_blocksById.find(cur->m_parent);
|
auto it = m_blocksById.find(cur->m_parent);
|
||||||
if (it == m_blocksById.end()) {
|
if (it == m_blocksById.end()) {
|
||||||
LOGWARN(3, "get_shares: can't find parent block at height = " << cur->m_sidechainHeight - 1 << ", id = " << cur->m_parent);
|
LOGWARN(L, "get_shares: can't find parent block at height = " << cur->m_sidechainHeight - 1 << ", id = " << cur->m_parent);
|
||||||
LOGWARN(3, "get_shares: can't calculate shares for block at height = " << tip->m_sidechainHeight << ", id = " << tip->m_sidechainId << ", mainchain height = " << tip->m_txinGenHeight);
|
LOGWARN(L, "get_shares: can't calculate shares for block at height = " << tip->m_sidechainHeight << ", id = " << tip->m_sidechainId << ", mainchain height = " << tip->m_txinGenHeight);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,50 +432,6 @@ bool SideChain::get_shares(const PoolBlock* tip, std::vector<MinerShare>& shares
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SideChain::get_wallets(const PoolBlock* tip, std::vector<const Wallet*>& wallets) const
|
|
||||||
{
|
|
||||||
// Collect wallets from each block in the PPLNS window, starting from the "tip"
|
|
||||||
wallets.clear();
|
|
||||||
wallets.reserve(m_chainWindowSize * 2);
|
|
||||||
|
|
||||||
uint64_t block_depth = 0;
|
|
||||||
const PoolBlock* cur = tip;
|
|
||||||
|
|
||||||
do {
|
|
||||||
wallets.push_back(&cur->m_minerWallet);
|
|
||||||
|
|
||||||
for (const hash& uncle_id : cur->m_uncles) {
|
|
||||||
auto it = m_blocksById.find(uncle_id);
|
|
||||||
if (it == m_blocksById.end()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip uncles which are already out of PPLNS window
|
|
||||||
if (tip->m_sidechainHeight - it->second->m_sidechainHeight < m_chainWindowSize) {
|
|
||||||
wallets.push_back(&it->second->m_minerWallet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
++block_depth;
|
|
||||||
if ((block_depth >= m_chainWindowSize) || (cur->m_sidechainHeight == 0)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto it = m_blocksById.find(cur->m_parent);
|
|
||||||
if (it == m_blocksById.end()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
cur = it->second;
|
|
||||||
} while (true);
|
|
||||||
|
|
||||||
// Remove duplicates
|
|
||||||
std::sort(wallets.begin(), wallets.end(), [](const Wallet* a, const Wallet* b) { return *a < *b; });
|
|
||||||
wallets.erase(std::unique(wallets.begin(), wallets.end(), [](const Wallet* a, const Wallet* b) { return *a == *b; }), wallets.end());
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SideChain::block_seen(const PoolBlock& block)
|
bool SideChain::block_seen(const PoolBlock& block)
|
||||||
{
|
{
|
||||||
// Check if it's some old block
|
// Check if it's some old block
|
||||||
|
@ -2090,10 +2085,10 @@ void SideChain::launch_precalc(const PoolBlock* block)
|
||||||
if (b->m_precalculated) {
|
if (b->m_precalculated) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
std::vector<const Wallet*> wallets;
|
std::vector<MinerShare> shares;
|
||||||
if (get_wallets(b, wallets)) {
|
if (get_shares(b, shares, true)) {
|
||||||
b->m_precalculated = true;
|
b->m_precalculated = true;
|
||||||
PrecalcJob* job = new PrecalcJob{ b, std::move(wallets) };
|
PrecalcJob* job = new PrecalcJob{ b, std::move(shares) };
|
||||||
{
|
{
|
||||||
MutexLock lock2(m_precalcJobsMutex);
|
MutexLock lock2(m_precalcJobsMutex);
|
||||||
m_precalcJobs.push_back(job);
|
m_precalcJobs.push_back(job);
|
||||||
|
@ -2130,20 +2125,20 @@ void SideChain::precalc_worker()
|
||||||
uint8_t t[HASH_SIZE * 2 + sizeof(size_t)];
|
uint8_t t[HASH_SIZE * 2 + sizeof(size_t)];
|
||||||
memcpy(t, job->b->m_txkeySec.h, HASH_SIZE);
|
memcpy(t, job->b->m_txkeySec.h, HASH_SIZE);
|
||||||
|
|
||||||
for (size_t i = 0, n = job->wallets.size(); i < n; ++i) {
|
for (size_t i = 0, n = job->shares.size(); i < n; ++i) {
|
||||||
memcpy(t + HASH_SIZE, job->wallets[i]->view_public_key().h, HASH_SIZE);
|
memcpy(t + HASH_SIZE, job->shares[i].m_wallet->view_public_key().h, HASH_SIZE);
|
||||||
memcpy(t + HASH_SIZE * 2, &i, sizeof(i));
|
memcpy(t + HASH_SIZE * 2, &i, sizeof(i));
|
||||||
if (!m_uniquePrecalcInputs->insert(robin_hood::hash_bytes(t, array_size(t))).second) {
|
if (!m_uniquePrecalcInputs->insert(robin_hood::hash_bytes(t, array_size(t))).second) {
|
||||||
job->wallets[i] = nullptr;
|
job->shares[i].m_wallet = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0, n = job->wallets.size(); i < n; ++i) {
|
for (size_t i = 0, n = job->shares.size(); i < n; ++i) {
|
||||||
if (job->wallets[i]) {
|
if (job->shares[i].m_wallet) {
|
||||||
hash eph_public_key;
|
hash eph_public_key;
|
||||||
uint8_t view_tag;
|
uint8_t view_tag;
|
||||||
job->wallets[i]->get_eph_public_key(job->b->m_txkeySec, i, eph_public_key, view_tag);
|
job->shares[i].m_wallet->get_eph_public_key(job->b->m_txkeySec, i, eph_public_key, view_tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete job;
|
delete job;
|
||||||
|
|
|
@ -77,6 +77,10 @@ public:
|
||||||
const PoolBlock* chainTip() const { return m_chainTip; }
|
const PoolBlock* chainTip() const { return m_chainTip; }
|
||||||
bool precalcFinished() const { return m_precalcFinished.load(); }
|
bool precalcFinished() const { return m_precalcFinished.load(); }
|
||||||
|
|
||||||
|
#ifdef P2POOL_UNIT_TESTS
|
||||||
|
difficulty_type m_testMainChainDiff;
|
||||||
|
#endif
|
||||||
|
|
||||||
static bool split_reward(uint64_t reward, const std::vector<MinerShare>& shares, std::vector<uint64_t>& rewards);
|
static bool split_reward(uint64_t reward, const std::vector<MinerShare>& shares, std::vector<uint64_t>& rewards);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -85,9 +89,8 @@ private:
|
||||||
NetworkType m_networkType;
|
NetworkType m_networkType;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool get_shares(const PoolBlock* tip, std::vector<MinerShare>& shares) const;
|
bool get_shares(const PoolBlock* tip, std::vector<MinerShare>& shares, bool quiet = false) const;
|
||||||
bool get_difficulty(const PoolBlock* tip, std::vector<DifficultyData>& difficultyData, difficulty_type& curDifficulty) const;
|
bool get_difficulty(const PoolBlock* tip, std::vector<DifficultyData>& difficultyData, difficulty_type& curDifficulty) const;
|
||||||
bool get_wallets(const PoolBlock* tip, std::vector<const Wallet*>& wallets) const;
|
|
||||||
void verify_loop(PoolBlock* block);
|
void verify_loop(PoolBlock* block);
|
||||||
void verify(PoolBlock* block);
|
void verify(PoolBlock* block);
|
||||||
void update_chain_tip(const PoolBlock* block);
|
void update_chain_tip(const PoolBlock* block);
|
||||||
|
@ -134,7 +137,7 @@ private:
|
||||||
struct PrecalcJob
|
struct PrecalcJob
|
||||||
{
|
{
|
||||||
const PoolBlock* b;
|
const PoolBlock* b;
|
||||||
std::vector<const Wallet*> wallets;
|
std::vector<MinerShare> shares;
|
||||||
};
|
};
|
||||||
|
|
||||||
uv_cond_t m_precalcJobsCond;
|
uv_cond_t m_precalcJobsCond;
|
||||||
|
|
|
@ -16,6 +16,8 @@ add_subdirectory(../external/src/RandomX RandomX)
|
||||||
set(LIBS ${LIBS} randomx)
|
set(LIBS ${LIBS} randomx)
|
||||||
add_definitions(-DWITH_RANDOMX)
|
add_definitions(-DWITH_RANDOMX)
|
||||||
|
|
||||||
|
add_definitions(-DP2POOL_UNIT_TESTS)
|
||||||
|
|
||||||
include(cmake/flags.cmake)
|
include(cmake/flags.cmake)
|
||||||
|
|
||||||
set(HEADERS
|
set(HEADERS
|
||||||
|
|
Loading…
Reference in a new issue