Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
bitcoin-sock
Low-level, cross-platform socket primitives extracted from the Bitcoin Core networking layer, written in Rust.
bitcoin-sock provides a thin, well-instrumented RAII wrapper over OS sockets (CSocket) together with carefully-behaved higher-level operations such as:
- total-send with transient error handling and timeouts
- incremental receive until a terminator byte with zero over-read
- connectivity probing without consuming data (via
MSG_PEEK) - portable timeout/wait logic via
select(2)orpoll(2) - error classification (
io_error_is_permanent) and OS-neutral error strings
The crate is designed for consumers that need explicit control over system calls and error semantics (e.g. Bitcoin node implementations, protocol daemons, or custom P2P stacks) while still benefiting from RAII and structured logging.
Design overview
CSocket and platform abstraction
The core primitive is the platform-dependent alias CSocket:
pub type CSocket = usize; // SOCKET
pub type CSocket = c_int; // POSIX fd
``
All operations in this crate are expressed in terms of `CSocket`, with conditional compilation to choose between Winsock and POSIX .
This makes the crate appropriate for cross-platform network stacks where you want the exact C API semantics, but in Rust.
### Sock: RAII wrapper around CSocket
`Sock` is the central type:
```rust
Key properties:
- RAII lifetime management:
Dropcloses the socket (if not empty), mirroringstd::unique_ptr<SOCKET>semantics from C++. - Ownership stealing:
assign_fromandFrom<CSocket>implement move-style transfer of ownership betweenSockinstances. - Empty state: The sentinel
INVALID_SOCKETdenotes an inertSockthat does nothing on drop.
Low-level system call wrappers
Sock exposes exact-thin wrappers around the core socket syscalls:
send(&self, data: *const c_void, len: usize, flags: i32) -> isizerecv(&self, buf: *mut c_void, len: usize, flags: i32) -> isizeconnect(&self, addr: *const SocketAddr, addr_len: libc::socklen_t) -> i32get_sock_opt(&self, level, opt_name, opt_val, opt_len) -> i32
These mirror the C interfaces verbatim, including flags like MSG_NOSIGNAL and MSG_PEEK where applicable. The functions are deliberately unsafe in spirit: they accept raw pointers and sizes and return raw OS return codes, converted to Rust integer types.
Higher-level behaviour and algorithms
On top of the primitive calls, Sock adds value in several ways:
1. Error classification: io_error_is_permanent
Network errors are not all equal. Temporary conditions such as EAGAIN, EINTR, or EWOULDBLOCK should be retried, while others should be treated as fatal.
This function is used centrally in send/receive loops to distinguish between retryable and terminal errors.
2. Wait and readiness: wait and is_selectable_socket
Sock::wait blocks until the socket becomes ready for the requested SockEvents (read and/or write), bounded by a timeout:
- On Linux with
feature = "use_poll": usespoll(2). - Otherwise: falls back to
select(2)with anFD_SETSIZEcheck viais_selectable_socketto avoid UB on large descriptors.
This design is intended to faithfully reproduce the behaviour of the original Bitcoin networking code while staying portable.
3. Total send with timeout and interruption: send_complete
Algorithmically:
- Convert the user-specified logical timeout (
chrono::Duration) into a hard deadline (Instant). - Loop until all bytes are written.
- For each iteration:
- Invoke
sendon the remaining byte slice. - On success, advance
sentand continue. - On failure (
ret <= 0):- Fetch
last_socket_errorand decide permanent vs transient viaio_error_is_permanent. - If permanent, panic with an error message from
network_error_string. - Check
deadlineandinterrupt; if either fires, panic with an informative message about partial progress. - Compute a bounded wait interval with
compute_bounded_wait(deadline)and callwaitforSOCK_SENDbefore retry.
- Fetch
- Invoke
The logic is carefully structured to avoid busy-waiting while still honouring both timeout and interrupt conditions.
Mathematically, you can model the loop as a bounded retry algorithm on a non-deterministic channel with the following invariants:
- Progress:
sentis monotonically non-decreasing and bounded bydata.len(). - Safety: All error paths either classify the error as transient (retry) or terminate the function with a precise error message.
- Liveness: Assuming the OS eventually makes the socket writable before
deadlineandinterruptstays false, the loop must terminate withsent == len.
4. Efficient receive-until-terminator: recv_until_terminator
Core idea: we want to read until a sentinel byte without consuming any data beyond that point from the socket. Reading byte-by-byte would preserve the invariant but be asymptotically inefficient (roughly O(n) syscalls for n bytes).
Instead, the implementation uses a repeated pattern of:
MSG_PEEK-stylerecvinto a small buffer (e.g. 512 bytes).- Search for the terminator.
- If found at index
pos, issue a non-peeking read of exactlypos + 1bytes and append them todata. - If not found, read the full peeked slice.
- Maintain
data.len() <= max_data, panicking if exceeded. - Honour timeout and
interrupt, with the samecompute_bounded_waitpattern as insend_complete.
This yields a roughly 50× improvement over byte-wise reading for realistic workloads (as noted by the inline comments), while preserving a strong invariant:
After
recv_until_terminatorreturns, the underlying socket has consumed exactly the bytes up to and including the first occurrence ofterminator, and no further.
The returned String strips the terminator before returning, and asserts UTF‑8 validity.
5. Connectivity probe without consumption: is_connected
is_connected implements the canonical Bitcoin approach:
- If the socket is
INVALID_SOCKET, seterrmsgto"not connected"and returnfalse. - Issue a
recvof one byte withMSG_PEEK. - Interpret results as:
ret == -1and error is transient → treat as still connected.ret == -1and error is permanent → seterrmsgto the OS error string and returnfalse.ret == 0→ remote has closed the connection; seterrmsgto"closed"and returnfalse.ret > 0→ data available; socket considered connected.
This function allows higher-level code to cheaply check connectivity status without altering the read cursor.
Error reporting helpers
The crate also provides utilities for system-level error introspection:
last_socket_error() -> i32: returnserrnoon Unix,WSAGetLastError()on Windows.network_error_string(err: i32) -> String: returns a descriptive message including the error code, usingstrerror_ron Unix orFormatMessageWon Windows.
These are engineered to behave consistently across libc variants (GNU vs POSIX strerror_r) and to produce strings of the form:
"<system message> (<code>)"
which mirrors the logic used in C++ Bitcoin Core.
Socket selectability and descriptor constraints
On Unix platforms that use select(2), there is a hard ceiling (FD_SETSIZE) on descriptors that can safely be used. is_selectable_socket encodes this constraint:
``
Higher-level code can use this to decide whether a given socket can participate in a global `select`-based event loop or must instead be handled differently .
### Socket pair utility
For local IPC and test harnesses, the crate includes:
```rust
This constructs a bidirectional connected pair of Unix domain sockets, asserting on failure; useful for deterministic test constructions and local pipelines.
Traits and abstraction points
The crate defines several traits that abstract over socket-like backends:
The concrete Sock type effectively implements the semantics of these traits. In your own code, you can:
- Implement these traits for alternative backends (e.g., mock sockets for tests, different transport layers, wrappers around async runtimes), while still reusing shared logic.
- Depend on trait objects or generics bounded by these traits to write code that is backend-agnostic, e.g. a protocol parser that only needs
SockRecvUntilTerminatorandSockSendComplete.
This allows you to decouple protocol logic from transport implementation while retaining the precise semantics that the Bitcoin networking stack expects.
Usage examples
Basic RAII management
use ;
Complete send with timeout and interrupt
use Sock;
use Duration;
The function will either:
- send all bytes, or
- panic on a permanent error, timeout, or interrupt, describing how many bytes were sent and why the operation aborted.
You can wrap it and convert panics into error values if you need a non-panicking API at a higher layer.
Receive a line (or other delimited frame)
use Sock;
use Duration;
The returned String will exclude the newline terminator and be valid UTF‑8.
Connectivity probing
use Sock;
This is particularly useful for long-lived P2P connections where you want to periodically verify liveness without mutating the receive stream.
Waiting for readiness
use ;
use Duration;
const SOCK_RECV: SockEvent = 0x01;
const SOCK_SEND: SockEvent = 0x02;
This pattern allows you to build sophisticated event loops or integrate bitcoin-sock into existing synchronous multiplexing infrastructure.
Integration notes
- Logging / tracing: The implementation is instrumented with
trace!,debug!, andwarn!macros. You should configure your logging/tracing backend (for example viatracingorlog) to obtain detailed diagnostics about socket behaviour. - Panics vs errors: Several higher-level operations (
send_complete,recv_until_terminator) panic on unrecoverable conditions, as they are modeled after C++ exceptions. If your application requires error-returning APIs, wrap these functions and translate panic payloads into typed error values. - ThreadInterrupt: The crate expects a
ThreadInterrupttype exposing anas_bool()method to indicate whether a cooperative interrupt has been requested. Integrate this with your own cancellation infrastructure. - Select vs poll: On Linux, enabling the
use_pollfeature switches the readiness mechanism fromselecttopoll. This is useful when dealing with many file descriptors or when you need to avoid theFD_SETSIZElimit.
Repository, license, and provenance
- Crate name:
bitcoin-sock - Version:
0.1.19 - Repository: https://github.com/klebs6/bitcoin-rs
- License: MIT
- Edition: Rust 2021
- Author:
klebs <none>
This crate appears to be a Rust reimplementation/port of socket handling from Bitcoin Core, with the goal of preserving semantics and behaviour while leveraging Rust's ownership model and RAII.
Reminder: This README was generated automatically by an AI model. It may omit details or slightly misrepresent edge semantics. Consult the source code in the repository for definitive behaviour.