mirror of
https://github.com/curly60e/pyblock.git
synced 2026-08-13 12:33:15 +02:00
- Replace all shell=True subprocess calls with Python-native processing (nodeconnection.py, SPV/nodeconnection.py, SPV/ppi.py) - Mask sensitive inputs (private keys, passwords, tokens) with getpass - Add threading.Lock to block_explorer.py shared state - Use json.loads() instead of fragile string splitting in apisnd.py - Add path validation before file open in apisnd.py - Replace random.randint with secrets.randbelow for mining nonces - Fix destructive exception handlers in clone.py and feed.py - Replace bare except clauses with specific exceptions + logging - Remove unused imports (psutil, xmltodict, block_visualizer, base64, say) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
123 lines
4.7 KiB
Python
123 lines
4.7 KiB
Python
import asyncio
|
|
from rich.live import Live
|
|
from rich.table import Table
|
|
from rich.panel import Panel
|
|
from rich.layout import Layout
|
|
from rich.text import Text
|
|
from rich.align import Align
|
|
from rich.console import Group
|
|
import subprocess
|
|
import json
|
|
import time
|
|
from threading import Event, Lock, Thread
|
|
from execute_load_config import load_config
|
|
|
|
# Load configuration
|
|
path, settings, settingsClock = load_config()
|
|
|
|
_block_tables_lock = Lock()
|
|
|
|
def fetch_blockchain_info(path):
|
|
raw_info = subprocess.run([path["bitcoincli"], "getblockchaininfo"], capture_output=True, text=True)
|
|
blockchain_info = json.loads(raw_info.stdout)
|
|
return blockchain_info
|
|
|
|
def fetch_block_info(path, blockhash):
|
|
raw_block_info = subprocess.run([path["bitcoincli"], "getblock", blockhash], capture_output=True, text=True)
|
|
block_info = json.loads(raw_block_info.stdout)
|
|
return block_info
|
|
|
|
def create_block_info_table(block_height, block_data):
|
|
table = Table(title=f"Block #{block_height}")
|
|
table.add_column("Metric", style="green")
|
|
table.add_column("Value", style="yellow")
|
|
|
|
table.add_row("Transactions", str(block_data['nTx']))
|
|
table.add_row("Size", f"{block_data['size']} bytes")
|
|
table.add_row("Weight", f"{block_data['weight']} weight units")
|
|
table.add_row("Version", str(block_data['version']))
|
|
table.add_row("Merkle Root", block_data['merkleroot'])
|
|
table.add_row("Time", time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(block_data['time'])))
|
|
table.add_row("Median Time", time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(block_data['mediantime'])))
|
|
table.add_row("Nonce", str(block_data['nonce']))
|
|
table.add_row("Bits", str(block_data['bits']))
|
|
table.add_row("Difficulty", f"{block_data['difficulty']:.2f}")
|
|
table.add_row("Chainwork", block_data['chainwork'])
|
|
table.add_row("Previous Block", block_data['previousblockhash'])
|
|
if 'nextblockhash' in block_data:
|
|
table.add_row("Next Block", block_data['nextblockhash'])
|
|
|
|
return table
|
|
|
|
def fetch_and_store_block_data(path, start_height, count, block_tables):
|
|
latest_height = start_height
|
|
|
|
for i in range(count): # Limitar a los bloques solicitados
|
|
block_height = latest_height - i
|
|
block_hash = subprocess.run([path["bitcoincli"], "getblockhash", str(block_height)], capture_output=True, text=True).stdout.strip()
|
|
block_data = fetch_block_info(path, block_hash)
|
|
table = create_block_info_table(block_height, block_data)
|
|
with _block_tables_lock:
|
|
block_tables.append(table)
|
|
|
|
def background_block_fetch(path, block_tables, stop_event):
|
|
latest_height = fetch_blockchain_info(path)['blocks']
|
|
while not stop_event.is_set():
|
|
current_height = fetch_blockchain_info(path)['blocks']
|
|
if current_height > latest_height:
|
|
latest_height = current_height
|
|
with _block_tables_lock:
|
|
block_tables.clear()
|
|
fetch_and_store_block_data(path, current_height, 3, block_tables)
|
|
time.sleep(10)
|
|
|
|
async def display_blocks_info():
|
|
layout = Layout()
|
|
layout.split_column(
|
|
Layout(name="header", size=3),
|
|
Layout(name="main", ratio=1),
|
|
Layout(name="footer", size=1),
|
|
)
|
|
layout["main"].split_row(
|
|
Layout(name="recent_blocks", ratio=1),
|
|
)
|
|
layout["footer"].update(Text("Cypherpunk style loading..."))
|
|
|
|
layout["recent_blocks"].update(Panel(Text("Cypherpunk Style loading..."), title="Recent Blocks"))
|
|
layout["header"].update(Text("Block Monitor", style="bold cyan"))
|
|
|
|
block_tables = []
|
|
blockchain_info = fetch_blockchain_info(path)
|
|
latest_block_height = blockchain_info['blocks']
|
|
fetch_and_store_block_data(path, latest_block_height, 3, block_tables)
|
|
|
|
stop_event = Event()
|
|
fetch_thread = Thread(target=background_block_fetch, args=(path, block_tables, stop_event))
|
|
fetch_thread.start()
|
|
|
|
async def input_handler():
|
|
while True:
|
|
key = await asyncio.get_event_loop().run_in_executor(None, input)
|
|
if key == 'q':
|
|
stop_event.set()
|
|
fetch_thread.join()
|
|
break
|
|
|
|
with Live(layout, refresh_per_second=1, screen=True):
|
|
input_task = asyncio.create_task(input_handler())
|
|
while not stop_event.is_set():
|
|
with _block_tables_lock:
|
|
tables_snapshot = list(block_tables)
|
|
recent_blocks_group = Group(*tables_snapshot)
|
|
centered_recent_blocks = Align.center(recent_blocks_group)
|
|
|
|
layout["recent_blocks"].update(Panel(centered_recent_blocks, title="Recent Blocks"))
|
|
layout["footer"].update(Text("Running the node."))
|
|
|
|
await asyncio.sleep(1)
|
|
|
|
def call_blocks():
|
|
asyncio.run(display_blocks_info())
|
|
|
|
if __name__ == "__main__":
|
|
call_blocks()
|