Store Verification (Part 1)
Crate: kinetic-network Stage: 8 Reading time: 30 minutes Depends on: Stage 7 (kinetic-core), Stage 1 (kinetic-types)
What Is This?
This file (verification.rs, specifically lines 1 to 330) represents the primary cryptographic and logical firewall for the Kinetic network’s Distributed Hash Table (DHT). In a standard libp2p Kademlia implementation, nodes will happily accept, store, and propagate any data given to them by their peers. This is fine for a generic file-sharing network, but for a decentralized naming system.
This file intercepts records before they are written to the local Sled database. It enforces strict rules on two fundamental data types:
- HostRoutingRecords: The network’s phonebook entries (IPs mapped to PeerIds).
- Reveals: The final step of the name registration process where a user proves they performed the VDF computation.
Important
If a record fails the checks in this file, it is dropped. It is not saved, and it is not gossiped to other nodes. This file is what keeps the Kinetic network clean from spam, sybil identities, and invalid mathematical proofs.
Why Kinetic Needs This
To understand the necessity of this file, consider the attack vectors it mitigates:
| Attack Vector | Description |
|---|---|
| The Time-Travel Attack (Stale Records) | Without timestamp validation, an attacker could observe your HostRoutingRecord today, save it, wait a year until your IP address changes, and then broadcast the old record. The network would overwrite your new IP with your old one, effectively disconnecting you. Standard Kademlia keeps records until they are evicted. This file forces all routing records to have a recent, verifiable Drand timestamp (a 100 kyn sliding window). If it is outside this 5-minute window, it is instantly discarded. |
| The Infinite Namespace Spam (Cost Evasion) | Kinetic uses Verifiable Delay Functions (VDFs) to attach a computational cost to registering names. Short names cost more time than long names. If this file didn’t exist, a user could submit a Reveal for ai.kyn claiming they did the work, when they actually did nothing. This file recalculates exactly how much work was supposed to be done, verifies the math using the consensus module, and rejects anything that falls short of the threshold. |
| The Identity Forgery (Sybil Attack) | A libp2p PeerId is just a hash. Without validation, node A could broadcast a routing record claiming to be node B. This file enforces that the PeerId must literally contain the raw Ed25519 public key inline. It then ensures that the record must be signed by that exact key. Identity is cryptographically bound to the data payload itself. |
| The Unfair Renewal Trap | If you registered a name by burning CPU for a week, you shouldn’t have to burn CPU for a week every time you renew it. This file implements a complex “Loyalty Discount” system. It proves that you owned the name previously. It checks if you are within the grace period (not expired). It drastically reduces the VDF iteration requirement for your renewal, allowing early adopters to maintain their names cheaply. |
How It Works
The logic is dense and procedural. We break it down into the specific functions handling the verification pipeline.
1. Verifying Host Routing Records
When a peer sends a HostRoutingRecord, it is routed to verify_host_routing_record.
#![allow(unused)]
fn main() {
// -> See: crates/kinetic-network/src/store/verification.rs — Lines 25 to 87
}
Step 1: Freshness via Drand Kyns
- The function takes the
current_drand_kyn(the current global clock pulse) and compares it to therecord.drand_kyn. - It uses
saturating_subfor safety. In Rust, standard subtraction (a - b) can underflow and panic ifb > a.saturating_subbottoms out at0instead. - The record is rejected if it is older than 100 kyns (roughly 5 minutes).
- The record is also rejected if it claims to be from the future
(
record.drand_kyn > current_drand_kyn). - This prevents attackers from setting timestamps to
u64::MAXto permanently pin their record in caches. - Both rejections trigger a specific error log with codes (
KIN-STORE-023andKIN-STORE-024).
Step 2: Multihash Unpacking
- The
host_idstring is parsed into alibp2p::PeerId. - A
PeerIdis essentially a Multihash (a self-describing hash format). - Kinetic requires Ed25519 keys for all node identities.
- libp2p encodes Ed25519 keys inline within the Multihash payload instead of hashing them, because they are already short (32 bytes).
- The code inspects the raw bytes of the multihash digest.
- It expects exactly 38 bytes total for this specific format.
- It enforces a strict byte prefix:
[0x00, 0x24, 0x08, 0x01, 0x12, 0x20]. 0x00indicates an Identity hash (the hash is just the public key itself).- The following bytes indicate Ed25519 formatting and the 32-byte length.
- If this exact prefix matches, it copies the final 32 bytes into a new array. This is the raw Ed25519 public key.
Step 3: Signature Verification
- The extracted bytes are converted into an
ed25519_dalek::VerifyingKey. - The
record.signaturebytes are parsed into aSignature. - The record’s
signable_bytes(NETWORK_ID)are generated. - Including the
NETWORK_IDensures that a valid signature from the testnet cannot be maliciously replayed on the mainnet to overwrite routing tables. - The signature is verified. If it passes, the routing record is authentic and timely.
2. Extracting Integers from Storage
#![allow(unused)]
fn main() {
// -> See: crates/kinetic-network/src/store/verification.rs — Lines 89 to 100
}
- The
get_u64_from_sledfunction is a small, inline utility. - When retrieving counters or timestamps from the Sled database, they are returned as raw byte vectors.
- This function ensures the byte vector is exactly 8 bytes long.
- It converts it into a fixed array using
try_into. - It then parses it into a
u64usingu64::from_be_bytes. - Big-endian is standard for network transmission and ensures that when integers are used as database keys, their lexical sorting matches their numerical sorting.
3. Computing Required VDF Iterations
When a Reveal is submitted, the network must know how many VDF iterations to demand. This is handled by compute_required_iterations.
#![allow(unused)]
fn main() {
// -> See: crates/kinetic-network/src/store/verification.rs — Lines 114 to 274
}
Step 1: Sanity Checks and BLS Verification
- It first validates the name format (
is_valid_apex_name). - It decodes the
drand_signaturefrom hex. - If the node is not in Dev Mode, it performs a complex BLS signature
verification using
drand_verify::G2PubkeyRfc. - It hardcodes the Drand network’s public key.
- It maps it to the G2 curve.
- It verifies that the provided signature matches the
drand_kyn. - This proves the randomness wasn’t faked by the user.
Step 2: The Base Iteration Cost
- It fetches the
base_required_iterationsby querying the consensus math module with the name’s length. - Shorter names return exponentially higher base numbers.
Step 3: The Previous Proof Validation (Loyalty Discount)
- If the
Revealcontains aprevious_proof, the user is requesting a renewal discount. - The network trusts nothing and verifies the old proof from scratch.
- It creates a new
Sha256hasher (prev_hasher). - It hashes the name bytes (
reveal.name.as_bytes()). - It hashes the old salt (
prev.salt). - It decodes the old Drand signature to verify its validity too.
- It hashes the old Drand signature to extract the old randomness seed.
- It hashes the user’s public key (
reveal.pubkey). - It reconstructs the old
Commitmenthash from all these pieces combined. - It passes this reconstructed commitment, along with the old VDF proof and
old iteration count, to the
VdfEngine. - The engine runs the math to ensure the old proof was genuinely valid.
Step 4: Age and Governance Pause Compensation
- Even if the old proof is valid mathematically, it must not be too old.
- The code calculates the
paused_kynsby querying theGLOBAL_GOVERNANCE_STATE. - If the blockchain was halted for maintenance for 5,000 kyns, those kyns are essentially erased from history so users aren’t penalized for downtime.
- The
effective_ageis calculated as:Current Kyn - Old Kyn - Paused Kyns. - The proof is only valid if
effective_age <= 2 * RESQUARING_EPOCH_KYNS. This is the grace period window.
Step 5: Applying the Discount Tiers
- If all checks pass, the loyalty discount is granted based on the length
of the normalized name (ignoring the
.kynsuffix). - Length 1: The requirement drops to the absolute floor
(
CONSENSUS_VDF_DISCOUNT_MIN_ITERATIONS). This is an extreme reward for keeping a 1-character name alive. - Length 2-6: The requirement is cut in half (50% discount).
- Length 7-10: The requirement is reduced by 80% (divided by 5).
- Length 11+: The requirement is reduced by 85%.
- The code uses
std::cmp::maxto ensure the final iteration count never falls below the absolute network minimum, preventing free registrations.
4. Verifying the Reveal (Initial Steps)
#![allow(unused)]
fn main() {
// -> See: crates/kinetic-network/src/store/verification.rs — Lines 296 to 330
}
- When
verify_revealbegins, it immediately checks theGLOBAL_GOVERNANCE_STATE. - If
is_haltedis true, the entire reveal process aborts and returns aNetworkHaltederror. - It then validates the Reveal payload structure itself.
- If not in Dev Mode, it verifies the Ed25519 signature on the Reveal.
- It includes the
NETWORK_IDto prevent cross-chain replay attacks.
Key Pieces
verify_host_routing_record()
- Location:
verification.rs— Lines 25 to 87 - What it does: Extracts an Ed25519 public key directly from a libp2p multihash and uses it to verify the signature and timestamp of a routing announcement.
- Why it matters: It is the primary defense against network mapping poisoning and identity theft in the DHT.
get_u64_from_sled()
- Location:
verification.rs— Lines 89 to 100 - What it does: Safely decodes 8-byte big-endian vectors from the Sled
database into usable Rust
u64integers. - Why it matters: Provides a safe, panic-free way to read timestamps and counters from raw disk storage.
compute_required_iterations()
- Location:
verification.rs— Lines 114 to 274 - What it does: Dynamically calculates the computational price (in VDF iterations) required to register or renew a name. It validates BLS signatures and historical VDF proofs to grant discounts.
- Why it matters: This function implements the core economic policy of the Kinetic namespace. It ensures name scarcity while making long-term ownership computationally viable.
verify_reveal() (Lines 296-330)
- Location:
verification.rs— Lines 296 to 330 - What it does: The entry point for validating a full
Revealpayload, checking governance halts, validating structural correctness, and verifying the user’s signature. - Why it matters: Serves as the first barrier against malformed or maliciously crafted name registrations.
How This Connects to the Rest of Kinetic
This verification module is where the abstract types from kinetic-core meet the harsh reality of the open internet.
- CROSS-CRATE:
HostRoutingRecordandReveal— These data structures are defined, explained, and serialized indocs/learn/core/03_network_types.md(Stage 7). - CROSS-CRATE:
VdfEngine— The trait definition for the VDF verifier lives indocs/learn/core/04_traits.md(Stage 7), while the actual mathematical implementation lives inkyn-vdf(Stage 6). - CROSS-CRATE:
GLOBAL_GOVERNANCE_STATE— The global mutex that tracks network pauses and emergency halts. Documented indocs/learn/core/06_governance.md(Stage 7). - External Dependency:
drand_verify— An external crate responsible for mapping public keys to the BLS12-381 G2 curve and validating aggregate signatures from the League of Entropy.
Quick Reference
Note
Error Triggers in this file:
KIN-STORE-023: HostRoutingRecord is older than 100 kyns.KIN-STORE-024: HostRoutingRecord is timestamped in the future.KIN-STORE-028: Reveal contains a malformed hex string for the Drand signature.KIN-STORE-029: Reveal name fails structural validation.KIN-STORE-030: Reveal Drand signature fails BLS curve verification.
| Loyalty Discount Tier | Reduction |
|---|---|
| 1 char | 100% reduction (Drops to 1,000,000 minimum limit) |
| 2-6 chars | 50% reduction |
| 7-10 chars | 80% reduction |
| 11+ chars | 85% reduction |
Multihash Identity Prefix:
To extract an Ed25519 key from a libp2p PeerId, Kinetic expects exactly 38 bytes starting with [0x00, 0x24, 0x08, 0x01, 0x12, 0x20].
Open Questions / Things to Revisit
Warning
- Multihash Extraction Brittleness The method used in
verify_host_routing_recordto extract the Ed25519 public key relies on hardcoded byte offset slices (&bytes[6..38]) and exact prefix matching. If libp2p introduces a new multihash encoding variant or changes the internal representation of an Identity hash, this logic will instantly fail and reject all routing records. Should Kinetic migrate to using libp2p’s built-inPeerId::as_public_key()method instead of raw slice manipulation?- Denial of Service Vector In
compute_required_iterations, if a malicious user attaches a bogusprevious_proofto aReveal, the node still performs multiple SHA256 hashes and invokes theVdfEngine::verifyfunction before discovering it is invalid. Since VDF verification can take milliseconds depending on the engine backend, could an attacker spam the network with fake renewals to exhaust CPU resources on verifying nodes? The current fallback simply logs a warning and charges full price, which does not penalize the attacker for forcing the node to do unnecessary math.- Dev Mode Branching in Consensus Logic The
kinetic_core::config::is_dev_mode()check is deeply embedded in the verification logic to skip BLS checks and signature checks. Having dev mode checks in the middle of core network validation logic can be a security risk if the dev mode flag is accidentally enabled in production. Is there a safer architectural way to mock these checks, perhaps by injecting a Mock trait implementation, rather than branching the logic inline?