Files
dragonx/util/block_time_calculator.py
2026-03-10 17:07:16 -05:00

164 lines
5.3 KiB
Python
Executable File

#!/usr/bin/env python3
"""
DragonX RandomX Block Time Calculator
Estimates how long it will take to find a block given your hashrate
and the current network difficulty.
Usage:
python3 block_time_calculator.py <hashrate_h/s> [--difficulty <diff>]
Examples:
python3 block_time_calculator.py 1000 # 1000 H/s, auto-fetch difficulty
python3 block_time_calculator.py 5K # 5 KH/s
python3 block_time_calculator.py 1.2M # 1.2 MH/s
python3 block_time_calculator.py 500 --difficulty 1234.56
"""
import argparse
import json
import subprocess
import sys
# DragonX chain constants
BLOCK_TIME = 36 # seconds
# powLimit = 0x0f0f0f0f... (32 bytes of 0x0f) = (2^256 - 1) / 17
# The multiplier 2^256 / powLimit ≈ 17
POW_LIMIT_HEX = "0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f"
POW_LIMIT = int(POW_LIMIT_HEX, 16)
TWO_256 = 2 ** 256
def parse_hashrate(value):
"""Parse hashrate string with optional K/M/G/T suffix."""
suffixes = {"K": 1e3, "M": 1e6, "G": 1e9, "T": 1e12}
value = value.strip().upper()
if value and value[-1] in suffixes:
return float(value[:-1]) * suffixes[value[-1]]
return float(value)
def get_difficulty_from_node():
"""Try to fetch current difficulty from a running DragonX node."""
try:
result = subprocess.run(
["dragonx-cli", "getmininginfo"],
capture_output=True, text=True, timeout=10
)
if result.returncode == 0:
info = json.loads(result.stdout)
return float(info["difficulty"])
except FileNotFoundError:
pass
except (subprocess.TimeoutExpired, json.JSONDecodeError, KeyError):
pass
# Try with src/ path relative to script location
try:
import os
script_dir = os.path.dirname(os.path.abspath(__file__))
cli_path = os.path.join(script_dir, "src", "dragonx-cli")
result = subprocess.run(
[cli_path, "getmininginfo"],
capture_output=True, text=True, timeout=10
)
if result.returncode == 0:
info = json.loads(result.stdout)
return float(info["difficulty"])
except (FileNotFoundError, subprocess.TimeoutExpired, json.JSONDecodeError, KeyError):
pass
return None
def format_duration(seconds):
"""Format seconds into a human-readable duration string."""
days = seconds / 86400
if days >= 365:
years = days / 365.25
return f"{years:.2f} years ({days:.1f} days)"
if days >= 1:
hours = (seconds % 86400) / 3600
return f"{days:.2f} days ({days * 24:.1f} hours)"
hours = seconds / 3600
if hours >= 1:
return f"{hours:.2f} hours"
minutes = seconds / 60
return f"{minutes:.1f} minutes"
def main():
parser = argparse.ArgumentParser(
description="DragonX RandomX Block Time Calculator"
)
parser.add_argument(
"hashrate",
help="Your hashrate in H/s (supports K/M/G/T suffixes, e.g. 5K, 1.2M)"
)
parser.add_argument(
"--difficulty", "-d", type=float, default=None,
help="Network difficulty (auto-fetched from local node if omitted)"
)
args = parser.parse_args()
try:
hashrate = parse_hashrate(args.hashrate)
except ValueError:
print(f"Error: Invalid hashrate '{args.hashrate}'", file=sys.stderr)
sys.exit(1)
if hashrate <= 0:
print("Error: Hashrate must be positive", file=sys.stderr)
sys.exit(1)
difficulty = args.difficulty
if difficulty is None:
print("Querying local DragonX node for current difficulty...")
difficulty = get_difficulty_from_node()
if difficulty is None:
print(
"Error: Could not connect to DragonX node.\n"
"Make sure dragonxd is running, or pass --difficulty manually.",
file=sys.stderr
)
sys.exit(1)
if difficulty <= 0:
print("Error: Difficulty must be positive", file=sys.stderr)
sys.exit(1)
# Expected hashes to find a block = 2^256 / current_target
# Since difficulty = powLimit / current_target:
# current_target = powLimit / difficulty
# expected_hashes = 2^256 / (powLimit / difficulty) = difficulty * 2^256 / powLimit
expected_hashes = difficulty * TWO_256 / POW_LIMIT
time_seconds = expected_hashes / hashrate
time_days = time_seconds / 86400
# Estimate network hashrate from difficulty and block time
network_hashrate = expected_hashes / BLOCK_TIME
print()
print("=" * 50)
print(" DragonX Block Time Estimator (RandomX)")
print("=" * 50)
print(f" Network difficulty : {difficulty:,.4f}")
print(f" Your hashrate : {hashrate:,.0f} H/s")
print(f" Est. network hash : {network_hashrate:,.0f} H/s")
print(f" Block time target : {BLOCK_TIME}s")
print(f" Block reward : 3 DRGX")
print("-" * 50)
print(f" Expected time to find a block:")
print(f" {format_duration(time_seconds)}")
print(f" ({time_days:.4f} days)")
print("-" * 50)
print(f" Est. blocks/day : {86400 / time_seconds:.6f}")
print(f" Est. DRGX/day : {86400 / time_seconds * 3:.6f}")
print("=" * 50)
print()
print("Note: This is a statistical estimate. Actual time varies due to randomness.")
if __name__ == "__main__":
main()