Skip to main content

mina_core/
constants.rs

1use std::sync::OnceLock;
2
3use binprot_derive::BinProtWrite;
4use mina_curves::pasta::Fp;
5use mina_p2p_messages::{bigint, number, v2};
6
7pub const GENESIS_PRODUCER_SK: &str = "EKFKgDtU3rcuFTVSEpmpXSkukjmX4cKefYREi6Sdsk7E7wsT7KRw";
8
9pub const PROTOCOL_VERSION: v2::ProtocolVersionStableV2 = v2::ProtocolVersionStableV2 {
10    transaction: number::Number(3),
11    network: number::Number(0),
12    patch: number::Number(0),
13};
14
15pub fn constraint_constants() -> &'static ConstraintConstants {
16    CONSTRAINT_CONSTANTS_OVERRIDE
17        .get()
18        .unwrap_or(NetworkConfig::global().constraint_constants)
19}
20
21static CONSTRAINT_CONSTANTS_OVERRIDE: OnceLock<ConstraintConstants> = OnceLock::new();
22
23/// Override the fork constants used during genesis computation. (interop testing)
24pub fn set_fork_override(fork: ForkConstants) {
25    let mut cc = NetworkConfig::global().constraint_constants.clone();
26    cc.fork = Some(fork);
27    let _ = CONSTRAINT_CONSTANTS_OVERRIDE.set(cc);
28}
29
30/// Constants that define fork-specific blockchain state.
31///
32/// Fork constants specify the blockchain state at which a protocol upgrade or fork occurred.
33/// These are used to handle protocol changes and ensure compatibility across network upgrades.
34#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
35pub struct ForkConstants {
36    /// Hash of the blockchain state at the fork point
37    #[serde(with = "fp_state_hash_serde")]
38    pub state_hash: Fp,
39
40    /// Blockchain length (number of blocks) at the fork point
41    pub blockchain_length: u32,
42
43    /// Global slot number since genesis at the fork point
44    pub global_slot_since_genesis: u32,
45}
46
47mod fp_state_hash_serde {
48    use super::*;
49
50    pub fn serialize<S: serde::Serializer>(fp: &Fp, serializer: S) -> Result<S::Ok, S::Error> {
51        let state_hash = v2::StateHash::from_fp(*fp);
52        serializer.serialize_str(&state_hash.to_string())
53    }
54
55    pub fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<Fp, D::Error> {
56        let s: String = serde::Deserialize::deserialize(deserializer)?;
57        let state_hash: v2::StateHash = s.parse().map_err(serde::de::Error::custom)?;
58        state_hash
59            .inner()
60            .0
61            .to_field()
62            .map_err(|e| serde::de::Error::custom(format!("invalid state hash: {e}")))
63    }
64}
65
66/// Protocol constraint constants that define core blockchain behavior.
67///
68/// These constants configure fundamental aspects of the Mina protocol including consensus,
69/// transaction processing, economic parameters, and ledger structure. They are compile-time
70/// parameters that must be consistent across all nodes in a network.
71///
72/// ## Consensus and Timing Parameters
73///
74/// The consensus mechanism relies on slot-based timing where blocks are
75/// produced in discrete time slots. The timing hierarchy is:
76/// - **Slots**: Basic time units for block production
77/// - **Sub-windows**: Groups of slots within an epoch
78/// - **Windows**: Collections of sub-windows that define epoch structure
79/// - **Epochs**: Complete consensus periods
80///
81/// ## Economic Parameters
82///
83/// The protocol defines economic incentives through fees and rewards:
84/// - **Coinbase rewards**: Paid to block producers for successful blocks
85/// - **Account creation fees**: Required to create new accounts on the ledger
86/// - **Supercharged rewards**: Multiplier for enhanced block producer rewards
87///
88/// ## Ledger and Transaction Structure
89///
90/// The ledger uses a Merkle tree structure for efficient verification:
91/// - **Ledger depth**: Determines the maximum number of accounts (2^depth)
92/// - **Transaction capacity**: Limits transactions per block for performance
93/// - **Pending coinbase**: Manages delayed coinbase payouts
94///
95/// ## Usage Example
96///
97/// ```rust
98/// use mina_core::constants::constraint_constants;
99///
100/// // Access global constraint constants
101/// let constants = constraint_constants();
102///
103/// // Calculate slots per window
104/// let slots_per_window = constants.sub_windows_per_window;
105/// println!("Sub-windows per window: {}", slots_per_window);
106///
107/// // Get block timing
108/// let block_time_ms = constants.block_window_duration_ms;
109/// println!("Block time: {}ms", block_time_ms);
110///
111/// // Check economic parameters
112/// let coinbase_reward = constants.coinbase_amount;
113/// let creation_fee = constants.account_creation_fee;
114/// println!("Coinbase: {} nanomina, Account fee: {} nanomina",
115///          coinbase_reward, creation_fee);
116/// ```
117///
118/// ## Network Differences
119///
120/// While most constraint constants are identical across networks, some parameters
121/// may differ between mainnet and testnets for development purposes.
122///
123/// OCaml: <https://github.com/MinaProtocol/mina/tree/compatible/src/config>
124/// Protocol specification: <https://github.com/MinaProtocol/mina/blob/compatible/docs/specs/types_and_structures/serialized_key.md>
125#[derive(Clone, Debug)]
126pub struct ConstraintConstants {
127    /// Number of sub-windows that make up a complete window.
128    ///
129    /// Used in the consensus mechanism to structure epoch timing. Combined with
130    /// `slots_per_sub_window` from protocol constants, this determines the total
131    /// slots per window: `slots_per_window = slots_per_sub_window × sub_windows_per_window`.
132    ///
133    /// **Value**: 11 (both mainnet and devnet)
134    pub sub_windows_per_window: u64,
135
136    /// Depth of the account ledger Merkle tree.
137    ///
138    /// This determines the maximum number of accounts that can be stored in the ledger:
139    /// `max_accounts = 2^ledger_depth`. The depth affects proof sizes and verification time.
140    /// A larger depth allows more accounts but increases computational overhead.
141    ///
142    /// **Value**: 35 (supports ~34 billion accounts)
143    /// **Usage**: Account addressing, sparse ledger proofs, zkSNARK constraints
144    pub ledger_depth: u64,
145
146    /// Number of blocks to delay before SNARK work becomes available.
147    ///
148    /// This creates a buffer period between when a block is produced and when
149    /// the associated SNARK work can be included in subsequent blocks. This delay
150    /// helps ensure fair distribution of SNARK work opportunities.
151    ///
152    /// **Value**: 2 blocks
153    /// **Usage**: SNARK work scheduling, proof marketplace timing
154    pub work_delay: u64,
155
156    /// Duration of each block production slot in milliseconds.
157    ///
158    /// This is the fundamental time unit for the consensus protocol. Block producers
159    /// attempt to create blocks during their assigned slots. The duration affects
160    /// network synchronization requirements and transaction confirmation times.
161    ///
162    /// **Value**: 180,000ms (3 minutes)
163    /// **Usage**: Consensus timing, slot calculations, network synchronization
164    pub block_window_duration_ms: u64,
165
166    /// Log₂ of the maximum number of transactions per block.
167    ///
168    /// The actual transaction capacity is `2^transaction_capacity_log_2`. This logarithmic
169    /// representation is used because the value directly affects zkSNARK circuit constraints.
170    /// Higher capacity allows more transactions but increases block processing time.
171    ///
172    /// Corresponds to `transaction_capacity` in the protocol specification, which defines
173    /// the maximum transactions per block (represented as `two_to_the`).
174    ///
175    /// **Value**: 7 (supports 2^7 = 128 transactions per block)
176    /// **Usage**: Transaction pool management, block construction, circuit constraints
177    pub transaction_capacity_log_2: u64,
178
179    /// Number of confirmations before coinbase reward is spendable.
180    ///
181    /// Coinbase rewards are not immediately spendable and require a certain number
182    /// of block confirmations before they can be used. This parameter defines the
183    /// depth of the pending coinbase Merkle tree structure used to track these
184    /// delayed rewards until they mature.
185    ///
186    /// **Value**: 5 (coinbase rewards require 5 block confirmations)
187    /// **Usage**: Coinbase reward management, staged ledger operations, reward maturity
188    pub pending_coinbase_depth: usize,
189
190    /// Block reward amount in nanomina (10⁻⁹ MINA).
191    ///
192    /// This is the base reward paid to block producers for successfully creating a block.
193    /// The amount is specified in nanomina, where 1 MINA = 10⁹ nanomina. Block producers
194    /// may receive additional rewards through the supercharged coinbase mechanism.
195    ///
196    /// **Value**: 720,000,000,000 nanomina (720 MINA)
197    /// **Usage**: Block producer rewards, economic incentives, reward calculations
198    pub coinbase_amount: u64,
199
200    /// Multiplier for supercharged coinbase rewards.
201    ///
202    /// Supercharged rewards were designed to provide double block rewards (factor of 2)
203    /// to block producers staking with unlocked tokens during the early mainnet period
204    /// following the 2021 launch. This mechanism incentivized participation and orderly
205    /// markets after mainnet launch.
206    ///
207    /// **Historical values**:
208    /// - Original mainnet: 2 (double rewards for unlocked tokens)
209    /// - Berkeley hardfork (June 2024): 1 (supercharged rewards removed via MIP1)
210    ///
211    /// The removal was decided by community vote on January 1, 2023, as proposed by
212    /// community member Gareth Davies. This change ensures uniform rewards for all
213    /// tokens and reduces inflation, promoting a sustainable economic model.
214    ///
215    /// **References**:
216    /// - Berkeley Upgrade: <https://minaprotocol.com/blog/minas-berkeley-upgrade-what-to-expect>
217    /// - Supercharged Rewards Removal: <https://minaprotocol.com/blog/update-on-minas-supercharged-rewards-schedule>
218    /// - Original Proposal: <https://github.com/MinaProtocol/mina/issues/5753>
219    ///
220    /// **Usage**: Enhanced reward calculations, incentive mechanisms
221    pub supercharged_coinbase_factor: u64,
222
223    /// Fee required to create a new account in nanomina.
224    ///
225    /// When a transaction creates a new account that doesn't exist on the ledger,
226    /// this fee is charged in addition to the transaction fee. This prevents
227    /// spam account creation and manages ledger growth.
228    ///
229    /// **Value**: 1,000,000,000 nanomina (1 MINA)
230    /// **Usage**: Account creation, transaction validation, fee calculations
231    pub account_creation_fee: u64,
232
233    /// Optional fork constants defining a protocol upgrade point.
234    ///
235    /// When present, these constants specify the blockchain state at which a protocol
236    /// fork or upgrade occurred. This allows the protocol to handle transitions between
237    /// different versions while maintaining consensus.
238    ///
239    /// **Usage**: Protocol upgrades, compatibility handling, genesis configuration
240    pub fork: Option<ForkConstants>,
241}
242#[derive(Clone, Debug, BinProtWrite)]
243pub struct ForkConstantsUnversioned {
244    previous_state_hash: bigint::BigInt,
245    previous_length: number::Int32,
246    genesis_slot: number::Int32,
247}
248
249impl From<&ForkConstants> for ForkConstantsUnversioned {
250    fn from(fork_constants: &ForkConstants) -> Self {
251        Self {
252            previous_state_hash: fork_constants.state_hash.into(),
253            previous_length: fork_constants.blockchain_length.into(),
254            genesis_slot: fork_constants.global_slot_since_genesis.into(),
255        }
256    }
257}
258
259#[derive(Clone, Debug, BinProtWrite)]
260pub struct ConstraintConstantsUnversioned {
261    pub sub_windows_per_window: number::Int64,
262    pub ledger_depth: number::Int64,
263    pub work_delay: number::Int64,
264    pub block_window_duration_ms: number::Int64,
265    pub transaction_capacity_log_2: number::Int64,
266    pub pending_coinbase_depth: number::Int64,
267    pub coinbase_amount: number::UInt64,
268    pub supercharged_coinbase_factor: number::Int64,
269    pub account_creation_fee: number::UInt64,
270    pub fork: Option<ForkConstantsUnversioned>,
271}
272
273impl From<&ConstraintConstants> for ConstraintConstantsUnversioned {
274    fn from(constraints: &ConstraintConstants) -> Self {
275        Self {
276            sub_windows_per_window: constraints.sub_windows_per_window.into(),
277            ledger_depth: constraints.ledger_depth.into(),
278            work_delay: constraints.work_delay.into(),
279            block_window_duration_ms: constraints.block_window_duration_ms.into(),
280            transaction_capacity_log_2: constraints.transaction_capacity_log_2.into(),
281            pending_coinbase_depth: (constraints.pending_coinbase_depth as u64).into(),
282            coinbase_amount: constraints.coinbase_amount.into(),
283            supercharged_coinbase_factor: constraints.supercharged_coinbase_factor.into(),
284            account_creation_fee: constraints.account_creation_fee.into(),
285            fork: constraints.fork.as_ref().map(|fork| fork.into()),
286        }
287    }
288}
289
290impl binprot::BinProtWrite for ConstraintConstants {
291    fn binprot_write<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
292        let constraints: ConstraintConstantsUnversioned = self.into();
293        constraints.binprot_write(w)
294    }
295}
296
297pub fn slots_per_window(constants: &v2::MinaBaseProtocolConstantsCheckedValueStableV1) -> u32 {
298    constants.slots_per_sub_window.as_u32() * (constraint_constants().sub_windows_per_window as u32)
299}
300
301const fn days_to_ms(days: u64) -> u64 {
302    days * 24 * 60 * 60 * 1000
303}
304
305pub const CHECKPOINTS_PER_YEAR: u64 = 12;
306
307pub fn checkpoint_window_size_in_slots() -> u32 {
308    let one_year_ms = days_to_ms(365);
309    let slots_per_year = one_year_ms / constraint_constants().block_window_duration_ms;
310    let size_in_slots = slots_per_year / CHECKPOINTS_PER_YEAR;
311    assert_eq!(slots_per_year % CHECKPOINTS_PER_YEAR, 0);
312    size_in_slots as u32
313}
314
315pub const DEFAULT_GENESIS_TIMESTAMP_MILLISECONDS: u64 = 1707157200000;
316
317pub const PROTOCOL_TRANSACTION_VERSION: u8 = 3;
318pub const PROTOCOL_NETWORK_VERSION: u8 = 3;
319pub const TX_POOL_MAX_SIZE: u32 = 3000;
320
321pub use v2::PROTOCOL_CONSTANTS;
322
323use crate::NetworkConfig;