You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
ROOT CAUSES (post-v29 forensic on live testnet)
1) Empty-batch sync feedback loop: handle_blocks_batch attributed the requested
to_height to the sender's last_block_height even when zero blocks were
returned. Fresh genesis cluster locked into permanent SYNC mode at a phantom
1600 ceiling, never producing the first block.
2) Cost-asymmetric DoS: an external impersonator could spend ~100 bytes/req
firing forged dilithium handshake_proof at QUIC :10876 while the receiver
paid TLS state + ~3.3 KB Dilithium parse + ML-DSA-65 verify per attempt.
3) HealthPing identity-squat: verify_health_ping_signature consumed the PK
embedded in the message body. Any valid ML-DSA-65 keypair could sign
QNET_HEALTH_PING_V1:genesis_node_001:<ts>:<h>, attach its own PK, and have
a poisoned height accepted as authentic — the last identity-squat hole at
the height-gossip layer.
4) /api/v1/peers was the single most-attractive enumeration / DoS target with
no rate-limit, exhausting the warp accept queue under flood.
FIXES (all scale to hundreds of thousands of super-node identities)
A1 handle_blocks_batch attests max(actual block heights) only; empty batches
refresh liveness without raising the cached height.
A2 get_max_peer_height + sync_blockchain_height require ≥ 2 attested peers
before reporting a non-local network height; single-peer claims no longer
steer sync. Initial sync target derives from this quorum-attested view.
A3 PeerInfo.last_height_attested_at TTL window (120 s). Height entries are
accepted into network_height only when their attestation is fresh; stale
or gossip-relayed (unauthenticated) values are excluded.
B1 Early IP-identity gate at handle_server_handshake — claimed
genesis_node_NNN must originate from its pinned IPv4; registered super-
node identity must match its on-chain NodeRegistration endpoint. Mismatch
drops the connection before any Dilithium math is run. NODE_ENDPOINT_REGISTRY
is populated by chain-applied NodeRegistration / NodeReactivation TX.
B2 Per-source-IP failed-handshake token bucket (20 fails / 60 s → 600 s
cooldown). On the next attempt the IP is refused pre-TLS via
incoming.refuse(). Genesis IPs are never banned. Sharded DashMap, O(1).
B3 /api/v1/peers gated through the existing check_api_rate_limit middleware.
C1 verify_health_ping_signature now resolves the verifying PK from
CONSENSUS_PK_REGISTRY against `from` — message-supplied PK is ignored.
Unknown identities are rejected outright.
C2 Audit of remaining identity-binding sites: VrfKeyAnnounce, BlockAttestation,
EmptySlotAttestation, TimeoutVote already consult the registry. Mobile
wallet/RPC paths are wallet-ownership proofs (separate domain) — left
intact.
Scalability invariants preserved: every new lookup is O(1) DashMap (sharded,
lock-free); registry caps mirror existing CONSENSUS_PK_REGISTRY (100 K). No
hot-path scanning, no global locks, no protocol-message format changes
(wire compat with pre-v30 peers retained).
Validation: cargo check + lib tests, 147/147 pass, 0 regressions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
0 commit comments