Skip to content

Commit 20d04cc

Browse files
AIQnetLabclaude
andcommitted
fix: v25.2 — unify TX-sig verifier (apply<->gossip) + permanent attacker-PK blacklist
- block_pipeline TX-sig verifier now delegates to the canonical verify_dilithium_tx_signature_async helper used by gossip/RPC paths, via parallel join_all on SIGVERIFY_RUNTIME. The previous inline hex-decode path only understood the user-wallet format (hex 3309-byte sig, hex 1952-byte PK) and hard-rejected every node-signed system TX (HeartbeatCommitment, PingCommitment, PingCommitmentWithSampling, LightNodeEligibilityBitmap) whose signature is the dilithium_sig_<id>_<b64> wrapper and whose dilithium_public_key field carries the node_id string (PK resolved via CONSENSUS_PK_REGISTRY). First inclusion in the commitment window of any epoch (last 50 blocks) caused every receiver to HARD REJECT the block, freezing the testnet at h=14350. - consensus_crypto: replace the partial SPOOFER_PK_OFFENSES log-suppression with a permanent attacker-PK blacklist keyed by SHA3-256 fingerprint of the attacker's Dilithium3 public key. First Tier-2 PK mismatch is conclusive proof of impersonation in an immutable registry, so the key is banned on first sighting. Fast-path drop runs at the top of verify_with_real_dilithium, before the registry lock and ML-DSA-65 open call. Bounded memory (12k entries, lazy LRU eviction). Mirrored to RocksDB metadata CF with prefix attacker_pk_bl/<fp>, replayed at boot before the QUIC listener opens. Single [CRIT][SECURITY] log per first sighting, silent thereafter. - unified_p2p: BlacklistReason::PkImpersonation variant (hard, not subject to reputation recovery — attacker key structurally fails Tier-2 verify forever). Public API on SimplifiedP2P: is_pk_blacklisted, report_pk_impersonation, clear_attacker_pk, clear_attacker_pk_blacklist_all, attacker_pk_blacklist_len. - storage: save/load_attacker_pk_entry on PersistentStorage and Storage wrappers (metadata CF, length-prefixed self-describing record format). - node boot: load_all_attacker_pk_entries -> seed -> install persist callback that resolves storage via try_get_storage at every call, runs before any verify path is reachable. - Light = mobile-only API client (ZERO chain storage, max_storage_bytes=0). v3.18 Full tier fully removed. All stale references in code, comments and docs cleaned up. Backward-compat parser keeps "full" -> NodeType::Super for legacy strings. - 2-tier log format throughout: [INFO|WARN|ERR|CRIT][TAG]. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 6a3792e commit 20d04cc

23 files changed

Lines changed: 1215 additions & 367 deletions

QNet_Whitepaper.md

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -937,13 +937,18 @@ Response requirements:
937937
└── Super Nodes: 90% (9+ out of 10 heartbeats in current window)
938938
(v3.18: Full Nodes removed)
939939
940-
Architecture (v2.23):
941-
├── Light: Full/Super nodes ping via FCM V1 API → Light signs challenge → attestation
942-
├── Full/Super: Self-attest via heartbeats (10 per 4h window, HYBRID signature - quantum-resistant)
943-
├── 256-shard ping system: Light nodes assigned to pingers based on SHA3-256(node_id)[0]
940+
Architecture (v3.18+):
941+
├── Light: 5 Genesis nodes ping mobile Light nodes (FCM V1 / UnifiedPush / Polling)
942+
│ → Light signs challenge with Dilithium3 ping-delegation key
943+
│ → pinger creates HYBRID Ed25519+Dilithium3 attestation
944+
├── Super: Self-attest via heartbeats (10 per 4h window, HYBRID signature, quantum-resistant)
945+
├── Linear sharding: each of 5 Genesis pings 20% of Light registry (sorted by node_id)
944946
├── Light node reputation: Fixed at 70 (immutable, not affected by events)
945-
├── Storage: Tiered (Light ~10MB thin client, Full ~500GB pruned, Super ~2TB full)
946-
└── Mobile monitoring: viewing only, no attestations
947+
├── Storage tiers (v3.18+ — only two roles exist):
948+
│ * Light → 0 bytes of on-device chain data (mobile pure API client;
949+
│ balance/TX history read via REST API on Super nodes)
950+
│ * Super → ~2 TB (full history, archival, no pruning)
951+
└── Mobile role: ping-response only — no block download, no broadcast, no relay
947952
948953
Deterministic On-Chain Heartbeats (v2.41):
949954
├── Heartbeats collected via gossip → stored in heartbeat_history (RAM)
@@ -963,8 +968,10 @@ Deterministic On-Chain Heartbeats (v2.41):
963968

964969
**NEW Rewards eligibility (unified for ALL node types):**
965970
- **ALL Nodes (Light/Full/Super)**: Reputation ≥70 required for network to ping you → NEW rewards
966-
- **Light Nodes**: Do NOT participate in consensus (viewing only)
967-
- **Full/Super Nodes**: Participate in consensus (reputation ≥70 required)
971+
- **Light Nodes**: Mobile-only, do NOT participate in consensus. No on-device chain storage; do not download or relay blocks. Earn rewards purely by responding to Genesis-driven PoP pings.
972+
- **Super Nodes**: Participate in consensus (reputation ≥70 required). Produce and validate blocks; archive full history; serve Light-node REST API queries.
973+
974+
*(v3.18: the "Full" tier was removed from the protocol. Only Light and Super exist.)*
968975

969976
**Claiming rewards logic:**
970977
- **NEW rewards**: Network pings you ONLY if reputation ≥70 (applies to ALL node types)
@@ -1096,7 +1103,8 @@ Recovery: If no issues for 5 minutes → reset counter
10961103

10971104
```
10981105
get_sync_peers_filtered(max: 20):
1099-
1. Filter: Exclude Light nodes (don't store full blocks)
1106+
1. Filter: Exclude Light nodes (mobile API clients — store ZERO chain
1107+
data on-device, cannot serve sync requests).
11001108
2. Filter: Exclude blacklisted peers
11011109
3. Filter: Check consensus_score ≥ 70% (Byzantine-safe)
11021110
4. Sort by:
@@ -1863,11 +1871,12 @@ pub struct QNetSignature {
18631871

18641872
**Node-Specific Storage Requirements:**
18651873

1866-
| Node Type | Storage | Data Stored |
1867-
|-----------|---------|-------------|
1868-
| **Light** | **~10 MB** | Nothing (thin client - all data via RPC) |
1869-
| **Full** | ~50 GB | Sliding window (100K blocks) + snapshots |
1870-
| **Super** | 400+ GB | Full history with archival |
1874+
| Node Type | On-device chain data | Notes |
1875+
|-----------|----------------------|-------|
1876+
| **Light** | **0 bytes** | Pure mobile API client — all chain data fetched via REST API on Super nodes. Wallet keeps user's own TX list in AsyncStorage / localStorage. |
1877+
| **Super** | ~2 TB (full archival) | Complete chain history, no pruning. Only role that stores and serves blocks. |
1878+
1879+
*(v3.18+ has only two node roles: Light and Super. The historical "Full" tier has been removed from the protocol.)*
18711880

18721881
**Pruning System (v2.19.7):**
18731882

@@ -2415,10 +2424,14 @@ Security Thresholds:
24152424
├── 10-69: Limited network access (no NEW rewards, no network pings, no consensus)
24162425
└── <10: Network ban enforced (can still claim OLD rewards)
24172426
2418-
NEW Rewards Distribution (unified for ALL node types):
2427+
NEW Rewards Distribution (v3.18+ — only two node roles: Light and Super):
24192428
├── ALL Nodes: reputation ≥70 required (network pings you → NEW rewards)
2420-
├── Light Nodes: Do NOT participate in consensus (viewing only)
2421-
└── Full/Super Nodes: Participate in consensus (reputation ≥70 required)
2429+
├── Light Nodes: Mobile-only PoP — do NOT participate in consensus. No
2430+
│ on-device chain storage; do not download or relay
2431+
│ blocks. Earn rewards purely by responding to
2432+
│ Genesis-driven pings.
2433+
└── Super Nodes: Server-class validators (reputation ≥70 required).
2434+
Produce/validate blocks, archive full history.
24222435
24232436
OLD Rewards Claiming:
24242437
├── No reputation requirement (only wallet ownership)

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -602,9 +602,10 @@ For production testnet deployment, see: **[PRODUCTION_TESTNET_MANUAL.md](PRODUCT
602602
- Auto-cleanup: Keeps only last 5 snapshots
603603
- Zstd-15 compression for snapshot data
604604
- SHA3-256 integrity verification
605-
- **Tiered Storage** (v2.19.10 - corrected estimates with Zstd-3 only):
606-
- Light nodes: **~100 MB** (headers only, FIFO auto-rotation)
607-
- Full nodes: **~500 GB** (30-day pruning window)
605+
- **Tiered Storage** (v3.18+ — Light/Super only; "Full" merged into Super):
606+
- Light nodes: **0 bytes of on-device chain data** — pure mobile API
607+
client. Balance / TX history fetched via REST API on Super nodes;
608+
wallet app stores user TX list in AsyncStorage / localStorage.
608609
- Super nodes (archival): **~2 TB** (full history, no pruning)
609610
- Storage with Zstd-3 (~50% reduction):
610611
- 100 TPS: ~220 GB/year (Super), ~18 GB (Full 30d)

core/qnet-consensus/src/burn_security.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ pub struct NodeActivationInfo {
8080
/// When they activated
8181
pub activated_at: u64,
8282

83-
/// Node type (Light/Full/Super)
83+
/// Node type (v3.18+: Light or Super; "Full" tier removed from the protocol)
8484
pub node_type: NodeType,
8585

8686
/// Current reputation

0 commit comments

Comments
 (0)