@@ -22,7 +22,7 @@ def _find_config(filename: str = "rpc_config.ini") -> Optional[str]:
2222 for _ in range (5 ):
2323 candidate = os .path .join (directory , filename )
2424 if os .path .isfile (candidate ):
25- return candidate
25+ return os . path . abspath ( candidate )
2626 directory = os .path .dirname (directory )
2727 return None
2828
@@ -117,16 +117,92 @@ def get_single_block_stats(height: int) -> Dict[str, Any]:
117117# Public RPC wrappers
118118# ---------------------------------------------------------------------------
119119
120+ # Chain name mapping: Bitcoin Core returns "main"|"test"|"signet"|"regtest"
121+ CHAIN_DISPLAY_NAMES = {"main" : "MAINNET" , "test" : "TESTNET" , "signet" : "SIGNET" , "regtest" : "REGTEST" }
122+
123+ # Cached chain for DB writes (fixed for process lifetime)
124+ _current_chain_cache : Optional [str ] = None
125+
126+
127+ def get_current_chain () -> str :
128+ """Return current network chain for DB isolation. Cached for process lifetime."""
129+ global _current_chain_cache
130+ if _current_chain_cache is None :
131+ info = get_blockchain_info ()
132+ _current_chain_cache = info ["chain" ]
133+ return _current_chain_cache
134+
135+
120136def get_block_count () -> int :
121137 return _rpc_call ("getblockcount" , [])
122138
139+
140+ def get_blockchain_info () -> Dict [str , Any ]:
141+ """
142+ Returns chain and block count from getblockchaininfo RPC.
143+ Used for dynamic network detection (mainnet/testnet/signet/regtest).
144+ """
145+ result = _rpc_call ("getblockchaininfo" , [])
146+ if not result :
147+ return {"chain" : "main" , "blockcount" : get_block_count ()}
148+ chain = result .get ("chain" , "main" )
149+ blocks = result .get ("blocks" , get_block_count ())
150+ display_chain = CHAIN_DISPLAY_NAMES .get (chain , chain .upper ())
151+ logger .debug (f"Network detected: { display_chain } (chain={ chain } )" )
152+ return {"chain" : chain , "chain_display" : display_chain , "blockcount" : blocks }
153+
154+
155+ def get_mempool_health_statistics () -> List [Dict [str , Any ]]:
156+ """
157+ Fetches stats for the last 5 blocks to compare their weights with
158+ the current mempool's readiness.
159+ """
160+ current_height = get_block_count ()
161+ stats = []
162+
163+ # Using getmempoolfeeratediagram for accurate total weight
164+ mempool_diagram = _rpc_call ("getmempoolfeeratediagram" , [])
165+ total_mempool_weight = mempool_diagram [- 1 ]["weight" ] if mempool_diagram else 0
166+
167+ for h in range (current_height - 4 , current_height + 1 ):
168+ try :
169+ b = get_single_block_stats (h )
170+ weight = b .get ("total_weight" , 0 )
171+
172+ stats .append ({
173+ "block_height" : h ,
174+ "block_weight" : weight ,
175+ "mempool_txs_weight" : total_mempool_weight ,
176+ "ratio" : min (1.0 , total_mempool_weight / 4_000_000 )
177+ })
178+ except Exception :
179+ continue
180+ return stats
181+
182+
123183def estimate_smart_fee (conf_target : int , mode : str = "unset" , verbosity_level : int = 2 ) -> Dict [str , Any ]:
124184 effective_target = _clamp_target (conf_target )
125- result = _rpc_call ("estimatesmartfee" , [effective_target , mode , verbosity_level ])
185+ # Bitcoin Core estimatesmartfee: (conf_target, estimate_mode) — verbosity added in newer versions
186+ result = _rpc_call ("estimatesmartfee" , [effective_target , mode ])
126187 if result and "feerate" in result :
127188 # feerate is BTC/kVB → sat/vB: × 1e8 (BTC→sat) ÷ 1e3 (kVB→vB) = × 1e5
128189 result ["feerate_sat_per_vb" ] = result ["feerate" ] * 100_000
129-
190+
191+ # Include chain so frontend shows correct network (mainnet/testnet/signet/regtest)
192+ if result is not None :
193+ try :
194+ info = get_blockchain_info ()
195+ result ["chain" ] = info ["chain" ]
196+ result ["chain_display" ] = info ["chain_display" ]
197+ except Exception as e :
198+ logger .debug (f"Could not attach chain to fee response: { e } " )
199+
200+ # Include health stats for the frontend
201+ try :
202+ result ["mempool_health_statistics" ] = get_mempool_health_statistics ()
203+ except Exception as e :
204+ logger .error (f"Failed to include health stats: { e } " )
205+
130206 return result
131207
132208def get_mempool_feerate_diagram_analysis () -> Dict [str , Any ]:
@@ -192,7 +268,8 @@ def get_performance_data(start_height: int, count: int = 100, target: int = 2) -
192268 import services .database_service as db_service # late import — breaks circular dep
193269
194270 effective_target = _clamp_target (target )
195- db_rows = db_service .get_estimates_in_range (start_height , start_height + count , effective_target )
271+ chain = get_current_chain ()
272+ db_rows = db_service .get_estimates_in_range (start_height , start_height + count , effective_target , network = chain )
196273
197274 # Deduplicate to latest estimate per height (dict preserves insertion order in Py3.7+)
198275 latest_estimates_map = {row ["poll_height" ]: row ["estimate_feerate" ] for row in db_rows }
@@ -216,8 +293,8 @@ def calculate_local_summary(target: int = 2) -> Dict[str, Any]:
216293
217294 effective_target = _clamp_target (target )
218295 current_h = get_block_count ()
219-
220- db_rows = db_service .get_estimates_in_range (current_h - 1000 , current_h , effective_target )
296+ chain = get_current_chain ()
297+ db_rows = db_service .get_estimates_in_range (current_h - 1000 , current_h , effective_target , network = chain )
221298
222299 total = 0
223300 over = 0
0 commit comments