Core Errors: Network, VDF, DNS, and Drand
Crate: kinetic-core
Stage: 7
Reading Time: ~45 minutes
Depends On:
kinetic-core/src/error/network.rskinetic-core/src/error/vdf.rskinetic-core/src/error/dns.rskinetic-core/src/error/drand.rs
What Is This?
This document provides a highly detailed, comprehensive examination of four distinct error handling modules within the kinetic-core crate.
These modules are:
network.rsvdf.rsdns.rsdrand.rs
In the Kinetic architecture, error handling is treated as a first-class citizen. It is a critical component of the state machine. Rather than relying on a single, monolithic, and vaguely defined error enumerator, the system fragments its failure states. It places them into highly specific, protocol-aware domains. This fragmentation allows the node to differentiate between completely unrelated classes of failure. For example, an operational timeout in the peer-to-peer network is fundamentally different from a mathematical cryptographic failure in a verifiable delay function (VDF). These four modules define the rigorous standards by which Kinetic handles interactions. They govern interactions with the underlying libp2p network stack. They dictate the failure states for Wesolowski VDF generation and validation. They provide the structural boundary for decentralized DNS zone validation within DHT reveal payloads. They also manage the Drand randomness beacon acquisitions. These acquisitions form the bedrock of the protocol’s time-locks. Each error type is equipped with strict RFC 7807 codes. They also have carefully defined severity levels. Finally, they provide actionable retry heuristics that the surrounding async event loops rely upon heavily.
Why Kinetic Needs This
In a peer-to-peer environment, simply stating “an error occurred” is insufficient. The system must be capable of autonomously deciding its next operational action without any human intervention. For instance, consider if a peer submits a VDF proof over the network. The local system needs to know exactly why the proof failed. Did it fail because the peer is intentionally malicious, resulting in a mathematical mismatch? Or did it fail because the local engine crashed due to an out-of-memory platform error? By categorizing these errors strictly, the system can apply tailored logic to every situation. It can penalize a malicious peer by dropping them from the Kademlia routing table. It can retry a transient network timeout with a randomized exponential backoff. It can gracefully degrade to local storage caches if an external randomness beacon is currently offline.
Furthermore, these specific error types prevent the daemon from panicking under edge-case loads. The error namespaces in Kinetic are rigidly structured and enforced. These namespaces include:
KIN-NET-NNNKIN-VDF-NNNKIN-DNS-NNNKIN-DRA-NNN
This rigid structure ensures that frontend clients interacting with the node’s RPC or HTTP interfaces can parse failure states programmatically. Remote network debuggers and local logging aggregators can trace the exact lineage of a failure through the network stack. Developers and client applications do not need to parse brittle, continuously changing error string messages. Instead, they can rely on the stable protocol error codes provided by these modules. These codes act as an immutable contract between the node and the outside world. Without this extreme level of granularity, the Kinetic daemon would be completely unable to maintain stability. It must survive the harsh realities of async network churn. It must survive CPU-bound cryptographic operations. It must survive potentially malformed or explicitly malicious network payloads.
Scenario: Handling Network Churn
Imagine a peer connects to the node and requests a DHT record.
Halfway through the transfer, the peer loses internet connectivity.
Without NetworkClientError::StreamDropped, the node might treat this as a systemic disk failure and crash.
By explicitly identifying the stream drop, the node simply logs a warning and closes the local socket, preserving system health.
Scenario: Defending Against JSON Bombs
An attacker attempts to upload a DNS zone payload containing 10,000 nested JSON arrays.
Without DnsError::NestedTooDeeply, the serde_json parser might overflow the stack memory. The explicit bounds checking prevents this.
How It Works
This section unpacks the implementation details, variant structures, and trait implementations for each of the four error modules.
We will examine how they map to specific protocol behaviors.
We will see how they interface with external dependencies like libp2p, chiavdf, reqwest, and serde_json.
We will also examine how they handle Rust-specific nuances like manual trait implementations.
Network Errors (network.rs)
The network error module serves as a critical isolation layer.
It sits between the raw, underlying libp2p primitives and Kinetic’s internal command loop.
It converts complex state transitions into standard, understandable Kinetic errors.
These transitions originate from:
- The Kademlia Distributed Hash Table (DHT).
- The GossipSub pub/sub router.
- Multiplexed network streams.
-> See: kinetic-core/src/error/network.rs — Lines 19-38
The NetworkClientError enum is decorated with standard Rust derives.
Specifically: #[derive(Error, Debug, PartialEq, Eq)].
It isolates client-side operational failures encountered during network command dispatch.
The variants are specifically designed to capture the lifecycle of asynchronous peer-to-peer operations:
Timeout:- Emitted when a requested DHT query or an open stream operation exceeds its hard deadline.
- In P2P networks, peers vanish silently without sending TCP FIN packets.
- Timeouts are standard operating procedure and must be handled gracefully.
Offline:- Emitted when the local node detects it has zero reachable peers.
- When the node is offline, it cannot perform any outbound operations.
- It must wait for bootstrap nodes to connect.
RoutingTableEmpty:- Emitted specifically when the Kademlia routing table contains no known peers.
- A node might have generic connections via GossipSub.
- However, if they aren’t structured in the DHT routing table, Kademlia operations (
PUT/GET) fundamentally cannot proceed.
ChannelClosed:- A critical internal fault.
- Kinetic uses internal asynchronous channels (like
mpscoroneshot) to pass messages. - These messages go between the HTTP API threads and the singleton network event loop.
- If this channel closes unexpectedly, the node is internally broken and cannot dispatch commands.
StreamDropped:- Happens when the remote peer ungracefully severs the underlying TCP or QUIC stream.
- This occurs before a response is fully delivered to the local node.
UnsupportedProtocol:- Fired during the libp2p
identifyprotocol handshake. - If the remote peer speaks an incompatible version of the Kinetic protocol, communication halts immediately.
- This prevents parsing errors down the line.
- Fired during the libp2p
GossipSubError(String):- Wraps dynamic, stringified errors from the GossipSub protocol.
- Examples include publish rejections, subscription limits, or mesh validation failures.
StoreError(String):- Wraps dynamic errors from the Kademlia record store.
- Examples include out-of-space errors when trying to
PUTa record into the local database.
Other(String):- A fallback catch-all for miscellaneous faults that do not fit into the standard taxonomy.
-> See: kinetic-core/src/error/network.rs — Lines 41-94
The impl NetworkClientError block provides the operational metadata required by the broader system state machine:
code():- Exhaustively matches every variant to a string in the
KIN-NET-001throughKIN-NET-009range.
- Exhaustively matches every variant to a string in the
error_type_uri():- Constructs a full URI for RFC 7807 compliance.
- It does this by prepending the official documentation URL.
severity():- Splits the errors by operational impact.
- Timeouts, empty routing tables, offline status, dropped streams, and GossipSub errors are treated as
Severity::Warning. - This is because they are expected realities of decentralized networking.
- Conversely,
UnsupportedProtocol,StoreError, andOtherindicate systemic misconfigurations or storage issues. - Thus, they are marked as
Severity::Error.
is_retryable():- This is a crucial heuristic for the command dispatcher.
- It explicitly returns
trueforTimeout,Offline,RoutingTableEmpty, andStreamDropped. - When this is true, the outer application can loop and attempt the network call again with an exponential backoff.
user_message():- Maps every variant to a clean, human-readable string.
- This string is intended for CLI outputs or frontend UI display.
Crucial Namespace Note:
As heavily documented in the module comments, the namespace KIN-NET-NNN is heavily overloaded across the codebase.
This NetworkClientError occupies 001..009.
However, the KineticStoreError defined upstream in the kinetic-network crate occupies 001..020.
The KineticStoreError takes precedence for external API responses.
This is because it carries richer rejection context regarding the DHT.
NetworkClientError is used almost exclusively internally within the event loop to signal command dispatch failures.
VDF Errors (vdf.rs)
Kinetic utilizes Verifiable Delay Functions (VDFs) to enforce a strict cryptographic time-delay on name registrations.
This prevents network spam, squatting, and front-running.
Specifically, it embeds the Wesolowski VDF protocol through the chiavdf C++ library.
The error surface in this module is deliberately split into two distinct enums.
This separates remote validation logic from local computation execution.
-> See: kinetic-core/src/error/vdf.rs — Lines 23-37
The VdfRejectReason enum defines exactly why a VDF proof submitted by a remote peer was rejected.
This verification happens at the local DHT store verifier:
MalformedProof:- The byte array provided over the network was the wrong size.
- Or, it structurally failed to parse before any cryptographic math was attempted.
ChallengeMismatch:- A critical security violation.
- The proof mathematically verifies as a valid VDF.
- However, it was generated for the wrong challenge.
- In Kinetic, the challenge is deterministic:
SHA-256(network_id || name || salt || drand_signature_hex). - A mismatch means the peer is trying to reuse an old proof.
- Or they are trying to apply a proof to a completely different domain name.
EngineError(String):- The underlying
chiavdfverification bindings threw a C++ exception. - Or they encountered an internal math fault.
- The underlying
DiscriminantFailed:- The SHA-256 challenge payload could not be mathematically mapped into an RSA discriminant.
- This discriminant is required to initialize the Wesolowski group.
-> See: kinetic-core/src/error/vdf.rs — Lines 40-60
The VdfError enum covers failures that occur when the local node itself attempts to run the VDF prover.
It needs to do this to generate a new proof for its own outbound registrations:
LockFileError(String):- VDF generation is an intensive, single-threaded CPU task that can starve a machine.
- Kinetic serializes prover execution across the entire operating system using a filesystem lock.
- This error occurs if the OS denies permission to create the lock file.
LockAcquireError(String):- Occurs when the local node times out waiting for another local process to release the VDF lock.
DiscriminantError:- Local failure to create the VDF discriminant prior to computation.
ProofGenerationError:- The local
chiavdfprover panicked. - Or it exhausted system memory resources.
- Or it aborted computation midway.
- The local
UnsupportedPlatform:- Emitted immediately if the node is running on an unsupported architecture.
- Examples include certain ARM variants or older Windows OS.
- This happens because the embedded C++ library is not compiled or supported.
InvalidProof:- The locally generated proof exceeded acceptable byte bounds.
- This is checked before it was even returned to the caller.
-> See: kinetic-core/src/error/vdf.rs — Lines 62-109
The behavior implementation for VdfError manages the node’s local prover state:
code():- Error codes map sequentially from
KIN-VDF-001toKIN-VDF-006.
- Error codes map sequentially from
severity():- The
severity()method is notable here. UnsupportedPlatformis escalated toSeverity::Critical.- This is because it means the daemon is fundamentally crippled and cannot participate in the protocol’s write operations.
- All other errors are standard
Severity::Error.
- The
is_retryable():- The
is_retryable()method only returnstrueforLockAcquireError. - If the prover is locked by another process, it is perfectly safe (and expected) for the node to sleep and retry.
- All other cryptographic errors are fatal for that specific proof attempt.
- The
DNS Validation Errors (dns.rs)
Kinetic supports decentralized websites by embedding standard DNS zone data inside DHT reveal records. Because this data is serialized as JSON and propagated globally without central authority, it represents a massive attack surface. Strict, aggressive validation is applied to this payload. This happens before the node accepts it into memory or writes it to disk.
-> See: kinetic-core/src/error/dns.rs — Lines 12-57
The DnsError enum captures every conceivable structural and protocol violation associated with DNS zones:
NestedTooDeeply:- Guards against JSON-bomb attacks.
- These attacks are designed to blow the call stack and crash the node during deserialization of heavily nested arrays or objects.
ParseError(serde_json::Error):- Standard JSON parsing failure if the payload is malformed.
TooManyRecords:- A critical protocol cap.
- A zone is limited to a maximum of 50 records.
- This limit is mathematically derived to ensure that the final JSON payload stays well below the 80 KB libp2p maximum DHT record size limit.
- This leaves adequate room for VDF proofs and cryptographic signatures.
InvalidLabelLength(String):- Rejects DNS labels that are empty.
- Or, it rejects them if they exceed the 62-character maximum defined by RFC 1035.
InvalidLabelCharacters(String):- Rejects labels containing characters outside the standard alphanumeric-and-hyphen specification.
InvalidCnameConfiguration(String):- Enforces the rigid DNS RFC rule regarding CNAMEs.
- The rule states that if a CNAME record exists for a label, no other records (like A, TXT, or MX) can exist for that same label.
TxtRecordTooLong(String):- Enforces a strict 255-byte limit on TXT records.
- This is designed to prevent network bloat and abuse.
InvalidCnameTarget(String):- Rejects empty or excessively long string targets for CNAMEs.
InvalidPeerId(String):- Specific to Kinetic’s decentralized routing.
- Ensures that libp2p PeerId strings are correctly formatted base-58 or base-36 representations.
InvalidKid(String):- Ensures cryptographic DID identifiers are valid.
- They must start with the mandatory
did:kin:prefix scheme.
InvalidIpfsCid(String):- Ensures IPFS pointers are correctly formatted Content Identifiers (CIDs).
-> See: kinetic-core/src/error/dns.rs — Lines 59-78
A significant Rust implementation detail is found here: the manual PartialEq implementation.
Because the ParseError variant contains a serde_json::Error, things get tricky.
The serde_json::Error does not implement PartialEq natively in the standard library.
Therefore, the DnsError enum cannot simply use #[derive(PartialEq)].
Instead, the code manually implements the trait.
It unwraps the variants and uses string comparison (a.to_string() == b.to_string()) for the serde error.
This ensures tests and state machines can still safely check for equality without compilation failures.
-> See: kinetic-core/src/error/dns.rs — Lines 80-142
The impl DnsError block specifies the error lifecycle:
code():- Codes map sequentially from
KIN-DNS-001toKIN-DNS-011.
- Codes map sequentially from
is_retryable():- Every single error variant evaluates to
is_retryable() == false. - These are deterministic payload validation failures.
- Resubmitting the identical JSON payload will always yield the exact same error.
- Every single error variant evaluates to
severity():- The
severity()is uniformly designated asSeverity::Warning. - From the perspective of the Kinetic daemon, receiving an invalid payload from the network is not a local system failure.
- It is simply a bad request from a peer.
- It is handled by dropping the payload and moving on.
- The
Drand Quicknet Errors (drand.rs)
The Drand randomness beacon is the cryptographic heartbeat of the Kinetic protocol. Every single VDF commitment utilizes the current Drand “kyn”. A “kyn” is a discrete round of randomness. It is used as a mathematical salt. When the commitment is finally revealed to the network, it must include the exact Drand randomness signature. This signature must have been valid at the precise time of the commitment. If a node cannot fetch this randomness, the system halts. Or, if the randomness is proven invalid, the entire time-lock guarantee collapses.
-> See: kinetic-core/src/error/drand.rs — Lines 19-53
The DrandError enum encapsulates the myriad ways fetching from the external Quicknet network can fail:
AllEndpointsFailed:- The node maintains a list of multiple external Drand HTTP relays.
- This error fires if every single relay in the fallback list is unreachable or returns an error.
Network(String):- Wraps lower-level connection faults.
- Examples include DNS resolution failures or TCP connections being refused by the host.
HttpError(u16):- Fired when a relay successfully connects but returns a non-200 status code.
- Examples include 502 Bad Gateway or 429 Too Many Requests.
NoCachedKyn:- Kinetic attempts to failover to a locally cached randomness payload if the external network is completely down.
- This error occurs if that local cache is empty.
Serde(serde_json::Error):- The relay returned a 200 OK.
- However, the JSON payload was malformed or missing required protocol fields.
Storage(StorageError):- An IO failure occurred when attempting to read or write the local fallback cache to the filesystem.
Reqwest(reqwest::Error):- Wraps internal failures from the HTTP client library itself.
InvalidSignature:- The most critical cryptographic check in the module.
- Drand randomness is accompanied by a BLS threshold signature generated by the League of Entropy.
- Kinetic mathematically verifies this signature locally.
- If it fails, the relay is attempting to feed forged randomness to the node.
- This would allow attackers to bypass the VDF time delay.
StaleKyn { expected: u64, got: u64 }:- Enforces strict temporal bounds on the protocol.
- The node calculates what the current Drand round should be based on the local system clock.
- If the relay returns a round (
got) that is significantly older than theexpectedround, it is rejected. - This prevents replay attacks where old randomness is used to pre-compute VDF proofs.
-> See: kinetic-core/src/error/drand.rs — Lines 55-80
Similar to DnsError, the DrandError enum requires a complex manual implementation of the PartialEq trait.
It must handle stringified comparisons for serde_json::Error.
It must also handle stringified comparisons for reqwest::Error.
Crucially, it must also safely unwrap and compare the internal fields of the StaleKyn struct variant.
This is done using e1 == e2 && g1 == g2 to verify equality.
-> See: kinetic-core/src/error/drand.rs — Lines 82-144
The operational definitions for Drand:
code():- Codes map sequentially from
KIN-DRA-001toKIN-DRA-009.
- Codes map sequentially from
severity():- The
severity()method flagsSerde,Storage, andInvalidSignatureasSeverity::Error. - This highlights severe data corruption or active malicious interference.
- Network timeouts and stale kyns are considered
Severity::Warning.
- The
is_retryable():- The
is_retryable()method dictates thatAllEndpointsFailed,Network,HttpError,Reqwest, andStaleKynare fully retryable. - Querying the relay pool again seconds later will likely succeed.
- However,
InvalidSignatureorStoragefaults immediately halt the sequence.
- The
Rust Trait Implementation Deep Dive
-> See RUST_CONCEPTS.md for explanations of how thiserror automates Display generation, and how PartialEq is manually implemented for external crate errors like serde_json::Error and reqwest::Error to enable ergonomic unit testing.
Key Pieces
NetworkClientError::code
- What it does: Assigns the
KIN-NET-001toKIN-NET-009string identifiers to client-side network failures. - File & Line:
kinetic-core/src/error/network.rs— Line 41 - Why it matters: Provides a programmatic, stable identifier. This avoids parsing display strings when deciding how to handle P2P layer drops. The system uses these strings to index telemetry metrics.
VdfRejectReason::ChallengeMismatch
- What it does: Identifies a scenario where a mathematical VDF proof is structurally valid, but explicitly generated for the wrong Drand challenge.
- File & Line:
kinetic-core/src/error/vdf.rs— Line 28 - Why it matters: This is the absolute primary defense against proof-reuse attacks. If an attacker tries to reuse an old VDF proof for a new name registration, the challenge mismatch guarantees the record is discarded by the network.
The PartialEq implementation for DnsError
- What it does: Manually defines equality comparisons for an enum containing
serde_json::Error. - File & Line:
kinetic-core/src/error/dns.rs— Line 59 - Why it matters: Standard Rust
#[derive(PartialEq)]fails on complex third-party errors. By unwrapping and comparing.to_string(), Kinetic ensures that its state machine and unit test assertions can reliably compare expected validation states without compilation boilerplate.
DrandError::StaleKyn
- What it does: Compares the incoming Drand round number (
got) against the mathematically expected round number (expected). - File & Line:
kinetic-core/src/error/drand.rs— Line 47 - Why it matters: Prevents temporal drift and replay attacks. If an HTTP relay goes out of sync and serves randomness from two hours ago, accepting it would allow attackers to pre-compute VDF proofs for that exact salt. Stale kyns are strictly rejected to preserve network fairness.
How This Connects to the Rest of Kinetic
CROSS-CRATE DEPENDENCY:
As noted, the KIN-NET-NNN namespace is dangerously overloaded.
kinetic-core uses it for local operational NetworkClientError (001..009).
However, the upstream kinetic-network crate hijacks the identical namespace for KineticStoreError (001..020).
In practice, NetworkClientError is swallowed internally by the multiplexing loops.
So end-users only ever see the KineticStoreError variant over HTTP or RPC.
Developers working across both crates must be acutely aware of this overlap.
FORWARD DEPENDENCY:
The VdfRejectReason logic heavily influences the DHT validation layer found in kinetic-network.
When a remote peer pushes a Reveal record, the local DHT store extracts the embedded VDF proof.
It validates it using the chiavdf engine.
If any variant of VdfRejectReason is returned, the libp2p Kademlia store immediately drops the record.
It then penalizes the peer’s routing table reputation.
FORWARD DEPENDENCY:
The DnsError represents the absolute gatekeeper for the DnsZone structs.
These structs are defined in kinetic-core/src/types/dns.rs.
Any time a name registration attempts to attach domain data, it must survive this exact list of enum constraints.
This must happen before it can ever be serialized to the network or stored in local persistence.
FORWARD DEPENDENCY:
The DrandError dictates the lifecycle of the DrandClient fetching loop.
The system’s heartbeat entirely relies on this error module.
If is_retryable() returns true, the heartbeat loop will sleep and poll again.
If it returns false, the entire node synchronization process might stall.
It will then await manual intervention or local cache repair.
Quick Reference
network.rs- Codes:
KIN-NET-001throughKIN-NET-009. - Retryable: Timeouts, Offline, RoutingTableEmpty, StreamDropped.
- Focus: Libp2p stream drops, empty routing tables, and internal channel failures.
- Codes:
vdf.rs- Codes:
KIN-VDF-001throughKIN-VDF-006. - Retryable:
LockAcquireErroris the only retryable engine state. - Focus: Chiavdf C++ bindings, platform support, and mathematical proof constraints.
- Codes:
dns.rs- Codes:
KIN-DNS-001throughKIN-DNS-011. - Retryable: None. Validation failures are strictly deterministic.
- Focus: Enforcing the 50-record limit, JSON parsing depth, and strict RFC character lengths.
- Codes:
drand.rs- Codes:
KIN-DRA-001throughKIN-DRA-009. - Retryable: Network connection faults, timeouts, and stale kyns.
- Focus: BLS signature verification, stale randomness bounds, and JSON structure.
- Codes:
Open Questions / Things to Revisit
- Namespace Overlap Danger:
- The overlapping
KIN-NET-NNNnamespace betweenNetworkClientError(client-side) andKineticStoreError(DHT store-side) is a major architectural quirk. - It could lead to severe debugging confusion if a developer searches for
KIN-NET-005in an internal telemetry log versus an external API response, assuming they map to the same failure. - Should these namespaces be hard-segregated in the future (e.g., migrating internal errors to
KIN-NET-INT-NNN)?
- The overlapping
- VDF Lock Granularity:
VdfError::LockAcquireErroruses a global, system-wide filesystem lock to prevent CPU starvation.- However, does this single lock severely bottleneck high-performance nodes running on massive 64-core enterprise servers that could technically parallelize multiple Wesolowski VDF computations simultaneously?
- Drand Fallback Expiry Priorities:
- The
DrandError::NoCachedKynmodule implies a graceful fallback to a local cache when HTTP relays fail. - However, how long is a cached kyn considered functionally valid before it triggers a
StaleKynerror? - The documentation does not strictly define the exact temporal window allowed for cache drift before the node must forcibly halt.
- The
- Pure Rust VDF Platform Support:
- The
VdfError::UnsupportedPlatformvariant immediately signalsSeverity::Critical. - This cripples the node because the
chiavdfC++ bindings are not portable to every environment. - Is there a roadmap for a pure-Rust, unoptimized fallback VDF prover so that low-end hardware can still participate in the protocol, albeit slower?
- The
Debugging Workflow for Developers
When a Kinetic node encounters one of these categorized errors, developers must follow a systematic approach. First, identify the exact error namespace emitted in the logs. If the error falls under KIN-NET-NNN, immediately check the libp2p connection manager state. Verify if the peer is actively dropping connections or if the local network interface is saturated. If the error is KIN-VDF-NNN, the first check must be the local filesystem permissions. Ensure the daemon has write access to the directory where the VDF lock file is created. For KIN-DNS-NNN errors, developers should not debug the node itself. Instead, they should capture the raw JSON payload submitted by the client application. Run that JSON payload through a standard linter to find the exact character or nesting violation. Finally, for KIN-DRA-NNN, the primary debugging step is to curl the Drand HTTP relay directly. Compare the local system clock of the server against an NTP time source. If the local clock is skewed by even a few seconds, the node will aggressively emit StaleKyn errors. This clock skew is the most common operational failure mode for Drand integrations.