1use std::{
2 borrow::Cow,
3 fs::File,
4 io::{Read, Write},
5 path::PathBuf,
6 str::FromStr,
7};
8
9use crate::{account::AccountSecretKey, daemon_json::EpochData};
10use ledger::{
11 proofs::caching::{ensure_path_exists, mina_cache_path},
12 scan_state::currency::Balance,
13 BaseLedger,
14};
15use mina_core::constants::{constraint_constants, DEFAULT_GENESIS_TIMESTAMP_MILLISECONDS};
16use mina_curves::pasta::Fp;
17use mina_p2p_messages::{
18 bigint::InvalidBigInt,
19 binprot::{
20 self,
21 macros::{BinProtRead, BinProtWrite},
22 BinProtRead, BinProtWrite,
23 },
24 v2::{self, PROTOCOL_CONSTANTS},
25};
26use serde::{Deserialize, Serialize};
27
28use crate::{
29 daemon_json::{self, AccountConfigError, DaemonJson},
30 ProtocolConstants,
31};
32
33pub use GenesisConfig as TransitionFrontierGenesisConfig;
34
35#[derive(Serialize, Deserialize, Debug, Clone)]
36pub enum GenesisConfig {
37 Counts {
38 whales: usize,
39 fish: usize,
40 non_stakers: NonStakers,
41 constants: ProtocolConstants,
42 },
43 BalancesDelegateTable {
44 table: Vec<(u64, Vec<u64>)>,
45 constants: ProtocolConstants,
46 },
47 AccountsBinProt {
48 bytes: Cow<'static, [u8]>,
49 constants: ProtocolConstants,
50 },
51 Prebuilt(Cow<'static, [u8]>),
52 DaemonJson(Box<DaemonJson>),
53 DaemonJsonFile(PathBuf),
54}
55
56#[derive(Serialize, Deserialize, Debug, Clone)]
57pub enum NonStakers {
58 Fill,
60 None,
62 Count(usize),
64}
65
66#[derive(Serialize, Deserialize, Debug, Clone)]
67pub struct GenesisConfigLoaded {
68 pub constants: ProtocolConstants,
69 pub genesis_ledger_hash: v2::LedgerHash,
70 pub genesis_total_currency: v2::CurrencyAmountStableV1,
71 pub genesis_producer_stake_proof: v2::MinaBaseSparseLedgerBaseStableV2,
72 pub staking_epoch_ledger_hash: v2::LedgerHash,
73 pub staking_epoch_total_currency: v2::CurrencyAmountStableV1,
74 pub staking_epoch_seed: v2::EpochSeed,
75 pub next_epoch_ledger_hash: v2::LedgerHash,
76 pub next_epoch_total_currency: v2::CurrencyAmountStableV1,
77 pub next_epoch_seed: v2::EpochSeed,
78}
79
80fn bp_num_delegators(i: usize) -> usize {
81 (i.saturating_add(1))
82 .checked_mul(2)
83 .expect("overflow")
84 .min(32)
85}
86
87#[derive(Debug, thiserror::Error)]
88pub enum GenesisConfigError {
89 #[error("no ledger in configuration")]
90 NoLedger,
91 #[error("declared and computed ledger hashes don't match: {expected} != {computed}")]
92 LedgerHashMismatch {
93 expected: v2::LedgerHash,
94 computed: v2::LedgerHash,
95 },
96 #[error("account error: {0}")]
97 Account(#[from] AccountConfigError),
98 #[error("error loading genesis config from precomputed data: {0}")]
99 Prebuilt(#[from] binprot::Error),
100 #[error("error deserializing daemon.json: {0}")]
101 Json(#[from] serde_json::Error),
102 #[error("I/O error: {0}")]
103 Io(#[from] std::io::Error),
104 #[error("Invalid bigint")]
105 InvalidBigInt(#[from] InvalidBigInt),
106}
107
108impl GenesisConfig {
109 pub fn default_constants(timestamp_ms: u64) -> ProtocolConstants {
110 ProtocolConstants {
111 k: 290.into(),
112 slots_per_epoch: 7140.into(),
113 slots_per_sub_window: 7.into(),
114 grace_period_slots: 2160.into(),
115 delta: 0.into(),
116 genesis_state_timestamp: v2::BlockTimeTimeStableV1(
117 v2::UnsignedExtendedUInt64Int64ForVersionTagsStableV1(timestamp_ms.into()),
118 ),
119 }
120 }
121
122 pub fn protocol_constants(&self) -> Result<ProtocolConstants, time::error::Parse> {
125 match self {
126 Self::Counts { constants, .. }
127 | Self::BalancesDelegateTable { constants, .. }
128 | Self::AccountsBinProt { constants, .. } => Ok(constants.clone()),
129 Self::Prebuilt { .. } => Ok(self.load().unwrap().1.constants),
130 Self::DaemonJson(config) => Ok(config
131 .genesis
132 .as_ref()
133 .map(|g: &daemon_json::Genesis| g.protocol_constants())
134 .unwrap_or(Self::default_constants(
135 DEFAULT_GENESIS_TIMESTAMP_MILLISECONDS,
136 ))),
137 Self::DaemonJsonFile(_) => todo!(),
138 }
139 }
140
141 pub fn override_genesis_state_timestamp(&mut self, timestamp: v2::BlockTimeTimeStableV1) {
142 match self {
143 Self::Counts { constants, .. }
144 | Self::BalancesDelegateTable { constants, .. }
145 | Self::AccountsBinProt { constants, .. } => {
146 constants.genesis_state_timestamp = timestamp;
147 }
148 _ => todo!(),
149 }
150 }
151
152 pub fn load(
153 &self,
154 ) -> anyhow::Result<(Vec<ledger::Mask>, GenesisConfigLoaded), GenesisConfigError> {
155 Ok(match self {
156 Self::Counts {
157 whales,
158 fish,
159 non_stakers,
160 constants,
161 } => {
162 let (whales, fish) = (*whales, *fish);
163 let delegator_balance =
164 |balance: u64| move |i| balance.checked_div(i as u64).expect("division by 0");
165 let whales = (0..whales).map(|i| {
166 let balance = 8333_u64;
167 let delegators = (1..=bp_num_delegators(i)).map(delegator_balance(50_000_000));
168 (balance, delegators)
169 });
170 let fish = (0..fish).map(|i| {
171 let balance = 6333_u64;
172 let delegators = (1..=bp_num_delegators(i)).map(delegator_balance(5_000_000));
173 (balance, delegators)
174 });
175 let delegator_table = whales.chain(fish);
176 let (mut mask, genesis_total_currency) =
177 Self::build_ledger_from_balances_delegator_table(delegator_table, non_stakers)?;
178 let genesis_ledger_hash = ledger_hash(&mut mask);
179 let staking_epoch_total_currency = genesis_total_currency.clone();
180 let next_epoch_total_currency = genesis_total_currency.clone();
181 let staking_epoch_seed = v2::EpochSeed::zero();
182 let next_epoch_seed = v2::EpochSeed::zero();
183
184 let load_result = GenesisConfigLoaded {
185 constants: constants.clone(),
186 genesis_ledger_hash: genesis_ledger_hash.clone(),
187 genesis_total_currency,
188 genesis_producer_stake_proof: create_genesis_producer_stake_proof(&mask),
189 staking_epoch_ledger_hash: genesis_ledger_hash.clone(),
190 staking_epoch_total_currency,
191 next_epoch_ledger_hash: genesis_ledger_hash,
192 next_epoch_total_currency,
193 staking_epoch_seed,
194 next_epoch_seed,
195 };
196 let masks = vec![mask];
197 (masks, load_result)
198 }
199 Self::BalancesDelegateTable { table, constants } => {
200 let table = table.iter().map(|(bp_balance, delegators)| {
201 let delegators = delegators.iter().copied();
202 (*bp_balance, delegators)
203 });
204 let (mut mask, genesis_total_currency) =
205 Self::build_ledger_from_balances_delegator_table(table, &NonStakers::None)?;
206 let genesis_ledger_hash = ledger_hash(&mut mask);
207 let staking_epoch_total_currency = genesis_total_currency.clone();
208 let next_epoch_total_currency = genesis_total_currency.clone();
209 let staking_epoch_seed = v2::EpochSeed::zero();
210 let next_epoch_seed = v2::EpochSeed::zero();
211
212 let load_result = GenesisConfigLoaded {
213 constants: constants.clone(),
214 genesis_ledger_hash: genesis_ledger_hash.clone(),
215 genesis_total_currency,
216 genesis_producer_stake_proof: create_genesis_producer_stake_proof(&mask),
217 staking_epoch_ledger_hash: genesis_ledger_hash.clone(),
218 staking_epoch_total_currency,
219 next_epoch_ledger_hash: genesis_ledger_hash,
220 next_epoch_total_currency,
221 staking_epoch_seed,
222 next_epoch_seed,
223 };
224 let masks = vec![mask];
225 (masks, load_result)
226 }
227 Self::Prebuilt(bytes) => {
228 let prebuilt = PrebuiltGenesisConfig::read(&mut bytes.as_ref())?;
229 prebuilt.load()?
230 }
231 Self::AccountsBinProt { bytes, .. } => {
232 let mut bytes = bytes.as_ref();
233 let _expected_hash = Option::<v2::LedgerHash>::binprot_read(&mut bytes)?;
234 let hashes = Vec::<(u64, v2::LedgerHash)>::binprot_read(&mut bytes)?
235 .into_iter()
236 .map(|(idx, hash)| Ok((idx, hash.0.to_field()?)))
237 .collect::<Result<Vec<_>, InvalidBigInt>>()?;
238 let accounts = Vec::<ledger::Account>::binprot_read(&mut bytes)?;
239
240 let (mut mask, _total_currency) = Self::build_ledger_from_accounts_and_hashes(
241 accounts.into_iter().map(Ok),
242 hashes,
243 )?;
244 let _ledger_hash = ledger_hash(&mut mask);
245
246 todo!()
247
248 }
263 Self::DaemonJson(config) => {
264 if let Some(fork) = config.proof.as_ref().and_then(|p| p.fork.clone()) {
268 mina_core::constants::set_fork_override(fork);
269 }
270
271 let mut masks = Vec::new();
272 let constants = config
273 .genesis
274 .as_ref()
275 .map_or(PROTOCOL_CONSTANTS, |genesis| genesis.protocol_constants());
276 let ledger = config.ledger.as_ref().ok_or(GenesisConfigError::NoLedger)?;
277 let accounts = ledger
278 .accounts_with_genesis_winner()
279 .iter()
280 .map(daemon_json::Account::to_account)
281 .collect::<Result<Vec<_>, _>>()?;
282 let (mask, total_currency, genesis_ledger_hash) =
283 Self::build_or_load_ledger(ledger.ledger_name(), accounts.into_iter())?;
284
285 masks.push(mask.clone());
286 if let Some(expected_hash) = config.ledger.as_ref().and_then(|l| l.hash.as_ref()) {
287 if expected_hash != &genesis_ledger_hash.to_string() {
288 return Err(GenesisConfigError::LedgerHashMismatch {
289 expected: expected_hash.parse().unwrap(),
290 computed: genesis_ledger_hash.clone(),
291 });
292 }
293 }
294
295 let staking_epoch_ledger_hash;
296 let staking_epoch_total_currency;
297 let staking_epoch_seed: v2::EpochSeed;
298 let next_epoch_ledger_hash;
299 let next_epoch_total_currency;
300 let next_epoch_seed: v2::EpochSeed;
301 let genesis_producer_stake_proof: v2::MinaBaseSparseLedgerBaseStableV2;
302 if let Some(data) = &config.epoch_data {
305 let accounts = data
306 .staking
307 .accounts
308 .as_ref()
309 .unwrap()
310 .iter()
311 .map(daemon_json::Account::to_account)
312 .collect::<Result<Vec<_>, _>>()?;
313 let (staking_ledger_mask, total_currency, hash) = Self::build_or_load_ledger(
314 data.staking.ledger_name(),
315 accounts.into_iter(),
316 )?;
317 staking_epoch_ledger_hash = hash;
318 staking_epoch_total_currency = total_currency;
319 staking_epoch_seed = v2::EpochSeed::from_str(&data.staking.seed).unwrap();
320 genesis_producer_stake_proof =
321 create_genesis_producer_stake_proof(&staking_ledger_mask);
322 masks.push(staking_ledger_mask);
323
324 let next = data.next.as_ref().unwrap();
325 let accounts = next
326 .accounts
327 .as_ref()
328 .unwrap()
329 .iter()
330 .map(daemon_json::Account::to_account)
331 .collect::<Result<Vec<_>, _>>()?;
332 let (mut _mask, total_currency, hash) =
333 Self::build_or_load_ledger(next.ledger_name(), accounts.into_iter())?;
334 next_epoch_ledger_hash = hash;
335 next_epoch_total_currency = total_currency;
336 next_epoch_seed =
337 v2::EpochSeed::from_str(&data.next.as_ref().unwrap().seed).unwrap();
338 } else {
339 staking_epoch_ledger_hash = genesis_ledger_hash.clone();
340 staking_epoch_total_currency = total_currency.clone();
341 staking_epoch_seed = v2::EpochSeed::zero();
342 next_epoch_ledger_hash = genesis_ledger_hash.clone();
343 next_epoch_total_currency = total_currency.clone();
344 next_epoch_seed = v2::EpochSeed::zero();
345 genesis_producer_stake_proof = create_genesis_producer_stake_proof(&mask);
346 }
347
348 let result = GenesisConfigLoaded {
349 constants,
350 genesis_ledger_hash,
351 genesis_total_currency: total_currency,
352 genesis_producer_stake_proof,
353 staking_epoch_ledger_hash,
354 staking_epoch_total_currency,
355 next_epoch_ledger_hash,
356 next_epoch_total_currency,
357 staking_epoch_seed,
358 next_epoch_seed,
359 };
360 (masks, result)
361 }
362 Self::DaemonJsonFile(path) => {
363 let reader = File::open(path)?;
364 let c = serde_json::from_reader(reader)?;
365 Self::DaemonJson(c).load()?
366 }
367 })
368 }
369
370 fn build_or_load_ledger(
371 ledger_name: String,
372 accounts: impl Iterator<Item = ledger::Account>,
373 ) -> Result<(ledger::Mask, v2::CurrencyAmountStableV1, LedgerHash), GenesisConfigError> {
374 mina_core::info!(
375 mina_core::log::system_time();
376 kind = "ledger loading",
377 message = "loading the ledger",
378 ledger_name = ledger_name,
379 );
380 match LedgerAccountsWithHash::load(ledger_name)? {
381 Some(accounts_with_hash) => {
382 let (mask, total_currency) = Self::build_ledger_from_accounts_and_hashes(
383 accounts_with_hash
384 .accounts
385 .iter()
386 .map(ledger::Account::try_from),
387 accounts_with_hash
388 .hashes
389 .into_iter()
390 .map(|(n, h)| Ok((n, h.to_field()?)))
391 .collect::<Result<Vec<_>, InvalidBigInt>>()?,
392 )?;
393 mina_core::info!(
394 mina_core::log::system_time();
395 kind = "ledger loaded",
396 message = "loaded from cache",
397 ledger_hash = accounts_with_hash.ledger_hash.to_string(),
398 );
399 Ok((mask, total_currency, accounts_with_hash.ledger_hash))
400 }
401 None => {
402 let (mut mask, total_currency) =
403 Self::build_ledger_from_accounts(accounts.into_iter().map(Ok))?;
404 let hash = ledger_hash(&mut mask);
405 let ledger_accounts = LedgerAccountsWithHash {
406 accounts: mask.fold(Vec::new(), |mut acc, a| {
407 acc.push(a.into());
408 acc
409 }),
410 ledger_hash: hash.clone(),
411 hashes: mask
412 .get_raw_inner_hashes()
413 .into_iter()
414 .map(|(idx, hash)| (idx, v2::LedgerHash::from_fp(hash)))
415 .collect(),
416 };
417 ledger_accounts.cache()?;
418 mina_core::info!(
419 mina_core::log::system_time();
420 kind = "ledger loaded",
421 message = "built from config and cached",
422 ledger_hash = hash.to_string(),
423 );
424 Ok((mask, total_currency, hash))
425 }
426 }
427 }
428
429 fn build_ledger_from_balances_delegator_table(
430 block_producers: impl IntoIterator<Item = (u64, impl IntoIterator<Item = u64>)>,
431 non_stakers: &NonStakers,
432 ) -> Result<(ledger::Mask, v2::CurrencyAmountStableV1), InvalidBigInt> {
433 let mut counter = 0;
434 let mut total_balance = 0;
435
436 fn create_account(
437 counter: &mut u64,
438 balance: u64,
439 delegate: Option<mina_signer::CompressedPubKey>,
440 total_balance: &mut u64,
441 ) -> ledger::Account {
442 let sec_key = AccountSecretKey::deterministic(*counter);
443 let pub_key: mina_signer::CompressedPubKey = sec_key.public_key().try_into().unwrap(); let account_id = ledger::AccountId::new(pub_key.clone(), Default::default());
445 let mut account =
446 ledger::Account::create_with(account_id, Balance::from_mina(balance).unwrap());
447 account.delegate = delegate;
448 *total_balance = total_balance.checked_add(balance).expect("overflow");
449 *counter = counter.checked_add(1).expect("overflow");
450 account
452 }
453
454 let mut accounts = genesis_account_iter().map(Ok).collect::<Vec<_>>();
455
456 for (bp_balance, delegators) in block_producers {
458 let bp_account = create_account(&mut counter, bp_balance, None, &mut total_balance);
459 let bp_pub_key = bp_account.public_key.clone();
460 accounts.push(Ok(bp_account));
461
462 for balance in delegators {
463 let delegator_account = create_account(
464 &mut counter,
465 balance,
466 Some(bp_pub_key.clone()),
467 &mut total_balance,
468 );
469 accounts.push(Ok(delegator_account));
470 }
471 }
472
473 let remaining_accounts =
474 AccountSecretKey::max_deterministic_count().saturating_sub(counter as usize);
475
476 let non_staker_count = match non_stakers {
477 NonStakers::Fill => remaining_accounts,
478 NonStakers::None => 0,
479 NonStakers::Count(count) => *std::cmp::min(count, &remaining_accounts),
480 };
481
482 let non_staker_total = total_balance.checked_mul(20).expect("overflow") / 80;
483 let non_staker_balance = non_staker_total
484 .checked_div(non_staker_count as u64)
485 .unwrap_or(0);
486
487 println!("Non staker total balance: {}", non_staker_total);
488
489 if matches!(non_stakers, NonStakers::Fill | NonStakers::Count(_)) {
491 for _ in 0..non_staker_count {
492 let non_staker_account =
493 create_account(&mut counter, non_staker_balance, None, &mut total_balance);
494 accounts.push(Ok(non_staker_account));
495 }
496 }
497
498 Self::build_ledger_from_accounts(accounts)
499 }
500
501 fn build_ledger_from_accounts(
502 accounts: impl IntoIterator<Item = Result<ledger::Account, InvalidBigInt>>,
503 ) -> Result<(ledger::Mask, v2::CurrencyAmountStableV1), InvalidBigInt> {
504 let db =
505 ledger::Database::create_with_token_owners(constraint_constants().ledger_depth as u8);
506 let mask = ledger::Mask::new_root(db);
507 let (mask, total_currency) = accounts.into_iter().try_fold(
508 (mask, 0),
509 |(mut mask, mut total_currency): (ledger::Mask, u64), account| {
510 let account = account?;
511 let account_id = account.id();
512 total_currency = total_currency
513 .checked_add(account.balance.as_u64())
514 .expect("overflow");
515 mask.get_or_create_account(account_id, account).unwrap();
516 Ok((mask, total_currency))
517 },
518 )?;
519
520 Ok((mask, v2::CurrencyAmountStableV1(total_currency.into())))
521 }
522
523 fn build_ledger_from_accounts_and_hashes(
524 accounts: impl IntoIterator<Item = Result<ledger::Account, InvalidBigInt>>,
525 hashes: Vec<(u64, Fp)>,
526 ) -> Result<(ledger::Mask, v2::CurrencyAmountStableV1), InvalidBigInt> {
527 let (mask, total_currency) = Self::build_ledger_from_accounts(accounts)?;
528
529 mask.set_raw_inner_hashes(hashes);
532
533 Ok((mask, total_currency))
534 }
535}
536
537fn ledger_hash(mask: &mut ledger::Mask) -> v2::LedgerHash {
538 v2::MinaBaseLedgerHash0StableV1(mask.merkle_root().into()).into()
539}
540
541fn genesis_account_iter() -> impl Iterator<Item = ledger::Account> {
542 std::iter::once({
543 let pub_key = AccountSecretKey::genesis_producer().public_key();
545 let account_id = ledger::AccountId::new(pub_key.try_into().unwrap(), Default::default()); ledger::Account::create_with(account_id, Balance::from_u64(0))
547 })
548}
549
550fn create_genesis_producer_stake_proof(
551 mask: &ledger::Mask,
552) -> v2::MinaBaseSparseLedgerBaseStableV2 {
553 let producer = AccountSecretKey::genesis_producer().public_key();
554 let producer_id =
555 ledger::AccountId::new(producer.try_into().unwrap(), ledger::TokenId::default()); let sparse_ledger =
557 ledger::sparse_ledger::SparseLedger::of_ledger_subset_exn(mask.clone(), &[producer_id]);
558 (&sparse_ledger).into()
559}
560
561#[derive(Debug, BinProtRead, BinProtWrite)]
562struct LedgerAccountsWithHash {
563 ledger_hash: LedgerHash,
564 accounts: Vec<MinaBaseAccountBinableArgStableV2>,
565 hashes: Vec<(u64, LedgerHash)>,
566}
567
568impl LedgerAccountsWithHash {
569 fn cache(&self) -> Result<(), std::io::Error> {
570 let cache_dir = mina_cache_path("ledgers").unwrap();
571 let cache_file = cache_dir.join(format!("{}.bin", self.ledger_hash));
572 ensure_path_exists(cache_dir)?;
573 let mut file = File::create(cache_file)?;
574 self.binprot_write(&mut file)
575 }
576
577 fn load(ledger_name: String) -> Result<Option<Self>, binprot::Error> {
578 let cache_filename = mina_cache_path(format!("ledgers/{}.bin", ledger_name)).unwrap();
579 if cache_filename.is_file() {
580 let mut file = File::open(cache_filename)?;
581 LedgerAccountsWithHash::binprot_read(&mut file).map(Some)
582 } else {
583 Ok(None)
584 }
585 }
586}
587
588use mina_p2p_messages::v2::{LedgerHash, MinaBaseAccountBinableArgStableV2};
589
590#[derive(Debug, Serialize, Deserialize, BinProtRead, BinProtWrite)]
592pub struct PrebuiltGenesisConfig {
593 constants: ProtocolConstants,
594 accounts: Vec<MinaBaseAccountBinableArgStableV2>,
595 ledger_hash: LedgerHash,
596 hashes: Vec<(u64, LedgerHash)>,
597 staking_epoch_data: PrebuiltGenesisEpochData,
598 next_epoch_data: PrebuiltGenesisEpochData,
599}
600
601impl PrebuiltGenesisConfig {
602 pub fn read<R: Read>(mut reader: R) -> Result<Self, binprot::Error> {
603 PrebuiltGenesisConfig::binprot_read(&mut reader)
604 }
605
606 pub fn store<W: Write>(&self, mut writer: W) -> Result<(), std::io::Error> {
607 self.binprot_write(&mut writer)
608 }
609
610 pub fn load(self) -> Result<(Vec<ledger::Mask>, GenesisConfigLoaded), GenesisConfigError> {
611 let mut masks = Vec::new();
612 let (mask, genesis_total_currency) = GenesisConfig::build_ledger_from_accounts_and_hashes(
613 self.accounts.into_iter().map(|acc| (&acc).try_into()),
614 self.hashes
615 .into_iter()
616 .map(|(n, h)| Ok((n, h.to_field()?)))
617 .collect::<Result<Vec<_>, InvalidBigInt>>()?,
618 )?;
619 masks.push(mask);
620 let (staking_ledger_mask, staking_epoch_total_currency) =
621 GenesisConfig::build_ledger_from_accounts_and_hashes(
622 self.staking_epoch_data
623 .accounts
624 .into_iter()
625 .map(|acc| (&acc).try_into()),
626 self.staking_epoch_data
627 .hashes
628 .into_iter()
629 .map(|(n, h)| Ok((n, h.to_field()?)))
630 .collect::<Result<Vec<_>, InvalidBigInt>>()?,
631 )?;
632 let (_mask, next_epoch_total_currency) =
633 GenesisConfig::build_ledger_from_accounts_and_hashes(
634 self.next_epoch_data
635 .accounts
636 .into_iter()
637 .map(|acc| (&acc).try_into()),
638 self.next_epoch_data
639 .hashes
640 .into_iter()
641 .map(|(n, h)| Ok((n, h.to_field()?)))
642 .collect::<Result<Vec<_>, InvalidBigInt>>()?,
643 )?;
644
645 let load_result = GenesisConfigLoaded {
646 constants: self.constants,
647 genesis_ledger_hash: self.ledger_hash,
648 genesis_total_currency,
649 genesis_producer_stake_proof: create_genesis_producer_stake_proof(&staking_ledger_mask),
650 staking_epoch_ledger_hash: self.staking_epoch_data.ledger_hash.clone(),
651 staking_epoch_total_currency,
652 next_epoch_ledger_hash: self.next_epoch_data.ledger_hash.clone(),
653 next_epoch_total_currency,
654 staking_epoch_seed: self.staking_epoch_data.seed,
655 next_epoch_seed: self.next_epoch_data.seed,
656 };
657 masks.push(staking_ledger_mask);
658 Ok((masks, load_result))
659 }
660
661 #[allow(clippy::result_unit_err)]
662 pub fn from_loaded(
663 (masks, data): (Vec<ledger::Mask>, GenesisConfigLoaded),
664 ) -> Result<Self, ()> {
665 let find_mask_by_hash = |hash: &v2::LedgerHash| {
666 masks
667 .iter()
668 .find(|&m| m.clone().merkle_root() == hash.to_field().unwrap())
669 .ok_or(())
670 };
671 let inner_hashes = |mask: &ledger::Mask| {
672 mask.get_raw_inner_hashes()
673 .into_iter()
674 .map(|(idx, hash)| (idx, v2::LedgerHash::from_fp(hash)))
675 .collect()
676 };
677 let genesis_mask = find_mask_by_hash(&data.genesis_ledger_hash)?;
678 let epoch_data = |hash: v2::LedgerHash, seed: v2::EpochSeed| {
679 find_mask_by_hash(&hash).map(|mask| PrebuiltGenesisEpochData {
680 accounts: mask.to_list().into_iter().map(Into::into).collect(),
681 ledger_hash: hash,
682 hashes: inner_hashes(mask),
683 seed,
684 })
685 };
686 Ok(Self {
687 constants: data.constants,
688 accounts: genesis_mask.to_list().into_iter().map(Into::into).collect(),
689 ledger_hash: data.genesis_ledger_hash,
690 hashes: inner_hashes(genesis_mask),
691 staking_epoch_data: epoch_data(
692 data.staking_epoch_ledger_hash,
693 data.staking_epoch_seed,
694 )?,
695 next_epoch_data: epoch_data(data.next_epoch_ledger_hash, data.next_epoch_seed)?,
696 })
697 }
698}
699
700#[derive(Debug, Serialize, Deserialize, BinProtRead, BinProtWrite)]
701pub struct PrebuiltGenesisEpochData {
702 accounts: Vec<MinaBaseAccountBinableArgStableV2>,
703 ledger_hash: LedgerHash,
704 hashes: Vec<(u64, LedgerHash)>,
705 seed: v2::EpochSeed,
706}
707
708impl TryFrom<DaemonJson> for PrebuiltGenesisConfig {
709 type Error = GenesisConfigError;
710
711 fn try_from(config: DaemonJson) -> Result<Self, Self::Error> {
712 let constants = config
713 .genesis
714 .as_ref()
715 .map_or(PROTOCOL_CONSTANTS, |genesis| genesis.protocol_constants());
716 let ledger = config.ledger.as_ref().ok_or(GenesisConfigError::NoLedger)?;
717 let ledger_accounts = ledger
718 .accounts_with_genesis_winner()
719 .iter()
720 .map(daemon_json::Account::to_account)
721 .collect::<Result<Vec<_>, _>>()?;
722 let accounts = ledger_accounts.iter().map(Into::into).collect();
723 let (mut mask, _total_currency) =
724 GenesisConfig::build_ledger_from_accounts(ledger_accounts.into_iter().map(Ok))?;
725 let ledger_hash = ledger_hash(&mut mask);
726 let hashes = mask
727 .get_raw_inner_hashes()
728 .into_iter()
729 .map(|(idx, hash)| (idx, v2::LedgerHash::from_fp(hash)))
730 .collect();
731 let daemon_json::Epochs { staking, next } = config.epoch_data.unwrap();
732 let staking_epoch_data = staking.try_into().unwrap();
733 let next_epoch_data = next.unwrap().try_into().unwrap();
734 let result = PrebuiltGenesisConfig {
735 constants,
736 accounts,
737 ledger_hash,
738 hashes,
739 staking_epoch_data,
740 next_epoch_data,
741 };
742 Ok(result)
743 }
744}
745
746impl TryFrom<EpochData> for PrebuiltGenesisEpochData {
747 type Error = GenesisConfigError;
748
749 fn try_from(value: EpochData) -> Result<Self, Self::Error> {
750 let EpochData {
751 accounts,
752 hash,
753 s3_data_hash: _,
754 seed,
755 } = value;
756
757 let expected_ledger_hash = hash.unwrap().parse().unwrap();
758 let seed = seed.parse().unwrap();
759 let ledger_accounts = accounts
760 .unwrap()
761 .iter()
762 .map(daemon_json::Account::to_account)
763 .collect::<Result<Vec<_>, _>>()?;
764 let accounts = ledger_accounts.iter().map(Into::into).collect();
765 let (mut mask, _total_currency) =
766 GenesisConfig::build_ledger_from_accounts(ledger_accounts.into_iter().map(Ok))?;
767 let ledger_hash = ledger_hash(&mut mask);
768
769 if ledger_hash != expected_ledger_hash {
770 return Err(Self::Error::LedgerHashMismatch {
771 expected: expected_ledger_hash,
772 computed: ledger_hash,
773 });
774 }
775
776 let hashes = mask
777 .get_raw_inner_hashes()
778 .into_iter()
779 .map(|(idx, hash)| (idx, v2::LedgerHash::from_fp(hash)))
780 .collect();
781
782 Ok(Self {
783 accounts,
784 ledger_hash,
785 hashes,
786 seed,
787 })
788 }
789}