Skip to content

Fix zero rewards for vintage miners: three-state (unmeasured) fingerprint checks - #8064

Open
Scottcjn wants to merge 1 commit into
mainfrom
fix/capability-aware-fingerprint-unmeasured
Open

Fix zero rewards for vintage miners: three-state (unmeasured) fingerprint checks#8064
Scottcjn wants to merge 1 commit into
mainfrom
fix/capability-aware-fingerprint-unmeasured

Conversation

@Scottcjn

Copy link
Copy Markdown
Owner

What was broken

Several of the most emblematic vintage miners earned exactly zero RTC, not because the hardware was suspect but because of validation ordering:

  • Apple II 6502 (miners/apple2/miner6502.c): sends a fingerprint with no checks map at all (flat cycle_count / ram_kb / aux_ram, plus simd_identity as a bare hash string). validate_fingerprint_data bailed at empty_fingerprint_checks (line 3442 pre-patch) before the console/vintage branch (line 3471 pre-patch) could ever run. fingerprint_passed = 0, weight 0. On top of that, the reverse-x86 ARM heuristic in _detect_arm_evidence classified it as ARM/aarch64 (0.0005x) because family defaults to x86 and the brand MOS6502 is unrecognized.
  • i386 (miners/i386/miner386.c): sends a flat payload with arch, cpu_vendor, clock_ticks, ram_kb, hw_fingerprint at top level and no fingerprint or device object at all. Server saw no_fingerprint_submitted and a claimed arch of modern. Weight 0.
  • Pico bridge console miners (miners/pico_bridge/pico_bridge_miner.py, checks built at lines 352-378): emit only one of the six rotating RIP-309 names (anti_emulation; the other three bridge checks are ctrl_port_timing, rom_execution_timing, bus_jitter, which are not rotating names). Because _fingerprint_check_passed treated an absent check as failed, active_ratio was 0.25 when anti_emulation happened to be in the active 4-of-6 set and 0.00 otherwise. NES/SNES/N64/Genesis/Game Boy/PS1 miners were penalized or zeroed every epoch. They also failed Phase 1 validation outright: the bridge reports its anti-emulation evidence under emulator_indicators, which the evidence whitelist did not recognize, so they got anti_emulation_no_evidence.
  • Honest 486: has no usable TSC, honestly reports clock_drift as failed, and lost the whole epoch for saying so.

The model

Checks are no longer just pass/fail. They can be unmeasured: the hardware structurally cannot perform that measurement. A 6502 has no SIMD unit. A 386/486 has no TSC. A Pico bridge cannot run cache-timing sweeps on the console CPU. unmeasured is strictly neutral: it is excluded from the active_ratio denominator, no credit and no penalty.

Both binary alternatives are wrong:

  • Treating a missing check as passing is the fail-open hole that lets a spoofer claim every check it never ran.
  • Treating a missing check as failing zeroes the entire vintage and console fleet, which is what was happening.

Implementation:

  • _structurally_unmeasurable_checks() maps a device class to the rotating checks it cannot perform. Standalone micros (6502, 65C816, Z80, 386, 486, and friends) cannot perform any of the six; Pico-bridge consoles can perform only anti_emulation.
  • The class comes from the server-derived device (derive_verified_device), never from the raw client claim. A new _detect_capability_limited_device() step classifies console and micro claims before the exotic/ARM heuristics (which is also what stops the Apple II from being mislabeled ARM), and it refuses the classification when _capability_contradiction() finds modern-platform evidence: a 64-bit machine field, a modern CPU brand string, or x86/PowerPC SIMD features in the fingerprint. A vetoed claim is downgraded to x86_64/default (0.8x), or falls through to the ARM override (0.0005x) on positive ARM evidence, so a modern box can never do better by claiming to be limited than by being honest.
  • evaluate_rotating_fingerprint_checks now takes the verified device and evaluates each active check three ways via _fingerprint_check_state. active_ratio = passed / (passed + failed); unmeasured checks are not in the denominator. With zero measurable active checks the ratio is 1.0 and validity rests entirely on validate_fingerprint_data. Callers that do not pass a device keep the old strict behavior bit for bit, so nothing is weakened for hardware that can measure.
  • validate_fingerprint_data determines the device class before the empty-checks bail. Standalone micros with no checks map are accepted on device-native evidence (at least two flat measurement fields such as cycle_count, clock_ticks, ram_kb). An honest incapacity report (486 clock_drift failure) is neutral instead of fatal. anti_emulation failures are never neutralized for any class, and a reported pass without raw evidence is still rejected.
  • The attest endpoint folds the i386 miner's flat arch / cpu_vendor into the device dict and rebuilds a fingerprint from its flat evidence fields (micro claims only, still fully validated and veto-checked).
  • HARDWARE_WEIGHTS gains MOS (6502 2.8, 65C816 2.7) and Zilog (Z80 2.6) families, mirroring the exact values already assigned to the same silicon in the console table. The i386 claim canonicalizes to the existing x86/386 (2.5) and 486 (2.0) tiers. The settlement-side ANTIQUITY_MULTIPLIERS table already has 6502, 386, 486 and the console names, so the canonical arch strings plug straight into get_time_aged_multiplier.

Who was earning zero and now does not

Apple II 6502 miners, i386/486 DOS-class miners, and every Pico-bridge console miner (NES, SNES, N64, Genesis, Game Boy, Game Boy Color, Master System, Saturn, PS1). An honest 486 that reports its missing TSC no longer forfeits the epoch.

Anti-spoof, stated plainly

A modern machine that claims a limited class while presenting any modern evidence (SIMD features, 64-bit machine field, modern CPU brand) is vetoed, held to the full check requirements, and its derived class is downgraded, so its weight cannot exceed its honest 0.8x. This is covered by tests in both directions. Known limit, unchanged from the status quo for the console path: a spoofer that fabricates a byte-identical minimal vintage payload with no contradicting evidence is not distinguishable over HTTP alone. The existing hardware binding, IP rate limits, entropy scoring and temporal-consistency review still apply to those, and maintainers may want to require bridge-style external attestation for standalone micros later.

Verified line numbers (pre-patch, against this repo)

  • validate_fingerprint_data def: line 3416; empty_fingerprint_checks return: line 3442; console/vintage branch: line 3471 (task description said "around 3441/3471", both confirmed)
  • _fingerprint_check_passed: line 2214; evaluate_rotating_fingerprint_checks: line 2288
  • Pico bridge check construction: miners/pico_bridge/pico_bridge_miner.py lines 352-378 (task said 355-378)

Tests

New: tests/test_capability_aware_fingerprint.py, 20 tests, all passing. They cover: Apple II / i386 / console / honest 486 validating and getting non-zero weight with active_ratio 1.0 and correct family/arch; console anti_emulation failure still fatal; modern box claiming 6502/486 vetoed and downgraded with full denominator restored; ARM SBC claiming 6502 keeping the 0.0005x penalty; legacy callers keeping strict absent-equals-failed behavior; PowerPC vintage path unchanged.

Regression runs on this branch:

  • test_attestation_fuzz, test_attestation_regression, test_fingerprint, test_fingerprint_improved, test_device_classification_corpus, test_enroll_fingerprint_fallback, test_rip201_bucket_spoof: 209 passed, 18 skipped, 2 xfailed
  • broader sweep (-k "attest or enroll or fingerprint or epoch or weight or rotation or classif or spoof or console or vintage or miner"): 958 passed, 19 skipped, 2 xfailed, 4 failed in test_epoch_settlement_formal.py; those same 4 fail identically on a clean checkout of main, so they are pre-existing and unrelated
  • py_compile clean on the node file and the new test file

Rollout notes

  • This is not deployed anywhere. No production node was touched. Rolling it out to the attestation nodes is a maintainer decision.
  • No client changes are required: the server-side neutral treatment fully covers the console miners' single-rotating-check payloads as they exist today. That said, a coordinated client-side change teaching the Pico bridge to also report its native checks under stable names would let future server logic score them directly instead of neutrally, and is worth considering.
  • The MOS/Zilog weight values mirror existing console-table numbers but are still a tokenomics call; happy to adjust.

…ingerprint checks)

Apple II 6502, i386, Pico-bridge console and honest 486 miners were all
earning zero because validation-order bugs treated checks their hardware
structurally cannot perform as failures:

- validate_fingerprint_data bailed with empty_fingerprint_checks before
  the console/vintage branch could run (6502 sends no checks map, the
  i386 miner sends no fingerprint object at all)
- evaluate_rotating_fingerprint_checks counted every absent RIP-309
  check as failed, so Pico bridge consoles (which emit only
  anti_emulation of the six rotating names) settled at active_ratio
  0.25 or 0.00 every epoch
- an honest 486 reporting its clock_drift failure (no usable TSC) lost
  the whole epoch for telling the truth
- the reverse-x86 ARM heuristic reclassified the Apple II as
  ARM/aarch64 at 0.0005x

Fix: checks now have a third state, unmeasured, granted only from the
server-derived device class and only when no modern-platform evidence
contradicts the claim. unmeasured is neutral: excluded from the
active_ratio denominator, no credit, no penalty. Treating absent as
passed would reopen the fail-open spoofing hole; treating it as failed
zeroes the vintage fleet. anti_emulation failures are never neutralized.

A modern box claiming to be capability-limited is vetoed by
_capability_contradiction (machine field, CPU brand, SIMD evidence) and
downgraded to x86_64/default, so the claim can never beat its honest
classification. Positive ARM evidence keeps the 0.0005x ARM override.

Not deployed anywhere; needs a maintainer rollout decision.

Signed-off-by: Scott <scottbphone12@gmail.com>
@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 tests Test suite changes size/XL PR: 500+ lines labels Jul 25, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ BCOS v2 Scan Results

Metric Value
Trust Score 49/100
Certificate ID BCOS-c48e969c
Tier L1 (not met)

BCOS Badge

What does this mean?

The BCOS (Beacon Certified Open Source) engine scans for:

  • SPDX license header compliance
  • Known CVE vulnerabilities (OSV database)
  • Static analysis findings (Semgrep)
  • SBOM completeness
  • Dependency freshness
  • Test infrastructure evidence
  • Review attestation tier

Full report | What is BCOS?


BCOS v2 Engine - Free & Open Source (MIT) - Elyan Labs

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/XL PR: 500+ lines tests Test suite changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants