Skip to main content

dig_peer_protocol/
opcodes.rs

1//! The complete DIG opcode namespace — the `200..=222` band, in one place.
2//!
3//! DIG extends Chia's `ProtocolMessageTypes` (which stops at `RespondCostInfo = 107`) with a
4//! band that starts at **200**, leaving a 100-value gap for future upstream additions. The band
5//! has two halves:
6//!
7//! | Range | Half | Where it is defined |
8//! |---|---|---|
9//! | `200..=219` | **consensus** — the DIG L2 gossip opcodes | [`DigMessageType`], one variant each |
10//! | `220..=` | **free** — directed / broadcast application protocols | the constants below |
11//!
12//! The consensus half is an enum because each opcode has a fixed body type and a gossip
13//! strategy; the free half is plain constants because each opcode's body is owned by the
14//! application protocol that defines it, not by this crate.
15//!
16//! These values are **canonical**: a second implementation must match them byte for byte, and
17//! no assigned value ever moves (§5.1, additive only).
18
19use crate::DigMessageType;
20
21/// First opcode of the DIG band. Everything below this belongs to Chia.
22pub const DIG_BAND_START: u8 = 200;
23
24/// First opcode of the **free** half of the DIG band — application protocols, not L2 consensus.
25pub const FREE_BAND_START: u8 = 220;
26
27/// Wire opcode for a directed **dig-message** envelope (WU6, epic #796).
28///
29/// Carries a `dig-message` directed envelope as OPAQUE bytes in [`DigMessage::data`]. The
30/// transport (dig-gossip) never seals, opens, or parses it; end-to-end sealing to the
31/// recipient's DID key is `dig-message`'s job.
32///
33/// [`DigMessage::data`]: crate::DigMessage::data
34pub const DIG_MESSAGE: u8 = 220;
35
36/// Wire opcode for a **store-melted** broadcast (epic #1316).
37///
38/// Announces that a dig-store's on-chain coin has been melted, so peers stop hosting its `.dig`
39/// content. A public all-peers flood: signed and mTLS-authenticated, but NOT recipient-sealed —
40/// store deletion is addressed to everyone (the §5.4 public-broadcast carve-out).
41pub const STORE_MELTED: u8 = 221;
42
43/// Wire opcode for a **holdings-announce** broadcast (#1428, spec #1394).
44///
45/// Announces a batch of signed holdings add/remove deltas so peers learn which content a
46/// provider holds; this feeds dig-dht's holder set. Public flood, same carve-out as
47/// [`STORE_MELTED`].
48pub const HOLDINGS_ANNOUNCE: u8 = 222;
49
50/// Every opcode DIG has assigned, ascending — the 20 consensus opcodes plus the 3 free-band ones.
51///
52/// This is the list a peer link dispatches on and the list a conformance test checks against
53/// Chia's namespace for collisions.
54pub const ALL_DIG_OPCODES: [u8; 23] = [
55    DigMessageType::NewAttestation as u8,
56    DigMessageType::NewCheckpointProposal as u8,
57    DigMessageType::NewCheckpointSignature as u8,
58    DigMessageType::RequestCheckpointSignatures as u8,
59    DigMessageType::RespondCheckpointSignatures as u8,
60    DigMessageType::RequestStatus as u8,
61    DigMessageType::RespondStatus as u8,
62    DigMessageType::NewCheckpointSubmission as u8,
63    DigMessageType::ValidatorAnnounce as u8,
64    DigMessageType::RequestBlockTransactions as u8,
65    DigMessageType::RespondBlockTransactions as u8,
66    DigMessageType::ReconciliationSketch as u8,
67    DigMessageType::ReconciliationResponse as u8,
68    DigMessageType::StemTransaction as u8,
69    DigMessageType::PlumtreeLazyAnnounce as u8,
70    DigMessageType::PlumtreePrune as u8,
71    DigMessageType::PlumtreeGraft as u8,
72    DigMessageType::PlumtreeRequestByHash as u8,
73    DigMessageType::RegisterPeer as u8,
74    DigMessageType::RegisterAck as u8,
75    DIG_MESSAGE,
76    STORE_MELTED,
77    HOLDINGS_ANNOUNCE,
78];
79
80/// Whether `opcode` belongs to the DIG band rather than Chia's namespace.
81///
82/// This is a *band* test, not an *assigned* test: an unassigned value such as `250` is still
83/// DIG's to allocate, and a link must route it to DIG handling (where it is rejected as unknown)
84/// rather than to a Chia decoder that would reject the whole connection.
85#[must_use]
86pub const fn is_dig_opcode(opcode: u8) -> bool {
87    opcode >= DIG_BAND_START
88}
89
90#[cfg(test)]
91mod tests {
92    use super::{
93        is_dig_opcode, ALL_DIG_OPCODES, DIG_BAND_START, DIG_MESSAGE, FREE_BAND_START,
94        HOLDINGS_ANNOUNCE, STORE_MELTED,
95    };
96    use chia_protocol::ProtocolMessageTypes;
97    use chia_traits::Streamable;
98
99    /// No DIG opcode may ever collide with one Chia accepts — probed against the real decoder
100    /// over the whole `u8` space rather than against a transcribed copy of Chia's enum, so an
101    /// upstream addition that reached into the band would fail this test instead of silently
102    /// producing two meanings for one byte.
103    #[test]
104    fn dig_opcodes_are_disjoint_from_the_chia_namespace() {
105        for opcode in ALL_DIG_OPCODES {
106            assert!(
107                ProtocolMessageTypes::from_bytes(&[opcode]).is_err(),
108                "opcode {opcode} is claimed by both DIG and Chia"
109            );
110        }
111    }
112
113    /// The band is contiguous from 200 with no gaps and no duplicates: a gap would mean an
114    /// opcode was silently dropped from the list, a duplicate that two protocols share a byte.
115    #[test]
116    fn the_assigned_band_is_contiguous_from_200() {
117        let expected: Vec<u8> = (DIG_BAND_START..=HOLDINGS_ANNOUNCE).collect();
118        assert_eq!(ALL_DIG_OPCODES.to_vec(), expected);
119    }
120
121    /// The free band starts exactly where the consensus band ends, and its three assigned
122    /// values are pinned — these are cross-repo canonical constants that must not drift.
123    #[test]
124    fn free_band_constants_are_pinned() {
125        assert_eq!(FREE_BAND_START, 220);
126        assert_eq!(DIG_MESSAGE, 220);
127        assert_eq!(STORE_MELTED, 221);
128        assert_eq!(HOLDINGS_ANNOUNCE, 222);
129    }
130
131    /// The band predicate is pinned from BOTH sides: 199 is Chia's, 200 is DIG's.
132    #[test]
133    fn band_predicate_is_pinned_from_both_sides() {
134        assert!(!is_dig_opcode(DIG_BAND_START - 1));
135        assert!(is_dig_opcode(DIG_BAND_START));
136        assert!(is_dig_opcode(u8::MAX));
137    }
138}