Store Handlers and Verification Tests
Crate: kinetic-network Stage: 8 Reading time: 20 minutes Depends on: 16_store_core.md, 15_store_verification.md, kinetic-core, kinetic-types, kinetic-kid
What Is This?
This document provides an exhaustive explanation of the test suites that validate the behavior of the Kinetic network’s localized storage handlers and data verification logic.
Specifically, it covers the test cases found in handlers_tests.rs and verification_tests.rs within the store module of the kinetic-network crate.
These test suites are meticulously designed to simulate and assert the correct processing of network primitives under various conditions.
The primitives tested include:
- Reveals (namespace claims)
- Heartbeats (liveness signals)
- Host routing updates
- Decentralized identity (KID) authorizations
The tests simulate various edge cases, time-shifting scenarios, and active attack vectors.
They serve as the executable proof that the theoretical security boundaries designed into the Kinetic network actually function correctly in the compiled Rust binaries.
Why Kinetic Needs This
In a decentralized peer-to-peer network like Kinetic, nodes operate in an environment of zero inherent trust.
Nodes cannot blindly trust the incoming data they receive from other peers.
Every piece of data traversing the gossipsub network or the Kademlia DHT must be rigorously validated.
It must be scrutinized.
It must be bounds-checked before it is ever committed to local persistent storage.
If the handlers and verification logic have bugs, or if they behave unexpectedly under load, the network becomes immediately vulnerable to several critical, systemic failures:
-
Spam and Resource Exhaustion (Denial of Service):
- Without strict rate limiting on incoming reveals, an attacker could trivially flood the network.
- They could send valid but excessive name registrations.
- Because evaluating a reveal requires verifying a Verifiable Delay Function (VDF) proof—a computationally expensive operation—and then writing it to the Sled database, a flood would bloat the storage of all nodes.
- It would force them to expend all available CPU cycles.
- This would effectively halt the network.
- The rate-limiting tests ensure this theoretical defense is practically enforced.
-
Replay Attacks and State Reversion:
- If heartbeats are not monotonic, a malicious peer could capture an old, valid heartbeat from the network traffic.
- They could replay it hours or days later.
- This could falsely keep a name alive that should have expired.
- Or worse, it could overwrite a newer, legitimate routing state with an obsolete one.
- The heartbeat monotonicity tests ensure time only flows forward.
-
Routing Poisoning and Topology Disruption:
- If host routing records are accepted without strict freshness checks relative to the current Drand pulse, the DHT becomes poisoned.
- If malformed
PeerIdformats cause the Rust runtime to panic, an attacker could either misdirect network traffic to malicious nodes or perform a denial-of-service attack. - A single malformed identity key could crash every node it touches.
- The tests prove that the network handles toxic data gracefully.
-
Post-Quantum Cryptography Integration Failures:
- The Kinetic network relies on ML-DSA (Module-Lattice-Based Digital Signature Algorithm) for future-proof security.
- If the authorization logic fails to properly parse ML-DSA signatures, the post-quantum security guarantee of the network collapses.
- If it fails to base64-decode the controller keys embedded within KID documents, it collapses.
- The tests act as a live rehearsal of this complex cryptographic chain.
These test files are not just checking for typos.
They are the cryptographic and logical firewall for the node’s local database.
They ensure that Saif’s architectural rules—such as the maximum age of a pulse, the sliding window of rate limits, and the exact byte-layout of a signed identity payload—are correctly enforced in the living codebase.
How It Works
The test suites break down into two main domains:
- Testing the internal state mutations of the store (
handlers_tests.rs) - Testing the stateless cryptographic and logical boundary checks (
verification_tests.rs).
We will explore both in deep, step-by-step detail.
1. Store Instantiation and Mocking (The Sandbox)
Before any tests can run that mutate state, the test environment must synthesize a fully functioning KineticRecordStore.
This involves wiring together several heavy dependencies that normally run in a live node.
They must be kept lightweight and isolated enough for a unit test.
-> See: crates/kinetic-network/src/store/handlers_tests.rs — Lines 30 to 51
The setup_store function acts as the central dependency injector for the test suite.
It constructs the sandbox with the following steps:
- Ephemeral Storage:
- It creates a temporary directory using
tempfile::tempdir(). - The
SledStoragedatabase is initialized here. - This is crucial because it ensures the database starts empty for every test.
- It is automatically deleted from the operating system when the test variable goes out of scope.
- No state bleeds between tests.
- It creates a temporary directory using
- Identity Mocking:
- It generates a random ed25519
PeerId. - This acts as the core identity of the mock store.
- In a real node, this comes from the local keystore.
- It generates a random ed25519
- VDF Engine Injection:
- It instantiates a
ChiaVdfEngine. - Even though tests might use dummy proofs, the store’s constructor requires an object that implements the
VdfEnginetrait.
- It instantiates a
- Initial State Seeding:
- It sets the initial Drand pulse (the
drand_kyn) to a baseline integer of 100. - This provides a known starting point for time-relative tests.
- It sets the initial Drand pulse (the
- Limit Configuration:
- It configures the maximum reveals allowed per window (passed in as an argument, e.g., 5).
This function returns both the initialized KineticRecordStore and an Arc pointer to the underlying storage engine.
This allows tests to manipulate the store exactly as the network handlers would.
2. Testing Rate Limiting (The Sliding Time Window)
To prevent network flooding, the store limits how many reveals a specific namespace can process within a rolling time window.
For example, a maximum of 5 reveals per rolling hour.
-> See: crates/kinetic-network/src/store/handlers_tests.rs — Lines 53 to 111
The test test_rate_limiting verifies the logic of this sliding window algorithm.
It bypasses the network layer and directly manipulates the store’s internal memory structures:
- The store tracks reveal timestamps in a
std::collections::VecDeque. - This is a double-ended queue.
- It is mapped to each namespace string inside a hash map (
accepted_reveals_timestamps). - The test creates a dummy namespace
domain0.kinetic. - It artificially injects 5 timestamps representing recent reveals.
- This maxes out the theoretical limit.
- To simulate the passage of time without actually pausing the test thread for an hour, the test manually clears the queue.
- It then manually injects specific historical timestamps.
- It calculates
now = web_time::Instant::now(). - It injects one timestamp from
now - 4000 seconds. - This is older than the 3600-second (1-hour) limit, so it should be considered expired.
- It injects a second timestamp from
now - 3000 seconds. - This is within the 1-hour window, so it is active.
- It then injects 5 more timestamps that occurred exactly at
now. - The core logic is then executed: the code iterates over the front of the
VecDeque. - If the time elapsed since a timestamp is greater than 3600 seconds, it uses
deque.pop_front()to discard it. - Finally, the assertion checks that after pruning, exactly 6 timestamps remain in the queue.
- The 6 timestamps are the 1 active historical one from 3000 seconds ago, plus the 5 recent ones at
now. - The 4000-second old timestamp is gone.
- This confirms that expired records are evicted correctly.
- The sliding window moves forward.
- The node frees up memory over time.
3. Testing Heartbeat Monotonicity (Defeating Replay Attacks)
Heartbeats are how nodes prove they are currently online and actively participating in maintaining a name.
They are cryptographically bound to a specific Drand pulse (drand_kyn).
Time in Kinetic is measured in these pulses.
-> See: crates/kinetic-network/src/store/handlers_tests.rs — Lines 113 to 151
The test_heartbeat_monotonicity test ensures that a node will reject any heartbeat that attempts to rewind time.
This is a classic replay attack.
The test proceeds as follows:
- First, it seeds the store with a dummy reveal for the namespace
test.kinetic. - It manually generates a real ML-DSA signature keypair.
- It attaches the public key bytes to the reveal.
- It then sets the existing state: it inserts a record into
last_heartbeats_by_name. - This indicates that the most recent valid heartbeat for this name occurred at Drand pulse 200.
- Next, it constructs the malicious payload: a new
Heartbeatstruct for the same name. - It sets its
latest_drand_kynto 49. - Because 49 is less than 200, this heartbeat is in the past relative to what the node already knows.
- To ensure the test evaluates the temporal logic and doesn’t just fail a signature check, the test actively signs this stale heartbeat.
- It uses the correct ML-DSA private key, producing a valid cryptographic signature over a stale payload.
- The heartbeat is passed to
store.handle_heartbeat(). - The handler analyzes the payload.
- It looks up the existing pulse of 200.
- It compares it to the incoming pulse of 49.
- It immediately halts.
- The test asserts that the result is an error specifically matching
KineticStoreError::StaleHeartbeat. - The takeaway is absolute: a valid signature does not save a temporally stale payload.
4. Testing Host Routing Freshness (Topological Expiry)
Routing records in the DHT tell peers how to physically connect to a specific service.
Like heartbeats, they decay and must be continually refreshed by the host.
-> See: crates/kinetic-network/src/store/verification_tests.rs — Lines 8 to 28
The test_host_routing_freshness function rigorously checks the maximum age constraint of routing data:
- It assumes a hypothetical current Drand round of 1000.
- It creates a
HostRoutingRecordand artificially sets itsdrand_kynto 850. - This means the record is claiming to be from 150 pulses ago.
- The Kinetic architecture enforces a strict maximum age for routing records (typically 100 rounds).
- When the
verify_host_routing_recordfunction analyzes this record, it performs subtraction. - 1000 minus 850 equals 150.
- Since 150 is greater than 100, the record is flagged as unacceptably old.
- The assertion confirms the error.
- Notably, the test expects it to return
KineticStoreError::InvalidHostRouteSignature. - While this might seem misnamed initially, architecturally it implies that the freshness check is the very first gate inside the signature verification wrapper.
- By failing the freshness check, the signature is deemed structurally invalid for the current network epoch.
- This bypasses the expensive cryptographic math entirely.
5. Testing Peer ID Format Safeties (Defensive Parsing)
When working with distributed hash tables, nodes constantly receive raw byte arrays over the wire.
These arrays claim to be valid PeerId representations.
If a node panics (crashes) while trying to parse unexpectedly malformed bytes, a single malicious packet can take down the node.
-> See: crates/kinetic-network/src/store/verification_tests.rs — Lines 30 to 53
The test_peer_id_extraction_safeguard ensures robust, panic-free error handling when confronting invalid identity data:
- It uses the
libp2p::multihashmodule to deliberately construct a multihash using the SHA2-256 algorithm. - The Kinetic network specifically expects identity multihashes (typically ed25519 public keys embedded directly in the hash).
- It embeds this intentionally incompatible multihash format into a
HostRoutingRecord. - When the
verify_host_routing_recordlogic executes, it attempts to extract the public key from the string representation of thisPeerId. - The internal parsing logic recognizes that a SHA2-256 multihash does not contain an inline public key.
- The critical part: the test asserts that the function gracefully returns a
KineticStoreError::InvalidPublicKeyenum variant. - It does not call
.unwrap()on aNonevalue. - A panic would trigger a thread panic and a node crash.
- This is a vital demonstration of defensive programming at the network edge.
6. Testing ML-DSA Authorized KID Validation (The Post-Quantum Chain)
This is the most complex and critical test in the suite.
It ensures that the post-quantum signature schemes correctly bind decentralized identities (KIDs) to network namespaces.
-> See: crates/kinetic-network/src/store/verification_tests.rs — Lines 55 to 148
The test_mldsa_authorized_kid_validation test constructs an entire cryptographic chain of trust from scratch:
- Key Generation:
- It uses the
ml_dsacrate to generate a fresh, correct ML-DSA-65 keypair.
- It uses the
- DID Construction:
- It simulates the identity generation process.
- It takes the public key bytes.
- It hashes them using SHA-256.
- It converts the hash to a hex string.
- It prepends the Kinetic DID prefix (
did:kinetic:) to formulate a valid Decentralized Identifier string.
- Document Creation:
- It builds a comprehensive
KidDocumentstruct. - Crucially, it populates the
controller_keysvector. - It sets the key type to “MlDsa65”.
- It provides the Base64-URL-encoded version of the ML-DSA public key.
- It builds a comprehensive
- Inner Signature:
- The DID document itself requires a self-signature to prove ownership.
- The test calls
.sign()on the document using the private ML-DSA key.
- Authorization Wrapper:
- The signed DID document is then packaged inside an
AuthorizedKidstruct. - It targets a specific network name (
test.kinetic). - This outer wrapper is then signed again by the ML-DSA key.
- The signature is over a specific signable byte payload that includes the
NETWORK_ID. - This inclusion prevents cross-network replay attacks.
- The signed DID document is then packaged inside an
- Validation Execution:
- It passes this nested, doubly-signed payload into the
verify_authorized_kidfunction. - It also passes a mock existing Kademlia record.
- It passes this nested, doubly-signed payload into the
- The Assertion:
- The assertion ensures that the result is either
Okor, at the very least, does not fail with aKineticStoreError::InvalidKidSignature. - This proves that the multi-layered ML-DSA signature verification interoperate correctly.
- The parsing logic interoperates correctly.
- Hex matching interoperates correctly.
- Base64 decoding interoperates correctly without throwing false negatives.
- The assertion ensures that the result is either
Key Pieces
-
setup_store- What it does: Bootstraps an ephemeral environment with a temporary database, a mock peer identity, and a VDF engine.
- Where it lives:
crates/kinetic-network/src/store/handlers_tests.rs— Lines 30 to 51 - Why it matters: Provides a clean, isolated state for state-mutating tests without polluting the developer’s local filesystem or causing test crosstalk.
-
test_rate_limiting- What it does: Verifies the
VecDequesliding window logic for incoming reveals, checking expiration thresholds. - Where it lives:
crates/kinetic-network/src/store/handlers_tests.rs— Lines 53 to 111 - Why it matters: Proves the node has an active defense against malicious peers attempting to overwhelm the local Sled database with rapid-fire reveal spam.
- What it does: Verifies the
-
test_heartbeat_monotonicity- What it does: Rejects heartbeats containing a
drand_kynpulse that is lower than the previously stored pulse. - Where it lives:
crates/kinetic-network/src/store/handlers_tests.rs— Lines 113 to 151 - Why it matters: Defends the network against temporal replay attacks where old, validly-signed heartbeats are rebroadcast to manipulate the active state of a namespace.
- What it does: Rejects heartbeats containing a
-
test_host_routing_freshness- What it does: Ensures routing records cannot exceed a maximum age threshold relative to the current live Drand round.
- Where it lives:
crates/kinetic-network/src/store/verification_tests.rs— Lines 8 to 28 - Why it matters: Keeps the network topology accurate and responsive by enforcing strict TTLs (Time To Live) on peer locations in the DHT.
-
test_peer_id_extraction_safeguard- What it does: Passes an unsupported multihash format (SHA-256 instead of an identity hash) as a Peer ID to verify the parser fails safely.
- Where it lives:
crates/kinetic-network/src/store/verification_tests.rs— Lines 30 to 53 - Why it matters: Protects the node from panic-based denial-of-service attacks triggered by maliciously crafted, un-parsable routing payloads.
-
test_mldsa_authorized_kid_validation- What it does: Generates and validates a complete, accurate ML-DSA signed KID document and nested authorization payload.
- Where it lives:
crates/kinetic-network/src/store/verification_tests.rs— Lines 55 to 148 - Why it matters: Unarguably proves that the integration between the complex post-quantum signature schemes and the decentralized identity layer works flawlessly in the compiled binary.
How This Connects to the Rest of Kinetic
- CROSS-CRATE: The tests instantiate and utilize
Reveal,VdfProof,Heartbeat, andHostRoutingRecordstructs. These are defined and explained in detail withinkinetic-coreandkinetic-types. - CROSS-CRATE: The identity validation relies on
KineticDidandKidDocument, which are the foundational primitives defined and explained inkinetic-kid. - Storage Layer Interface: The tests utilize
SledStorageimported fromkinetic-storage. This serves as the underlying, persistent state backend for thesetup_storeinitialization, demonstrating how the network layer talks to the disk. - Drand Pulses as Clocks: The
drand_kynacts as the decentralized, universal clock. The tests demonstrate how the network uses these integer pulses to measure elapsed time, evaluate freshness, and define causality, ignoring the local hardware system clocks of individual machines.
Quick Reference
- Rate Limiting Mechanism: Managed via a
std::collections::VecDequeof timestamps. Expired timestamps are sequentially popped from the front of the queue to maintain the sliding window. - Temporal Monotonicity: An incoming pulse must always be greater than (
>) the existing pulse to be accepted by the heartbeat handler. - Freshness Boundaries: The calculation
current_drand_round - record.drand_kynmust yield a result less than the network’s allowed maximum age constant. - Safe Payload Parsing: Converting untrusted network strings into libp2p
PeerIdstructs requires explicit error mapping to catch unsupported multihash formats safely, preventingunwrap()panics. - ML-DSA Signatures: ML-DSA keypairs are utilized to sign both the inner DID Document and the outer wrapping
AuthorizedKidpayload, creating a nested proof of authorization. - Wasm-Compatible Time Modules: The
web_time::Instantandweb_time::Durationmodules are used instead of the standardstd::timeto guarantee that time calculations remain compatible if the node is compiled to WebAssembly.
Open Questions / Things to Revisit
-
Error Type Clarity in Freshness Checks: In
test_host_routing_freshness, providing a structurally stale pulse causes the assertion to expect aKineticStoreError::InvalidHostRouteSignature.- Architecturally, one might expect a distinct error variant like
StaleRoutingRecordrather than overloading and falling back to a generic “invalid signature” error. - Saif should review if returning a signature error for a pure age violation obscures debugging logs in production.
- Architecturally, one might expect a distinct error variant like
-
Hardcoded Genesis Assumptions: The
setup_storefunction hardcodes the initial Drand pulse to 100.- If the global network configuration ever changes to require a higher genesis pulse (e.g., 500000), these tests might instantly fail due to violating genesis bounds constraints.
- It may be wise to link this initial integer to a constant derived from the core crate.
-
ML-DSA Test Code Density: The
test_mldsa_authorized_kid_validationtest is dense and procedural.- It manually implements the hashing and base64 encoding steps line-by-line.
- Ideally, these steps belong inside a fluent builder pattern within the
kinetic-kidcrate itself, which would reduce the boilerplate required in network-level testing.
-
Time Mocks vs. web_time: While
web_timeis used for Wasm compatibility, the tests still rely on sleeping or manually injecting historical timestamps.- A true deterministic time-mocking library might make the rate-limiting tests even more robust and isolated from the host machine’s execution speed.