1use std::{
2 fs::{self, File},
3 io::{self, Error, ErrorKind, Write},
4 path::Path,
5 str::FromStr,
6};
7
8use crate::{errors, ids::short, key, packer};
9use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
13#[serde(rename_all = "camelCase")]
14pub struct Genesis {
15 #[serde(default)]
16 pub timestamp: i64,
17 #[serde(skip_serializing_if = "Option::is_none")]
18 pub allocations: Option<Vec<Allocation>>,
19}
20
21impl Default for Genesis {
22 fn default() -> Self {
23 Self {
24 timestamp: 0,
25 allocations: Some(vec![Allocation {
26 address: short::Id::from_str("6Y3kysjF9jnHnYkdS9yGAuoHyae2eNmeV").unwrap(),
28 balance: DEFAULT_INITIAL_AMOUNT,
29 }]),
30 }
31 }
32}
33
34pub const CODEC_VERSION: u16 = 0;
35pub const DEFAULT_INITIAL_AMOUNT: u64 = 1000000000;
36
37impl Genesis {
38 pub fn new<T: key::secp256k1::ReadOnly>(seed_keys: &[T]) -> io::Result<Self> {
40 let max_total_alloc = u64::MAX;
42 let total_keys = seed_keys.len();
43 let alloc_per_key = if total_keys > 0 {
44 max_total_alloc / total_keys as u64
45 } else {
46 0u64
47 };
48 let alloc_per_key = alloc_per_key / 2;
50
51 let mut allocs = Vec::new();
52 for k in seed_keys.iter() {
53 allocs.push(Allocation {
54 address: k.short_address().unwrap(),
55 balance: alloc_per_key,
56 });
57 }
58
59 let genesis = Self {
60 allocations: Some(allocs),
61 ..Self::default()
62 };
63
64 Ok(genesis)
65 }
66
67 pub fn encode_json(&self) -> io::Result<String> {
68 serde_json::to_string(&self)
69 .map_err(|e| Error::new(ErrorKind::Other, format!("failed to serialize JSON {}", e)))
70 }
71
72 pub fn to_json_bytes(&self) -> io::Result<Vec<u8>> {
74 serde_json::to_vec(self)
75 .map_err(|e| Error::new(ErrorKind::Other, format!("failed encode JSON {}", e)))
76 }
77
78 pub fn sync_json(&self, file_path: &str) -> io::Result<()> {
81 log::info!("syncing '{}' in JSON", file_path);
82 let path = Path::new(file_path);
83 if let Some(parent_dir) = path.parent() {
84 log::info!("creating parent dir '{}'", parent_dir.display());
85 fs::create_dir_all(parent_dir)?;
86 }
87
88 let d = self.to_json_bytes()?;
89
90 let mut f = File::create(file_path)?;
91 f.write_all(&d)?;
92
93 Ok(())
94 }
95
96 pub fn to_packer_bytes(&self) -> errors::Result<Vec<u8>> {
100 let packer = packer::Packer::new((1 << 31) - 1, 128);
101
102 packer.pack_u16(CODEC_VERSION)?;
105 packer.pack_u64(self.timestamp as u64)?;
106
107 if let Some(allocs) = &self.allocations {
108 packer.pack_u32(allocs.len() as u32)?;
109 for alloc in allocs.iter() {
110 packer.pack_bytes(alloc.address.as_ref())?;
111 packer.pack_u64(alloc.balance)?;
112 }
113 }
114
115 Ok(packer.take_bytes().into())
116 }
117}
118
119#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
122#[serde(rename_all = "camelCase")]
123pub struct Allocation {
124 pub address: short::Id,
125 #[serde(default)]
126 pub balance: u64,
127}
128
129impl Default for Allocation {
130 fn default() -> Self {
131 Self {
132 address: short::Id::empty(),
133 balance: 0,
134 }
135 }
136}
137
138#[test]
140fn test_encode_packer_bytes() {
141 let _ = env_logger::builder().is_test(true).try_init();
142
143 let genesis = Genesis {
144 timestamp: 123,
145 allocations: Some(vec![
146 Allocation {
147 address: short::Id::from_str("6Y3kysjF9jnHnYkdS9yGAuoHyae2eNmeV").unwrap(),
149 balance: 1000000000,
150 },
151 Allocation {
152 address: short::Id::from_str("LeKrndtsMxcLMzHz3w4uo1XtLDpfi66c").unwrap(),
154 balance: 3000000000,
155 },
156 ]),
157 };
158 let genesis_packer_bytes = genesis.to_packer_bytes().unwrap();
159
160 let expected = vec![
161 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7b, 0x0, 0x0, 0x0, 0x2, 0x3c, 0xb7, 0xd3,
162 0x84, 0x2e, 0x8c, 0xee, 0x6a, 0xe, 0xbd, 0x9, 0xf1, 0xfe, 0x88, 0x4f, 0x68, 0x61, 0xe1,
163 0xb2, 0x9c, 0x0, 0x0, 0x0, 0x0, 0x3b, 0x9a, 0xca, 0x0, 0x3, 0xb7, 0xf, 0x8c, 0x60, 0x6b,
164 0x2b, 0xf, 0x99, 0x84, 0x9d, 0xc8, 0x5c, 0x40, 0xf, 0xd1, 0x7e, 0xfe, 0x1f, 0x60, 0x0, 0x0,
165 0x0, 0x0, 0xb2, 0xd0, 0x5e, 0x0,
166 ];
167 assert_eq!(genesis_packer_bytes, expected);
168}
169
170#[test]
172fn test_parse() {
173 let _ = env_logger::builder().is_test(true).try_init();
174
175 let resp: Genesis = serde_json::from_str(
177 r#"
178{
179 "unknown1": "field1",
180 "unknown2": "field2",
181
182 "timestamp": 123,
183
184 "allocations": [
185 {"address": "6Y3kysjF9jnHnYkdS9yGAuoHyae2eNmeV", "balance": 2000000000},
186 {"address": "LeKrndtsMxcLMzHz3w4uo1XtLDpfi66c", "balance": 3000000000}
187 ]
188}
189"#,
190 )
191 .unwrap();
192
193 let expected = Genesis {
194 timestamp: 123,
195 allocations: Some(vec![
196 Allocation {
197 address: short::Id::from_str("6Y3kysjF9jnHnYkdS9yGAuoHyae2eNmeV").unwrap(),
199 balance: 2000000000,
200 },
201 Allocation {
202 address: short::Id::from_str("LeKrndtsMxcLMzHz3w4uo1XtLDpfi66c").unwrap(),
204 balance: 3000000000,
205 },
206 ]),
207 };
208 assert_eq!(resp, expected);
209
210 let d = expected.encode_json().unwrap();
211 log::info!("{}", d);
212}