@@ -247,10 +247,10 @@ int NodeImpl::get_verified_count() const { return get_tracker_snapshot().verifie
247247void NodeImpl::send_version (peer_ptr peer)
248248{
249249 auto rmsg = ltc::message_version::make_raw (
250- ltc::PoolConfig:: ADVERTISED_PROTOCOL_VERSION ,
250+ m_tracker. m_params -> minimum_protocol_version ,
251251 1 , // services
252252 addr_t {1 , peer->addr ()}, // addr_to (the remote)
253- addr_t {1 , NetService{" 0.0.0.0" , ltc::PoolConfig:: P2P_PORT }}, // addr_from (us)
253+ addr_t {1 , NetService{" 0.0.0.0" , m_tracker. m_params -> p2p_port }}, // addr_from (us)
254254 m_nonce,
255255 m_software_version,
256256 1 , // mode (always 1 for legacy compat)
@@ -304,11 +304,11 @@ std::optional<pool::PeerConnectionType> NodeImpl::handle_version(std::unique_ptr
304304 }
305305
306306 // Reject peers running too-old protocol
307- if (msg->m_version < ltc::PoolConfig:: MINIMUM_PROTOCOL_VERSION )
307+ if (msg->m_version < m_tracker. m_params -> minimum_protocol_version )
308308 {
309309 LOG_WARNING << " Peer " << msg->m_addr_from .m_endpoint .to_string ()
310310 << " protocol " << msg->m_version
311- << " < minimum " << ltc::PoolConfig:: MINIMUM_PROTOCOL_VERSION
311+ << " < minimum " << m_tracker. m_params -> minimum_protocol_version
312312 << " , disconnecting" ;
313313 throw std::runtime_error (" peer protocol too old" );
314314 }
@@ -362,7 +362,7 @@ void NodeImpl::processing_shares(HandleSharesData& data_ref, NetService addr)
362362 try
363363 {
364364 share.ACTION ({
365- obj->m_hash = share_init_verify (*obj, true );
365+ obj->m_hash = share_init_verify (*obj, *m_tracker. m_params , true );
366366 });
367367 }
368368 catch (const std::exception&)
@@ -391,22 +391,21 @@ void NodeImpl::processing_shares_phase2(HandleSharesData& data, NetService addr)
391391 // Non-blocking mutex: if think() holds the exclusive lock on the compute
392392 // thread, queue this batch for processing after think() releases. The IO
393393 // thread never blocks — keepalive timers and network I/O continue.
394- // Acquire the tracker lock and HOLD it across the entire mutation body below.
395- // try_to_lock keeps the IO thread non-blocking — if think()/clean holds the
396- // exclusive lock we queue the batch and return. Once acquired we must NOT
397- // release until all m_tracker.chain mutations are done: the prior code
398- // released here and ran the body lock-free, letting the compute-thread
399- // clean_tracker() exclusive prune (drop_tails) free chain nodes mid-mutation
400- // → SIGSEGV (kr1z1s LTC/DGB). Released just before run_think() below.
401- std::unique_lock lock (m_tracker_mutex, std::try_to_lock);
402- if (!lock.owns_lock ()) {
403- LOG_INFO << " [ASYNC-DEFER] processing_shares_phase2: mutex busy, queuing "
404- << data.m_items .size () << " shares from " << addr.to_string ()
405- << " (pending=" << m_pending_adds.size () + 1 << " )" ;
406- m_pending_adds.push_back (PendingShareBatch{
407- std::make_unique<HandleSharesData>(std::move (data)), addr});
408- return ;
394+ {
395+ std::unique_lock lock (m_tracker_mutex, std::try_to_lock);
396+ if (!lock.owns_lock ()) {
397+ LOG_INFO << " [ASYNC-DEFER] processing_shares_phase2: mutex busy, queuing "
398+ << data.m_items .size () << " shares from " << addr.to_string ()
399+ << " (pending=" << m_pending_adds.size () + 1 << " )" ;
400+ m_pending_adds.push_back (PendingShareBatch{
401+ std::make_unique<HandleSharesData>(std::move (data)), addr});
402+ return ;
403+ }
409404 }
405+ // Lock released — proceed with normal processing.
406+ // No lock needed for the rest: we only enter here when think() is NOT
407+ // running (m_tracker_mutex was available), and ASIO single-thread
408+ // guarantees no other IO handler overlaps.
410409
411410 // Step 1: collect verified shares (skip any that failed verification, hash still null)
412411 std::vector<ShareType> valid_shares;
@@ -567,11 +566,6 @@ void NodeImpl::processing_shares_phase2(HandleSharesData& data, NetService addr)
567566 << " chain=" << m_tracker.chain .size () << " )" ;
568567 }
569568
570- // Release the tracker lock before triggering think(): run_think() posts the
571- // think+prune job to the compute thread, which needs the exclusive lock. All
572- // chain mutations above are complete at this point.
573- lock.unlock ();
574-
575569 // Trigger think() after every share batch (p2pool: set_best_share after handle_shares).
576570 // p2pool calls set_best_share() after EVERY batch with new_count > 0 — no size gate.
577571 // think() scores heads and updates best_share + desired set for download_shares.
@@ -796,18 +790,14 @@ void NodeImpl::notify_local_share(const uint256& share_hash)
796790{
797791 // p2pool: set_best_share() → think() synchronously on the reactor thread.
798792 // Use think() for ALL best_share decisions, matching p2pool exactly.
799- if (share_hash.IsNull ())
793+ if (share_hash.IsNull () || !m_tracker. chain . contains (share_hash) )
800794 return ;
801795
802- // Both the chain.contains() read AND attempt_verify() run UNDER the tracker
803- // lock. A bare m_tracker.chain.contains() here (the prior code) raced the
804- // compute-thread clean_tracker() exclusive prune freeing chain nodes →
805- // SIGSEGV (kr1z1s LTC/DGB). try_to_lock keeps the IO thread non-blocking; if
806- // think()/clean holds the lock we skip the inline verify — the share is
807- // already in-chain and run_think() below will score it next cycle.
796+ // Try inline verify — if think() holds the mutex, defer to next think() cycle.
797+ // The share is already in the chain; think() will verify + score it.
808798 {
809799 std::unique_lock lock (m_tracker_mutex, std::try_to_lock);
810- if (lock.owns_lock () && m_tracker. chain . contains (share_hash) )
800+ if (lock.owns_lock ())
811801 m_tracker.attempt_verify (share_hash);
812802 }
813803
@@ -1059,7 +1049,7 @@ void NodeImpl::load_persisted_shares()
10591049 return ;
10601050 }
10611051
1062- const size_t keep_per_head = PoolConfig:: chain_length() * 2 + 10 ;
1052+ const size_t keep_per_head = m_tracker. m_params -> chain_length * 2 + 10 ;
10631053 const size_t total_in_db = all_hashes.size ();
10641054
10651055 // Only load the most recent shares (highest height = end of vector)
@@ -1121,7 +1111,7 @@ void NodeImpl::load_persisted_shares()
11211111 g_last_pow_hash = uint256 ();
11221112 g_last_init_is_block = false ;
11231113 uint256 computed_hash;
1124- share.ACTION ({ computed_hash = share_init_verify (*obj, true ); });
1114+ share.ACTION ({ computed_hash = share_init_verify (*obj, *m_tracker. m_params , true ); });
11251115 if (!g_last_pow_hash.IsNull ())
11261116 idx->pow_hash = g_last_pow_hash;
11271117 if (!computed_hash.IsNull () && computed_hash != hash) {
@@ -1264,7 +1254,7 @@ void NodeImpl::prune_shares(const uint256& /*best_share*/)
12641254 // - Remove ONE child of qualifying tail per iteration
12651255 // - Loop up to 1000 times (gradual, not bulk)
12661256 // - Also cascade removal to verified
1267- const auto CL = static_cast <int32_t >(PoolConfig:: chain_length() );
1257+ const auto CL = static_cast <int32_t >(m_tracker. m_params -> chain_length );
12681258 const int32_t min_depth = 2 * CL + 10 ;
12691259
12701260 for (int iter = 0 ; iter < 1000 ; ++iter)
@@ -1603,7 +1593,7 @@ void NodeImpl::heartbeat_log()
16031593 }
16041594 if (!walk_start.IsNull () && m_tracker.chain .contains (walk_start)) {
16051595 int window = std::min (height, static_cast <int >(
1606- std::min (size_t (3600 ) / PoolConfig:: share_period() , size_t (height))));
1596+ std::min (size_t (3600 ) / m_tracker. m_params -> share_period , size_t (height))));
16071597 if (window > 0 ) {
16081598 auto walkable = m_tracker.chain .get_height (walk_start);
16091599 auto walk_n = std::min (window, walkable);
@@ -1663,7 +1653,7 @@ void NodeImpl::heartbeat_log()
16631653 try {
16641654 auto aps = m_tracker.get_pool_attempts_per_second (
16651655 m_best_share_hash,
1666- std::min (height - 1 , static_cast <int >(PoolConfig:: TARGET_LOOKBEHIND )),
1656+ std::min (height - 1 , static_cast <int >(m_tracker. m_params -> target_lookbehind )),
16671657 /* min_work=*/ false );
16681658 double pool_hs = static_cast <double >(aps.GetLow64 ());
16691659 double real_pool_hs = (stale_prop < 0.999 && pool_hs > 0 )
@@ -1751,7 +1741,7 @@ void NodeImpl::clean_tracker()
17511741
17521742 // Steps 2-3: Prune (still holding exclusive lock)
17531743 auto now_sec = static_cast <int64_t >(std::time (nullptr ));
1754- auto CL = static_cast <int32_t >(ltc::PoolConfig:: chain_length() );
1744+ auto CL = static_cast <int32_t >(m_tracker. m_params -> chain_length );
17551745
17561746 // Step 2: Eat stale heads (p2pool node.py:358-378)
17571747 // Three guards protect useful heads:
0 commit comments