test scripts
This commit is contained in:
163
util/block_time_calculator.py
Executable file
163
util/block_time_calculator.py
Executable file
@@ -0,0 +1,163 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user