Skip to content
Open
Show file tree
Hide file tree
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
2 changes: 1 addition & 1 deletion miners/windows/rustchain_miner_setup.bat
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ set "PYTHON_URL=https://www.python.org/ftp/python/3.11.5/python-3.11.5-amd64.exe
set "PYTHON_INSTALLER=%SCRIPT_DIR%python-3.11.5-amd64.exe"
set "MINER_URL=https://raw.githubusercontent.com/Scottcjn/Rustchain/main/miners/windows/rustchain_windows_miner.py"
set "MINER_SCRIPT=%SCRIPT_DIR%rustchain_windows_miner.py"
set "MINER_SHA256=2381b9448c83556fae84e7ddd20a61789f718a4e4a01dc6986731b5064043811"
set "MINER_SHA256=b2abc6bf75acc562297137b20f719c3ef850a4de43377b55157e1d90a043340a"
set "CRYPTO_URL=https://raw.githubusercontent.com/Scottcjn/Rustchain/main/miners/windows/miner_crypto.py"
set "CRYPTO_SCRIPT=%SCRIPT_DIR%miner_crypto.py"
set "CRYPTO_SHA256=ffe2e4c78fdc3f53c129a2ef820cc84549a5720655140e69a3e0baf1f7f385fa"
Expand Down
13 changes: 9 additions & 4 deletions miners/windows/rustchain_windows_miner.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,8 @@ def _mine_loop(self, callback):
"type": "share",
"submitted": self.shares_submitted,
"accepted": self.shares_accepted,
"success": success
"success": success,
"error": "" if success else self.last_header_error,
})
time.sleep(10)
except Exception as e:
Expand Down Expand Up @@ -725,8 +726,10 @@ def generate_header(self, slot):
}

def submit_header(self, payload):
"""Submit one signed header and remember accepted slots."""
"""Submit one signed header and remember attempted slots."""
slot = payload.get("header", {}).get("slot")
if slot is not None:
self._last_submitted_slot = slot
try:
response = requests.post(
f"{self.node_url}/headers/ingest_signed",
Expand All @@ -740,7 +743,6 @@ def submit_header(self, payload):
and bool(result.get("ok"))
)
if success:
self._last_submitted_slot = slot
self.last_header_error = ""
else:
self.last_header_error = self._response_diagnostic(response)
Expand Down Expand Up @@ -842,10 +844,13 @@ def _format_headless_event(evt):
t = evt.get("type")
if t == "share":
ok = "OK" if evt.get("success") else "FAIL"
return (
line = (
f"[share] submitted={evt.get('submitted')} "
f"accepted={evt.get('accepted')} {ok}"
)
if not evt.get("success") and evt.get("error"):
line = f"{line} error={evt.get('error')}"
return line
if t == "attest":
return (
f"[attest] {evt.get('message')} "
Expand Down
2 changes: 1 addition & 1 deletion setup_miner.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
},
"Windows": {
"url": "https://raw.githubusercontent.com/Scottcjn/Rustchain/main/miners/windows/rustchain_windows_miner.py",
"sha256": "2381b9448c83556fae84e7ddd20a61789f718a4e4a01dc6986731b5064043811",
"sha256": "b2abc6bf75acc562297137b20f719c3ef850a4de43377b55157e1d90a043340a",
},
}

Expand Down
10 changes: 10 additions & 0 deletions tests/test_setup_miner_downloads.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import hashlib
import re
from pathlib import Path

import setup_miner


ROOT = Path(__file__).resolve().parents[1]
WINDOWS_BOOTSTRAP = ROOT / "miners" / "windows" / "rustchain_miner_setup.bat"


def test_setup_miner_pins_current_miner_artifacts():
Expand All @@ -19,6 +21,14 @@ def test_setup_miner_pins_current_miner_artifacts():
assert artifact["sha256"] == hashlib.sha256(expected_files[platform].read_bytes()).hexdigest()


def test_windows_bootstrap_pins_current_miner_script():
content = WINDOWS_BOOTSTRAP.read_text(encoding="utf-8")
match = re.search(r'^set "MINER_SHA256=([0-9a-fA-F]{64})"$', content, re.MULTILINE)
assert match is not None
expected = hashlib.sha256((ROOT / "miners" / "windows" / "rustchain_windows_miner.py").read_bytes()).hexdigest()
assert match.group(1).lower() == expected


def test_setup_miner_pins_current_macos_artifact():
expected_file = ROOT / "miners" / "macos" / "rustchain_mac_miner_v2.5.py"
artifact = setup_miner.MINER_ARTIFACTS["Darwin"]
Expand Down
10 changes: 10 additions & 0 deletions tests/test_windows_headless_lifecycle_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@ def test_ready_status_and_headless_format_include_lifecycle_details():
"message": "Epoch enrollment succeeded",
"miner_id": "windows_abc123",
}) == "[enroll] Epoch enrollment succeeded miner_id=windows_abc123"
assert module._format_headless_event({
"type": "share",
"submitted": 3,
"accepted": 1,
"success": False,
"error": "HTTP 403 error=no pubkey registered for miner",
}) == (
"[share] submitted=3 accepted=1 FAIL "
"error=HTTP 403 error=no pubkey registered for miner"
)


def test_ensure_ready_surfaces_attestation_diagnostics(monkeypatch):
Expand Down
46 changes: 46 additions & 0 deletions tests/test_windows_miner_chain_identity.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,49 @@ def fake_post(url, **kwargs):
"json": payload,
"timeout": 15,
}


def test_submit_deduplicates_rejected_slot_and_records_diagnostic(monkeypatch):
module = load_miner_module()
miner = make_miner(module)
payload = {
"miner_id": "RTCwallet",
"header": {"slot": 43, "miner": "RTCwallet", "timestamp": 1234},
"message": "00",
"signature": "cd" * 64,
"pubkey": "ab" * 32,
}

def fake_post(url, **kwargs):
return FakeResponse(
{"ok": False, "error": "no pubkey registered for miner"},
status_code=403,
)

monkeypatch.setattr(module.requests, "post", fake_post)

assert miner.submit_header(payload) is False
assert miner._last_submitted_slot == 43
assert miner.last_header_error == "HTTP 403 error=no pubkey registered for miner"


def test_submit_without_slot_does_not_clear_last_submitted_slot(monkeypatch):
module = load_miner_module()
miner = make_miner(module)
miner._last_submitted_slot = 41
payload = {
"miner_id": "RTCwallet",
"header": {"miner": "RTCwallet", "timestamp": 1234},
"message": "00",
"signature": "cd" * 64,
"pubkey": "ab" * 32,
}

def fake_post(url, **kwargs):
return FakeResponse({"ok": False, "error": "missing slot"}, status_code=400)

monkeypatch.setattr(module.requests, "post", fake_post)

assert miner.submit_header(payload) is False
assert miner._last_submitted_slot == 41
assert miner.last_header_error == "HTTP 400 error=missing slot"
Loading