Files
nym/sdk/rust/nym-sdk/src/error.rs
T
mfahampshire c07ef0253d Max/sdk stream wrapper (#6320)
* Replace MixnetStream with LP framing
- Replace custom header with LpFrameHeader
- Added sequence number for message ordering

* IPR: support LP Stream-framed client connections
- Detect and route LP Stream frames in mixnet_listener
- Wrap inline responses in LP Stream frames
- Thread stream_id to ConnectedClientHandler for TUN responses

* sdk: add ipr_wrapper module with IpMixStream
- IpMixStream wraps MixnetStream for IPR tunnel over mixnet
- LP Stream framing handled automatically by MixnetStream
- Gateway discovery, connect handshake, IP packet send/receive

* sdk: remove superseded stream_wrapper module

* Trim obvious comments, add architecture.md stub

* sdk: add missing deps and fix warnings

* Cut down architecture diagram until finished with rest of the code, leaving stubs

* sdk: refactor IpMixStream, extract shared helpers

- Extract gateway discovery and connect response parsing
- Add recv() to MixnetStream, remove 64KB read buffer
- Simplify IpMixStream constructor

* Fix SphinxStream renames missed during rebase

* Add IpPacketResponse::from_bytes() for stream-based deserialization

* Clean up ip_packet_client: delete stale connect.rs, take raw bytes not ReconstructedMessage

* Clippy

* Delete unused ip_packet_client modules

- Remove helpers.rs (ICMP utilities moved to example)
- Remove error.rs (errors consolidated into sdk/error.rs)
- Remove README.md
- Update module root to only export discovery + listener

* Simplify listener, IpMixStream, and network_env

- Collapse IprListener struct into standalone handle_ipr_response()
- Move check_ipr_message_version() into listener.rs
- Remove IpMixStream test module (moved to example)
- Remove parse_network() and commented-out Sandbox arms
- Return Result from find_workspace_root() instead of panicking
- Add IprTunnelDisconnected and WorkspaceRootNotFound error variants

* Refactor IPR stream handling and document seq conventions
- Inline stream_id tracking (remove current_stream_id field)
- Re-export encode_stream_frame from clients module
- Document seq=0 reservation for inline control responses
- Document data-path counter starting at 1 with skip-on-wrap

* Add ipr_tunnel example for integration testing
- ICMP ping through IPR with --gateway flag for targeting specific exits
- Move pnet_packet from dependencies to dev-dependencies

* Add message reordering to stream router
- Buffer out-of-order messages per-stream using BTreeMap
- Drain contiguous sequences individually to preserve message boundaries
- Drop duplicate/old sequence numbers with a warning
- Remove dead_code allow on StreamFrame::sequence_num

* Clean up comments and fill architecture.md
- Remove separator line comments
- Update stale comments about ordering not being implemented
- Remove collapsible_if allows, use let-else instead
- Fill in architecture.md data flow and connection lifecycle

* Simplify ipr_tunnel example to minimal smoke test
- Single ping instead of multi-ping loop
- Remove identifier and PING_COUNT
- Collapse ICMP helpers into single build_icmp_ping function

* Add dual-stack IPv6 ping and rename gateway → ipr
- Rename --gateway flag to --ipr and new_with_gateway() to new_with_ipr()
- Add ICMPv6 ping to ipr_tunnel example for dual-stack smoke test
- Tighten echo reply validation (protocol field check, diagnostic output)
- Document IP allocation (subnets, static vs dynamic, client keying) in architecture.md
- Promote LP Stream Open handshake log to INFO

* Tweak subnet comment in docs

* Don't stop IPR listener on decode failure
- Change break to continue so garbage packets can't kill the listener
- Remaining valid packets in the bundle are still processed

* Fix license headers and use workspace dep for pnet_packet
- Switch GPL-3.0 to Apache-2.0 on all SDK library files
- Add missing license headers to 7 files
- Use workspace version for pnet_packet dependency

* Document IP pool isolation from WG/LP dVPN pool
- IPR uses 10.0.0.0/16 on nymtun, WG uses 10.1.0.0/16 on nymwg
- Reference constants.rs as source of truth

* Remove network_env.rs and simplify IpMixStream API
  - Default to mainnet via setup_env(None) instead of requiring env param
  - Remove NetworkEnvironment enum and workspace root detection
  - Remove WorkspaceRootNotFound error variant
  - Update ipr_tunnel example to match new signatures

* Use weighted random selection for IPR gateway discovery
  - Replace max_by_key with choose_weighted biased by performance score
  - Prevents all clients converging on a single highest-performing IPR

* Cap stream reorder buffer to prevent unbounded memory growth
- Add MAX_REORDER_BUFFER (256) to limit per-stream pending messages:
	- buffer overflows = skip ahead to lowest buffered seq and drain
	- protects against malicious senders that deliberately skip sequence numbers

* Extract shared IPR response helpers into nym-ip-packet-requests
  - Add response_helpers module with version check, connect response
    parsing, and control response dispatch
  - SDK ip_packet_client now delegates to shared module
  - Monorepo nym-ip-packet-client uses shared version check and
    connect response parsing
  - Fix doc comment attributing fork to nym-vpn-client

* Extract ICMP test helpers into nym-ip-packet-requests
  - Add icmp_utils module behind test-utils feature flag
  - Move build_icmp_ping, build_icmpv6_ping, is_echo_reply_v4/v6 from
    example
  - Update ipr_tunnel example to use shared helpers

* Add protocol v9 LP-framed transport marker

- Add v9 module (re-exports v8, VERSION=9)
- Accept v9 requests and responses in IPR
- Switch SDK IpMixStream to send v9

* Log protocol version in dynamic connect requests

* Remove KCP from IPR and fix unwrap_or_default in SDK
- Remove all KCP session management from ip-packet-router (replaced by
  LP Stream framing)
- Drop nym-kcp dependency and KcpError variant from IPR
- Replace unwrap_or_default with ok_or(Error::NoNymAPIUrl) in
  IpMixStream::new()

* Add v9 protocol wrapper constructors and enforce version/transport
consistency
- Add v9::new_connect_request(), new_data_request(),
  new_ip_packet_response() to centralise version stamping
- Replace manual protocol.version overrides in SDK and IPR with v9
  wrapper calls
- Bump nym-ip-packet-client current re-export from v8 to v9
- Enforce LP Stream frames must carry v9+ payloads, non-stream must be
  v8 or lower

* Filter IPR exit nodes by minimum v9-compatible release version
- Define MIN_RELEASE_VERSION (1.30.0) in ip-packet-requests/v9 alongside protocol constants
- Add semver-based filtering in SDK gateway discovery to skip nodes below v9 threshold
- Add semver dependency to ip-packet-requests and nym-sdk

* Use numeric version comparison for transport/version enforcement
- Compare version as u8 instead of enum equality so future v10+ is handled correctly
- Remove unused `use super::*` import left over from KCP test removal
2026-03-27 20:35:26 +00:00

158 lines
4.8 KiB
Rust

// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_ip_packet_requests::v8::response::{ConnectFailureReason, IpPacketResponseData};
use nym_validator_client::nym_api::error::NymAPIError;
use nym_validator_client::nyxd::error::NyxdError;
use std::path::PathBuf;
/// Top-level Error enum for the mixnet client and its relevant types.
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("i/o error: {0}")]
IoError(#[from] std::io::Error),
#[error("toml serialization error: {0}")]
TomlSerializationError(#[from] toml::ser::Error),
#[error("toml deserialization error: {0}")]
TomlDeserializationError(#[from] toml::de::Error),
#[error("Ed25519 error: {0}")]
Ed25519RecoveryError(#[from] nym_crypto::asymmetric::ed25519::Ed25519RecoveryError),
#[error(transparent)]
ClientCoreError(#[from] nym_client_core::error::ClientCoreError),
#[error("key file encountered that we don't want to overwrite: {0}")]
DontOverwrite(PathBuf),
#[error("shared gateway key file encountered that we don't want to overwrite: {0}")]
DontOverwriteGatewayKey(PathBuf),
#[error("no gateway config available for writing")]
GatewayNotAvailableForWriting,
#[error("expected to received a directory, received: {0}")]
ExpectedDirectory(PathBuf),
#[error("failed to transition to registered state before connection to mixnet")]
FailedToTransitionToRegisteredState,
#[error(
"registering with gateway when the client is already in a registered state is not \
supported, and likely and user mistake"
)]
ReregisteringGatewayNotSupported,
#[error("no gateway key set")]
NoGatewayKeySet,
#[error("credentials mode not enabled")]
DisabledCredentialsMode,
#[error("bad validator details: {0}")]
BadValidatorDetails(#[from] NyxdError),
#[error("socks5 configuration set: {}, but expected to be {}", set, !set)]
Socks5Config { set: bool },
#[error("socks5 channel could not be started")]
Socks5NotStarted,
#[error("bandwidth controller error: {0}")]
BandwidthControllerError(#[from] nym_bandwidth_controller::error::BandwidthControllerError),
#[error("invalid voucher blob")]
InvalidVoucherBlob,
#[error("invalid mnemonic: {0}")]
InvalidMnemonic(#[from] bip39::Error),
#[error("failed to use reply storage backend: {source}")]
ReplyStorageError {
source: Box<dyn std::error::Error + Send + Sync>,
},
#[error("failed to use key storage backend: {source}")]
KeyStorageError {
source: Box<dyn std::error::Error + Send + Sync>,
},
#[error("failed to use credential storage backend: {source}")]
CredentialStorageError {
source: Box<dyn std::error::Error + Send + Sync>,
},
#[error(transparent)]
CredentialIssuanceError {
#[from]
source: nym_credential_utils::Error,
},
#[error("loaded shared gateway key without providing information about what gateway it corresponds to")]
GatewayWithUnknownEndpoint,
#[error("failed to send the provided message")]
MessageSendingFailure,
#[error("this operation is currently unsupported: {details}")]
Unsupported { details: String },
#[error(transparent)]
Bincode(#[from] bincode::Error),
#[error("Failed to get shutdown tracker from the task runtime registry: {0}")]
RegistryAccess(#[from] nym_task::RegistryAccessError),
#[error("Cannot use message-based functions after stream mode is activated")]
StreamModeActive,
#[error("Stream listener has already been taken — listener() can only be called once")]
ListenerAlreadyTaken,
#[error("Stream subsystem failed to initialise: reconstructed_receiver unavailable")]
StreamInitFailure,
#[error("client not connected")]
IprStreamClientNotConnected,
#[error("listening for connection response timed out")]
IPRConnectResponseTimeout,
#[error("stream closed")]
IPRClientStreamClosed,
#[error("expected control response, got {0:?}")]
UnexpectedResponseType(IpPacketResponseData),
#[error("connect denied: {0:?}")]
ConnectDenied(ConnectFailureReason),
#[allow(clippy::result_large_err)]
#[error("api directory error: {0}")]
GatewayDirectoryError(#[from] NymAPIError),
#[error("did not receive Nym API URL")]
NoNymAPIUrl,
#[error("no available gateway")]
NoGatewayAvailable,
#[error("tunnel disconnected by IPR")]
IprTunnelDisconnected,
#[error("message version check failed: {0}")]
IPRMessageVersionCheckFailed(String),
}
impl Error {
pub fn new_unsupported<S: Into<String>>(details: S) -> Self {
Error::Unsupported {
details: details.into(),
}
}
}
pub type Result<T, E = Error> = std::result::Result<T, E>;