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 Node localhost endpoint
226//
227// A client connecting to a local DIG node (§5.3 client→node connection order)
228// resolves `dig.local` or `localhost` to reach the node via localhost TCP on
229// port 9778. This constant is the single source of truth for that port so
230// consumers (dig-node, dig-dns, dig-installer, SDK, CLI) don't each hardcode it.
231// =============================================================================
232
233/// The default localhost port a client uses to reach the local DIG node.
234///
235/// This is used to implement §5.3 client→node connection order: when a client
236/// needs to connect to a DIG node, it tries `dig.local` and `localhost` on this
237/// port before falling back to the public `rpc.dig.net` gateway. This constant
238/// ensures all consumers (dig-node, dig-dns, dig-installer, dig-sdk, digstore CLI)
239/// use an identical port, preventing port-mismatch bugs. It MUST stay byte-identical
240/// to `dig-node`'s documented localhost serve port and the installer's registered
241/// `dig.local` address.
242pub const DIG_NODE_PORT: u16 = 9778;
243
244// ---------------------------------------------------------------------------
245// DIG Testnet
246// ---------------------------------------------------------------------------
247
248/// Placeholder DIG testnet genesis challenge.
249const DIG_TESTNET_GENESIS_CHALLENGE: [u8; 32] =
250 hex!("0000000000000000000000000000000000000000000000000000000000000001");
251
252/// DIG testnet constants.
253///
254/// Same structure as mainnet but with a different genesis challenge.
255/// Useful for testing without risking mainnet state.
256pub const DIG_TESTNET: NetworkConstants = NetworkConstants {
257 inner: ConsensusConstants {
258 genesis_challenge: Bytes32::new(DIG_TESTNET_GENESIS_CHALLENGE),
259 // AGG_SIG_ME = genesis_challenge. Others = sha256(genesis || opcode_byte).
260 agg_sig_me_additional_data: Bytes32::new(DIG_TESTNET_GENESIS_CHALLENGE),
261 agg_sig_parent_additional_data: Bytes32::new(hex!(
262 "6ae3f62deccdc8d56baf955e45dad1d40332a7b8e4afbb38f07719a863658054"
263 )),
264 agg_sig_puzzle_additional_data: Bytes32::new(hex!(
265 "5d962189ce65d3b3799f032add1ab29ef94ebc0e349fe1db231752304cdd6904"
266 )),
267 agg_sig_amount_additional_data: Bytes32::new(hex!(
268 "3724d66f2da5614aa650517a2feb7d681807d5107441c9c72579e9a751b82d67"
269 )),
270 agg_sig_puzzle_amount_additional_data: Bytes32::new(hex!(
271 "fb6a54a5b51e9734a6ff72fe4105cd5db891d4dbaef9361586091f0d4486581b"
272 )),
273 agg_sig_parent_amount_additional_data: Bytes32::new(hex!(
274 "a5556086f1b58dfd5966bf1aac5257c7f60774ffe5a9e219919c56640993f68b"
275 )),
276 agg_sig_parent_puzzle_additional_data: Bytes32::new(hex!(
277 "3fcc94e67cf3975473b065f0b21a4e92dd3df498d8b4464d7da9582669ac4e48"
278 )),
279 // All other fields same as mainnet
280 max_block_cost_clvm: 11_000_000_000,
281 cost_per_byte: 12_000,
282 max_coin_amount: u64::MAX,
283 max_generator_size: 1_000_000,
284 max_generator_ref_list_size: 512,
285 hard_fork_height: 0,
286 hard_fork2_height: 0,
287 genesis_pre_farm_pool_puzzle_hash: Bytes32::new([0u8; 32]),
288 genesis_pre_farm_farmer_puzzle_hash: Bytes32::new([0u8; 32]),
289 slot_blocks_target: 32,
290 min_blocks_per_challenge_block: 16,
291 max_sub_slot_blocks: 128,
292 num_sps_sub_slot: 64,
293 sub_slot_iters_starting: 1 << 27,
294 difficulty_constant_factor: 1 << 67,
295 difficulty_starting: 7,
296 difficulty_change_max_factor: 3,
297 sub_epoch_blocks: 384,
298 epoch_blocks: 4608,
299 significant_bits: 8,
300 discriminant_size_bits: 1024,
301 number_zero_bits_plot_filter_v1: 9,
302 number_zero_bits_plot_filter_v2: 9,
303 min_plot_size_v1: 32,
304 max_plot_size_v1: 50,
305 min_plot_size_v2: 28,
306 max_plot_size_v2: 32,
307 sub_slot_time_target: 600,
308 num_sp_intervals_extra: 3,
309 max_future_time2: 120,
310 number_of_timestamps: 11,
311 max_vdf_witness_size: 64,
312 mempool_block_buffer: 10,
313 weight_proof_threshold: 2,
314 blocks_cache_size: 4608 + (128 * 4),
315 weight_proof_recent_blocks: 1000,
316 max_block_count_per_requests: 32,
317 pool_sub_slot_iters: 37_600_000_000,
318 plot_filter_128_height: 0xffff_ffff,
319 plot_filter_64_height: 0xffff_ffff,
320 plot_filter_32_height: 0xffff_ffff,
321 plot_difficulty_initial: 2,
322 plot_difficulty_4_height: 0xffff_ffff,
323 plot_difficulty_5_height: 0xffff_ffff,
324 plot_difficulty_6_height: 0xffff_ffff,
325 plot_difficulty_7_height: 0xffff_ffff,
326 plot_difficulty_8_height: 0xffff_ffff,
327 },
328};
329
330#[cfg(test)]
331mod tests {
332 use super::*;
333
334 /// The canonical relay endpoint must equal exactly what a DIG Node dials by
335 /// default. This pins the value byte-for-byte against `dig-node`'s
336 /// `relay::DEFAULT_RELAY_URL` (`wss://relay.dig.net:9450`) and the
337 /// `dig-relay` server's documented client endpoint. If either side ever
338 /// changes the scheme, host, or port, this guard fails so the shared
339 /// contract can't silently drift.
340 #[test]
341 fn dig_relay_url_is_canonical_endpoint() {
342 assert_eq!(DIG_RELAY_URL, "wss://relay.dig.net:9450");
343 }
344
345 /// The relay endpoint is a secure-WebSocket URL pointing at the canonical
346 /// public host on the relay protocol port.
347 #[test]
348 fn dig_relay_url_is_well_formed() {
349 assert!(
350 DIG_RELAY_URL.starts_with("wss://"),
351 "relay must use secure WebSocket"
352 );
353 assert!(
354 DIG_RELAY_URL.contains("relay.dig.net"),
355 "relay must point at the canonical host"
356 );
357 assert!(
358 DIG_RELAY_URL.ends_with(":9450"),
359 "relay must use the RelayMessage wire port 9450"
360 );
361 }
362
363 /// The DIG node localhost port must equal the expected default.
364 ///
365 /// This guards against accidental mutations and ensures all consumers
366 /// (dig-node, dig-dns, dig-installer, dig-sdk, digstore) use a consistent
367 /// port when connecting to the local node on `dig.local` or `localhost`.
368 #[test]
369 fn dig_node_port_is_canonical() {
370 assert_eq!(DIG_NODE_PORT, 9778);
371 }
372}