diff --git a/node/rustchain_v2_integrated_v2.2.1_rip200.py b/node/rustchain_v2_integrated_v2.2.1_rip200.py index 8af0d63..95d449f 100644 --- a/node/rustchain_v2_integrated_v2.2.1_rip200.py +++ b/node/rustchain_v2_integrated_v2.2.1_rip200.py @@ -1023,8 +1023,7 @@ def validate_fingerprint_data(fingerprint: dict, claimed_device: dict = None) -> - C miner format: {"checks": {"clock_drift": true}} """ if not fingerprint: - # Legacy miners without fingerprint get reduced weight (not zero, not full) - return True, "no_fingerprint_data_legacy_reduced" + return False, "missing_fingerprint_data" checks = fingerprint.get("checks", {}) claimed_device = claimed_device or {} @@ -1079,6 +1078,8 @@ def get_check_status(check_data): if clock_check.get("passed") == True and samples == 0 and cv == 0: print(f"[FINGERPRINT] REJECT: clock_drift claims pass but no samples/cv") return False, "clock_drift_no_evidence" + if clock_check.get("passed") == True and samples < 32: + return False, f"clock_drift_insufficient_samples:{samples}" if cv < 0.0001 and cv != 0: return False, "timing_too_uniform" @@ -1888,8 +1889,8 @@ def submit_attestation(): return jsonify(oui_info), 412 # NEW: Validate fingerprint data (RIP-PoA) - fingerprint_passed = True - fingerprint_reason = "not_checked" + fingerprint_passed = False + fingerprint_reason = "missing_fingerprint_data" if fingerprint: fingerprint_passed, fingerprint_reason = validate_fingerprint_data(fingerprint, claimed_device=device) @@ -1900,12 +1901,15 @@ def submit_attestation(): if not fingerprint_passed: # VM/emulator detected - allow attestation but with zero weight print(f"[FINGERPRINT] VM/EMULATOR DETECTED - will receive ZERO rewards") + else: + print(f"[FINGERPRINT] Missing fingerprint payload for miner {miner} - zero reward weight") # NEW: Server-side VM check (double-check device/signals) vm_ok, vm_reason = check_vm_signatures_server_side(device, signals) if not vm_ok: print(f"[VM_CHECK] Miner: {miner} - VM DETECTED (zero rewards): {vm_reason}") fingerprint_passed = False # Mark as failed for zero weight + fingerprint_reason = f"server_vm_check_failed:{vm_reason}" # Record successful attestation (with fingerprint status) record_attestation_success(miner, device, fingerprint_passed, client_ip) @@ -1978,6 +1982,7 @@ def submit_attestation(): "status": "accepted", "device": device, "fingerprint_passed": fingerprint_passed, + "fingerprint_reason": fingerprint_reason, "macs_recorded": len(macs) if macs else 0 }) diff --git a/tests/test_fingerprint.py b/tests/test_fingerprint.py index a41e8e9..dbd3637 100644 --- a/tests/test_fingerprint.py +++ b/tests/test_fingerprint.py @@ -31,10 +31,10 @@ def test_compute_hardware_id_consistency(): assert id1 == id2 def test_validate_fingerprint_data_no_data(): - """Verify handling of missing fingerprint data (legacy reduced).""" + """Missing fingerprint payload must fail validation.""" passed, reason = validate_fingerprint_data(None) - assert passed is True - assert reason == "no_fingerprint_data_legacy_reduced" + assert passed is False + assert reason == "missing_fingerprint_data" def test_validate_fingerprint_data_vm_detection(): """Verify detection of VM indicators.""" @@ -78,6 +78,20 @@ def test_validate_fingerprint_data_clock_drift_threshold(): assert passed is False assert reason == "timing_too_uniform" +def test_validate_fingerprint_data_clock_drift_insufficient_samples(): + """Clock drift cannot pass with extremely low sample count.""" + fingerprint = { + "checks": { + "clock_drift": { + "passed": True, + "data": {"cv": 0.02, "samples": 1} + } + } + } + passed, reason = validate_fingerprint_data(fingerprint) + assert passed is False + assert reason.startswith("clock_drift_insufficient_samples") + def test_validate_fingerprint_data_vintage_stability(): """Verify rejection of suspicious stability on vintage hardware.""" claimed_device = {"device_arch": "G4"}