dig_peer/error.rs
1//! The dig-peer error taxonomy.
2//!
3//! Every fallible entry point on [`DigPeer`](crate::DigPeer) returns [`DigPeerError`]. The variants
4//! separate the four failure domains a peer client faces so a caller can react precisely:
5//! *transport* (could not reach the peer), *protocol* (the peer answered, but the RPC failed),
6//! *seal* (the §5.4 end-to-end encryption could not be applied or verified — always fail-closed),
7//! and *state* (the operation is invalid for the connection's current lifecycle state).
8
9use dig_nat::NatError;
10use dig_rpc_protocol::RpcError;
11
12/// The result type for every [`DigPeer`](crate::DigPeer) operation.
13pub type Result<T> = std::result::Result<T, DigPeerError>;
14
15/// A failure of a [`DigPeer`](crate::DigPeer) operation.
16///
17/// The variants are grouped by domain (transport / protocol / seal / state / codec) so a caller can
18/// distinguish "could not reach the peer" from "the peer refused the request" from "the message could
19/// not be sealed to the peer" — each of which warrants a different response.
20#[derive(Debug, thiserror::Error)]
21#[non_exhaustive]
22pub enum DigPeerError {
23 /// The transport could not establish (or lost) the connection — every NAT-traversal tier failed,
24 /// no tier was composable, or the mux/stream I/O broke. Wraps the underlying [`NatError`].
25 #[error("peer transport failed: {0}")]
26 Transport(#[from] NatError),
27
28 /// Stream I/O failed while sending a request or receiving a response (the connection dropped
29 /// mid-call, or a framed body exceeded the size bound).
30 #[error("peer stream I/O failed: {0}")]
31 Io(#[from] std::io::Error),
32
33 /// The peer answered with a JSON-RPC error envelope. Carries the peer's canonical [`RpcError`]
34 /// (code + message + origin) verbatim so the caller sees exactly what the peer reported. Boxed so
35 /// the large error envelope does not bloat every [`Result`] on the hot path.
36 #[error("peer returned an RPC error: {0:?}")]
37 Rpc(Box<RpcError>),
38
39 /// A directed (sealed) RPC was requested but the connection has NO verified peer BLS-G1 key
40 /// (a legacy/unbound peer, or binding verification was off). Sealing is **fail-closed**: without
41 /// a verified recipient key dig-peer refuses to send, rather than fall back to an unsealed send.
42 #[error("cannot send a directed sealed RPC: the peer presented no verified BLS-G1 identity")]
43 PeerNotSealable,
44
45 /// A directed (sealed) RPC was requested but no local sealing identity was configured. Set one
46 /// with [`DigPeer::with_sealing_identity`](crate::DigPeer::with_sealing_identity) before making
47 /// directed calls.
48 #[error("cannot seal a directed RPC: no local sealing identity configured")]
49 NoSealingIdentity,
50
51 /// The §5.4 seal or open failed — the payload could not be sealed to the peer, or a received
52 /// response failed authenticated decryption / signature / replay verification. Fail-closed: the
53 /// call errors rather than surfacing unverified bytes.
54 #[error("message seal/open failed: {0}")]
55 Seal(String),
56
57 /// The peer delivered a sealed response that does not correlate with the request that was sent
58 /// (wrong correlation id) — discarded rather than surfaced (a misdelivery cannot be trusted).
59 #[error("sealed response did not correlate with the request")]
60 Misdelivered,
61
62 /// A response body could not be (de)serialized into the expected typed shape.
63 #[error("could not (de)serialize an RPC payload: {0}")]
64 Codec(String),
65
66 /// The operation is invalid for the connection's current lifecycle state (e.g. an RPC after
67 /// [`disconnect`](crate::DigPeer::disconnect)).
68 #[error("operation invalid in state {0:?}")]
69 InvalidState(crate::state::PeerState),
70}