Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 64 additions & 23 deletions deprecated/old_miners/rustchain_universal_miner.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,10 @@ def detect_hardware():


class UniversalMiner:
def __init__(self, miner_id=None):
def __init__(self, miner_id=None, json_mode=False):
self.node_url = NODE_URL
self.hw_info = detect_hardware()
self.json_mode = json_mode

# Generate miner_id if not provided
if miner_id:
Expand All @@ -173,7 +174,41 @@ def __init__(self, miner_id=None):
self.shares_submitted = 0
self.shares_accepted = 0

self._print_banner()
if self.json_mode:
self.emit("startup", wallet=self.wallet, node=self.node_url,
hardware={"arch": self.hw_info["arch"], "family": self.hw_info["family"]})
else:
self._print_banner()

def emit(self, event_type, **data):
"""Output either JSON or human-readable text based on json_mode"""
if self.json_mode:
output = {"event": event_type, "timestamp": int(time.time()), **data}
print(json.dumps(output))
else:
# Human-readable output
if event_type == "startup":
pass # Already handled in _print_banner
elif event_type == "attestation":
status = data.get("status", "unknown")
epoch = data.get("epoch", 0)
slot = data.get("slot", 0)
print(f"\n[{datetime.now().strftime('%H:%M:%S')}] Attestation: {status} (epoch={epoch}, slot={slot})")
elif event_type == "attestation_error":
print(f" ERROR: {data.get('message')}")
elif event_type == "eligible":
slot = data.get("slot", 0)
print(f"\n[{datetime.now().strftime('%H:%M:%S')}] ELIGIBLE for slot {slot}!")
elif event_type == "header_accepted":
slot = data.get("slot", 0)
print(f" Header ACCEPTED! Slot {slot}")
elif event_type == "header_rejected":
print(f" Header rejected: {data.get('message')}")
elif event_type == "status":
slot = data.get("slot", 0)
submitted = data.get("submitted", 0)
accepted = data.get("accepted", 0)
print(f"[{datetime.now().strftime('%H:%M:%S')}] Slot {slot} | Submitted: {submitted} | Accepted: {accepted}")

def _print_banner(self):
print("=" * 70)
Expand Down Expand Up @@ -214,18 +249,19 @@ def _get_expected_weight(self):

def attest(self):
"""Complete hardware attestation with RIP server"""
print(f"\n[{datetime.now().strftime('%H:%M:%S')}] Attesting hardware...")
self.emit("attestation", status="in_progress")

try:
# Step 1: Get challenge nonce
resp = requests.post(f"{self.node_url}/attest/challenge", json={}, timeout=15)
if resp.status_code != 200:
print(f" ERROR: Challenge failed ({resp.status_code})")
self.emit("attestation_error", message=f"Challenge failed ({resp.status_code})")
return False

challenge = resp.json()
nonce = challenge.get("nonce", "")
print(f" Got challenge nonce: {nonce[:16]}...")
if not self.json_mode:
print(f" Got challenge nonce: {nonce[:16]}...")

# Step 2: Build attestation payload
commitment = hashlib.sha256(f"{nonce}{self.wallet}{self.miner_id}".encode()).hexdigest()
Expand Down Expand Up @@ -261,18 +297,17 @@ def attest(self):
result = resp.json()
if result.get("ok") or result.get("status") == "accepted":
self.attestation_valid_until = time.time() + ATTESTATION_INTERVAL
print(f" SUCCESS: Attestation accepted!")
print(f" Ticket: {result.get('ticket_id', 'N/A')}")
self.emit("attestation", status="success", ticket_id=result.get('ticket_id', 'N/A'))
return True
else:
print(f" WARNING: {result}")
self.emit("attestation_error", message=str(result))
return False
else:
print(f" ERROR: Attestation failed ({resp.status_code})")
self.emit("attestation_error", message=f"Attestation failed ({resp.status_code})")
return False

except Exception as e:
print(f" ERROR: {e}")
self.emit("attestation_error", message=str(e))
return False

def check_eligibility(self):
Expand Down Expand Up @@ -334,11 +369,12 @@ def submit_header(self, slot):

def run(self):
"""Main mining loop"""
print(f"\n[{datetime.now().strftime('%H:%M:%S')}] Starting miner...")
self.emit("startup", status="miner_running")

# Initial attestation
while not self.attest():
print(" Retrying attestation in 30 seconds...")
if not self.json_mode:
print(" Retrying attestation in 30 seconds...")
time.sleep(30)

last_slot = 0
Expand All @@ -354,38 +390,37 @@ def run(self):
slot = eligibility.get("slot", 0)

if eligibility.get("eligible"):
print(f"\n[{datetime.now().strftime('%H:%M:%S')}] ELIGIBLE for slot {slot}!")
self.emit("eligible", slot=slot)

if slot != last_slot:
# Submit header
success, result = self.submit_header(slot)
if success:
print(f" Header ACCEPTED! Slot {slot}")
self.emit("header_accepted", slot=slot)
else:
print(f" Header rejected: {result}")
self.emit("header_rejected", message=str(result))
last_slot = slot
else:
reason = eligibility.get("reason", "unknown")
if reason == "not_attested":
print(f"[{datetime.now().strftime('%H:%M:%S')}] Not attested - re-attesting...")
self.emit("attestation", status="re-attesting")
self.attest()
else:
# Normal not-eligible, just wait
pass

# Status update every 60 seconds
if int(time.time()) % 60 == 0:
print(f"[{datetime.now().strftime('%H:%M:%S')}] Slot {slot} | "
f"Submitted: {self.shares_submitted} | "
f"Accepted: {self.shares_accepted}")
self.emit("status", slot=slot, submitted=self.shares_submitted, accepted=self.shares_accepted)

time.sleep(LOTTERY_CHECK_INTERVAL)

except KeyboardInterrupt:
print("\n\nShutting down miner...")
if not self.json_mode:
print("\n\nShutting down miner...")
break
except Exception as e:
print(f"[{datetime.now().strftime('%H:%M:%S')}] Error: {e}")
self.emit("error", message=str(e))
time.sleep(30)


Expand All @@ -395,10 +430,16 @@ def run(self):
parser = argparse.ArgumentParser(description="RustChain Universal Miner")
parser.add_argument("--miner-id", "-m", help="Custom miner ID")
parser.add_argument("--node", "-n", default=NODE_URL, help="RIP node URL")
parser.add_argument("--json", action="store_true", help="Output structured JSON instead of human-readable text")
parser.add_argument("--dry-run", action="store_true", help="Run in dry-run mode (don't actually mine)")
args = parser.parse_args()

if args.node:
NODE_URL = args.node

miner = UniversalMiner(miner_id=args.miner_id)
miner.run()
miner = UniversalMiner(miner_id=args.miner_id, json_mode=args.json)
if args.dry_run and args.json:
# In dry-run + JSON mode, just emit startup event and exit
print(json.dumps({"event": "ready", "status": "dry-run complete"}))
else:
miner.run()