Skip to content

fix(arch_cross_validation): flat fingerprints lose all features and are scored as spoofed#7976

Open
Vyacheslav-Tomashevskiy wants to merge 1 commit into
Scottcjn:mainfrom
Vyacheslav-Tomashevskiy:fix/arch-cross-validation-flat-fingerprint
Open

fix(arch_cross_validation): flat fingerprints lose all features and are scored as spoofed#7976
Vyacheslav-Tomashevskiy wants to merge 1 commit into
Scottcjn:mainfrom
Vyacheslav-Tomashevskiy:fix/arch-cross-validation-flat-fingerprint

Conversation

@Vyacheslav-Tomashevskiy

Copy link
Copy Markdown
Contributor

Bug

extract_all_features() in node/arch_cross_validation.py has an explicit branch for the flat fingerprint layout (no checks wrapper) — it collects the known check keys straight off the fingerprint:

if not checks and isinstance(fingerprint, dict):
    checks = {k: v for k, v in fingerprint.items()
              if k in ("clock_drift", "cache_timing", "simd_identity", ...)}

But the loop that follows reads each check with check_value.get("data", {}) — and a flat check is the payload; it has no "data" key. So every flat check resolves to {}, the feature map comes back empty, and that flat branch is silently dead code.

The four sibling helpers (extract_simd_features, extract_clock_features, extract_cache_features, extract_thermal_features) all already handle both shapes with X.get("data", X) — this one place doesn't.

Impact

An identical, legitimate G4 fingerprint scores differently depending only on its shape:

fingerprint form score >= 0.70 threshold verdict
wrapped (checks + data) 1.0 pass consistent
flat (same evidence) 0.69 fail SUSPICIOUS: significant arch mismatch

0.69 lands just under ARCH_VALIDATION_SCORE_THRESHOLD = 0.70 in fleet_immune_system.py, so the decision actually flips: passed=Falsevalidated_bucket = "modern" → the vintage miner loses the arch bonus it earned. Real hardware gets treated as a spoofer.

Scope, stated honestly: run_and_store_arch_validation() is reached via fleet_immune_system, whose own docstring says it "must be called from the attestation submission flow" — so as of this commit the hook isn't yet wired into the node's submit path. This is a latent correctness bug in the validation library, not live reward loss today. I'm not inflating it beyond that.

Fix

One line — use the same X.get("data", X) fallback the sibling extractors use, so a flat check falls back to itself instead of emptying the branch.

Verification

  • node/test_arch_cross_validation.py — added test_g4_flat_fingerprint_not_flagged, asserting the flat form scores >= 0.8 and equals the wrapped score.
    • Fails on clean main: AssertionError: flat G4 fingerprint falsely flagged: 0.69
    • Passes with fix (flat == wrapped == 1.0)
  • Full test_arch_cross_validation.py suite green — including test_g4_x86_spoofing, confirming spoof detection is not weakened (a spoofed fingerprint still scores low).
  • tests/test_bucket_spoof_fix.py (the dependent bucket/spoof suite): 6 passed.

Verified against upstream/main @ 96660f06.


RTC: RTCd1554f0f35576faf01d386a6be1c947f560dd0b7

…core as spoofed

extract_all_features() has an explicit branch for the flat fingerprint
layout (no "checks" wrapper): it collects the known check keys straight
off the fingerprint. But the loop that follows reads each check with
check_value.get("data", {}), and a flat check IS the payload - it has no
"data" key. Every flat check therefore resolves to {} and the feature
map comes back empty, so the flat branch is silently dead.

Consequence: an identical, legitimate G4 fingerprint scores 1.0 in the
wrapped form but 0.69 in the flat form - just under the 0.70
ARCH_VALIDATION_SCORE_THRESHOLD in fleet_immune_system - and is reported
as "SUSPICIOUS: significant arch mismatch", dropping the miner to the
"modern" bucket and its vintage bonus.

Fix mirrors the X.get("data", X) fallback that extract_simd_features,
extract_clock_features, extract_cache_features and extract_thermal_features
all already use. Spoof-detection tests are unaffected (a spoofed
fingerprint still scores low).
@github-actions github-actions Bot added BCOS-L1 Beacon Certified Open Source tier BCOS-L1 (required for non-doc PRs) BCOS-L2 Beacon Certified Open Source tier BCOS-L2 (required for non-doc PRs) node Node server related labels Jul 15, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Welcome to RustChain! Thanks for your first pull request.

Before we review, please make sure:

  • Non-doc PRs have a BCOS-L1 or BCOS-L2 label
  • Doc-only PRs are exempt from BCOS tier labels when they only touch docs/**, *.md, or common image/PDF files
  • New code files include an SPDX license header
  • You've tested your changes against the live node

Bounty tiers: Micro (1-10 RTC) | Standard (20-50) | Major (75-100) | Critical (100-150)

A maintainer will review your PR soon. Thanks for contributing!

@github-actions github-actions Bot added the size/S PR: 11-50 lines label Jul 15, 2026
@Scottcjn

Copy link
Copy Markdown
Owner

Genuine fix for flat-fingerprint false-spoofing. Held on one confirmation: can you point to a production miner client that actually emits the flat (unwrapped) fingerprint shape this handles? If a real client sends it, this is High-value and pays immediately; want to confirm it's not a test-only shape first. — Sophia

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

BCOS-L1 Beacon Certified Open Source tier BCOS-L1 (required for non-doc PRs) BCOS-L2 Beacon Certified Open Source tier BCOS-L2 (required for non-doc PRs) node Node server related size/S PR: 11-50 lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants