iuna

iuna

iuna - experimental mainnet-candidate protocol
git clone https://getiuna.org/git/iuna.git
Log | Files | Refs | README | LICENSE

profile.rs (6350B)


      1 use serde::{Deserialize, Serialize};
      2 
      3 use super::{
      4     BURN_LINEAGE_MATURITY_HEIGHTS, DEFAULT_TICKET_EXPIRY_WINDOW, DEFAULT_TICKET_MATURITY_DELAY,
      5     MAX_BLOCK_BYTES, MAX_BLOCK_TRANSACTIONS, MAX_PENDING_TRANSACTIONS, MINE_DIFFICULTY_BITS,
      6     hex_hash,
      7 };
      8 
      9 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
     10 pub struct LaunchProfile {
     11     pub profile_id: String,
     12     pub ticket_maturity_delay_heights: u64,
     13     #[serde(default = "default_ticket_expiry_window_heights")]
     14     pub ticket_expiry_window_heights: u64,
     15     #[serde(default = "default_mine_difficulty_bits")]
     16     pub mine_difficulty_bits: u32,
     17     #[serde(default = "default_burn_lineage_maturity_heights")]
     18     pub burn_lineage_maturity_heights: u64,
     19     pub max_pending_transactions: usize,
     20     pub max_block_transactions: usize,
     21     #[serde(default = "default_max_block_bytes")]
     22     pub max_block_bytes: usize,
     23 }
     24 
     25 impl Default for LaunchProfile {
     26     fn default() -> Self {
     27         Self {
     28             profile_id: "iuna-mainnet-candidate".to_string(),
     29             ticket_maturity_delay_heights: DEFAULT_TICKET_MATURITY_DELAY,
     30             ticket_expiry_window_heights: DEFAULT_TICKET_EXPIRY_WINDOW,
     31             mine_difficulty_bits: MINE_DIFFICULTY_BITS,
     32             burn_lineage_maturity_heights: BURN_LINEAGE_MATURITY_HEIGHTS,
     33             max_pending_transactions: MAX_PENDING_TRANSACTIONS,
     34             max_block_transactions: MAX_BLOCK_TRANSACTIONS,
     35             max_block_bytes: MAX_BLOCK_BYTES,
     36         }
     37     }
     38 }
     39 
     40 fn default_max_block_bytes() -> usize {
     41     MAX_BLOCK_BYTES
     42 }
     43 
     44 fn default_ticket_expiry_window_heights() -> u64 {
     45     DEFAULT_TICKET_EXPIRY_WINDOW
     46 }
     47 
     48 fn default_mine_difficulty_bits() -> u32 {
     49     MINE_DIFFICULTY_BITS
     50 }
     51 
     52 fn default_burn_lineage_maturity_heights() -> u64 {
     53     BURN_LINEAGE_MATURITY_HEIGHTS
     54 }
     55 
     56 impl LaunchProfile {
     57     pub fn local_testnet() -> Self {
     58         Self {
     59             profile_id: local_testnet_profile_id().to_string(),
     60             burn_lineage_maturity_heights: 0,
     61             ..Self::default()
     62         }
     63     }
     64 
     65     pub fn hash(&self) -> String {
     66         let canonical = if self.burn_lineage_maturity_heights == BURN_LINEAGE_MATURITY_HEIGHTS {
     67             // Preserve the legacy canonical format when maturity remains at its
     68             // protocol default. The field used to be a frozen constant.
     69             format!(
     70                 "iuna-launch-profile:{}:{}:{}:{}:{}:{}:{}",
     71                 self.profile_id,
     72                 self.ticket_maturity_delay_heights,
     73                 self.ticket_expiry_window_heights,
     74                 self.mine_difficulty_bits,
     75                 self.max_pending_transactions,
     76                 self.max_block_transactions,
     77                 self.max_block_bytes
     78             )
     79         } else {
     80             format!(
     81                 "iuna-launch-profile-v2:{}:{}:{}:{}:{}:{}:{}:{}",
     82                 self.profile_id,
     83                 self.ticket_maturity_delay_heights,
     84                 self.ticket_expiry_window_heights,
     85                 self.mine_difficulty_bits,
     86                 self.burn_lineage_maturity_heights,
     87                 self.max_pending_transactions,
     88                 self.max_block_transactions,
     89                 self.max_block_bytes
     90             )
     91         };
     92         hex_hash(canonical)
     93     }
     94 }
     95 
     96 #[cfg(not(feature = "e2e"))]
     97 fn local_testnet_profile_id() -> &'static str {
     98     "iuna-local-testnet-v1"
     99 }
    100 
    101 #[cfg(feature = "e2e")]
    102 fn local_testnet_profile_id() -> &'static str {
    103     "iuna-local-e2e-5s-v1"
    104 }
    105 
    106 #[derive(Clone, Debug, Eq, PartialEq)]
    107 pub struct GenesisBurn {
    108     pub from: String,
    109     pub amount: u64,
    110 }
    111 
    112 impl GenesisBurn {
    113     pub fn new(from: impl Into<String>, amount: u64) -> Self {
    114         Self {
    115             from: from.into(),
    116             amount,
    117         }
    118     }
    119 }
    120 
    121 #[cfg(test)]
    122 mod tests {
    123     use super::{GenesisBurn, LaunchProfile};
    124     use crate::domain::{
    125         DEFAULT_TICKET_EXPIRY_WINDOW, DEFAULT_TICKET_MATURITY_DELAY, MAX_BLOCK_BYTES,
    126         MAX_BLOCK_TRANSACTIONS, MAX_PENDING_TRANSACTIONS, MINE_DIFFICULTY_BITS,
    127     };
    128 
    129     #[test]
    130     fn default_launch_profile_matches_protocol_defaults() {
    131         let profile = LaunchProfile::default();
    132 
    133         assert_eq!(profile.profile_id, "iuna-mainnet-candidate");
    134         assert_eq!(
    135             profile.ticket_maturity_delay_heights,
    136             DEFAULT_TICKET_MATURITY_DELAY
    137         );
    138         assert_eq!(
    139             profile.ticket_expiry_window_heights,
    140             DEFAULT_TICKET_EXPIRY_WINDOW
    141         );
    142         assert_eq!(profile.mine_difficulty_bits, MINE_DIFFICULTY_BITS);
    143         assert_eq!(
    144             profile.burn_lineage_maturity_heights,
    145             crate::domain::BURN_LINEAGE_MATURITY_HEIGHTS
    146         );
    147         assert_eq!(profile.max_pending_transactions, MAX_PENDING_TRANSACTIONS);
    148         assert_eq!(profile.max_block_transactions, MAX_BLOCK_TRANSACTIONS);
    149         assert_eq!(profile.max_block_bytes, MAX_BLOCK_BYTES);
    150         assert_eq!(
    151             profile.hash(),
    152             "eb2f67e9d735474859ceb1fe124fe270977214f6e2f4cd855a4d8c3b5ecac558"
    153         );
    154     }
    155 
    156     #[test]
    157     fn local_testnet_profile_has_immediate_burn_lineage_eligibility() {
    158         let profile = LaunchProfile::local_testnet();
    159 
    160         #[cfg(not(feature = "e2e"))]
    161         assert_eq!(profile.profile_id, "iuna-local-testnet-v1");
    162         #[cfg(feature = "e2e")]
    163         assert_eq!(profile.profile_id, "iuna-local-e2e-5s-v1");
    164         assert_eq!(profile.burn_lineage_maturity_heights, 0);
    165         assert_ne!(profile.hash(), LaunchProfile::default().hash());
    166     }
    167 
    168     #[test]
    169     fn legacy_json_profile_defaults_burn_lineage_maturity() {
    170         let profile: LaunchProfile = serde_json::from_str(
    171             r#"{
    172                 "profile_id":"legacy",
    173                 "ticket_maturity_delay_heights":3,
    174                 "ticket_expiry_window_heights":3,
    175                 "mine_difficulty_bits":12,
    176                 "max_pending_transactions":10000,
    177                 "max_block_transactions":1000,
    178                 "max_block_bytes":1000000
    179             }"#,
    180         )
    181         .unwrap();
    182 
    183         assert_eq!(
    184             profile.burn_lineage_maturity_heights,
    185             crate::domain::BURN_LINEAGE_MATURITY_HEIGHTS
    186         );
    187     }
    188 
    189     #[test]
    190     fn genesis_burn_constructor_preserves_fields() {
    191         let burn = GenesisBurn::new("alice", 42);
    192 
    193         assert_eq!(burn.from, "alice");
    194         assert_eq!(burn.amount, 42);
    195     }
    196 }