Skip to main content

dig_constants/
lib.rs

1//! DIG Network Constants
2//!
3//! Defines network parameters for the DIG L2 blockchain. This crate exists
4//! separately so that any DIG crate can import network constants without
5//! pulling in the full CLVM engine or other heavy dependencies.
6//!
7//! The core type is [`NetworkConstants`], which wraps `chia-consensus`'s
8//! `ConsensusConstants` with DIG-specific values (genesis challenge,
9//! AGG_SIG additional data, cost limits, etc.).
10//!
11//! # Usage
12//!
13//! ```rust,ignore
14//! use dig_constants::DIG_MAINNET;
15//!
16//! let genesis = DIG_MAINNET.genesis_challenge();
17//! let consensus = DIG_MAINNET.consensus();
18//! ```
19
20use chia_consensus::consensus_constants::ConsensusConstants;
21use chia_protocol::Bytes32;
22use hex_literal::hex;
23
24/// DIG network constants.
25///
26/// Wraps `chia-consensus::ConsensusConstants` with accessors for the fields
27/// that DIG validators and wallet code commonly need. The underlying
28/// `ConsensusConstants` is available via [`consensus()`](Self::consensus)
29/// for direct use with `chia-consensus` functions like `run_spendbundle()`.
30#[derive(Debug, Clone)]
31pub struct NetworkConstants {
32    inner: ConsensusConstants,
33}
34
35impl NetworkConstants {
36    /// The underlying `chia-consensus` constants, for passing directly to
37    /// `run_spendbundle()`, `validate_clvm_and_signature()`, etc.
38    pub fn consensus(&self) -> &ConsensusConstants {
39        &self.inner
40    }
41
42    /// DIG genesis challenge.
43    pub fn genesis_challenge(&self) -> Bytes32 {
44        self.inner.genesis_challenge
45    }
46
47    /// AGG_SIG_ME additional data (== genesis_challenge on Chia L1).
48    pub fn agg_sig_me_additional_data(&self) -> Bytes32 {
49        self.inner.agg_sig_me_additional_data
50    }
51
52    /// Maximum CLVM cost per block.
53    pub fn max_block_cost_clvm(&self) -> u64 {
54        self.inner.max_block_cost_clvm
55    }
56
57    /// Cost per byte of generator program.
58    pub fn cost_per_byte(&self) -> u64 {
59        self.inner.cost_per_byte
60    }
61
62    /// Maximum coin amount (u64::MAX).
63    pub fn max_coin_amount(&self) -> u64 {
64        self.inner.max_coin_amount
65    }
66}
67
68// =============================================================================
69// AGG_SIG additional data derivation
70//
71// On Chia L1, each AGG_SIG_* variant's additional_data is:
72//   sha256(genesis_challenge || opcode_byte)
73// except AGG_SIG_ME which uses genesis_challenge directly.
74//
75// See: condition_tools.py:58-71
76//   https://github.com/Chia-Network/chia-blockchain/blob/main/chia/consensus/condition_tools.py#L58
77// =============================================================================
78
79// ---------------------------------------------------------------------------
80// DIG Mainnet
81//
82// TODO: Replace placeholder genesis challenge with the real DIG mainnet value
83// once the network is launched. All agg_sig_*_additional_data values must be
84// recomputed as sha256(genesis_challenge || opcode_byte) at that time.
85// ---------------------------------------------------------------------------
86
87/// Placeholder DIG mainnet genesis challenge.
88///
89/// This MUST be replaced with the real value before mainnet launch.
90/// All `agg_sig_*_additional_data` fields are derived from this.
91const DIG_MAINNET_GENESIS_CHALLENGE: [u8; 32] =
92    hex!("0000000000000000000000000000000000000000000000000000000000000000");
93
94/// DIG mainnet constants.
95///
96/// Uses DIG's own genesis challenge and AGG_SIG domain separation.
97/// Proof-of-space and VDF fields are set to neutral values since DIG L2
98/// does not use Chia's proof-of-space consensus.
99pub const DIG_MAINNET: NetworkConstants = NetworkConstants {
100    inner: ConsensusConstants {
101        // -- DIG-specific values --
102        genesis_challenge: Bytes32::new(DIG_MAINNET_GENESIS_CHALLENGE),
103
104        // AGG_SIG additional data: derived from genesis_challenge.
105        // AGG_SIG_ME = genesis_challenge directly.
106        // Others = sha256(genesis_challenge || opcode_byte).
107        // Derivation: condition_tools.py:58-71
108        //   https://github.com/Chia-Network/chia-blockchain/blob/main/chia/consensus/condition_tools.py#L58
109        // Opcode bytes: AGG_SIG_PARENT=43, PUZZLE=44, AMOUNT=45,
110        //   PUZZLE_AMOUNT=46, PARENT_AMOUNT=47, PARENT_PUZZLE=48
111        // NOTE: Recompute ALL values when genesis_challenge is finalized.
112        agg_sig_me_additional_data: Bytes32::new(DIG_MAINNET_GENESIS_CHALLENGE),
113        agg_sig_parent_additional_data: Bytes32::new(hex!(
114            "978722459e638504a3c4ed25b0eae952f1cba668de5a44ccbb3b311eb6901218"
115        )),
116        agg_sig_puzzle_additional_data: Bytes32::new(hex!(
117            "b5b75cf3f16babd124b3c36ac239db038cf9384b6f4343ab65121e7994fa87e4"
118        )),
119        agg_sig_amount_additional_data: Bytes32::new(hex!(
120            "568b7e86b93e78c4a70a90902134266d5f666400d449c827c32422c14a8df42a"
121        )),
122        agg_sig_puzzle_amount_additional_data: Bytes32::new(hex!(
123            "73f82ca7a07025c76c91a3faf2e574ffa13759597fc5d9a0573b4df70245de2e"
124        )),
125        agg_sig_parent_amount_additional_data: Bytes32::new(hex!(
126            "3a2914bb834c69f745c1932450bad277f975b3e1b246d003e3a53e550cf74936"
127        )),
128        agg_sig_parent_puzzle_additional_data: Bytes32::new(hex!(
129            "4f59298d607f3143532ed694fb2f10454a684c1a29ef83e250bf5e234c6720b7"
130        )),
131
132        // DIG L2 cost limits
133        max_block_cost_clvm: 11_000_000_000, // per-spend limit, same as Chia L1
134        cost_per_byte: 12_000,
135        max_coin_amount: u64::MAX,
136
137        // Block generator limits
138        max_generator_size: 1_000_000,
139        max_generator_ref_list_size: 512,
140
141        // Hard fork heights — set to 0 to always use latest consensus rules.
142        // DIG L2 starts with all features enabled from block 0.
143        hard_fork_height: 0,
144        hard_fork2_height: 0,
145
146        // Pre-farm puzzle hashes — not used by DIG L2, set to zero.
147        genesis_pre_farm_pool_puzzle_hash: Bytes32::new([0u8; 32]),
148        genesis_pre_farm_farmer_puzzle_hash: Bytes32::new([0u8; 32]),
149
150        // -- Proof-of-space / VDF fields (not used by DIG L2) --
151        // These must be valid values since ConsensusConstants is passed to
152        // chia-consensus functions, but DIG does not use PoS consensus.
153        slot_blocks_target: 32,
154        min_blocks_per_challenge_block: 16,
155        max_sub_slot_blocks: 128,
156        num_sps_sub_slot: 64,
157        sub_slot_iters_starting: 1 << 27,
158        difficulty_constant_factor: 1 << 67,
159        difficulty_starting: 7,
160        difficulty_change_max_factor: 3,
161        sub_epoch_blocks: 384,
162        epoch_blocks: 4608,
163        significant_bits: 8,
164        discriminant_size_bits: 1024,
165        number_zero_bits_plot_filter_v1: 9,
166        number_zero_bits_plot_filter_v2: 9,
167        min_plot_size_v1: 32,
168        max_plot_size_v1: 50,
169        min_plot_size_v2: 28,
170        max_plot_size_v2: 32,
171        sub_slot_time_target: 600,
172        num_sp_intervals_extra: 3,
173        max_future_time2: 120,
174        number_of_timestamps: 11,
175        max_vdf_witness_size: 64,
176        mempool_block_buffer: 10,
177        weight_proof_threshold: 2,
178        blocks_cache_size: 4608 + (128 * 4),
179        weight_proof_recent_blocks: 1000,
180        max_block_count_per_requests: 32,
181        pool_sub_slot_iters: 37_600_000_000,
182        plot_filter_128_height: 0xffff_ffff,
183        plot_filter_64_height: 0xffff_ffff,
184        plot_filter_32_height: 0xffff_ffff,
185        plot_difficulty_initial: 2,
186        plot_difficulty_4_height: 0xffff_ffff,
187        plot_difficulty_5_height: 0xffff_ffff,
188        plot_difficulty_6_height: 0xffff_ffff,
189        plot_difficulty_7_height: 0xffff_ffff,
190        plot_difficulty_8_height: 0xffff_ffff,
191    },
192};
193
194// =============================================================================
195// NAT-traversal relay endpoint
196//
197// A DIG Node behind NAT cannot accept inbound dials, so it holds a constant
198// reservation with a publicly-reachable relay to stay discoverable. The
199// canonical public relay is `relay.dig.net`, serving the `RelayMessage`
200// WebSocket wire (RLY-001..RLY-007) on port 9450.
201//
202// This constant is the single source of truth for that endpoint so consumers
203// (`dig-node`, `dig-gossip`) don't each hardcode it. It MUST stay byte-identical
204// to `dig-node`'s `relay::DEFAULT_RELAY_URL` (the string a node actually dials
205// when `DIG_RELAY_URL` is unset) and to the `dig-relay` server's documented
206// client endpoint.
207// =============================================================================
208
209/// Canonical DIG NAT-traversal relay endpoint.
210///
211/// This is the WebSocket URL a DIG Node dials by default to obtain a relay
212/// reservation (so NAT'd peers stay reachable). It is the value used unless an
213/// operator overrides it via the `DIG_RELAY_URL` environment variable (or
214/// disables the reservation with `DIG_RELAY_URL=off`).
215///
216/// Format: `wss://<host>:<port>` — the relay protocol (`RelayMessage`,
217/// RLY-001..RLY-007) is JSON over a secure WebSocket. Mainnet uses the canonical
218/// public deployment `relay.dig.net` on port 9450.
219///
220/// Kept byte-identical to `dig-node`'s `relay::DEFAULT_RELAY_URL` and the
221/// `dig-relay` server's documented client endpoint.
222pub const DIG_RELAY_URL: &str = "wss://relay.dig.net:9450";
223
224// ---------------------------------------------------------------------------
225// DIG Testnet
226// ---------------------------------------------------------------------------
227
228/// Placeholder DIG testnet genesis challenge.
229const DIG_TESTNET_GENESIS_CHALLENGE: [u8; 32] =
230    hex!("0000000000000000000000000000000000000000000000000000000000000001");
231
232/// DIG testnet constants.
233///
234/// Same structure as mainnet but with a different genesis challenge.
235/// Useful for testing without risking mainnet state.
236pub const DIG_TESTNET: NetworkConstants = NetworkConstants {
237    inner: ConsensusConstants {
238        genesis_challenge: Bytes32::new(DIG_TESTNET_GENESIS_CHALLENGE),
239        // AGG_SIG_ME = genesis_challenge. Others = sha256(genesis || opcode_byte).
240        agg_sig_me_additional_data: Bytes32::new(DIG_TESTNET_GENESIS_CHALLENGE),
241        agg_sig_parent_additional_data: Bytes32::new(hex!(
242            "6ae3f62deccdc8d56baf955e45dad1d40332a7b8e4afbb38f07719a863658054"
243        )),
244        agg_sig_puzzle_additional_data: Bytes32::new(hex!(
245            "5d962189ce65d3b3799f032add1ab29ef94ebc0e349fe1db231752304cdd6904"
246        )),
247        agg_sig_amount_additional_data: Bytes32::new(hex!(
248            "3724d66f2da5614aa650517a2feb7d681807d5107441c9c72579e9a751b82d67"
249        )),
250        agg_sig_puzzle_amount_additional_data: Bytes32::new(hex!(
251            "fb6a54a5b51e9734a6ff72fe4105cd5db891d4dbaef9361586091f0d4486581b"
252        )),
253        agg_sig_parent_amount_additional_data: Bytes32::new(hex!(
254            "a5556086f1b58dfd5966bf1aac5257c7f60774ffe5a9e219919c56640993f68b"
255        )),
256        agg_sig_parent_puzzle_additional_data: Bytes32::new(hex!(
257            "3fcc94e67cf3975473b065f0b21a4e92dd3df498d8b4464d7da9582669ac4e48"
258        )),
259        // All other fields same as mainnet
260        max_block_cost_clvm: 11_000_000_000,
261        cost_per_byte: 12_000,
262        max_coin_amount: u64::MAX,
263        max_generator_size: 1_000_000,
264        max_generator_ref_list_size: 512,
265        hard_fork_height: 0,
266        hard_fork2_height: 0,
267        genesis_pre_farm_pool_puzzle_hash: Bytes32::new([0u8; 32]),
268        genesis_pre_farm_farmer_puzzle_hash: Bytes32::new([0u8; 32]),
269        slot_blocks_target: 32,
270        min_blocks_per_challenge_block: 16,
271        max_sub_slot_blocks: 128,
272        num_sps_sub_slot: 64,
273        sub_slot_iters_starting: 1 << 27,
274        difficulty_constant_factor: 1 << 67,
275        difficulty_starting: 7,
276        difficulty_change_max_factor: 3,
277        sub_epoch_blocks: 384,
278        epoch_blocks: 4608,
279        significant_bits: 8,
280        discriminant_size_bits: 1024,
281        number_zero_bits_plot_filter_v1: 9,
282        number_zero_bits_plot_filter_v2: 9,
283        min_plot_size_v1: 32,
284        max_plot_size_v1: 50,
285        min_plot_size_v2: 28,
286        max_plot_size_v2: 32,
287        sub_slot_time_target: 600,
288        num_sp_intervals_extra: 3,
289        max_future_time2: 120,
290        number_of_timestamps: 11,
291        max_vdf_witness_size: 64,
292        mempool_block_buffer: 10,
293        weight_proof_threshold: 2,
294        blocks_cache_size: 4608 + (128 * 4),
295        weight_proof_recent_blocks: 1000,
296        max_block_count_per_requests: 32,
297        pool_sub_slot_iters: 37_600_000_000,
298        plot_filter_128_height: 0xffff_ffff,
299        plot_filter_64_height: 0xffff_ffff,
300        plot_filter_32_height: 0xffff_ffff,
301        plot_difficulty_initial: 2,
302        plot_difficulty_4_height: 0xffff_ffff,
303        plot_difficulty_5_height: 0xffff_ffff,
304        plot_difficulty_6_height: 0xffff_ffff,
305        plot_difficulty_7_height: 0xffff_ffff,
306        plot_difficulty_8_height: 0xffff_ffff,
307    },
308};
309
310#[cfg(test)]
311mod tests {
312    use super::*;
313
314    /// The canonical relay endpoint must equal exactly what a DIG Node dials by
315    /// default. This pins the value byte-for-byte against `dig-node`'s
316    /// `relay::DEFAULT_RELAY_URL` (`wss://relay.dig.net:9450`) and the
317    /// `dig-relay` server's documented client endpoint. If either side ever
318    /// changes the scheme, host, or port, this guard fails so the shared
319    /// contract can't silently drift.
320    #[test]
321    fn dig_relay_url_is_canonical_endpoint() {
322        assert_eq!(DIG_RELAY_URL, "wss://relay.dig.net:9450");
323    }
324
325    /// The relay endpoint is a secure-WebSocket URL pointing at the canonical
326    /// public host on the relay protocol port.
327    #[test]
328    fn dig_relay_url_is_well_formed() {
329        assert!(
330            DIG_RELAY_URL.starts_with("wss://"),
331            "relay must use secure WebSocket"
332        );
333        assert!(
334            DIG_RELAY_URL.contains("relay.dig.net"),
335            "relay must point at the canonical host"
336        );
337        assert!(
338            DIG_RELAY_URL.ends_with(":9450"),
339            "relay must use the RelayMessage wire port 9450"
340        );
341    }
342}