pot_o_extensions/
genesis.rs1use 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}