2021-08-22 10:20:59 +00:00
/*
* This file is part of the Monero P2Pool < https : //github.com/SChernykh/p2pool>
2023-01-04 12:07:55 +00:00
* Copyright ( c ) 2021 - 2023 SChernykh < https : //github.com/SChernykh>
2021-08-22 10:20:59 +00:00
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , version 3.
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "common.h"
# include "p2pool.h"
# include "side_chain.h"
# include "pool_block.h"
# include "wallet.h"
# include "block_template.h"
2022-03-15 15:56:37 +00:00
# ifdef WITH_RANDOMX
2021-08-22 10:20:59 +00:00
# include "randomx.h"
# include "dataset.hpp"
# include "configuration.h"
# include "intrin_portable.h"
2022-03-15 15:56:37 +00:00
# endif
2021-08-22 10:20:59 +00:00
# include "keccak.h"
# include "p2p_server.h"
2022-02-17 10:19:11 +00:00
# include "stratum_server.h"
2021-08-22 10:20:59 +00:00
# include "params.h"
# include "json_parsers.h"
2022-10-04 12:35:38 +00:00
# include "crypto.h"
2022-12-19 08:58:43 +00:00
# include "hardforks/hardforks.h"
2021-08-22 10:20:59 +00:00
# include <rapidjson/document.h>
# include <rapidjson/istreamwrapper.h>
# include <fstream>
# include <iterator>
# include <numeric>
2023-08-16 11:00:11 +00:00
LOG_CATEGORY ( SideChain )
2021-08-22 10:20:59 +00:00
2022-06-03 15:28:46 +00:00
static constexpr uint64_t MIN_DIFFICULTY = 100000 ;
static constexpr size_t UNCLE_BLOCK_DEPTH = 3 ;
2021-08-22 10:20:59 +00:00
static_assert ( 1 < = UNCLE_BLOCK_DEPTH & & UNCLE_BLOCK_DEPTH < = 10 , " Invalid UNCLE_BLOCK_DEPTH " ) ;
2022-06-03 15:28:46 +00:00
static constexpr uint64_t MONERO_BLOCK_TIME = 120 ;
2021-08-22 10:20:59 +00:00
namespace p2pool {
2021-12-30 10:10:18 +00:00
static constexpr uint8_t default_consensus_id [ HASH_SIZE ] = { 34 , 175 , 126 , 231 , 181 , 11 , 104 , 146 , 227 , 153 , 218 , 107 , 44 , 108 , 68 , 39 , 178 , 81 , 4 , 212 , 169 , 4 , 142 , 0 , 177 , 110 , 157 , 240 , 68 , 7 , 249 , 24 } ;
static constexpr uint8_t mini_consensus_id [ HASH_SIZE ] = { 57 , 130 , 201 , 26 , 149 , 174 , 199 , 250 , 66 , 80 , 189 , 18 , 108 , 216 , 194 , 220 , 136 , 23 , 63 , 24 , 64 , 113 , 221 , 44 , 219 , 86 , 39 , 163 , 53 , 24 , 126 , 196 } ;
2021-09-18 08:03:06 +00:00
2023-01-16 13:18:34 +00:00
NetworkType SideChain : : s_networkType = NetworkType : : Invalid ;
2021-09-06 13:49:39 +00:00
SideChain : : SideChain ( p2pool * pool , NetworkType type , const char * pool_name )
2021-08-22 10:20:59 +00:00
: m_pool ( pool )
2022-04-08 21:14:08 +00:00
, m_chainTip { nullptr }
2022-05-12 20:18:08 +00:00
, m_seenWalletsLastPruneTime ( 0 )
2021-09-16 12:23:14 +00:00
, m_poolName ( pool_name ? pool_name : " default " )
2021-08-27 09:30:03 +00:00
, m_targetBlockTime ( 10 )
2021-08-22 10:20:59 +00:00
, m_minDifficulty ( MIN_DIFFICULTY , 0 )
, m_chainWindowSize ( 2160 )
, m_unclePenalty ( 20 )
2022-07-14 07:04:14 +00:00
, m_precalcFinished ( false )
2023-05-17 21:36:58 +00:00
# ifdef DEV_TEST_SYNC
2023-06-16 13:51:33 +00:00
, m_firstPruneTime ( 0 )
2023-05-17 21:36:58 +00:00
# endif
2021-08-22 10:20:59 +00:00
{
2023-01-16 13:18:34 +00:00
if ( s_networkType = = NetworkType : : Invalid ) {
s_networkType = type ;
}
else if ( s_networkType ! = type ) {
LOGERR ( 1 , " can't run both " < < s_networkType < < " and " < < type < < " at the same time " ) ;
PANIC_STOP ( ) ;
}
LOGINFO ( 1 , log : : LightCyan ( ) < < " network type = " < < type ) ;
2021-08-27 09:25:25 +00:00
2021-09-06 13:49:39 +00:00
if ( m_pool & & ! load_config ( m_pool - > params ( ) . m_config ) ) {
2023-01-14 11:19:25 +00:00
PANIC_STOP ( ) ;
2021-08-22 10:20:59 +00:00
}
if ( ! check_config ( ) ) {
2023-01-14 11:19:25 +00:00
PANIC_STOP ( ) ;
2021-08-22 10:20:59 +00:00
}
2023-01-18 15:08:07 +00:00
m_curDifficulty = m_minDifficulty ;
2022-05-12 13:19:58 +00:00
uv_rwlock_init_checked ( & m_sidechainLock ) ;
uv_mutex_init_checked ( & m_seenWalletsLock ) ;
2023-05-29 12:13:11 +00:00
uv_mutex_init_checked ( & m_incomingBlocksLock ) ;
2022-05-09 14:07:49 +00:00
uv_rwlock_init_checked ( & m_curDifficultyLock ) ;
2021-08-22 10:20:59 +00:00
m_difficultyData . reserve ( m_chainWindowSize ) ;
LOGINFO ( 1 , " generating consensus ID " ) ;
2021-09-16 12:23:14 +00:00
char buf [ log : : Stream : : BUF_SIZE + 1 ] ;
log : : Stream s ( buf ) ;
2021-08-22 10:20:59 +00:00
2023-01-16 13:18:34 +00:00
s < < s_networkType < < ' \0 '
2021-09-16 12:23:14 +00:00
< < m_poolName < < ' \0 '
< < m_poolPassword < < ' \0 '
< < m_targetBlockTime < < ' \0 '
< < m_minDifficulty < < ' \0 '
< < m_chainWindowSize < < ' \0 '
< < m_unclePenalty < < ' \0 ' ;
2021-08-22 10:20:59 +00:00
2021-09-16 12:23:14 +00:00
constexpr char default_config [ ] = " mainnet \0 " " default \0 " " \0 " " 10 \0 " " 100000 \0 " " 2160 \0 " " 20 \0 " ;
2021-12-30 10:10:18 +00:00
constexpr char mini_config [ ] = " mainnet \0 " " mini \0 " " \0 " " 10 \0 " " 100000 \0 " " 2160 \0 " " 20 \0 " ;
2021-08-22 10:20:59 +00:00
2021-09-18 08:03:06 +00:00
// Hardcoded default consensus ID
2021-09-16 12:23:14 +00:00
if ( memcmp ( buf , default_config , sizeof ( default_config ) - 1 ) = = 0 ) {
2021-09-18 08:03:06 +00:00
m_consensusId . assign ( default_consensus_id , default_consensus_id + HASH_SIZE ) ;
2021-08-22 10:20:59 +00:00
}
2021-12-30 10:10:18 +00:00
// Hardcoded mini consensus ID
else if ( memcmp ( buf , mini_config , sizeof ( mini_config ) - 1 ) = = 0 ) {
m_consensusId . assign ( mini_consensus_id , mini_consensus_id + HASH_SIZE ) ;
}
2021-09-16 12:23:14 +00:00
else {
2022-03-15 15:56:37 +00:00
# ifdef WITH_RANDOMX
2021-09-16 12:23:14 +00:00
const randomx_flags flags = randomx_get_flags ( ) ;
randomx_cache * cache = randomx_alloc_cache ( flags | RANDOMX_FLAG_LARGE_PAGES ) ;
if ( ! cache ) {
LOGWARN ( 1 , " couldn't allocate RandomX cache using large pages " ) ;
cache = randomx_alloc_cache ( flags ) ;
if ( ! cache ) {
LOGERR ( 1 , " couldn't allocate RandomX cache, aborting " ) ;
2023-01-14 11:19:25 +00:00
PANIC_STOP ( ) ;
2021-09-16 12:23:14 +00:00
}
}
2021-08-22 10:20:59 +00:00
2021-09-16 12:23:14 +00:00
randomx_init_cache ( cache , buf , s . m_pos ) ;
2021-08-22 10:20:59 +00:00
2021-09-16 12:23:14 +00:00
// Intentionally not a power of 2
constexpr size_t scratchpad_size = 1009 ;
2021-08-22 10:20:59 +00:00
2021-09-16 12:23:14 +00:00
rx_vec_i128 * scratchpad = reinterpret_cast < rx_vec_i128 * > ( cache - > memory ) ;
rx_vec_i128 * scratchpad_end = scratchpad + scratchpad_size ;
rx_vec_i128 * scratchpad_ptr = scratchpad ;
rx_vec_i128 * cache_ptr = scratchpad_end ;
2021-08-22 10:20:59 +00:00
2021-09-16 12:23:14 +00:00
for ( uint64_t i = scratchpad_size , n = RANDOMX_ARGON_MEMORY * 1024 / sizeof ( rx_vec_i128 ) ; i < n ; + + i ) {
* scratchpad_ptr = rx_xor_vec_i128 ( * scratchpad_ptr , * cache_ptr ) ;
+ + cache_ptr ;
+ + scratchpad_ptr ;
if ( scratchpad_ptr = = scratchpad_end ) {
scratchpad_ptr = scratchpad ;
}
}
2021-08-22 10:20:59 +00:00
2021-09-16 12:23:14 +00:00
hash id ;
2023-01-08 11:56:26 +00:00
keccak ( reinterpret_cast < uint8_t * > ( scratchpad ) , static_cast < int > ( scratchpad_size * sizeof ( rx_vec_i128 ) ) , id . h ) ;
2021-09-16 12:23:14 +00:00
randomx_release_cache ( cache ) ;
m_consensusId . assign ( id . h , id . h + HASH_SIZE ) ;
2022-03-15 15:56:37 +00:00
# else
LOGERR ( 1 , " Can't calculate consensus ID without RandomX library " ) ;
2023-01-14 11:19:25 +00:00
PANIC_STOP ( ) ;
2022-03-15 15:56:37 +00:00
# endif
2021-09-16 12:23:14 +00:00
}
2021-08-22 10:20:59 +00:00
2021-09-16 12:23:14 +00:00
s . m_pos = 0 ;
s < < log : : hex_buf ( m_consensusId . data ( ) , m_consensusId . size ( ) ) < < ' \0 ' ;
2021-08-22 10:20:59 +00:00
// Hide most consensus ID bytes, we only want it on screen to show that we're on the right sidechain
memset ( buf + 8 , ' * ' , HASH_SIZE * 2 - 16 ) ;
2023-08-21 09:20:50 +00:00
m_consensusIdDisplayStr = buf ;
2022-03-23 13:17:40 +00:00
LOGINFO ( 1 , " consensus ID = " < < log : : LightCyan ( ) < < m_consensusIdDisplayStr . c_str ( ) ) ;
2022-07-14 07:04:14 +00:00
2023-01-09 18:47:35 +00:00
memcpy ( m_consensusHash . h , m_consensusId . data ( ) , HASH_SIZE ) ;
2022-07-14 07:04:14 +00:00
uv_cond_init_checked ( & m_precalcJobsCond ) ;
uv_mutex_init_checked ( & m_precalcJobsMutex ) ;
m_precalcJobs . reserve ( 16 ) ;
uint32_t numThreads = std : : thread : : hardware_concurrency ( ) ;
// Leave 1 CPU core free from worker threads
if ( numThreads > 1 ) {
- - numThreads ;
}
2022-07-14 09:39:19 +00:00
// Use between 1 and 8 threads
if ( numThreads < 1 ) numThreads = 1 ;
2022-10-04 15:51:40 +00:00
2022-10-13 09:21:40 +00:00
// Don't limit thread count when debugging because debug builds are slow
# ifndef P2POOL_DEBUGGING
2022-07-14 09:39:19 +00:00
if ( numThreads > 8 ) numThreads = 8 ;
2022-10-04 15:51:40 +00:00
# endif
2022-07-14 07:04:14 +00:00
LOGINFO ( 4 , " running " < < numThreads < < " pre-calculation workers " ) ;
2022-07-14 12:28:06 +00:00
m_precalcWorkers . reserve ( numThreads ) ;
2022-07-14 07:04:14 +00:00
for ( uint32_t i = 0 ; i < numThreads ; + + i ) {
m_precalcWorkers . emplace_back ( & SideChain : : precalc_worker , this ) ;
}
m_uniquePrecalcInputs = new unordered_set < size_t > ( ) ;
2023-04-22 16:00:31 +00:00
m_uniquePrecalcInputs - > reserve ( 1 < < 18 ) ;
2021-08-22 10:20:59 +00:00
}
SideChain : : ~ SideChain ( )
{
2022-07-14 07:04:14 +00:00
finish_precalc ( ) ;
2022-05-12 13:19:58 +00:00
uv_rwlock_destroy ( & m_sidechainLock ) ;
uv_mutex_destroy ( & m_seenWalletsLock ) ;
2023-05-29 12:13:11 +00:00
uv_mutex_destroy ( & m_incomingBlocksLock ) ;
2022-05-09 14:07:49 +00:00
uv_rwlock_destroy ( & m_curDifficultyLock ) ;
2022-07-14 07:04:14 +00:00
2022-05-31 14:51:09 +00:00
for ( const auto & it : m_blocksById ) {
2021-08-22 10:20:59 +00:00
delete it . second ;
}
2023-01-16 13:18:34 +00:00
s_networkType = NetworkType : : Invalid ;
2021-08-22 10:20:59 +00:00
}
2023-01-09 00:15:06 +00:00
void SideChain : : fill_sidechain_data ( PoolBlock & block , std : : vector < MinerShare > & shares ) const
2021-08-22 10:20:59 +00:00
{
block . m_uncles . clear ( ) ;
2023-01-09 00:15:06 +00:00
ReadLock lock ( m_sidechainLock ) ;
2022-04-08 21:14:08 +00:00
const PoolBlock * tip = m_chainTip ;
if ( ! tip ) {
2021-08-22 10:20:59 +00:00
block . m_parent = { } ;
block . m_sidechainHeight = 0 ;
block . m_difficulty = m_minDifficulty ;
block . m_cumulativeDifficulty = m_minDifficulty ;
2023-03-19 15:47:12 +00:00
block . m_txkeySecSeed = m_consensusHash ;
get_tx_keys ( block . m_txkeyPub , block . m_txkeySec , block . m_txkeySecSeed , block . m_prevId ) ;
2023-01-09 14:55:52 +00:00
2021-08-22 10:20:59 +00:00
get_shares ( & block , shares ) ;
return ;
}
2023-03-19 15:47:12 +00:00
block . m_txkeySecSeed = ( block . m_prevId = = tip - > m_prevId ) ? tip - > m_txkeySecSeed : tip - > calculate_tx_key_seed ( ) ;
get_tx_keys ( block . m_txkeyPub , block . m_txkeySec , block . m_txkeySecSeed , block . m_prevId ) ;
2023-01-09 14:55:52 +00:00
2022-04-08 21:14:08 +00:00
block . m_parent = tip - > m_sidechainId ;
block . m_sidechainHeight = tip - > m_sidechainHeight + 1 ;
2021-08-22 10:20:59 +00:00
// Collect uncles from 3 previous block heights
// First get a list of already mined blocks at these heights
std : : vector < hash > mined_blocks ;
mined_blocks . reserve ( UNCLE_BLOCK_DEPTH * 2 + 1 ) ;
2022-04-08 21:14:08 +00:00
const PoolBlock * tmp = tip ;
for ( uint64_t i = 0 , n = std : : min < uint64_t > ( UNCLE_BLOCK_DEPTH , tip - > m_sidechainHeight + 1 ) ; tmp & & ( i < n ) ; + + i ) {
2021-08-22 10:20:59 +00:00
mined_blocks . push_back ( tmp - > m_sidechainId ) ;
mined_blocks . insert ( mined_blocks . end ( ) , tmp - > m_uncles . begin ( ) , tmp - > m_uncles . end ( ) ) ;
tmp = get_parent ( tmp ) ;
}
2022-04-08 21:14:08 +00:00
for ( uint64_t i = 0 , n = std : : min < uint64_t > ( UNCLE_BLOCK_DEPTH , tip - > m_sidechainHeight + 1 ) ; i < n ; + + i ) {
2022-05-12 13:19:58 +00:00
auto it = m_blocksByHeight . find ( tip - > m_sidechainHeight - i ) ;
if ( it = = m_blocksByHeight . end ( ) ) {
continue ;
}
for ( const PoolBlock * uncle : it - > second ) {
2021-08-22 10:20:59 +00:00
// Only add verified and valid blocks
if ( ! uncle | | ! uncle - > m_verified | | uncle - > m_invalid ) {
continue ;
}
// Only add it if it hasn't been mined already
if ( std : : find ( mined_blocks . begin ( ) , mined_blocks . end ( ) , uncle - > m_sidechainId ) ! = mined_blocks . end ( ) ) {
continue ;
}
// Only add it if it's on the same chain
bool same_chain = false ;
do {
2022-04-08 21:14:08 +00:00
tmp = tip ;
2022-05-26 16:20:29 +00:00
while ( tmp & & ( tmp - > m_sidechainHeight > uncle - > m_sidechainHeight ) ) {
2021-08-22 10:20:59 +00:00
tmp = get_parent ( tmp ) ;
}
if ( ! tmp | | ( tmp - > m_sidechainHeight < uncle - > m_sidechainHeight ) ) {
break ;
}
2022-05-12 13:19:58 +00:00
const PoolBlock * tmp2 = uncle ;
2021-08-22 10:20:59 +00:00
for ( size_t j = 0 ; ( j < UNCLE_BLOCK_DEPTH ) & & tmp & & tmp2 & & ( tmp - > m_sidechainHeight + UNCLE_BLOCK_DEPTH > = block . m_sidechainHeight ) ; + + j ) {
if ( tmp - > m_parent = = tmp2 - > m_parent ) {
same_chain = true ;
break ;
}
tmp = get_parent ( tmp ) ;
tmp2 = get_parent ( tmp2 ) ;
}
} while ( 0 ) ;
if ( same_chain ) {
block . m_uncles . emplace_back ( uncle - > m_sidechainId ) ;
LOGINFO ( 4 , " block template at height " < < block . m_sidechainHeight < <
" : added " < < uncle - > m_sidechainId < <
" (height " < < uncle - > m_sidechainHeight < <
" ) as an uncle block, depth " < < block . m_sidechainHeight - uncle - > m_sidechainHeight ) ;
}
else {
LOGINFO ( 4 , " block template at height " < < block . m_sidechainHeight < <
" : uncle block " < < uncle - > m_sidechainId < <
" (height " < < uncle - > m_sidechainHeight < <
" ) is not on the same chain, depth " < < block . m_sidechainHeight - uncle - > m_sidechainHeight ) ;
}
}
}
// Sort uncles and remove duplicates
if ( block . m_uncles . size ( ) > 1 ) {
std : : sort ( block . m_uncles . begin ( ) , block . m_uncles . end ( ) ) ;
block . m_uncles . erase ( std : : unique ( block . m_uncles . begin ( ) , block . m_uncles . end ( ) ) , block . m_uncles . end ( ) ) ;
}
2022-05-09 14:07:49 +00:00
block . m_difficulty = difficulty ( ) ;
2022-04-08 21:14:08 +00:00
block . m_cumulativeDifficulty = tip - > m_cumulativeDifficulty + block . m_difficulty ;
2021-08-22 10:20:59 +00:00
for ( const hash & uncle_id : block . m_uncles ) {
auto it = m_blocksById . find ( uncle_id ) ;
if ( it = = m_blocksById . end ( ) ) {
LOGERR ( 1 , " block template has an unknown uncle block " < < uncle_id < < " . Fix the code! " ) ;
continue ;
}
block . m_cumulativeDifficulty + = it - > second - > m_difficulty ;
}
get_shares ( & block , shares ) ;
}
2021-11-01 18:35:11 +00:00
P2PServer * SideChain : : p2pServer ( ) const
{
return m_pool ? m_pool - > p2p_server ( ) : nullptr ;
}
2023-01-13 15:56:31 +00:00
bool SideChain : : get_shares ( const PoolBlock * tip , std : : vector < MinerShare > & shares , uint64_t * bottom_height , bool quiet ) const
2021-08-22 10:20:59 +00:00
{
2023-01-09 18:47:35 +00:00
if ( tip - > m_txkeySecSeed . empty ( ) ) {
LOGERR ( 1 , " tx key seed is not set, fix the code! " ) ;
}
2023-01-07 23:34:38 +00:00
const int L = quiet ? 6 : 3 ;
2021-08-22 10:20:59 +00:00
// Collect shares from each block in the PPLNS window, starting from the "tip"
uint64_t block_depth = 0 ;
2022-07-10 08:24:03 +00:00
const PoolBlock * cur = tip ;
2023-01-07 23:34:38 +00:00
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)
2023-03-19 15:47:12 +00:00
const difficulty_type max_pplns_weight = mainchain_diff * 2 ;
2023-01-07 23:34:38 +00:00
difficulty_type pplns_weight ;
2023-01-10 15:33:19 +00:00
unordered_set < MinerShare > shares_set ;
shares_set . reserve ( m_chainWindowSize * 2 ) ;
2021-08-22 10:20:59 +00:00
do {
2023-01-10 15:33:19 +00:00
difficulty_type cur_weight = cur - > m_difficulty ;
2021-08-22 10:20:59 +00:00
for ( const hash & uncle_id : cur - > m_uncles ) {
auto it = m_blocksById . find ( uncle_id ) ;
if ( it = = m_blocksById . end ( ) ) {
2023-01-07 23:34:38 +00:00
LOGWARN ( L , " get_shares: can't find uncle block at height = " < < cur - > m_sidechainHeight < < " , id = " < < uncle_id ) ;
LOGWARN ( L , " get_shares: can't calculate shares for block at height = " < < tip - > m_sidechainHeight < < " , id = " < < tip - > m_sidechainId < < " , mainchain height = " < < tip - > m_txinGenHeight ) ;
2021-08-22 10:20:59 +00:00
return false ;
}
PoolBlock * uncle = it - > second ;
// Skip uncles which are already out of PPLNS window
if ( tip - > m_sidechainHeight - uncle - > m_sidechainHeight > = m_chainWindowSize ) {
continue ;
}
// Take some % of uncle's weight into this share
2022-11-15 21:20:54 +00:00
const difficulty_type uncle_penalty = uncle - > m_difficulty * m_unclePenalty / 100 ;
2023-01-07 23:34:38 +00:00
const difficulty_type uncle_weight = uncle - > m_difficulty - uncle_penalty ;
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 ;
}
2023-01-10 15:33:19 +00:00
cur_weight + = uncle_penalty ;
2023-01-07 23:34:38 +00:00
2023-01-10 15:33:19 +00:00
auto result = shares_set . emplace ( uncle_weight , & uncle - > m_minerWallet ) ;
if ( ! result . second ) {
result . first - > m_weight + = uncle_weight ;
}
2023-01-07 23:34:38 +00:00
pplns_weight = new_pplns_weight ;
2021-08-22 10:20:59 +00:00
}
2023-01-07 23:34:38 +00:00
// Always add non-uncle shares even if PPLNS weight goes above the limit
2023-01-10 15:33:19 +00:00
auto result = shares_set . emplace ( cur_weight , & cur - > m_minerWallet ) ;
if ( ! result . second ) {
result . first - > m_weight + = cur_weight ;
}
pplns_weight + = cur_weight ;
2023-01-07 23:34:38 +00:00
// 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 ;
}
2021-08-22 10:20:59 +00:00
+ + block_depth ;
if ( block_depth > = m_chainWindowSize ) {
break ;
}
// Reached the genesis block so we're done
if ( cur - > m_sidechainHeight = = 0 ) {
break ;
}
auto it = m_blocksById . find ( cur - > m_parent ) ;
if ( it = = m_blocksById . end ( ) ) {
2023-01-07 23:34:38 +00:00
LOGWARN ( L , " get_shares: can't find parent block at height = " < < cur - > m_sidechainHeight - 1 < < " , id = " < < cur - > m_parent ) ;
LOGWARN ( L , " get_shares: can't calculate shares for block at height = " < < tip - > m_sidechainHeight < < " , id = " < < tip - > m_sidechainId < < " , mainchain height = " < < tip - > m_txinGenHeight ) ;
2021-08-22 10:20:59 +00:00
return false ;
}
cur = it - > second ;
2022-06-13 05:30:59 +00:00
} while ( true ) ;
2021-08-22 10:20:59 +00:00
2023-01-13 15:56:31 +00:00
if ( bottom_height ) {
* bottom_height = cur - > m_sidechainHeight ;
}
2023-01-10 15:33:19 +00:00
shares . assign ( shares_set . begin ( ) , shares_set . end ( ) ) ;
2021-08-22 10:20:59 +00:00
std : : sort ( shares . begin ( ) , shares . end ( ) , [ ] ( const auto & a , const auto & b ) { return * a . m_wallet < * b . m_wallet ; } ) ;
2023-01-10 15:33:19 +00:00
const uint64_t n = shares . size ( ) ;
2023-01-09 18:47:35 +00:00
// Shuffle shares
2023-03-19 15:47:12 +00:00
if ( n > 1 ) {
2023-01-10 15:33:19 +00:00
hash h ;
keccak ( tip - > m_txkeySecSeed . h , HASH_SIZE , h . h ) ;
2023-07-07 09:00:19 +00:00
uint64_t seed = * h . u64 ( ) ;
2023-01-10 15:33:19 +00:00
if ( seed = = 0 ) seed = 1 ;
2023-01-09 18:47:35 +00:00
2023-01-10 15:33:19 +00:00
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 ] ) ;
2023-01-09 18:47:35 +00:00
}
}
2023-01-10 15:33:19 +00:00
LOGINFO ( 6 , " get_shares: " < < n < < " unique wallets in PPLNS window " ) ;
2021-08-22 10:20:59 +00:00
return true ;
}
2023-05-29 12:13:11 +00:00
bool SideChain : : incoming_block_seen ( const PoolBlock & block )
2021-08-22 10:20:59 +00:00
{
2021-08-23 22:27:48 +00:00
// Check if it's some old block
2021-10-31 09:26:13 +00:00
const PoolBlock * tip = m_chainTip ;
2022-04-08 21:14:08 +00:00
2021-10-31 09:26:13 +00:00
if ( tip & & tip - > m_sidechainHeight > block . m_sidechainHeight + m_chainWindowSize * 2 & &
block . m_cumulativeDifficulty < tip - > m_cumulativeDifficulty ) {
2021-08-23 22:27:48 +00:00
return true ;
}
2023-05-29 12:13:11 +00:00
const uint64_t cur_time = seconds_since_epoch ( ) ;
2021-08-23 22:27:48 +00:00
// Check if it was received before
2023-05-29 12:13:11 +00:00
MutexLock lock ( m_incomingBlocksLock ) ;
return ! m_incomingBlocks . emplace ( block . get_full_id ( ) , cur_time ) . second ;
2021-08-22 10:20:59 +00:00
}
2023-05-29 12:13:11 +00:00
void SideChain : : forget_incoming_block ( const PoolBlock & block )
2021-10-16 07:42:58 +00:00
{
2023-05-29 12:13:11 +00:00
MutexLock lock ( m_incomingBlocksLock ) ;
m_incomingBlocks . erase ( block . get_full_id ( ) ) ;
2021-10-16 07:42:58 +00:00
}
2023-05-29 12:13:11 +00:00
void SideChain : : cleanup_incoming_blocks ( )
2023-05-22 13:30:57 +00:00
{
2023-05-29 12:13:11 +00:00
const uint64_t cur_time = seconds_since_epoch ( ) ;
2023-05-22 13:30:57 +00:00
2023-05-29 12:13:11 +00:00
MutexLock lock ( m_incomingBlocksLock ) ;
2023-05-22 13:30:57 +00:00
2023-05-31 15:03:45 +00:00
// Forget seen blocks that were added more than 10 minutes ago
2023-05-22 13:30:57 +00:00
hash h ;
2023-05-29 12:13:11 +00:00
for ( auto i = m_incomingBlocks . begin ( ) ; i ! = m_incomingBlocks . end ( ) ; ) {
2023-05-31 15:03:45 +00:00
if ( cur_time < i - > second + 10 * 60 ) {
2023-05-29 12:13:11 +00:00
+ + i ;
2023-05-22 13:30:57 +00:00
}
else {
2023-05-29 12:13:11 +00:00
i = m_incomingBlocks . erase ( i ) ;
2023-05-22 13:30:57 +00:00
}
}
}
2021-08-22 10:20:59 +00:00
bool SideChain : : add_external_block ( PoolBlock & block , std : : vector < hash > & missing_blocks )
{
if ( block . m_difficulty < m_minDifficulty ) {
2022-11-03 10:38:43 +00:00
LOGWARN ( 3 , " add_external_block: block mined by " < < block . m_minerWallet < < " has invalid difficulty " < < block . m_difficulty < < " , expected >= " < < m_minDifficulty ) ;
2021-08-22 10:20:59 +00:00
return false ;
}
2022-05-09 14:07:49 +00:00
const difficulty_type expected_diff = difficulty ( ) ;
bool too_low_diff = ( block . m_difficulty < expected_diff ) ;
2021-08-22 10:20:59 +00:00
{
2022-05-12 13:19:58 +00:00
ReadLock lock ( m_sidechainLock ) ;
2021-08-22 10:20:59 +00:00
if ( m_blocksById . find ( block . m_sidechainId ) ! = m_blocksById . end ( ) ) {
LOGINFO ( 4 , " add_external_block: block " < < block . m_sidechainId < < " is already added " ) ;
return true ;
}
2021-10-29 09:39:15 +00:00
// This is mainly an anti-spam measure, not an actual verification step
if ( too_low_diff ) {
// Reduce required diff by 50% (by doubling this block's diff) to account for alternative chains
difficulty_type diff2 = block . m_difficulty ;
diff2 + = block . m_difficulty ;
2022-04-08 21:14:08 +00:00
const PoolBlock * tip = m_chainTip ;
for ( const PoolBlock * tmp = tip ; tmp & & ( tmp - > m_sidechainHeight + m_chainWindowSize > tip - > m_sidechainHeight ) ; tmp = get_parent ( tmp ) ) {
2021-10-29 09:39:15 +00:00
if ( diff2 > = tmp - > m_difficulty ) {
too_low_diff = false ;
break ;
}
2021-08-22 10:20:59 +00:00
}
}
}
LOGINFO ( 4 , " add_external_block: height = " < < block . m_sidechainHeight < < " , id = " < < block . m_sidechainId < < " , mainchain height = " < < block . m_txinGenHeight ) ;
2021-10-29 09:39:15 +00:00
if ( too_low_diff ) {
2022-11-03 10:38:43 +00:00
LOGWARN ( 4 , " add_external_block: block mined by " < < block . m_minerWallet < < " has too low difficulty " < < block . m_difficulty < < " , expected >= ~ " < < expected_diff < < " . Ignoring it. " ) ;
2021-08-22 10:20:59 +00:00
return true ;
}
// This check is not always possible to perform because of mainchain reorgs
ChainMain data ;
if ( m_pool - > chainmain_get_by_hash ( block . m_prevId , data ) ) {
if ( data . height + 1 ! = block . m_txinGenHeight ) {
2022-11-03 10:38:43 +00:00
LOGWARN ( 3 , " add_external_block mined by " < < block . m_minerWallet < < " : wrong mainchain height " < < block . m_txinGenHeight < < " , expected " < < data . height + 1 ) ;
2021-08-22 10:20:59 +00:00
return false ;
}
}
else {
LOGWARN ( 3 , " add_external_block: block is built on top of an unknown mainchain block " < < block . m_prevId < < " , mainchain reorg might've happened " ) ;
}
hash seed ;
if ( ! m_pool - > get_seed ( block . m_txinGenHeight , seed ) ) {
2022-11-03 10:38:43 +00:00
LOGWARN ( 3 , " add_external_block mined by " < < block . m_minerWallet < < " : couldn't get seed hash for mainchain height " < < block . m_txinGenHeight ) ;
2023-05-29 12:13:11 +00:00
forget_incoming_block ( block ) ;
2021-08-22 10:20:59 +00:00
return false ;
}
hash pow_hash ;
2021-11-20 10:51:22 +00:00
if ( ! block . get_pow_hash ( m_pool - > hasher ( ) , block . m_txinGenHeight , seed , pow_hash ) ) {
2021-10-16 07:42:58 +00:00
LOGWARN ( 3 , " add_external_block: couldn't get PoW hash for height = " < < block . m_sidechainHeight < < " , mainchain height " < < block . m_txinGenHeight < < " . Ignoring it. " ) ;
2023-05-29 12:13:11 +00:00
forget_incoming_block ( block ) ;
2021-10-16 07:42:58 +00:00
return true ;
2021-08-22 10:20:59 +00:00
}
2021-08-25 20:07:42 +00:00
// Check if it has the correct parent and difficulty to go right to monerod for checking
2022-04-08 21:14:08 +00:00
MinerData miner_data = m_pool - > miner_data ( ) ;
2021-08-25 20:07:42 +00:00
if ( ( block . m_prevId = = miner_data . prev_id ) & & miner_data . difficulty . check_pow ( pow_hash ) ) {
LOGINFO ( 0 , log : : LightGreen ( ) < < " add_external_block: block " < < block . m_sidechainId < < " has enough PoW for Monero network, submitting it " ) ;
2022-09-30 12:37:32 +00:00
m_pool - > submit_block_async ( block . serialize_mainchain_data ( ) ) ;
2021-08-25 20:07:42 +00:00
}
2021-09-13 08:14:53 +00:00
else {
difficulty_type diff ;
if ( ! m_pool - > get_difficulty_at_height ( block . m_txinGenHeight , diff ) ) {
LOGWARN ( 3 , " add_external_block: couldn't get mainchain difficulty for height = " < < block . m_txinGenHeight ) ;
}
else if ( diff . check_pow ( pow_hash ) ) {
LOGINFO ( 0 , log : : LightGreen ( ) < < " add_external_block: block " < < block . m_sidechainId < < " has enough PoW for Monero height " < < block . m_txinGenHeight < < " , submitting it " ) ;
2022-09-30 12:37:32 +00:00
m_pool - > submit_block_async ( block . serialize_mainchain_data ( ) ) ;
2021-09-13 08:14:53 +00:00
}
}
2021-08-25 20:07:42 +00:00
2021-08-22 10:20:59 +00:00
if ( ! block . m_difficulty . check_pow ( pow_hash ) ) {
2023-03-28 08:14:47 +00:00
LOGWARN ( 3 ,
" add_external_block mined by " < < block . m_minerWallet < <
" : not enough PoW for height = " < < block . m_sidechainHeight < <
" , id = " < < block . m_sidechainId < <
" , nonce = " < < block . m_nonce < <
" , mainchain height = " < < block . m_txinGenHeight
) ;
2023-06-14 09:22:55 +00:00
bool not_enough_pow = true ;
2023-03-28 08:14:47 +00:00
// Calculate the same hash second time to check if it's an unstable hardware that caused this
hash pow_hash2 ;
if ( block . get_pow_hash ( m_pool - > hasher ( ) , block . m_txinGenHeight , seed , pow_hash2 , true ) & & ( pow_hash2 ! = pow_hash ) ) {
LOGERR ( 0 , " UNSTABLE HARDWARE DETECTED: Calculated the same hash twice, got different results: " < < pow_hash < < " != " < < pow_hash2 < < " (sidechain id = " < < block . m_sidechainId < < ' ) ' ) ;
2023-06-14 09:22:55 +00:00
if ( block . m_difficulty . check_pow ( pow_hash2 ) ) {
LOGINFO ( 3 , " add_external_block second result has enough PoW for height = " < < block . m_sidechainHeight < < " , id = " < < block . m_sidechainId ) ;
not_enough_pow = false ;
}
2023-03-28 08:14:47 +00:00
}
2023-06-14 09:22:55 +00:00
if ( not_enough_pow ) {
return false ;
}
2021-08-22 10:20:59 +00:00
}
2021-09-06 21:33:52 +00:00
bool block_found = false ;
2021-08-22 10:20:59 +00:00
missing_blocks . clear ( ) ;
{
2022-05-12 13:19:58 +00:00
WriteLock lock ( m_sidechainLock ) ;
2021-08-22 10:20:59 +00:00
if ( ! block . m_parent . empty ( ) & & ( m_blocksById . find ( block . m_parent ) = = m_blocksById . end ( ) ) ) {
missing_blocks . push_back ( block . m_parent ) ;
}
for ( const hash & h : block . m_uncles ) {
if ( ! h . empty ( ) & & ( m_blocksById . find ( h ) = = m_blocksById . end ( ) ) ) {
missing_blocks . push_back ( h ) ;
}
}
2021-09-06 21:33:52 +00:00
if ( block . m_sidechainId = = m_watchBlockSidechainId ) {
2023-05-11 06:07:05 +00:00
const Wallet & w = m_pool - > params ( ) . m_wallet ;
const char * who = ( block . m_minerWallet = = w ) ? " you " : " someone else in this p2pool " ;
LOGINFO ( 0 , log : : LightGreen ( ) < < " BLOCK FOUND: main chain block at height " < < m_watchBlock . height < < " was mined by " < < who < < BLOCK_FOUND ) ;
2021-09-06 21:33:52 +00:00
m_watchBlockSidechainId = { } ;
data = m_watchBlock ;
block_found = true ;
2022-01-29 16:00:12 +00:00
2022-12-18 10:06:38 +00:00
const uint64_t payout = block . get_payout ( w ) ;
2022-01-29 16:00:12 +00:00
if ( payout ) {
2022-12-18 10:06:38 +00:00
LOGINFO ( 0 , log : : LightCyan ( ) < < " Your wallet " < < log : : LightGreen ( ) < < w < < log : : LightCyan ( ) < < " got a payout of " < < log : : LightGreen ( ) < < log : : XMRAmount ( payout ) < < log : : LightCyan ( ) < < " in block " < < log : : LightGreen ( ) < < data . height ) ;
}
else {
LOGINFO ( 0 , log : : LightCyan ( ) < < " Your wallet " < < log : : LightYellow ( ) < < w < < log : : LightCyan ( ) < < " didn't get a payout in block " < < log : : LightYellow ( ) < < data . height < < log : : LightCyan ( ) < < " because you had no shares in PPLNS window " ) ;
2022-01-29 16:00:12 +00:00
}
2021-09-06 21:33:52 +00:00
}
}
if ( block_found ) {
2022-10-09 16:17:01 +00:00
m_pool - > api_update_block_found ( & data , & block ) ;
2021-08-22 10:20:59 +00:00
}
add_block ( block ) ;
return true ;
}
2023-01-07 14:50:02 +00:00
bool SideChain : : add_block ( const PoolBlock & block )
2021-08-22 10:20:59 +00:00
{
LOGINFO ( 3 , " add_block: height = " < < block . m_sidechainHeight < <
" , id = " < < block . m_sidechainId < <
" , mainchain height = " < < block . m_txinGenHeight < <
" , verified = " < < ( block . m_verified ? 1 : 0 )
) ;
PoolBlock * new_block = new PoolBlock ( block ) ;
2022-05-12 13:19:58 +00:00
{
MutexLock lock ( m_seenWalletsLock ) ;
m_seenWallets [ new_block - > m_minerWallet . spend_public_key ( ) ] = new_block - > m_localTimestamp ;
}
2021-08-22 10:20:59 +00:00
2022-05-12 13:19:58 +00:00
WriteLock lock ( m_sidechainLock ) ;
2021-08-22 10:20:59 +00:00
auto result = m_blocksById . insert ( { new_block - > m_sidechainId , new_block } ) ;
if ( ! result . second ) {
2022-11-02 11:49:12 +00:00
const PoolBlock * old_block = result . first - > second ;
LOGWARN ( 3 , " add_block: trying to add the same block twice: "
< < " \n new block id = " < < new_block - > m_sidechainId
< < " , sidechain height = " < < new_block - > m_sidechainHeight
< < " , height = " < < new_block - > m_txinGenHeight
< < " , nonce = " < < new_block - > m_nonce
< < " , extra_nonce = " < < new_block - > m_extraNonce
< < " \n old block id = " < < old_block - > m_sidechainId
< < " , sidechain height = " < < old_block - > m_sidechainHeight
< < " , height = " < < old_block - > m_txinGenHeight
< < " , nonce = " < < old_block - > m_nonce
< < " , extra_nonce = " < < old_block - > m_extraNonce
) ;
2021-08-22 10:20:59 +00:00
delete new_block ;
2023-01-07 14:50:02 +00:00
return false ;
2021-08-22 10:20:59 +00:00
}
m_blocksByHeight [ new_block - > m_sidechainHeight ] . push_back ( new_block ) ;
update_depths ( new_block ) ;
if ( new_block - > m_verified ) {
if ( ! new_block - > m_invalid ) {
update_chain_tip ( new_block ) ;
2022-03-24 15:03:12 +00:00
// Save it for faster syncing on the next p2pool start
if ( P2PServer * server = p2pServer ( ) ) {
server - > store_in_cache ( * new_block ) ;
}
2021-08-22 10:20:59 +00:00
}
}
else {
verify_loop ( new_block ) ;
}
2023-01-07 14:50:02 +00:00
return true ;
2021-08-22 10:20:59 +00:00
}
2022-05-12 13:19:58 +00:00
PoolBlock * SideChain : : find_block ( const hash & id ) const
2021-08-22 10:20:59 +00:00
{
2022-05-12 13:19:58 +00:00
ReadLock lock ( m_sidechainLock ) ;
2022-01-29 16:00:12 +00:00
auto it = m_blocksById . find ( id ) ;
if ( it ! = m_blocksById . end ( ) ) {
return it - > second ;
}
return nullptr ;
2021-08-22 10:20:59 +00:00
}
2021-09-06 21:33:52 +00:00
void SideChain : : watch_mainchain_block ( const ChainMain & data , const hash & possible_id )
{
2022-05-12 13:19:58 +00:00
WriteLock lock ( m_sidechainLock ) ;
2021-09-06 21:33:52 +00:00
m_watchBlock = data ;
m_watchBlockSidechainId = possible_id ;
}
2023-07-07 14:42:24 +00:00
const PoolBlock * SideChain : : get_block_blob ( const hash & id , std : : vector < uint8_t > & blob ) const
2021-08-22 10:20:59 +00:00
{
2022-05-12 13:19:58 +00:00
ReadLock lock ( m_sidechainLock ) ;
2021-08-22 10:20:59 +00:00
2022-04-08 21:14:08 +00:00
const PoolBlock * block = nullptr ;
2021-08-22 10:20:59 +00:00
// Empty hash means we return current sidechain tip
2022-05-11 13:07:54 +00:00
if ( id . empty ( ) ) {
2021-08-22 10:20:59 +00:00
block = m_chainTip ;
2022-05-11 13:07:54 +00:00
// Don't return stale chain tip
if ( block & & ( block - > m_txinGenHeight + 2 < m_pool - > miner_data ( ) . height ) ) {
2023-07-07 14:42:24 +00:00
return nullptr ;
2022-05-11 13:07:54 +00:00
}
2021-08-22 10:20:59 +00:00
}
else {
auto it = m_blocksById . find ( id ) ;
if ( it ! = m_blocksById . end ( ) ) {
block = it - > second ;
}
}
if ( ! block ) {
2023-07-07 14:42:24 +00:00
return nullptr ;
2021-08-22 10:20:59 +00:00
}
2022-09-30 12:37:32 +00:00
blob = block - > serialize_mainchain_data ( ) ;
2022-10-04 13:44:57 +00:00
const std : : vector < uint8_t > sidechain_data = block - > serialize_sidechain_data ( ) ;
blob . insert ( blob . end ( ) , sidechain_data . begin ( ) , sidechain_data . end ( ) ) ;
2022-09-30 12:37:32 +00:00
2023-07-07 14:42:24 +00:00
return block ;
2021-08-22 10:20:59 +00:00
}
2022-08-15 09:16:00 +00:00
bool SideChain : : get_outputs_blob ( PoolBlock * block , uint64_t total_reward , std : : vector < uint8_t > & blob , uv_loop_t * loop ) const
2021-08-22 10:20:59 +00:00
{
2021-10-31 10:06:00 +00:00
blob . clear ( ) ;
2021-08-22 10:20:59 +00:00
2023-01-15 20:29:35 +00:00
struct Data
{
2023-08-18 19:43:04 +00:00
FORCEINLINE Data ( ) : blockMinerWallet ( nullptr ) , counter ( 0 ) { }
2023-01-15 20:29:35 +00:00
Data ( Data & & ) = delete ;
Data & operator = ( Data & & ) = delete ;
std : : vector < MinerShare > tmpShares ;
2023-08-18 19:43:04 +00:00
Wallet blockMinerWallet ;
2023-01-15 20:29:35 +00:00
hash txkeySec ;
std : : atomic < int > counter ;
} ;
std : : shared_ptr < Data > data ;
2022-11-16 13:23:13 +00:00
std : : vector < uint64_t > tmpRewards ;
{
ReadLock lock ( m_sidechainLock ) ;
2021-08-22 10:20:59 +00:00
2022-11-16 13:23:13 +00:00
auto it = m_blocksById . find ( block - > m_sidechainId ) ;
if ( it ! = m_blocksById . end ( ) ) {
PoolBlock * b = it - > second ;
const size_t n = b - > m_outputs . size ( ) ;
2021-10-31 10:06:00 +00:00
2022-11-16 13:23:13 +00:00
blob . reserve ( n * 39 + 64 ) ;
writeVarint ( n , blob ) ;
2021-10-31 10:06:00 +00:00
2022-11-16 13:23:13 +00:00
const uint8_t tx_type = b - > get_tx_type ( ) ;
2022-10-04 18:48:19 +00:00
2022-11-16 13:23:13 +00:00
for ( const PoolBlock : : TxOutput & output : b - > m_outputs ) {
writeVarint ( output . m_reward , blob ) ;
blob . emplace_back ( tx_type ) ;
blob . insert ( blob . end ( ) , output . m_ephPublicKey . h , output . m_ephPublicKey . h + HASH_SIZE ) ;
2022-04-01 14:52:23 +00:00
2022-11-16 13:23:13 +00:00
if ( tx_type = = TXOUT_TO_TAGGED_KEY ) {
blob . emplace_back ( static_cast < uint8_t > ( output . m_viewTag ) ) ;
}
2022-04-01 14:52:23 +00:00
}
2021-10-31 10:06:00 +00:00
2022-11-16 13:23:13 +00:00
block - > m_outputs = b - > m_outputs ;
return true ;
}
2022-05-12 13:19:58 +00:00
2023-01-15 20:29:35 +00:00
data = std : : make_shared < Data > ( ) ;
2023-08-18 19:43:04 +00:00
data - > blockMinerWallet = block - > m_minerWallet ;
2023-01-15 20:29:35 +00:00
data - > txkeySec = block - > m_txkeySec ;
if ( ! get_shares ( block , data - > tmpShares ) | | ! split_reward ( total_reward , data - > tmpShares , tmpRewards ) | | ( tmpRewards . size ( ) ! = data - > tmpShares . size ( ) ) ) {
2022-11-16 13:23:13 +00:00
return false ;
}
2021-08-22 10:20:59 +00:00
}
2023-01-15 20:29:35 +00:00
const size_t n = data - > tmpShares . size ( ) ;
data - > counter = static_cast < int > ( n ) - 1 ;
2021-08-22 10:20:59 +00:00
2022-08-15 09:16:00 +00:00
// Helper jobs call get_eph_public_key with indices in descending order
// Current thread will process indices in ascending order so when they meet, everything will be cached
if ( loop ) {
2023-08-18 19:43:04 +00:00
// Avoid accessing block->m_minerWallet from other threads in "parallel_run" below
for ( MinerShare & share : data - > tmpShares ) {
if ( share . m_wallet = = & block - > m_minerWallet ) {
share . m_wallet = & data - > blockMinerWallet ;
break ;
}
}
2023-01-16 06:18:08 +00:00
parallel_run ( loop , [ data ] ( ) {
Data * d = data . get ( ) ;
hash eph_public_key ;
int index ;
while ( ( index = d - > counter . fetch_sub ( 1 ) ) > = 0 ) {
uint8_t view_tag ;
if ( ! d - > tmpShares [ index ] . m_wallet - > get_eph_public_key ( d - > txkeySec , static_cast < size_t > ( index ) , eph_public_key , view_tag ) ) {
LOGWARN ( 6 , " get_eph_public_key failed at index " < < index ) ;
}
2022-08-15 09:16:00 +00:00
}
2023-01-16 06:18:08 +00:00
} ) ;
2022-08-15 09:16:00 +00:00
}
2022-04-01 14:52:23 +00:00
blob . reserve ( n * 39 + 64 ) ;
2021-08-22 10:20:59 +00:00
writeVarint ( n , blob ) ;
block - > m_outputs . clear ( ) ;
block - > m_outputs . reserve ( n ) ;
2022-04-01 14:52:23 +00:00
const uint8_t tx_type = block - > get_tx_type ( ) ;
2021-08-22 10:20:59 +00:00
hash eph_public_key ;
for ( size_t i = 0 ; i < n ; + + i ) {
2022-08-15 09:16:00 +00:00
// stop helper jobs when they meet with current thread
2023-01-15 20:29:35 +00:00
const int c = data - > counter . load ( ) ;
2022-08-15 09:16:00 +00:00
if ( ( c > = 0 ) & & ( static_cast < int > ( i ) > = c ) ) {
// this will cause all helper jobs to finish immediately
2023-01-15 20:29:35 +00:00
data - > counter = - 1 ;
2022-08-15 09:16:00 +00:00
}
2022-05-12 13:19:58 +00:00
writeVarint ( tmpRewards [ i ] , blob ) ;
2021-08-22 10:20:59 +00:00
2022-04-01 14:52:23 +00:00
blob . emplace_back ( tx_type ) ;
2021-08-22 10:20:59 +00:00
2022-04-01 14:52:23 +00:00
uint8_t view_tag ;
2023-01-15 20:29:35 +00:00
if ( ! data - > tmpShares [ i ] . m_wallet - > get_eph_public_key ( data - > txkeySec , i , eph_public_key , view_tag ) ) {
2021-08-31 15:23:20 +00:00
LOGWARN ( 6 , " get_eph_public_key failed at index " < < i ) ;
}
2021-08-22 10:20:59 +00:00
blob . insert ( blob . end ( ) , eph_public_key . h , eph_public_key . h + HASH_SIZE ) ;
2022-04-01 14:52:23 +00:00
if ( tx_type = = TXOUT_TO_TAGGED_KEY ) {
blob . emplace_back ( view_tag ) ;
}
2022-10-04 18:48:19 +00:00
block - > m_outputs . emplace_back ( tmpRewards [ i ] , eph_public_key , view_tag ) ;
2021-08-22 10:20:59 +00:00
}
2022-10-04 18:48:19 +00:00
block - > m_outputs . shrink_to_fit ( ) ;
2021-08-22 10:20:59 +00:00
return true ;
}
2022-08-23 12:13:09 +00:00
void SideChain : : print_status ( bool obtain_sidechain_lock ) const
2021-08-22 10:20:59 +00:00
{
2023-01-13 15:56:31 +00:00
unordered_set < hash > blocks_in_window ;
2021-08-25 19:00:06 +00:00
blocks_in_window . reserve ( m_chainWindowSize * 9 / 8 ) ;
2022-05-09 14:07:49 +00:00
const difficulty_type diff = difficulty ( ) ;
2022-08-23 12:13:09 +00:00
if ( obtain_sidechain_lock ) uv_rwlock_rdlock ( & m_sidechainLock ) ;
ON_SCOPE_LEAVE ( [ this , obtain_sidechain_lock ] ( ) { if ( obtain_sidechain_lock ) uv_rwlock_rdunlock ( & m_sidechainLock ) ; } ) ;
2021-08-25 19:00:06 +00:00
2023-01-13 15:56:31 +00:00
const uint64_t pool_hashrate = ( diff / m_targetBlockTime ) . lo ;
2021-08-22 10:20:59 +00:00
2023-01-13 15:56:31 +00:00
const difficulty_type network_diff = m_pool - > miner_data ( ) . difficulty ;
const uint64_t network_hashrate = ( network_diff / MONERO_BLOCK_TIME ) . lo ;
2021-08-22 10:20:59 +00:00
2022-04-08 21:14:08 +00:00
const PoolBlock * tip = m_chainTip ;
2023-01-13 15:56:31 +00:00
std : : vector < MinerShare > shares ;
2023-03-28 11:28:34 +00:00
uint64_t bh = 0 ;
2023-01-15 21:20:06 +00:00
if ( tip ) {
2023-03-28 11:28:34 +00:00
get_shares ( tip , shares , & bh , true ) ;
2023-01-15 21:20:06 +00:00
}
2023-01-13 15:56:31 +00:00
2023-03-28 11:28:34 +00:00
const uint64_t window_size = ( tip & & bh ) ? ( tip - > m_sidechainHeight - bh + 1U ) : m_chainWindowSize ;
2023-01-13 15:56:31 +00:00
2021-08-22 10:20:59 +00:00
uint64_t block_depth = 0 ;
2022-04-08 21:14:08 +00:00
const PoolBlock * cur = tip ;
const uint64_t tip_height = tip ? tip - > m_sidechainHeight : 0 ;
2021-08-22 10:20:59 +00:00
2023-01-13 15:56:31 +00:00
uint64_t total_blocks_in_window = 0 ;
uint64_t total_uncles_in_window = 0 ;
// each dot corresponds to window_size / 30 shares, with current values, 2160 / 30 = 72
2023-01-21 22:09:57 +00:00
constexpr size_t N = 30 ;
std : : array < uint64_t , N > our_blocks_in_window { } ;
std : : array < uint64_t , N > our_uncles_in_window { } ;
2021-08-22 10:20:59 +00:00
2023-01-13 15:56:31 +00:00
const Wallet & w = m_pool - > params ( ) . m_wallet ;
2021-08-22 10:20:59 +00:00
while ( cur ) {
2023-01-13 15:56:31 +00:00
blocks_in_window . emplace ( cur - > m_sidechainId ) ;
2021-08-22 10:20:59 +00:00
+ + total_blocks_in_window ;
2023-01-21 22:09:57 +00:00
// "block_depth <= window_size - 1" here (see the check below), so window_index will be <= N - 1
// This will map the range [0, window_size - 1] into [0, N - 1]
2023-01-26 20:42:15 +00:00
const size_t window_index = ( window_size > 1 ) ? ( block_depth * ( N - 1 ) / ( window_size - 1 ) ) : 0 ;
2023-01-21 22:09:57 +00:00
2023-01-13 15:56:31 +00:00
if ( cur - > m_minerWallet = = w ) {
2023-01-21 22:09:57 +00:00
+ + our_blocks_in_window [ window_index ] ;
2021-08-22 10:20:59 +00:00
}
+ + block_depth ;
2023-01-13 15:56:31 +00:00
if ( block_depth > = window_size ) {
2021-08-22 10:20:59 +00:00
break ;
}
for ( const hash & uncle_id : cur - > m_uncles ) {
2023-01-13 15:56:31 +00:00
blocks_in_window . emplace ( uncle_id ) ;
2021-08-22 10:20:59 +00:00
auto it = m_blocksById . find ( uncle_id ) ;
if ( it ! = m_blocksById . end ( ) ) {
2023-05-23 18:11:00 +00:00
const PoolBlock * uncle = it - > second ;
2023-01-13 15:56:31 +00:00
if ( tip_height - uncle - > m_sidechainHeight < window_size ) {
2021-08-22 10:20:59 +00:00
+ + total_uncles_in_window ;
2023-01-13 15:56:31 +00:00
if ( uncle - > m_minerWallet = = w ) {
2023-01-21 22:09:57 +00:00
+ + our_uncles_in_window [ window_index ] ;
2021-08-22 10:20:59 +00:00
}
}
}
}
cur = get_parent ( cur ) ;
}
uint64_t total_orphans = 0 ;
uint64_t our_orphans = 0 ;
2022-04-08 21:14:08 +00:00
if ( tip ) {
2023-01-13 15:56:31 +00:00
for ( uint64_t i = 0 ; ( i < window_size ) & & ( i < = tip_height ) ; + + i ) {
2022-05-12 13:19:58 +00:00
auto it = m_blocksByHeight . find ( tip_height - i ) ;
if ( it = = m_blocksByHeight . end ( ) ) {
continue ;
}
for ( const PoolBlock * block : it - > second ) {
2023-01-13 15:56:31 +00:00
if ( blocks_in_window . find ( block - > m_sidechainId ) = = blocks_in_window . end ( ) ) {
2021-08-22 10:20:59 +00:00
LOGINFO ( 4 , " orphan block at height " < < log : : Gray ( ) < < block - > m_sidechainHeight < < log : : NoColor ( ) < < " : " < < log : : Gray ( ) < < block - > m_sidechainId ) ;
+ + total_orphans ;
2023-01-13 15:56:31 +00:00
if ( block - > m_minerWallet = = w ) {
2021-08-22 10:20:59 +00:00
+ + our_orphans ;
}
}
}
}
2023-01-13 15:56:31 +00:00
}
2021-08-25 16:52:09 +00:00
2023-01-13 15:56:31 +00:00
difficulty_type your_shares_weight , pplns_weight ;
for ( const MinerShare & s : shares ) {
if ( * s . m_wallet = = w ) {
your_shares_weight = s . m_weight ;
2021-08-25 16:52:09 +00:00
}
2023-01-13 15:56:31 +00:00
pplns_weight + = s . m_weight ;
2021-08-25 16:52:09 +00:00
}
2021-08-25 09:13:38 +00:00
2023-01-13 15:56:31 +00:00
if ( pplns_weight = = 0 ) {
pplns_weight = m_minDifficulty ;
}
const uint64_t total_reward = m_pool - > block_template ( ) . get_reward ( ) ;
const uint64_t your_reward = ( ( your_shares_weight * total_reward ) / pplns_weight ) . lo ;
const uint64_t hashrate_est = ( ( your_shares_weight * pool_hashrate ) / pplns_weight ) . lo ;
2021-09-07 07:53:38 +00:00
const double block_share = total_reward ? ( ( static_cast < double > ( your_reward ) * 100.0 ) / static_cast < double > ( total_reward ) ) : 0.0 ;
2021-08-25 09:13:38 +00:00
2023-01-13 15:56:31 +00:00
const uint64_t our_blocks_in_window_total = std : : accumulate ( our_blocks_in_window . begin ( ) , our_blocks_in_window . end ( ) , 0ULL ) ;
const uint64_t our_uncles_in_window_total = std : : accumulate ( our_uncles_in_window . begin ( ) , our_uncles_in_window . end ( ) , 0ULL ) ;
2021-09-10 13:00:58 +00:00
std : : string our_blocks_in_window_chart ;
2022-03-23 13:17:40 +00:00
if ( our_blocks_in_window_total ) {
our_blocks_in_window_chart . reserve ( our_blocks_in_window . size ( ) + 32 ) ;
our_blocks_in_window_chart = " \n Your shares position = [ " ;
2023-01-13 15:56:31 +00:00
for ( uint64_t p : our_blocks_in_window ) {
2022-03-23 13:17:40 +00:00
our_blocks_in_window_chart + = ( p ? ( ( p > 9 ) ? ' + ' : static_cast < char > ( ' 0 ' + p ) ) : ' . ' ) ;
}
our_blocks_in_window_chart + = ' ] ' ;
2021-09-10 13:00:58 +00:00
}
std : : string our_uncles_in_window_chart ;
2022-03-23 13:17:40 +00:00
if ( our_uncles_in_window_total ) {
our_uncles_in_window_chart . reserve ( our_uncles_in_window . size ( ) + 32 ) ;
our_uncles_in_window_chart = " \n Your uncles position = [ " ;
2023-01-13 15:56:31 +00:00
for ( uint64_t p : our_uncles_in_window ) {
2022-03-23 13:17:40 +00:00
our_uncles_in_window_chart + = ( p ? ( ( p > 9 ) ? ' + ' : static_cast < char > ( ' 0 ' + p ) ) : ' . ' ) ;
}
our_uncles_in_window_chart + = ' ] ' ;
2021-09-10 13:00:58 +00:00
}
2021-08-22 10:20:59 +00:00
LOGINFO ( 0 , " status " < <
2023-06-16 13:51:33 +00:00
" \n Monero node = " < < m_pool - > current_host ( ) . m_displayName < <
2021-08-30 14:51:23 +00:00
" \n Main chain height = " < < m_pool - > block_template ( ) . height ( ) < <
" \n Main chain hashrate = " < < log : : Hashrate ( network_hashrate ) < <
2022-03-23 13:17:40 +00:00
" \n Side chain ID = " < < ( is_default ( ) ? " default " : ( is_mini ( ) ? " mini " : m_consensusIdDisplayStr . c_str ( ) ) ) < <
2021-08-30 14:51:23 +00:00
" \n Side chain height = " < < tip_height + 1 < <
" \n Side chain hashrate = " < < log : : Hashrate ( pool_hashrate ) < <
2021-09-05 13:56:50 +00:00
( hashrate_est ? " \n Your hashrate (pool-side) = " : " " ) < < ( hashrate_est ? log : : Hashrate ( hashrate_est ) : log : : Hashrate ( ) ) < <
2021-09-10 13:00:58 +00:00
" \n PPLNS window = " < < total_blocks_in_window < < " blocks (+ " < < total_uncles_in_window < < " uncles, " < < total_orphans < < " orphans) " < <
2023-01-13 15:56:31 +00:00
" \n PPLNS window duration = " < < log : : Duration ( ( pplns_weight / pool_hashrate ) . lo ) < <
" \n Your wallet address = " < < w < <
2022-03-23 13:17:40 +00:00
" \n Your shares = " < < our_blocks_in_window_total < < " blocks (+ " < < our_uncles_in_window_total < < " uncles, " < < our_orphans < < " orphans) "
< < our_blocks_in_window_chart < < our_uncles_in_window_chart < <
2021-09-07 07:53:38 +00:00
" \n Block reward share = " < < block_share < < " % ( " < < log : : XMRAmount ( your_reward ) < < ' ) '
2021-08-22 10:20:59 +00:00
) ;
}
2022-05-28 20:27:46 +00:00
double SideChain : : get_reward_share ( const Wallet & w ) const
{
uint64_t reward = 0 ;
uint64_t total_reward = 0 ;
{
ReadLock lock ( m_sidechainLock ) ;
const PoolBlock * tip = m_chainTip ;
if ( tip ) {
2022-10-04 18:48:19 +00:00
const uint8_t tx_type = tip - > get_tx_type ( ) ;
2022-05-28 20:27:46 +00:00
hash eph_public_key ;
for ( size_t i = 0 , n = tip - > m_outputs . size ( ) ; i < n ; + + i ) {
const PoolBlock : : TxOutput & out = tip - > m_outputs [ i ] ;
if ( ! reward ) {
2022-10-04 18:48:19 +00:00
if ( tx_type = = TXOUT_TO_TAGGED_KEY ) {
2022-11-03 10:38:43 +00:00
uint8_t view_tag ;
const uint8_t expected_view_tag = out . m_viewTag ;
if ( w . get_eph_public_key ( tip - > m_txkeySec , i , eph_public_key , view_tag , & expected_view_tag ) & & ( out . m_ephPublicKey = = eph_public_key ) ) {
2022-05-28 20:27:46 +00:00
reward = out . m_reward ;
}
}
else {
uint8_t view_tag ;
if ( w . get_eph_public_key ( tip - > m_txkeySec , i , eph_public_key , view_tag ) & & ( out . m_ephPublicKey = = eph_public_key ) ) {
reward = out . m_reward ;
}
}
}
total_reward + = out . m_reward ;
}
}
}
return total_reward ? ( static_cast < double > ( reward ) / static_cast < double > ( total_reward ) ) : 0.0 ;
}
2023-01-16 13:18:34 +00:00
uint64_t SideChain : : network_major_version ( uint64_t height )
2022-12-19 08:58:43 +00:00
{
const hardfork_t * hard_forks ;
size_t num_hard_forks ;
2023-01-16 13:18:34 +00:00
switch ( s_networkType )
2022-12-19 08:58:43 +00:00
{
case NetworkType : : Mainnet :
default :
hard_forks = mainnet_hard_forks ;
num_hard_forks = num_mainnet_hard_forks ;
break ;
case NetworkType : : Testnet :
hard_forks = testnet_hard_forks ;
num_hard_forks = num_testnet_hard_forks ;
break ;
case NetworkType : : Stagenet :
hard_forks = stagenet_hard_forks ;
num_hard_forks = num_stagenet_hard_forks ;
break ;
}
uint64_t result = 1 ;
for ( size_t i = 1 ; ( i < num_hard_forks ) & & ( height > = hard_forks [ i ] . height ) ; + + i ) {
result = hard_forks [ i ] . version ;
}
return result ;
}
2021-09-01 14:26:56 +00:00
difficulty_type SideChain : : total_hashes ( ) const
{
2022-04-08 21:14:08 +00:00
const PoolBlock * tip = m_chainTip ;
return tip ? tip - > m_cumulativeDifficulty : difficulty_type ( ) ;
2021-09-01 14:26:56 +00:00
}
2021-09-01 18:48:03 +00:00
uint64_t SideChain : : miner_count ( )
2021-09-01 14:26:56 +00:00
{
2022-03-23 10:30:38 +00:00
const uint64_t cur_time = seconds_since_epoch ( ) ;
2021-09-01 18:48:03 +00:00
2022-05-12 13:19:58 +00:00
MutexLock lock ( m_seenWalletsLock ) ;
2021-09-01 18:48:03 +00:00
2022-05-12 20:18:08 +00:00
// Every 5 minutes, delete wallets that weren't seen for more than 72 hours
if ( m_seenWalletsLastPruneTime + 5 * 60 < = cur_time ) {
for ( auto it = m_seenWallets . begin ( ) ; it ! = m_seenWallets . end ( ) ; ) {
if ( it - > second + 72 * 60 * 60 < cur_time ) {
it = m_seenWallets . erase ( it ) ;
}
else {
+ + it ;
}
2021-09-01 18:48:03 +00:00
}
2022-05-12 20:18:08 +00:00
m_seenWalletsLastPruneTime = cur_time ;
2021-09-01 18:48:03 +00:00
}
return m_seenWallets . size ( ) ;
2021-09-01 14:26:56 +00:00
}
2022-03-23 10:30:38 +00:00
uint64_t SideChain : : last_updated ( ) const
2021-09-13 12:26:52 +00:00
{
2022-04-08 21:14:08 +00:00
const PoolBlock * tip = m_chainTip ;
return tip ? tip - > m_localTimestamp : 0 ;
2021-09-13 12:26:52 +00:00
}
2021-09-07 19:30:52 +00:00
bool SideChain : : is_default ( ) const
{
return ( memcmp ( m_consensusId . data ( ) , default_consensus_id , HASH_SIZE ) = = 0 ) ;
}
2021-12-30 10:10:18 +00:00
bool SideChain : : is_mini ( ) const
{
return ( memcmp ( m_consensusId . data ( ) , mini_consensus_id , HASH_SIZE ) = = 0 ) ;
}
2023-03-28 11:28:34 +00:00
uint64_t SideChain : : bottom_height ( const PoolBlock * tip ) const
{
if ( ! tip ) {
return 0 ;
}
uint64_t bottom_height ;
std : : vector < MinerShare > shares ;
2023-04-24 18:23:45 +00:00
ReadLock lock ( m_sidechainLock ) ;
2023-03-28 11:28:34 +00:00
if ( ! get_shares ( tip , shares , & bottom_height , true ) ) {
return 0 ;
}
return bottom_height ;
}
2021-08-22 10:20:59 +00:00
bool SideChain : : split_reward ( uint64_t reward , const std : : vector < MinerShare > & shares , std : : vector < uint64_t > & rewards )
{
const size_t num_shares = shares . size ( ) ;
2022-11-15 21:20:54 +00:00
const difficulty_type total_weight = std : : accumulate ( shares . begin ( ) , shares . end ( ) , difficulty_type ( ) , [ ] ( const difficulty_type & a , const MinerShare & b ) { return a + b . m_weight ; } ) ;
2021-08-22 10:20:59 +00:00
2022-11-15 21:20:54 +00:00
if ( total_weight . empty ( ) ) {
2021-09-06 07:17:39 +00:00
LOGERR ( 1 , " total_weight is 0. Check the code! " ) ;
return false ;
}
2021-08-22 10:20:59 +00:00
rewards . clear ( ) ;
rewards . reserve ( num_shares ) ;
// Each miner gets a proportional fraction of the block reward
2022-11-15 21:20:54 +00:00
difficulty_type w ;
2021-08-22 10:20:59 +00:00
uint64_t reward_given = 0 ;
for ( uint64_t i = 0 ; i < num_shares ; + + i ) {
w + = shares [ i ] . m_weight ;
2022-11-15 21:20:54 +00:00
const difficulty_type next_value = w * reward / total_weight ;
rewards . emplace_back ( next_value . lo - reward_given ) ;
reward_given = next_value . lo ;
2021-08-22 10:20:59 +00:00
}
// Double check that we gave out the exact amount
if ( std : : accumulate ( rewards . begin ( ) , rewards . end ( ) , 0ULL ) ! = reward ) {
LOGERR ( 1 , " miners got incorrect reward. This should never happen because math says so. Check the code! " ) ;
return false ;
}
return true ;
}
2022-07-10 08:24:03 +00:00
bool SideChain : : get_difficulty ( const PoolBlock * tip , std : : vector < DifficultyData > & difficultyData , difficulty_type & curDifficulty ) const
2021-08-22 10:20:59 +00:00
{
difficultyData . clear ( ) ;
2022-07-10 08:24:03 +00:00
const PoolBlock * cur = tip ;
2021-08-22 10:20:59 +00:00
uint64_t oldest_timestamp = std : : numeric_limits < uint64_t > : : max ( ) ;
uint64_t block_depth = 0 ;
do {
oldest_timestamp = std : : min ( oldest_timestamp , cur - > m_timestamp ) ;
difficultyData . emplace_back ( cur - > m_timestamp , cur - > m_cumulativeDifficulty ) ;
for ( const hash & uncle_id : cur - > m_uncles ) {
auto it = m_blocksById . find ( uncle_id ) ;
if ( it = = m_blocksById . end ( ) ) {
2021-09-18 08:03:06 +00:00
LOGWARN ( 3 , " get_difficulty: can't find uncle block at height = " < < cur - > m_sidechainHeight < < " , id = " < < uncle_id ) ;
LOGWARN ( 3 , " get_difficulty: can't calculate diff for block at height = " < < tip - > m_sidechainHeight < < " , id = " < < tip - > m_sidechainId < < " , mainchain height = " < < tip - > m_txinGenHeight ) ;
2021-08-22 10:20:59 +00:00
return false ;
}
const PoolBlock * uncle = it - > second ;
if ( tip - > m_sidechainHeight - uncle - > m_sidechainHeight < m_chainWindowSize ) {
oldest_timestamp = std : : min ( oldest_timestamp , uncle - > m_timestamp ) ;
difficultyData . emplace_back ( uncle - > m_timestamp , uncle - > m_cumulativeDifficulty ) ;
}
}
+ + block_depth ;
if ( block_depth > = m_chainWindowSize ) {
break ;
}
// Reached the genesis block so we're done
if ( cur - > m_sidechainHeight = = 0 ) {
break ;
}
auto it = m_blocksById . find ( cur - > m_parent ) ;
if ( it = = m_blocksById . end ( ) ) {
2021-09-18 08:03:06 +00:00
LOGWARN ( 3 , " get_difficulty: can't find parent block at height = " < < cur - > m_sidechainHeight - 1 < < " , id = " < < cur - > m_parent ) ;
LOGWARN ( 3 , " get_difficulty: can't calculate diff for block at height = " < < tip - > m_sidechainHeight < < " , id = " < < tip - > m_sidechainId < < " , mainchain height = " < < tip - > m_txinGenHeight ) ;
2021-08-22 10:20:59 +00:00
return false ;
}
cur = it - > second ;
} while ( true ) ;
// Discard 10% oldest and 10% newest (by timestamp) blocks
std : : vector < uint32_t > tmpTimestamps ;
tmpTimestamps . reserve ( difficultyData . size ( ) ) ;
std : : transform ( difficultyData . begin ( ) , difficultyData . end ( ) , std : : back_inserter ( tmpTimestamps ) ,
[ oldest_timestamp ] ( const DifficultyData & d )
{
return static_cast < uint32_t > ( d . m_timestamp - oldest_timestamp ) ;
} ) ;
const uint64_t cut_size = ( difficultyData . size ( ) + 9 ) / 10 ;
const uint64_t index1 = cut_size - 1 ;
const uint64_t index2 = difficultyData . size ( ) - cut_size ;
std : : nth_element ( tmpTimestamps . begin ( ) , tmpTimestamps . begin ( ) + index1 , tmpTimestamps . end ( ) ) ;
const uint64_t timestamp1 = oldest_timestamp + tmpTimestamps [ index1 ] ;
std : : nth_element ( tmpTimestamps . begin ( ) , tmpTimestamps . begin ( ) + index2 , tmpTimestamps . end ( ) ) ;
const uint64_t timestamp2 = oldest_timestamp + tmpTimestamps [ index2 ] ;
2023-03-24 12:23:20 +00:00
// Make a reasonable assumption that each block has higher timestamp, so delta_t can't be less than delta_index
// Because if it is, someone is trying to mess with timestamps
// In reality, delta_t ~ delta_index*10 (sidechain block time)
const uint64_t delta_index = ( index2 > index1 ) ? ( index2 - index1 ) : 1U ;
const uint64_t delta_t = ( timestamp2 > timestamp1 + delta_index ) ? ( timestamp2 - timestamp1 ) : delta_index ;
2021-08-22 10:20:59 +00:00
difficulty_type diff1 { std : : numeric_limits < uint64_t > : : max ( ) , std : : numeric_limits < uint64_t > : : max ( ) } ;
difficulty_type diff2 { 0 , 0 } ;
for ( const DifficultyData & d : difficultyData ) {
if ( timestamp1 < = d . m_timestamp & & d . m_timestamp < = timestamp2 ) {
if ( d . m_cumulativeDifficulty < diff1 ) {
diff1 = d . m_cumulativeDifficulty ;
}
if ( diff2 < d . m_cumulativeDifficulty ) {
diff2 = d . m_cumulativeDifficulty ;
}
}
}
2022-11-15 21:20:54 +00:00
curDifficulty = ( diff2 - diff1 ) * m_targetBlockTime / delta_t ;
2021-08-22 10:20:59 +00:00
if ( curDifficulty < m_minDifficulty ) {
curDifficulty = m_minDifficulty ;
}
return true ;
}
void SideChain : : verify_loop ( PoolBlock * block )
{
// PoW is already checked at this point
std : : vector < PoolBlock * > blocks_to_verify ( 1 , block ) ;
PoolBlock * highest_block = nullptr ;
while ( ! blocks_to_verify . empty ( ) ) {
block = blocks_to_verify . back ( ) ;
blocks_to_verify . pop_back ( ) ;
if ( block - > m_verified ) {
continue ;
}
verify ( block ) ;
if ( ! block - > m_verified ) {
2021-08-25 13:01:05 +00:00
LOGINFO ( 6 , " can't verify block at height = " < < block - > m_sidechainHeight < <
2021-08-22 10:20:59 +00:00
" , id = " < < block - > m_sidechainId < <
2021-08-25 13:01:05 +00:00
" , mainchain height = " < < block - > m_txinGenHeight < < " : parent or uncle blocks are not available) " ) ;
2021-08-22 10:20:59 +00:00
continue ;
}
if ( block - > m_invalid ) {
LOGWARN ( 3 , " block at height = " < < block - > m_sidechainHeight < <
" , id = " < < block - > m_sidechainId < <
2022-11-10 07:22:31 +00:00
" , mainchain height = " < < block - > m_txinGenHeight < < " , mined by " < < block - > m_minerWallet < < " is invalid " ) ;
2021-08-22 10:20:59 +00:00
}
else {
LOGINFO ( 3 , " verified block at height = " < < block - > m_sidechainHeight < <
" , depth = " < < block - > m_depth < <
" , id = " < < block - > m_sidechainId < <
" , mainchain height = " < < block - > m_txinGenHeight ) ;
// This block is now verified
2022-02-17 10:19:11 +00:00
bool is_alternative ;
if ( is_longer_chain ( highest_block , block , is_alternative ) ) {
2021-08-22 10:20:59 +00:00
highest_block = block ;
}
else if ( highest_block & & ( highest_block - > m_sidechainHeight > block - > m_sidechainHeight ) ) {
LOGINFO ( 4 , " block " < < highest_block - > m_sidechainId < <
" , height = " < < highest_block - > m_sidechainHeight < <
" is not a longer chain than " < < block - > m_sidechainId < <
" , height " < < block - > m_sidechainHeight ) ;
}
2022-03-24 15:03:12 +00:00
P2PServer * server = p2pServer ( ) ;
2021-08-22 10:20:59 +00:00
// If it came through a broadcast, send it to our peers
if ( block - > m_wantBroadcast & & ! block - > m_broadcasted ) {
block - > m_broadcasted = true ;
2022-03-24 15:03:12 +00:00
if ( server & & ( block - > m_depth < UNCLE_BLOCK_DEPTH ) ) {
2022-11-10 07:22:31 +00:00
if ( m_pool & & ( block - > m_minerWallet = = m_pool - > params ( ) . m_wallet ) ) {
LOGINFO ( 0 , log : : Green ( ) < < " SHARE ADDED: height = " < < block - > m_sidechainHeight < < " , id = " < < block - > m_sidechainId < < " , mainchain height = " < < block - > m_txinGenHeight ) ;
}
2022-11-09 10:17:35 +00:00
server - > broadcast ( * block , get_parent ( block ) ) ;
2021-08-23 22:27:48 +00:00
}
2021-08-22 10:20:59 +00:00
}
2021-08-24 11:46:38 +00:00
// Save it for faster syncing on the next p2pool start
2022-03-24 15:03:12 +00:00
if ( server ) {
server - > store_in_cache ( * block ) ;
2021-08-26 22:41:09 +00:00
}
2021-08-24 11:46:38 +00:00
2021-08-22 10:20:59 +00:00
// Try to verify blocks on top of this one
for ( size_t i = 1 ; i < = UNCLE_BLOCK_DEPTH ; + + i ) {
auto it = m_blocksByHeight . find ( block - > m_sidechainHeight + i ) ;
if ( it = = m_blocksByHeight . end ( ) ) {
continue ;
}
const std : : vector < PoolBlock * > & next_blocks = it - > second ;
if ( ! next_blocks . empty ( ) ) {
blocks_to_verify . insert ( blocks_to_verify . end ( ) , next_blocks . begin ( ) , next_blocks . end ( ) ) ;
}
}
}
}
if ( highest_block ) {
update_chain_tip ( highest_block ) ;
}
return ;
}
void SideChain : : verify ( PoolBlock * block )
{
// Genesis block
if ( block - > m_sidechainHeight = = 0 ) {
if ( ! block - > m_parent . empty ( ) | |
! block - > m_uncles . empty ( ) | |
( block - > m_difficulty ! = m_minDifficulty ) | |
2023-01-09 14:55:52 +00:00
( block - > m_cumulativeDifficulty ! = m_minDifficulty ) | |
2023-03-19 15:47:12 +00:00
( block - > m_txkeySecSeed ! = m_consensusHash ) )
2021-08-22 10:20:59 +00:00
{
block - > m_invalid = true ;
}
block - > m_verified = true ;
return ;
}
// Deep block
//
// Blocks in PPLNS window (m_chainWindowSize) require up to m_chainWindowSize earlier blocks to verify
2023-04-22 16:55:11 +00:00
// If a block is deeper than (m_chainWindowSize - 1) * 2 + UNCLE_BLOCK_DEPTH it can't influence blocks in PPLNS window
2021-08-22 10:20:59 +00:00
// Also, having so many blocks on top of this one means it was verified by the network at some point
// We skip checks in this case to make pruning possible
2023-04-22 16:55:11 +00:00
if ( block - > m_depth > ( m_chainWindowSize - 1 ) * 2 + UNCLE_BLOCK_DEPTH ) {
2021-08-22 10:20:59 +00:00
LOGINFO ( 4 , " block " < < block - > m_sidechainId < < " skipped verification " ) ;
block - > m_verified = true ;
block - > m_invalid = false ;
return ;
}
// Regular block
// Must have a parent
if ( block - > m_parent . empty ( ) ) {
block - > m_verified = true ;
block - > m_invalid = true ;
return ;
}
// Check parent
auto it = m_blocksById . find ( block - > m_parent ) ;
if ( ( it = = m_blocksById . end ( ) ) | | ! it - > second - > m_verified ) {
block - > m_verified = false ;
return ;
}
// If it's invalid then this block is also invalid
PoolBlock * parent = it - > second ;
if ( parent - > m_invalid ) {
block - > m_verified = true ;
block - > m_invalid = true ;
return ;
}
2023-03-19 15:47:12 +00:00
// Check m_txkeySecSeed
const hash h = ( block - > m_prevId = = parent - > m_prevId ) ? parent - > m_txkeySecSeed : parent - > calculate_tx_key_seed ( ) ;
if ( block - > m_txkeySecSeed ! = h ) {
LOGWARN ( 3 , " block " < < block - > m_sidechainId < < " has invalid tx key seed: expected " < < h < < " , got " < < block - > m_txkeySecSeed ) ;
block - > m_verified = true ;
block - > m_invalid = true ;
return ;
2023-01-09 14:55:52 +00:00
}
2021-08-22 10:20:59 +00:00
const uint64_t expectedHeight = parent - > m_sidechainHeight + 1 ;
if ( block - > m_sidechainHeight ! = expectedHeight ) {
LOGWARN ( 3 , " block at height = " < < block - > m_sidechainHeight < <
" , id = " < < block - > m_sidechainId < <
" , mainchain height = " < < block - > m_txinGenHeight < <
" has wrong height: expected " < < expectedHeight ) ;
2023-01-09 14:55:52 +00:00
block - > m_verified = true ;
2021-08-22 10:20:59 +00:00
block - > m_invalid = true ;
return ;
}
// Uncle hashes must be sorted in the ascending order to prevent cheating when the same hash is repeated multiple times
for ( size_t i = 1 , n = block - > m_uncles . size ( ) ; i < n ; + + i ) {
if ( ! ( block - > m_uncles [ i - 1 ] < block - > m_uncles [ i ] ) ) {
LOGWARN ( 3 , " block at height = " < < block - > m_sidechainHeight < <
" , id = " < < block - > m_sidechainId < <
" , mainchain height = " < < block - > m_txinGenHeight < < " has invalid uncle order " ) ;
block - > m_verified = true ;
block - > m_invalid = true ;
return ;
}
}
difficulty_type expectedCumulativeDifficulty = parent - > m_cumulativeDifficulty + block - > m_difficulty ;
// Check uncles
// First get a list of already mined blocks at possible uncle heights
std : : vector < hash > mined_blocks ;
if ( ! block - > m_uncles . empty ( ) ) {
mined_blocks . reserve ( UNCLE_BLOCK_DEPTH * 2 + 1 ) ;
PoolBlock * tmp = parent ;
for ( uint64_t i = 0 , n = std : : min < uint64_t > ( UNCLE_BLOCK_DEPTH , block - > m_sidechainHeight + 1 ) ; tmp & & ( i < n ) ; + + i ) {
mined_blocks . push_back ( tmp - > m_sidechainId ) ;
mined_blocks . insert ( mined_blocks . end ( ) , tmp - > m_uncles . begin ( ) , tmp - > m_uncles . end ( ) ) ;
tmp = get_parent ( tmp ) ;
}
}
for ( const hash & uncle_id : block - > m_uncles ) {
// Empty hash is only used in the genesis block and only for its parent
// Uncles can't be empty
if ( uncle_id . empty ( ) ) {
LOGWARN ( 3 , " block at height = " < < block - > m_sidechainHeight < <
" , id = " < < block - > m_sidechainId < <
" , mainchain height = " < < block - > m_txinGenHeight < < " has empty uncle hash " ) ;
block - > m_verified = true ;
block - > m_invalid = true ;
return ;
}
// Can't mine the same uncle block twice
if ( std : : find ( mined_blocks . begin ( ) , mined_blocks . end ( ) , uncle_id ) ! = mined_blocks . end ( ) ) {
LOGWARN ( 3 , " block at height = " < < block - > m_sidechainHeight < <
" , id = " < < block - > m_sidechainId < <
" , mainchain height = " < < block - > m_txinGenHeight < < " has an uncle ( " < < uncle_id < < " ) that's already been mined " ) ;
block - > m_verified = true ;
block - > m_invalid = true ;
return ;
}
it = m_blocksById . find ( uncle_id ) ;
if ( ( it = = m_blocksById . end ( ) ) | | ! it - > second - > m_verified ) {
block - > m_verified = false ;
return ;
}
PoolBlock * uncle = it - > second ;
// If it's invalid then this block is also invalid
if ( uncle - > m_invalid ) {
block - > m_verified = true ;
block - > m_invalid = true ;
return ;
}
// Check that it has correct height
if ( ( uncle - > m_sidechainHeight > = block - > m_sidechainHeight ) | | ( uncle - > m_sidechainHeight + UNCLE_BLOCK_DEPTH < block - > m_sidechainHeight ) ) {
LOGWARN ( 3 , " block at height = " < < block - > m_sidechainHeight < <
" , id = " < < block - > m_sidechainId < <
" , mainchain height = " < < block - > m_txinGenHeight < < " has an uncle at the wrong height ( " < < uncle - > m_sidechainHeight < < ' ) ' ) ;
block - > m_verified = true ;
block - > m_invalid = true ;
return ;
}
// Check that uncle and parent have the same ancestor (they must be on the same chain)
PoolBlock * tmp = parent ;
while ( tmp - > m_sidechainHeight > uncle - > m_sidechainHeight ) {
tmp = get_parent ( tmp ) ;
if ( ! tmp ) {
LOGWARN ( 3 , " block at height = " < < block - > m_sidechainHeight < <
" , id = " < < block - > m_sidechainId < <
" , mainchain height = " < < block - > m_txinGenHeight < < " has an uncle from a different chain (check 1 failed) " ) ;
block - > m_verified = true ;
block - > m_invalid = true ;
return ;
}
}
if ( tmp - > m_sidechainHeight < uncle - > m_sidechainHeight ) {
LOGWARN ( 3 , " block at height = " < < block - > m_sidechainHeight < <
" , id = " < < block - > m_sidechainId < <
" , mainchain height = " < < block - > m_txinGenHeight < < " has an uncle from a different chain (check 2 failed) " ) ;
block - > m_verified = true ;
block - > m_invalid = true ;
return ;
}
bool same_chain = false ;
PoolBlock * tmp2 = uncle ;
for ( size_t j = 0 ; ( j < UNCLE_BLOCK_DEPTH ) & & tmp & & tmp2 & & ( tmp - > m_sidechainHeight + UNCLE_BLOCK_DEPTH > = block - > m_sidechainHeight ) ; + + j ) {
if ( tmp - > m_parent = = tmp2 - > m_parent ) {
same_chain = true ;
break ;
}
tmp = get_parent ( tmp ) ;
tmp2 = get_parent ( tmp2 ) ;
}
if ( ! same_chain ) {
LOGWARN ( 3 , " block at height = " < < block - > m_sidechainHeight < <
" , id = " < < block - > m_sidechainId < <
" , mainchain height = " < < block - > m_txinGenHeight < < " has an uncle from a different chain (check 3 failed) " ) ;
block - > m_verified = true ;
block - > m_invalid = true ;
return ;
}
expectedCumulativeDifficulty + = uncle - > m_difficulty ;
}
// We can verify this block now (all previous blocks in the window are verified and valid)
// It can still turn out to be invalid
block - > m_verified = true ;
if ( block - > m_cumulativeDifficulty ! = expectedCumulativeDifficulty ) {
LOGWARN ( 3 , " block at height = " < < block - > m_sidechainHeight < <
" , id = " < < block - > m_sidechainId < <
" , mainchain height = " < < block - > m_txinGenHeight < <
" has wrong cumulative difficulty: got " < < block - > m_cumulativeDifficulty < < " , expected " < < expectedCumulativeDifficulty ) ;
block - > m_invalid = true ;
return ;
}
// Verify difficulty and miner rewards only for blocks in PPLNS window
if ( block - > m_depth > = m_chainWindowSize ) {
LOGINFO ( 4 , " block " < < block - > m_sidechainId < < " skipped diff/reward verification " ) ;
block - > m_invalid = false ;
return ;
}
difficulty_type diff ;
2023-03-21 12:25:35 +00:00
if ( parent = = m_chainTip ) {
LOGINFO ( 6 , " block " < < block - > m_sidechainId < < " is built on top of the current chain tip, using current difficulty for verification " ) ;
diff = difficulty ( ) ;
}
else if ( ! get_difficulty ( parent , m_difficultyData , diff ) ) {
2021-08-22 10:20:59 +00:00
block - > m_invalid = true ;
return ;
}
if ( diff ! = block - > m_difficulty ) {
LOGWARN ( 3 , " block at height = " < < block - > m_sidechainHeight < <
" , id = " < < block - > m_sidechainId < <
" , mainchain height = " < < block - > m_txinGenHeight < <
" has wrong difficulty: got " < < block - > m_difficulty < < " , expected " < < diff ) ;
block - > m_invalid = true ;
return ;
}
std : : vector < MinerShare > shares ;
if ( ! get_shares ( block , shares ) ) {
block - > m_invalid = true ;
return ;
}
if ( shares . size ( ) ! = block - > m_outputs . size ( ) ) {
LOGWARN ( 3 , " block at height = " < < block - > m_sidechainHeight < <
" , id = " < < block - > m_sidechainId < <
" , mainchain height = " < < block - > m_txinGenHeight
< < " has invalid number of outputs: got " < < block - > m_outputs . size ( ) < < " , expected " < < shares . size ( ) ) ;
block - > m_invalid = true ;
return ;
}
uint64_t total_reward = std : : accumulate ( block - > m_outputs . begin ( ) , block - > m_outputs . end ( ) , 0ULL ,
[ ] ( uint64_t a , const PoolBlock : : TxOutput & b )
{
return a + b . m_reward ;
} ) ;
std : : vector < uint64_t > rewards ;
2022-04-07 17:33:39 +00:00
if ( ! split_reward ( total_reward , shares , rewards ) ) {
LOGWARN ( 3 , " block at height = " < < block - > m_sidechainHeight < <
" , id = " < < block - > m_sidechainId < <
" , mainchain height = " < < block - > m_txinGenHeight < < " : split_reward failed " ) ;
block - > m_invalid = true ;
return ;
}
2021-08-22 10:20:59 +00:00
if ( rewards . size ( ) ! = block - > m_outputs . size ( ) ) {
LOGWARN ( 3 , " block at height = " < < block - > m_sidechainHeight < <
" , id = " < < block - > m_sidechainId < <
" , mainchain height = " < < block - > m_txinGenHeight
< < " has invalid number of outputs: got " < < block - > m_outputs . size ( ) < < " , expected " < < rewards . size ( ) ) ;
block - > m_invalid = true ;
return ;
}
2022-10-04 18:48:19 +00:00
const uint8_t tx_type = block - > get_tx_type ( ) ;
2021-08-22 10:20:59 +00:00
for ( size_t i = 0 , n = rewards . size ( ) ; i < n ; + + i ) {
2022-04-01 14:52:23 +00:00
const PoolBlock : : TxOutput & out = block - > m_outputs [ i ] ;
if ( rewards [ i ] ! = out . m_reward ) {
2021-08-22 10:20:59 +00:00
LOGWARN ( 3 , " block at height = " < < block - > m_sidechainHeight < <
" , id = " < < block - > m_sidechainId < <
" , mainchain height = " < < block - > m_txinGenHeight < <
2022-04-01 14:52:23 +00:00
" has invalid reward at index " < < i < < " : got " < < out . m_reward < < " , expected " < < rewards [ i ] ) ;
2021-08-22 10:20:59 +00:00
block - > m_invalid = true ;
return ;
}
hash eph_public_key ;
2022-04-01 14:52:23 +00:00
uint8_t view_tag ;
if ( ! shares [ i ] . m_wallet - > get_eph_public_key ( block - > m_txkeySec , i , eph_public_key , view_tag ) ) {
2021-08-31 15:23:20 +00:00
LOGWARN ( 3 , " block at height = " < < block - > m_sidechainHeight < <
" , id = " < < block - > m_sidechainId < <
" , mainchain height = " < < block - > m_txinGenHeight < <
" failed to eph_public_key at index " < < i ) ;
block - > m_invalid = true ;
return ;
}
2021-08-22 10:20:59 +00:00
2022-10-04 18:48:19 +00:00
if ( ( tx_type = = TXOUT_TO_TAGGED_KEY ) & & ( out . m_viewTag ! = view_tag ) ) {
2022-04-01 14:52:23 +00:00
LOGWARN ( 3 , " block at height = " < < block - > m_sidechainHeight < <
" , id = " < < block - > m_sidechainId < <
" , mainchain height = " < < block - > m_txinGenHeight < <
" has an incorrect view tag at index " < < i ) ;
block - > m_invalid = true ;
return ;
}
if ( eph_public_key ! = out . m_ephPublicKey ) {
2021-08-22 10:20:59 +00:00
LOGWARN ( 3 , " block at height = " < < block - > m_sidechainHeight < <
" , id = " < < block - > m_sidechainId < <
" , mainchain height = " < < block - > m_txinGenHeight < <
" pays out to a wrong wallet at index " < < i ) ;
block - > m_invalid = true ;
return ;
}
}
// All checks passed
block - > m_invalid = false ;
}
2022-07-10 08:24:03 +00:00
void SideChain : : update_chain_tip ( const PoolBlock * block )
2021-08-22 10:20:59 +00:00
{
if ( ! block - > m_verified | | block - > m_invalid ) {
LOGERR ( 1 , " trying to update chain tip to an unverified or invalid block, fix the code! " ) ;
return ;
}
2021-08-23 21:08:46 +00:00
if ( block - > m_depth > = m_chainWindowSize ) {
LOGINFO ( 5 , " Trying to update chain tip to a block with depth " < < block - > m_depth < < " . Ignoring it. " ) ;
return ;
}
2022-04-08 21:14:08 +00:00
const PoolBlock * tip = m_chainTip ;
2023-01-08 23:33:37 +00:00
if ( block = = tip ) {
LOGINFO ( 5 , " Trying to update chain tip to the same block again. Ignoring it. " ) ;
return ;
}
2022-02-17 10:19:11 +00:00
bool is_alternative ;
2022-04-08 21:14:08 +00:00
if ( is_longer_chain ( tip , block , is_alternative ) ) {
2021-08-22 10:20:59 +00:00
difficulty_type diff ;
if ( get_difficulty ( block , m_difficultyData , diff ) ) {
2022-07-10 08:24:03 +00:00
m_chainTip = const_cast < PoolBlock * > ( block ) ;
2022-05-09 14:07:49 +00:00
{
WriteLock lock ( m_curDifficultyLock ) ;
m_curDifficulty = diff ;
}
2021-08-22 10:20:59 +00:00
LOGINFO ( 2 , " new chain tip: next height = " < < log : : Gray ( ) < < block - > m_sidechainHeight + 1 < < log : : NoColor ( ) < <
2022-05-09 14:07:49 +00:00
" , next difficulty = " < < log : : Gray ( ) < < diff < < log : : NoColor ( ) < <
2022-04-08 21:14:08 +00:00
" , main chain height = " < < log : : Gray ( ) < < block - > m_txinGenHeight ) ;
2021-08-22 10:20:59 +00:00
block - > m_wantBroadcast = true ;
2021-11-01 17:53:34 +00:00
if ( m_pool ) {
2022-07-14 11:14:20 +00:00
m_pool - > update_block_template_async ( is_alternative ) ;
2022-02-17 10:19:11 +00:00
// Reset stratum share counters when switching to an alternative chain to avoid confusion
2022-06-30 10:13:17 +00:00
if ( is_alternative ) {
StratumServer * s = m_pool - > stratum_server ( ) ;
if ( s ) {
s - > reset_share_counters ( ) ;
}
2022-10-04 12:35:38 +00:00
// Also clear cache because it has data from all old blocks now
clear_crypto_cache ( ) ;
2022-05-15 23:48:37 +00:00
LOGINFO ( 0 , log : : LightCyan ( ) < < " SYNCHRONIZED " ) ;
2022-02-17 10:19:11 +00:00
}
2021-11-01 17:53:34 +00:00
}
2021-08-22 10:20:59 +00:00
prune_old_blocks ( ) ;
2023-05-29 12:13:11 +00:00
cleanup_incoming_blocks ( ) ;
2021-08-22 10:20:59 +00:00
}
}
2022-04-08 21:14:08 +00:00
else if ( block - > m_sidechainHeight > tip - > m_sidechainHeight ) {
2021-08-22 10:20:59 +00:00
LOGINFO ( 4 , " block " < < block - > m_sidechainId < <
" , height = " < < block - > m_sidechainHeight < <
2022-04-08 21:14:08 +00:00
" is not a longer chain than " < < tip - > m_sidechainId < <
" , height " < < tip - > m_sidechainHeight ) ;
2021-08-22 10:20:59 +00:00
}
2023-07-11 22:25:37 +00:00
else if ( m_pool & & ( block - > m_sidechainHeight + UNCLE_BLOCK_DEPTH > tip - > m_sidechainHeight ) ) {
2021-08-22 10:20:59 +00:00
LOGINFO ( 4 , " possible uncle block: id = " < < log : : Gray ( ) < < block - > m_sidechainId < < log : : NoColor ( ) < <
" , height = " < < log : : Gray ( ) < < block - > m_sidechainHeight ) ;
m_pool - > update_block_template_async ( ) ;
}
2021-11-01 18:35:11 +00:00
if ( p2pServer ( ) & & block - > m_wantBroadcast & & ! block - > m_broadcasted ) {
2021-08-22 10:20:59 +00:00
block - > m_broadcasted = true ;
2022-11-09 10:17:35 +00:00
p2pServer ( ) - > broadcast ( * block , get_parent ( block ) ) ;
2021-08-22 10:20:59 +00:00
}
}
2022-05-12 13:19:58 +00:00
PoolBlock * SideChain : : get_parent ( const PoolBlock * block ) const
2021-08-22 10:20:59 +00:00
{
2023-07-02 11:10:05 +00:00
auto it = m_blocksById . find ( block - > m_parent ) ;
return ( it ! = m_blocksById . end ( ) ) ? it - > second : nullptr ;
2021-08-22 10:20:59 +00:00
}
2023-03-06 13:52:47 +00:00
bool SideChain : : is_longer_chain ( const PoolBlock * block , const PoolBlock * candidate , bool & is_alternative ) const
2021-08-22 10:20:59 +00:00
{
2022-02-17 10:19:11 +00:00
is_alternative = false ;
2021-08-22 10:20:59 +00:00
if ( ! candidate | | ! candidate - > m_verified | | candidate - > m_invalid ) {
return false ;
}
if ( ! block ) {
2022-05-16 13:59:48 +00:00
// Switching from an empty to a non-empty chain
is_alternative = true ;
2021-08-22 10:20:59 +00:00
return true ;
}
// If these two blocks are on the same chain, they must have a common ancestor
const PoolBlock * block_ancestor = block ;
2022-05-26 16:20:29 +00:00
while ( block_ancestor & & ( block_ancestor - > m_sidechainHeight > candidate - > m_sidechainHeight ) ) {
2021-08-22 10:20:59 +00:00
const hash & id = block_ancestor - > m_parent ;
block_ancestor = get_parent ( block_ancestor ) ;
if ( ! block_ancestor ) {
LOGINFO ( 4 , " couldn't find ancestor " < < id < < " of block " < < block - > m_sidechainId < < " at height " < < block - > m_sidechainHeight ) ;
break ;
}
}
if ( block_ancestor ) {
const PoolBlock * candidate_ancestor = candidate ;
while ( candidate_ancestor - > m_sidechainHeight > block_ancestor - > m_sidechainHeight ) {
const hash & id = candidate_ancestor - > m_parent ;
candidate_ancestor = get_parent ( candidate_ancestor ) ;
if ( ! candidate_ancestor ) {
LOGINFO ( 4 , " couldn't find ancestor " < < id < < " of block " < < candidate - > m_sidechainId < < " at height " < < candidate - > m_sidechainHeight ) ;
break ;
}
}
2022-06-13 05:30:59 +00:00
// cppcheck-suppress knownConditionTrueFalse
2021-08-22 10:20:59 +00:00
while ( block_ancestor & & candidate_ancestor ) {
if ( block_ancestor - > m_parent = = candidate_ancestor - > m_parent ) {
// If they are really on the same chain, we can just compare cumulative difficulties
return block - > m_cumulativeDifficulty < candidate - > m_cumulativeDifficulty ;
}
block_ancestor = get_parent ( block_ancestor ) ;
candidate_ancestor = get_parent ( candidate_ancestor ) ;
}
}
// They're on totally different chains. Compare total difficulties over the last m_chainWindowSize blocks
2022-02-17 10:19:11 +00:00
is_alternative = true ;
2021-08-22 10:20:59 +00:00
difficulty_type block_total_diff ;
difficulty_type candidate_total_diff ;
const PoolBlock * old_chain = block ;
const PoolBlock * new_chain = candidate ;
uint64_t candidate_mainchain_height = 0 ;
2022-06-03 15:28:46 +00:00
uint64_t candidate_mainchain_min_height = 0 ;
2023-03-24 10:50:39 +00:00
unordered_set < hash > current_chain_monero_blocks , candidate_chain_monero_blocks ;
{
const uint64_t k = m_chainWindowSize * m_targetBlockTime * 2 / MONERO_BLOCK_TIME ;
current_chain_monero_blocks . reserve ( k ) ;
candidate_chain_monero_blocks . reserve ( k ) ;
}
2021-08-22 10:20:59 +00:00
for ( uint64_t i = 0 ; ( i < m_chainWindowSize ) & & ( old_chain | | new_chain ) ; + + i ) {
if ( old_chain ) {
block_total_diff + = old_chain - > m_difficulty ;
2023-03-24 10:50:39 +00:00
for ( const hash & uncle : old_chain - > m_uncles ) {
auto it = m_blocksById . find ( uncle ) ;
if ( it ! = m_blocksById . end ( ) ) {
block_total_diff + = it - > second - > m_difficulty ;
}
}
ChainMain data ;
const hash & h = old_chain - > m_prevId ;
if ( ( current_chain_monero_blocks . count ( h ) = = 0 ) & & m_pool - > chainmain_get_by_hash ( h , data ) ) {
current_chain_monero_blocks . insert ( h ) ;
}
2021-08-22 10:20:59 +00:00
old_chain = get_parent ( old_chain ) ;
}
if ( new_chain ) {
2022-06-03 15:28:46 +00:00
candidate_mainchain_min_height = candidate_mainchain_min_height ? std : : min ( candidate_mainchain_min_height , new_chain - > m_txinGenHeight ) : new_chain - > m_txinGenHeight ;
2021-08-22 10:20:59 +00:00
candidate_total_diff + = new_chain - > m_difficulty ;
2023-03-24 10:50:39 +00:00
for ( const hash & uncle : new_chain - > m_uncles ) {
auto it = m_blocksById . find ( uncle ) ;
if ( it ! = m_blocksById . end ( ) ) {
candidate_total_diff + = it - > second - > m_difficulty ;
}
}
2021-08-22 10:20:59 +00:00
ChainMain data ;
2023-03-24 10:50:39 +00:00
const hash & h = new_chain - > m_prevId ;
if ( ( candidate_chain_monero_blocks . count ( h ) = = 0 ) & & m_pool - > chainmain_get_by_hash ( h , data ) ) {
candidate_chain_monero_blocks . insert ( h ) ;
2021-08-22 10:20:59 +00:00
candidate_mainchain_height = std : : max ( candidate_mainchain_height , data . height ) ;
}
new_chain = get_parent ( new_chain ) ;
}
}
if ( block_total_diff > = candidate_total_diff ) {
return false ;
}
2023-03-24 10:50:39 +00:00
// Candidate chain must be built on top of recent mainchain blocks
2022-04-08 21:14:08 +00:00
MinerData data = m_pool - > miner_data ( ) ;
if ( candidate_mainchain_height + 10 < data . height ) {
LOGWARN ( 3 , " received a longer alternative chain but it's stale: height " < < candidate_mainchain_height < < " , current height " < < data . height ) ;
2021-08-22 10:20:59 +00:00
return false ;
}
2022-06-03 15:28:46 +00:00
const uint64_t limit = m_chainWindowSize * 4 * m_targetBlockTime / MONERO_BLOCK_TIME ;
if ( candidate_mainchain_min_height + limit < data . height ) {
LOGWARN ( 3 , " received a longer alternative chain but it's stale: min height " < < candidate_mainchain_min_height < < " , must be >= " < < ( data . height - limit ) ) ;
return false ;
}
2023-03-24 10:50:39 +00:00
// Candidate chain must have been mined on top of at least half as many known Monero blocks, compared to the current chain
if ( candidate_chain_monero_blocks . size ( ) * 2 < current_chain_monero_blocks . size ( ) ) {
LOGWARN ( 3 , " received a longer alternative chain but it wasn't mined on current Monero blockchain: only " < < candidate_chain_monero_blocks . size ( ) < < ' / ' < < current_chain_monero_blocks . size ( ) < < " blocks found " ) ;
return false ;
}
2021-08-22 10:20:59 +00:00
LOGINFO ( 3 , " received a longer alternative chain: height " < <
log : : Gray ( ) < < block - > m_sidechainHeight < < log : : NoColor ( ) < < " -> " < <
log : : Gray ( ) < < candidate - > m_sidechainHeight < < log : : NoColor ( ) < < " , cumulative difficulty " < <
log : : Gray ( ) < < block - > m_cumulativeDifficulty < < log : : NoColor ( ) < < " -> " < <
log : : Gray ( ) < < candidate - > m_cumulativeDifficulty ) ;
return true ;
}
void SideChain : : update_depths ( PoolBlock * block )
{
2023-07-07 12:21:52 +00:00
const uint64_t precalc_depth = m_chainWindowSize + UNCLE_BLOCK_DEPTH - 1 ;
auto update_depth = [ this , precalc_depth ] ( PoolBlock * b , const uint64_t new_depth ) {
const uint64_t old_depth = b - > m_depth ;
if ( old_depth < new_depth ) {
b - > m_depth = new_depth ;
if ( ( old_depth < precalc_depth ) & & ( new_depth > = precalc_depth ) ) {
launch_precalc ( b ) ;
}
}
} ;
2021-08-22 10:20:59 +00:00
for ( size_t i = 1 ; i < = UNCLE_BLOCK_DEPTH ; + + i ) {
2023-03-06 13:52:47 +00:00
auto it = m_blocksByHeight . find ( block - > m_sidechainHeight + i ) ;
if ( it = = m_blocksByHeight . end ( ) ) {
continue ;
}
for ( PoolBlock * child : it - > second ) {
2021-08-22 10:20:59 +00:00
if ( child - > m_parent = = block - > m_sidechainId ) {
if ( i ! = 1 ) {
2023-03-21 15:22:11 +00:00
LOGWARN ( 3 , " Block " < < block - > m_sidechainId < < " : m_sidechainHeight is inconsistent with child's m_sidechainHeight. " ) ;
return ;
2021-08-22 10:20:59 +00:00
}
else {
2023-07-07 12:21:52 +00:00
update_depth ( block , child - > m_depth + 1 ) ;
2021-08-22 10:20:59 +00:00
}
}
2023-03-06 13:52:47 +00:00
if ( std : : find ( child - > m_uncles . begin ( ) , child - > m_uncles . end ( ) , block - > m_sidechainId ) ! = child - > m_uncles . end ( ) ) {
2023-07-07 12:21:52 +00:00
update_depth ( block , child - > m_depth + i ) ;
2021-08-22 10:20:59 +00:00
}
}
}
std : : vector < PoolBlock * > blocks_to_update ( 1 , block ) ;
do {
block = blocks_to_update . back ( ) ;
blocks_to_update . pop_back ( ) ;
2021-08-24 09:42:41 +00:00
// Verify this block and possibly other blocks on top of it when we're sure it will get verified
2023-04-22 16:55:11 +00:00
if ( ! block - > m_verified & & ( ( block - > m_depth > ( m_chainWindowSize - 1 ) * 2 + UNCLE_BLOCK_DEPTH ) | | ( block - > m_sidechainHeight = = 0 ) ) ) {
2021-08-24 09:42:41 +00:00
verify_loop ( block ) ;
}
2023-07-11 22:25:37 +00:00
for ( size_t i = 1 ; i < = UNCLE_BLOCK_DEPTH ; + + i ) {
auto it = m_blocksByHeight . find ( block - > m_sidechainHeight + i ) ;
if ( it = = m_blocksByHeight . end ( ) ) {
continue ;
}
for ( PoolBlock * child : it - > second ) {
const uint64_t old_depth = child - > m_depth ;
if ( child - > m_parent = = block - > m_sidechainId ) {
if ( i ! = 1 ) {
LOGWARN ( 3 , " Block " < < block - > m_sidechainId < < " : m_sidechainHeight is inconsistent with child's m_sidechainHeight. " ) ;
return ;
}
else if ( block - > m_depth > 0 ) {
update_depth ( child , block - > m_depth - 1 ) ;
}
}
if ( std : : find ( child - > m_uncles . begin ( ) , child - > m_uncles . end ( ) , block - > m_sidechainId ) ! = child - > m_uncles . end ( ) ) {
if ( block - > m_depth > i ) {
update_depth ( child , block - > m_depth - i ) ;
}
}
if ( child - > m_depth > old_depth ) {
blocks_to_update . push_back ( child ) ;
}
}
}
2021-08-22 10:20:59 +00:00
auto it = m_blocksById . find ( block - > m_parent ) ;
if ( it ! = m_blocksById . end ( ) ) {
if ( it - > second - > m_sidechainHeight + 1 ! = block - > m_sidechainHeight ) {
2023-03-21 15:22:11 +00:00
LOGWARN ( 3 , " Block " < < block - > m_sidechainId < < " : m_sidechainHeight is inconsistent with parent's m_sidechainHeight. " ) ;
return ;
2021-08-22 10:20:59 +00:00
}
if ( it - > second - > m_depth < block - > m_depth + 1 ) {
2023-07-07 12:21:52 +00:00
update_depth ( it - > second , block - > m_depth + 1 ) ;
2021-08-22 10:20:59 +00:00
blocks_to_update . push_back ( it - > second ) ;
}
}
for ( const hash & uncle_id : block - > m_uncles ) {
it = m_blocksById . find ( uncle_id ) ;
if ( it = = m_blocksById . end ( ) ) {
continue ;
}
if ( ( it - > second - > m_sidechainHeight > = block - > m_sidechainHeight ) | | ( it - > second - > m_sidechainHeight + UNCLE_BLOCK_DEPTH < block - > m_sidechainHeight ) ) {
2023-03-21 15:22:11 +00:00
LOGWARN ( 3 , " Block " < < block - > m_sidechainId < < " : m_sidechainHeight is inconsistent with uncle's m_sidechainHeight. " ) ;
return ;
2021-08-22 10:20:59 +00:00
}
const uint64_t d = block - > m_sidechainHeight - it - > second - > m_sidechainHeight ;
if ( it - > second - > m_depth < block - > m_depth + d ) {
2023-07-07 12:21:52 +00:00
update_depth ( it - > second , block - > m_depth + d ) ;
2021-08-22 10:20:59 +00:00
blocks_to_update . push_back ( it - > second ) ;
}
}
} while ( ! blocks_to_update . empty ( ) ) ;
}
void SideChain : : prune_old_blocks ( )
{
// Leave 2 minutes worth of spare blocks in addition to 2xPPLNS window for lagging nodes which need to sync
2022-06-03 15:28:46 +00:00
const uint64_t prune_distance = m_chainWindowSize * 2 + MONERO_BLOCK_TIME / m_targetBlockTime ;
2021-08-22 10:20:59 +00:00
2021-08-24 16:34:28 +00:00
// Remove old blocks from alternative unconnected chains after long enough time
2022-03-23 10:30:38 +00:00
const uint64_t cur_time = seconds_since_epoch ( ) ;
const uint64_t prune_delay = m_chainWindowSize * 4 * m_targetBlockTime ;
2021-08-24 16:34:28 +00:00
2022-04-08 21:14:08 +00:00
const PoolBlock * tip = m_chainTip ;
if ( tip - > m_sidechainHeight < prune_distance ) {
2021-08-22 10:20:59 +00:00
return ;
}
2022-04-08 21:14:08 +00:00
const uint64_t h = tip - > m_sidechainHeight - prune_distance ;
2021-08-22 10:20:59 +00:00
uint64_t num_blocks_pruned = 0 ;
for ( auto it = m_blocksByHeight . begin ( ) ; ( it ! = m_blocksByHeight . end ( ) ) & & ( it - > first < = h ) ; ) {
2021-08-24 16:34:28 +00:00
const uint64_t height = it - > first ;
std : : vector < PoolBlock * > & v = it - > second ;
v . erase ( std : : remove_if ( v . begin ( ) , v . end ( ) ,
2022-03-23 10:30:38 +00:00
[ this , prune_distance , cur_time , prune_delay , & num_blocks_pruned , height ] ( PoolBlock * block )
2021-08-24 16:34:28 +00:00
{
2022-03-23 10:30:38 +00:00
if ( ( block - > m_depth > = prune_distance ) | | ( cur_time > = block - > m_localTimestamp + prune_delay ) ) {
2021-08-24 16:34:28 +00:00
auto it2 = m_blocksById . find ( block - > m_sidechainId ) ;
if ( it2 ! = m_blocksById . end ( ) ) {
m_blocksById . erase ( it2 ) ;
delete block ;
+ + num_blocks_pruned ;
}
else {
LOGERR ( 1 , " m_blocksByHeight and m_blocksById are inconsistent at height " < < height < < " . Fix the code! " ) ;
}
return true ;
}
return false ;
} ) , v . end ( ) ) ;
2021-08-22 10:20:59 +00:00
2021-08-24 16:34:28 +00:00
if ( v . empty ( ) ) {
2021-09-01 14:26:56 +00:00
it = m_blocksByHeight . erase ( it ) ;
2021-08-24 16:34:28 +00:00
}
else {
+ + it ;
}
2021-08-22 10:20:59 +00:00
}
if ( num_blocks_pruned ) {
2021-08-28 21:34:46 +00:00
LOGINFO ( 4 , " pruned " < < num_blocks_pruned < < " old blocks at heights <= " < < h ) ;
2021-10-01 13:21:32 +00:00
// If side-chain started pruning blocks it means the initial sync is complete
// It's now safe to delete cached blocks
2021-11-01 18:35:11 +00:00
if ( p2pServer ( ) ) {
p2pServer ( ) - > clear_cached_blocks ( ) ;
2021-11-01 17:53:34 +00:00
}
2022-07-14 07:04:14 +00:00
// Pre-calc workers are not needed anymore
finish_precalc ( ) ;
2023-05-17 21:36:58 +00:00
# ifdef DEV_TEST_SYNC
2023-06-16 13:51:33 +00:00
if ( m_firstPruneTime = = 0 ) {
m_firstPruneTime = seconds_since_epoch ( ) ;
// Test Monero node switching
m_pool - > reconnect_to_host ( ) ;
}
2023-07-19 14:06:00 +00:00
if ( ( cur_time > = m_firstPruneTime + 120 ) & & ! m_pool - > stopped ( ) ) {
2023-05-17 21:36:58 +00:00
LOGINFO ( 0 , log : : LightGreen ( ) < < " [DEV] Synchronization finished successfully, stopping P2Pool now " ) ;
2023-07-19 11:13:00 +00:00
# ifdef DEV_TRACK_MEMORY
show_top_10_allocations ( ) ;
# endif
2023-05-17 21:36:58 +00:00
print_status ( false ) ;
2023-08-15 09:43:05 +00:00
StratumServer * server1 = m_pool - > stratum_server ( ) ;
P2PServer * server2 = m_pool - > p2p_server ( ) ;
if ( server1 & & server2 ) {
server1 - > print_status ( ) ;
server2 - > print_status ( ) ;
server1 - > print_bans ( ) ;
server2 - > print_bans ( ) ;
server1 - > show_workers_async ( ) ;
server2 - > show_peers_async ( ) ;
2023-05-17 21:36:58 +00:00
}
2023-08-15 09:43:05 +00:00
2023-06-16 13:51:33 +00:00
m_pool - > print_hosts ( ) ;
2023-08-15 09:43:05 +00:00
bkg_jobs_tracker . print_status ( ) ;
2023-05-17 21:36:58 +00:00
m_pool - > stop ( ) ;
}
# endif
2021-08-22 10:20:59 +00:00
}
}
2023-07-10 12:11:22 +00:00
void SideChain : : get_missing_blocks ( unordered_set < hash > & missing_blocks ) const
2021-08-22 10:20:59 +00:00
{
missing_blocks . clear ( ) ;
2022-05-12 13:19:58 +00:00
ReadLock lock ( m_sidechainLock ) ;
2021-08-22 10:20:59 +00:00
for ( auto & b : m_blocksById ) {
if ( b . second - > m_verified ) {
continue ;
}
if ( ! b . second - > m_parent . empty ( ) & & ( m_blocksById . find ( b . second - > m_parent ) = = m_blocksById . end ( ) ) ) {
2023-07-10 12:11:22 +00:00
missing_blocks . insert ( b . second - > m_parent ) ;
2021-08-22 10:20:59 +00:00
}
2022-08-17 14:44:40 +00:00
int num_missing_uncles = 0 ;
2021-08-22 10:20:59 +00:00
for ( const hash & h : b . second - > m_uncles ) {
if ( ! h . empty ( ) & & ( m_blocksById . find ( h ) = = m_blocksById . end ( ) ) ) {
2023-07-10 12:11:22 +00:00
missing_blocks . insert ( h ) ;
2022-08-17 14:44:40 +00:00
// Get no more than 2 first missing uncles at a time from each block
// Blocks with more than 2 uncles are very rare and they will be processed in several steps
+ + num_missing_uncles ;
if ( num_missing_uncles > = 2 ) {
break ;
}
2021-08-22 10:20:59 +00:00
}
}
}
}
bool SideChain : : load_config ( const std : : string & filename )
{
if ( filename . empty ( ) ) {
LOGINFO ( 1 , " using default config " ) ;
return true ;
}
LOGINFO ( 1 , " loading config from " < < log : : Gray ( ) < < filename ) ;
std : : ifstream f ( filename ) ;
if ( ! f . is_open ( ) ) {
LOGERR ( 1 , " can't open " < < filename ) ;
return false ;
}
rapidjson : : Document doc ;
rapidjson : : IStreamWrapper s ( f ) ;
2021-08-29 06:34:26 +00:00
if ( doc . ParseStream < rapidjson : : kParseCommentsFlag | rapidjson : : kParseTrailingCommasFlag > ( s ) . HasParseError ( ) ) {
2021-08-22 10:20:59 +00:00
LOGERR ( 1 , " failed to parse JSON data in " < < filename ) ;
return false ;
}
if ( ! doc . IsObject ( ) ) {
LOGERR ( 1 , " invalid JSON data in " < < filename < < " : top level is not an object " ) ;
return false ;
}
parseValue ( doc , " name " , m_poolName ) ;
parseValue ( doc , " password " , m_poolPassword ) ;
parseValue ( doc , " block_time " , m_targetBlockTime ) ;
uint64_t min_diff ;
2023-01-16 17:34:48 +00:00
if ( parseValue ( doc , " min_diff " , min_diff ) & & min_diff ) {
2021-08-22 10:20:59 +00:00
m_minDifficulty = { min_diff , 0 } ;
}
parseValue ( doc , " pplns_window " , m_chainWindowSize ) ;
parseValue ( doc , " uncle_penalty " , m_unclePenalty ) ;
return true ;
}
2022-12-22 12:33:44 +00:00
bool SideChain : : check_config ( ) const
2021-08-22 10:20:59 +00:00
{
if ( m_poolName . empty ( ) ) {
LOGERR ( 1 , " name can't be empty " ) ;
return false ;
}
if ( m_poolName . length ( ) > 128 ) {
LOGERR ( 1 , " name is too long (must be 128 characters max) " ) ;
return false ;
}
if ( m_poolPassword . length ( ) > 128 ) {
LOGERR ( 1 , " password is too long (must be 128 characters max) " ) ;
return false ;
}
2022-06-03 15:28:46 +00:00
if ( ( m_targetBlockTime < 1 ) | | ( m_targetBlockTime > MONERO_BLOCK_TIME ) ) {
LOGERR ( 1 , " block_time is invalid (must be between 1 and " < < MONERO_BLOCK_TIME < < " ) " ) ;
2021-08-22 10:20:59 +00:00
return false ;
}
2023-01-16 17:34:48 +00:00
if ( s_networkType = = NetworkType : : Mainnet ) {
const difficulty_type min_diff { MIN_DIFFICULTY , 0 } ;
const difficulty_type max_diff { 1000000000 , 0 } ;
2021-08-22 10:20:59 +00:00
2023-01-16 17:34:48 +00:00
if ( ( m_minDifficulty < min_diff ) | | ( max_diff < m_minDifficulty ) ) {
LOGERR ( 1 , " min_diff is invalid (must be between " < < min_diff < < " and " < < max_diff < < ' ) ' ) ;
return false ;
}
2021-08-22 10:20:59 +00:00
}
if ( ( m_chainWindowSize < 60 ) | | ( m_chainWindowSize > 2160 ) ) {
LOGERR ( 1 , " pplns_window is invalid (must be between 60 and 2160) " ) ;
return false ;
}
if ( ( m_unclePenalty < 1 ) | | ( m_unclePenalty > 99 ) ) {
LOGERR ( 1 , " uncle_penalty is invalid (must be between 1 and 99) " ) ;
return false ;
}
LOGINFO ( 1 , log : : LightCyan ( ) < < " pool name = " < < m_poolName ) ;
LOGINFO ( 1 , log : : LightCyan ( ) < < " block time = " < < m_targetBlockTime < < " seconds " ) ;
LOGINFO ( 1 , log : : LightCyan ( ) < < " min diff = " < < m_minDifficulty ) ;
LOGINFO ( 1 , log : : LightCyan ( ) < < " PPLNS window = " < < m_chainWindowSize < < " blocks " ) ;
LOGINFO ( 1 , log : : LightCyan ( ) < < " uncle penalty = " < < m_unclePenalty < < ' % ' ) ;
return true ;
}
2022-07-14 07:04:14 +00:00
void SideChain : : launch_precalc ( const PoolBlock * block )
{
if ( m_precalcFinished ) {
return ;
}
2023-04-22 16:00:31 +00:00
for ( int h = UNCLE_BLOCK_DEPTH ; h > = 0 ; - - h ) {
2022-07-14 17:29:41 +00:00
auto it = m_blocksByHeight . find ( block - > m_sidechainHeight + m_chainWindowSize + h - 1 ) ;
if ( it = = m_blocksByHeight . end ( ) ) {
continue ;
}
2022-07-14 07:04:14 +00:00
for ( PoolBlock * b : it - > second ) {
2022-07-14 17:29:41 +00:00
if ( b - > m_precalculated ) {
continue ;
}
2023-01-07 23:34:38 +00:00
std : : vector < MinerShare > shares ;
2023-01-13 15:56:31 +00:00
if ( get_shares ( b , shares , nullptr , true ) ) {
2022-07-14 07:04:14 +00:00
b - > m_precalculated = true ;
2023-01-07 23:34:38 +00:00
PrecalcJob * job = new PrecalcJob { b , std : : move ( shares ) } ;
2022-07-14 07:04:14 +00:00
{
MutexLock lock2 ( m_precalcJobsMutex ) ;
m_precalcJobs . push_back ( job ) ;
}
uv_cond_signal ( & m_precalcJobsCond ) ;
}
}
}
}
void SideChain : : precalc_worker ( )
{
do {
PrecalcJob * job ;
2023-04-22 16:00:31 +00:00
size_t num_inputs ;
2022-07-14 07:04:14 +00:00
{
MutexLock lock ( m_precalcJobsMutex ) ;
if ( m_precalcFinished ) {
return ;
}
while ( m_precalcJobs . empty ( ) ) {
uv_cond_wait ( & m_precalcJobsCond , & m_precalcJobsMutex ) ;
if ( m_precalcFinished ) {
return ;
}
}
job = m_precalcJobs . back ( ) ;
m_precalcJobs . pop_back ( ) ;
// Filter out duplicate inputs for get_eph_public_key()
uint8_t t [ HASH_SIZE * 2 + sizeof ( size_t ) ] ;
memcpy ( t , job - > b - > m_txkeySec . h , HASH_SIZE ) ;
2023-04-22 16:00:31 +00:00
const size_t n = job - > shares . size ( ) ;
num_inputs = n ;
for ( size_t i = 0 ; i < n ; + + i ) {
2023-01-07 23:34:38 +00:00
memcpy ( t + HASH_SIZE , job - > shares [ i ] . m_wallet - > view_public_key ( ) . h , HASH_SIZE ) ;
2022-07-14 07:04:14 +00:00
memcpy ( t + HASH_SIZE * 2 , & i , sizeof ( i ) ) ;
if ( ! m_uniquePrecalcInputs - > insert ( robin_hood : : hash_bytes ( t , array_size ( t ) ) ) . second ) {
2023-01-07 23:34:38 +00:00
job - > shares [ i ] . m_wallet = nullptr ;
2023-04-22 16:00:31 +00:00
- - num_inputs ;
2022-07-14 07:04:14 +00:00
}
}
}
2023-04-22 16:00:31 +00:00
if ( num_inputs ) {
for ( size_t i = 0 , n = job - > shares . size ( ) ; i < n ; + + i ) {
if ( job - > shares [ i ] . m_wallet ) {
hash eph_public_key ;
uint8_t view_tag ;
job - > shares [ i ] . m_wallet - > get_eph_public_key ( job - > b - > m_txkeySec , i , eph_public_key , view_tag ) ;
}
2022-07-14 07:04:14 +00:00
}
}
2023-04-22 16:00:31 +00:00
2022-07-14 07:04:14 +00:00
delete job ;
} while ( true ) ;
}
void SideChain : : finish_precalc ( )
{
if ( m_precalcFinished . exchange ( true ) ) {
return ;
}
2022-08-05 13:27:39 +00:00
try
2022-07-14 07:04:14 +00:00
{
2022-08-05 13:27:39 +00:00
{
MutexLock lock ( m_precalcJobsMutex ) ;
for ( PrecalcJob * job : m_precalcJobs ) {
delete job ;
}
m_precalcJobs . clear ( ) ;
m_precalcJobs . shrink_to_fit ( ) ;
uv_cond_broadcast ( & m_precalcJobsCond ) ;
2022-07-14 07:04:14 +00:00
}
2022-08-05 13:27:39 +00:00
for ( std : : thread & t : m_precalcWorkers ) {
t . join ( ) ;
}
m_precalcWorkers . clear ( ) ;
m_precalcWorkers . shrink_to_fit ( ) ;
2022-07-14 07:04:14 +00:00
2022-08-05 13:27:39 +00:00
delete m_uniquePrecalcInputs ;
m_uniquePrecalcInputs = nullptr ;
2022-07-14 07:04:14 +00:00
2022-08-05 13:27:39 +00:00
uv_mutex_destroy ( & m_precalcJobsMutex ) ;
uv_cond_destroy ( & m_precalcJobsCond ) ;
2022-07-14 07:04:14 +00:00
2022-11-24 07:43:13 +00:00
// Also clear cache because it has data from all old blocks now
clear_crypto_cache ( ) ;
2022-08-05 13:27:39 +00:00
LOGINFO ( 4 , " pre-calculation workers stopped " ) ;
}
catch ( const std : : exception & e )
{
LOGERR ( 1 , " exception in finish_precalc(): " < < e . what ( ) ) ;
}
2022-07-14 07:04:14 +00:00
}
2021-08-22 10:20:59 +00:00
} // namespace p2pool