Skip to main content

dig_peer_protocol/
lib.rs

1//! # dig-peer-protocol
2//!
3//! DIG Network L2 protocol types — a superset of `chia-protocol`.
4//!
5//! This crate re-exports the entire Chia protocol ecosystem (`chia-protocol`,
6//! `chia-sdk-client`, `chia-ssl`, `chia-traits`) plus DIG's own extensions: the
7//! `200..=222` opcode band, the [`DigMessage`] framing that can express it, and
8//! [`DigLink`], the websocket peer link that carries it. Consumers depend on
9//! `dig-peer-protocol` alone instead of importing multiple `chia-*` crates individually.
10//!
11//! ## The closed-enum problem, and how this crate closes it
12//!
13//! `chia_protocol::Message` stores its opcode as `ProtocolMessageTypes`, an enum that stops
14//! at `RespondCostInfo = 107` with no `Unknown(u8)`. A DIG opcode has no value in that enum, so
15//! it is neither constructible nor decodable through it — and worse, `chia-sdk-client`'s
16//! receive loop calls `Message::from_bytes`, so one inbound DIG frame drops the whole
17//! connection rather than that one frame.
18//!
19//! [`DigMessage`] answers the first half: the same wire bytes with a raw `u8` opcode.
20//! [`DigLink`] answers the second: a websocket link that frames `DigMessage` end to end.
21//! Together they replace the vendored `chia-protocol` / `chia-sdk-client` forks DIG used to
22//! carry, so `chia-protocol` is an ordinary dependency with no `[patch.crates-io]`.
23//!
24//! ## What's included
25//!
26//! | Source crate | What's re-exported |
27//! |-------------|-------------------|
28//! | `chia-protocol` | All wire types: `Message`, `Handshake`, `ProtocolMessageTypes`, `NodeType`, etc. |
29//! | `chia-sdk-client` | `Peer`, `Client`, `ClientError`, `ClientState`, `Network`, `PeerOptions`, rate limiting, TLS connectors |
30//! | `chia-ssl` | `ChiaCertificate` |
31//! | `chia-traits` | `Streamable` trait |
32//! | `chia_streamable_macro` | `#[streamable]` proc macro |
33//! | **DIG extensions** | `DigMessage`, `DigMessageType`, the `200..=222` opcode band, `RegisterPeer`, `RegisterAck`, introducer wire types |
34//! | **DIG peer link** | `DigLink`, `LinkOptions`, `LinkError`, `Admission`, `OpcodeRateLimiter`, `OpcodeRateLimits` |
35//!
36//! ## Feature flags
37//!
38//! | Flag | Forwards to | Effect |
39//! |------|-------------|--------|
40//! | `native-tls` | `chia-sdk-client/native-tls` | OS-native TLS; enables `Client`, `ClientState`, `Connector`, `create_native_tls_connector`, `DigLink::connect` |
41//! | `rustls` | `chia-sdk-client/rustls` | Pure-Rust TLS; enables `Client`, `ClientState`, `Connector`, `create_rustls_connector`, `DigLink::connect` |
42//!
43//! Neither feature is enabled by default. The crate builds without either but TLS-dependent
44//! items (`Client`, `ClientState`, `Connector`, and `DigLink::connect`) become unavailable;
45//! [`DigLink::from_websocket`] and [`DigLink::from_server_websocket`] stay available, since
46//! adopting an already-established socket needs no TLS backend of its own.
47
48// ============================================================================
49// Re-export: chia-protocol (all wire types)
50// ============================================================================
51pub use chia_protocol::*;
52
53// ============================================================================
54// Re-export: chia-sdk-client (peer IO, TLS, rate limiting)
55// ============================================================================
56// Backend-agnostic types — always available.
57pub use chia_sdk_client::{
58    load_ssl_cert, ClientError, Network, Peer, PeerOptions, RateLimit, RateLimiter, RateLimits,
59    V2_RATE_LIMITS,
60};
61
62// `Client`, `ClientState`, and `Connector` require a TLS backend in `chia-sdk-client`.
63// Enable either the `native-tls` or `rustls` feature to use them.
64#[cfg(any(feature = "native-tls", feature = "rustls"))]
65pub use chia_sdk_client::{Client, ClientState, Connector};
66
67#[cfg(feature = "native-tls")]
68pub use chia_sdk_client::create_native_tls_connector;
69
70#[cfg(feature = "rustls")]
71pub use chia_sdk_client::create_rustls_connector;
72
73// ============================================================================
74// Re-export: chia-ssl (certificate types)
75// ============================================================================
76pub use chia_ssl::ChiaCertificate;
77
78// ============================================================================
79// Re-export: chia-traits (serialization)
80// ============================================================================
81pub use chia_traits::Streamable;
82
83// ============================================================================
84// Re-export: chia_streamable_macro (proc macro for wire structs)
85// ============================================================================
86pub use chia_streamable_macro::streamable;
87
88// ============================================================================
89// DIG extensions
90// ============================================================================
91mod dig_message;
92mod dig_message_type;
93mod error;
94mod introducer_wire;
95mod link;
96mod opcodes;
97mod rate_limit;
98mod request_map;
99
100pub use dig_message::DigMessage;
101pub use dig_message_type::{DigMessageType, UnknownDigMessageType};
102pub use error::LinkError;
103pub use introducer_wire::{
104    RegisterAck, RegisterPeer, RequestPeersIntroducer, RespondPeersIntroducer,
105};
106pub use link::{DigLink, LinkOptions};
107pub use opcodes::{
108    is_dig_opcode, ALL_DIG_OPCODES, DIG_BAND_START, DIG_MESSAGE, FREE_BAND_START,
109    HOLDINGS_ANNOUNCE, STORE_MELTED,
110};
111pub use rate_limit::{Admission, OpcodeRateLimiter, OpcodeRateLimits};
112
113#[cfg(test)]
114mod dig_message_opcode_tests {
115    use super::{DigMessage, DigMessageType, DIG_MESSAGE};
116
117    /// The opcode frames a real [`DigMessage`] and survives a wire round-trip with its
118    /// `msg_type` intact — the canonical value (220) exercised through the actual encoder.
119    #[test]
120    fn dig_message_opcode_frames_and_round_trips() {
121        let msg = DigMessage::new(DIG_MESSAGE, Some(9), vec![1, 2, 3].into());
122        let back = DigMessage::from_bytes(&msg.to_bytes()).expect("round-trip");
123        assert_eq!(back.msg_type, 220);
124        assert_eq!(back.msg_type, DIG_MESSAGE);
125        assert_eq!(back.data.as_ref(), &[1, 2, 3]);
126    }
127
128    /// 220 is in the free band: it is NOT a consensus `DigMessageType` discriminant, so a
129    /// consensus-band decode of the opcode fails — the two bands can never collide.
130    #[test]
131    fn dig_message_opcode_is_not_a_consensus_type() {
132        assert!(DigMessageType::try_from(DIG_MESSAGE).is_err());
133    }
134}