Skip to main content

pot_o_extensions/
genesis.rs

1use pot_o_core::TokenType;
2use serde::{Deserialize, Serialize};
3use std::collections::HashSet;
4use std::fs;
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct GenesisEntry {
8    pub address: String,
9    pub token: TokenType,
10    pub balance: u64,
11}
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct Genesis {
15    pub entries: Vec<GenesisEntry>,
16    pub chain_id: String,
17    pub created_at: u64,
18    pub tribechain_genesis_version: u64,
19}
20
21impl Genesis {
22    pub fn load(path: &str) -> Result<Self, String> {
23        let data = fs::read_to_string(path)
24            .map_err(|e| format!("Failed to read genesis file '{}': {}", path, e))?;
25        serde_json::from_str(&data).map_err(|e| format!("Failed to parse genesis JSON: {}", e))
26    }
27
28    pub fn validate(&self) -> Result<(), String> {
29        let mut seen = HashSet::new();
30        for entry in &self.entries {
31            if entry.balance == 0 {
32                return Err(format!("Zero balance for address {}", entry.address));
33            }
34            let key = (entry.address.clone(), entry.token.clone());
35            if !seen.insert(key) {
36                return Err(format!(
37                    "Duplicate entry: {} / {:?}",
38                    entry.address, entry.token
39                ));
40            }
41        }
42        Ok(())
43    }
44
45    pub fn apply_to_ledger(&self, ledger: &mut super::ledger::Ledger) -> Result<(), String> {
46        for entry in &self.entries {
47            ledger.issue(&entry.address, &entry.token, entry.balance);
48        }
49        Ok(())
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56
57    #[test]
58    fn test_genesis_validate_no_duplicates() {
59        let genesis = Genesis {
60            entries: vec![
61                GenesisEntry {
62                    address: "alice".to_string(),
63                    token: TokenType::TribeChain,
64                    balance: 1000,
65                },
66                GenesisEntry {
67                    address: "alice".to_string(),
68                    token: TokenType::TribeChain,
69                    balance: 500,
70                },
71            ],
72            chain_id: "test".to_string(),
73            created_at: 0,
74            tribechain_genesis_version: 1,
75        };
76        assert!(genesis.validate().is_err());
77    }
78
79    #[test]
80    fn test_genesis_validate_no_zero_balance() {
81        let genesis = Genesis {
82            entries: vec![GenesisEntry {
83                address: "alice".to_string(),
84                token: TokenType::TribeChain,
85                balance: 0,
86            }],
87            chain_id: "test".to_string(),
88            created_at: 0,
89            tribechain_genesis_version: 1,
90        };
91        assert!(genesis.validate().is_err());
92    }
93
94    #[test]
95    fn test_genesis_validate_ok() {
96        let genesis = Genesis {
97            entries: vec![
98                GenesisEntry {
99                    address: "alice".to_string(),
100                    token: TokenType::TribeChain,
101                    balance: 1000,
102                },
103                GenesisEntry {
104                    address: "bob".to_string(),
105                    token: TokenType::TribeChain,
106                    balance: 500,
107                },
108            ],
109            chain_id: "test".to_string(),
110            created_at: 0,
111            tribechain_genesis_version: 1,
112        };
113        assert!(genesis.validate().is_ok());
114    }
115
116    #[test]
117    fn test_genesis_apply_to_ledger() {
118        let mut ledger = super::super::ledger::Ledger::new("fee".to_string());
119        let genesis = Genesis {
120            entries: vec![
121                GenesisEntry {
122                    address: "alice".to_string(),
123                    token: TokenType::TribeChain,
124                    balance: 1000,
125                },
126                GenesisEntry {
127                    address: "bob".to_string(),
128                    token: TokenType::TribeChain,
129                    balance: 500,
130                },
131            ],
132            chain_id: "test".to_string(),
133            created_at: 0,
134            tribechain_genesis_version: 1,
135        };
136        genesis.apply_to_ledger(&mut ledger).unwrap();
137        assert_eq!(ledger.balance_of("alice", &TokenType::TribeChain), 1000);
138        assert_eq!(ledger.balance_of("bob", &TokenType::TribeChain), 500);
139    }
140}