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 ark_ff::fields::arithmetic::InvalidBigInt;
11use ledger::{
12 proofs::caching::{ensure_path_exists, openmina_cache_path},
13 scan_state::currency::Balance,
14 BaseLedger,
15};
16use mina_curves::pasta::Fp;
17use mina_p2p_messages::{
18 binprot::{
19 self,
20 macros::{BinProtRead, BinProtWrite},
21 BinProtRead, BinProtWrite,
22 },
23 v2::{self, PROTOCOL_CONSTANTS},
24};
25use openmina_core::constants::{constraint_constants, DEFAULT_GENESIS_TIMESTAMP_MILLISECONDS};
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 let mut masks = Vec::new();
265 let constants = config
266 .genesis
267 .as_ref()
268 .map_or(PROTOCOL_CONSTANTS, |genesis| genesis.protocol_constants());
269 let ledger = config.ledger.as_ref().ok_or(GenesisConfigError::NoLedger)?;
270 let accounts = ledger
271 .accounts_with_genesis_winner()
272 .iter()
273 .map(daemon_json::Account::to_account)
274 .collect::<Result<Vec<_>, _>>()?;
275 let (mask, total_currency, genesis_ledger_hash) =
276 Self::build_or_load_ledger(ledger.ledger_name(), accounts.into_iter())?;
277
278 masks.push(mask.clone());
279 if let Some(expected_hash) = config.ledger.as_ref().and_then(|l| l.hash.as_ref()) {
280 if expected_hash != &genesis_ledger_hash.to_string() {
281 return Err(GenesisConfigError::LedgerHashMismatch {
282 expected: expected_hash.parse().unwrap(),
283 computed: genesis_ledger_hash.clone(),
284 });
285 }
286 }
287
288 let staking_epoch_ledger_hash;
289 let staking_epoch_total_currency;
290 let staking_epoch_seed: v2::EpochSeed;
291 let next_epoch_ledger_hash;
292 let next_epoch_total_currency;
293 let next_epoch_seed: v2::EpochSeed;
294 let genesis_producer_stake_proof: v2::MinaBaseSparseLedgerBaseStableV2;
295 if let Some(data) = &config.epoch_data {
298 let accounts = data
299 .staking
300 .accounts
301 .as_ref()
302 .unwrap()
303 .iter()
304 .map(daemon_json::Account::to_account)
305 .collect::<Result<Vec<_>, _>>()?;
306 let (staking_ledger_mask, total_currency, hash) = Self::build_or_load_ledger(
307 data.staking.ledger_name(),
308 accounts.into_iter(),
309 )?;
310 staking_epoch_ledger_hash = hash;
311 staking_epoch_total_currency = total_currency;
312 staking_epoch_seed = v2::EpochSeed::from_str(&data.staking.seed).unwrap();
313 genesis_producer_stake_proof =
314 create_genesis_producer_stake_proof(&staking_ledger_mask);
315 masks.push(staking_ledger_mask);
316
317 let next = data.next.as_ref().unwrap();
318 let accounts = next
319 .accounts
320 .as_ref()
321 .unwrap()
322 .iter()
323 .map(daemon_json::Account::to_account)
324 .collect::<Result<Vec<_>, _>>()?;
325 let (mut _mask, total_currency, hash) =
326 Self::build_or_load_ledger(next.ledger_name(), accounts.into_iter())?;
327 next_epoch_ledger_hash = hash;
328 next_epoch_total_currency = total_currency;
329 next_epoch_seed =
330 v2::EpochSeed::from_str(&data.next.as_ref().unwrap().seed).unwrap();
331 } else {
332 staking_epoch_ledger_hash = genesis_ledger_hash.clone();
333 staking_epoch_total_currency = total_currency.clone();
334 staking_epoch_seed = v2::EpochSeed::zero();
335 next_epoch_ledger_hash = genesis_ledger_hash.clone();
336 next_epoch_total_currency = total_currency.clone();
337 next_epoch_seed = v2::EpochSeed::zero();
338 genesis_producer_stake_proof = create_genesis_producer_stake_proof(&mask);
339 }
340
341 let result = GenesisConfigLoaded {
342 constants,
343 genesis_ledger_hash,
344 genesis_total_currency: total_currency,
345 genesis_producer_stake_proof,
346 staking_epoch_ledger_hash,
347 staking_epoch_total_currency,
348 next_epoch_ledger_hash,
349 next_epoch_total_currency,
350 staking_epoch_seed,
351 next_epoch_seed,
352 };
353 (masks, result)
354 }
355 Self::DaemonJsonFile(path) => {
356 let reader = File::open(path)?;
357 let c = serde_json::from_reader(reader)?;
358 Self::DaemonJson(c).load()?
359 }
360 })
361 }
362
363 fn build_or_load_ledger(
364 ledger_name: String,
365 accounts: impl Iterator<Item = ledger::Account>,
366 ) -> Result<(ledger::Mask, v2::CurrencyAmountStableV1, LedgerHash), GenesisConfigError> {
367 openmina_core::info!(
368 openmina_core::log::system_time();
369 kind = "ledger loading",
370 message = "loading the ledger",
371 ledger_name = ledger_name,
372 );
373 match LedgerAccountsWithHash::load(ledger_name)? {
374 Some(accounts_with_hash) => {
375 let (mask, total_currency) = Self::build_ledger_from_accounts_and_hashes(
376 accounts_with_hash
377 .accounts
378 .iter()
379 .map(ledger::Account::try_from),
380 accounts_with_hash
381 .hashes
382 .into_iter()
383 .map(|(n, h)| Ok((n, h.to_field()?)))
384 .collect::<Result<Vec<_>, InvalidBigInt>>()?,
385 )?;
386 openmina_core::info!(
387 openmina_core::log::system_time();
388 kind = "ledger loaded",
389 message = "loaded from cache",
390 ledger_hash = accounts_with_hash.ledger_hash.to_string(),
391 );
392 Ok((mask, total_currency, accounts_with_hash.ledger_hash))
393 }
394 None => {
395 let (mut mask, total_currency) =
396 Self::build_ledger_from_accounts(accounts.into_iter().map(Ok))?;
397 let hash = ledger_hash(&mut mask);
398 let ledger_accounts = LedgerAccountsWithHash {
399 accounts: mask.fold(Vec::new(), |mut acc, a| {
400 acc.push(a.into());
401 acc
402 }),
403 ledger_hash: hash.clone(),
404 hashes: mask
405 .get_raw_inner_hashes()
406 .into_iter()
407 .map(|(idx, hash)| (idx, v2::LedgerHash::from_fp(hash)))
408 .collect(),
409 };
410 ledger_accounts.cache()?;
411 openmina_core::info!(
412 openmina_core::log::system_time();
413 kind = "ledger loaded",
414 message = "built from config and cached",
415 ledger_hash = hash.to_string(),
416 );
417 Ok((mask, total_currency, hash))
418 }
419 }
420 }
421
422 fn build_ledger_from_balances_delegator_table(
423 block_producers: impl IntoIterator<Item = (u64, impl IntoIterator<Item = u64>)>,
424 non_stakers: &NonStakers,
425 ) -> Result<(ledger::Mask, v2::CurrencyAmountStableV1), InvalidBigInt> {
426 let mut counter = 0;
427 let mut total_balance = 0;
428
429 fn create_account(
430 counter: &mut u64,
431 balance: u64,
432 delegate: Option<mina_signer::CompressedPubKey>,
433 total_balance: &mut u64,
434 ) -> ledger::Account {
435 let sec_key = AccountSecretKey::deterministic(*counter);
436 let pub_key: mina_signer::CompressedPubKey = sec_key.public_key().try_into().unwrap(); let account_id = ledger::AccountId::new(pub_key.clone(), Default::default());
438 let mut account =
439 ledger::Account::create_with(account_id, Balance::from_mina(balance).unwrap());
440 account.delegate = delegate;
441 *total_balance = total_balance.checked_add(balance).expect("overflow");
442 *counter = counter.checked_add(1).expect("overflow");
443 account
445 }
446
447 let mut accounts = genesis_account_iter().map(Ok).collect::<Vec<_>>();
448
449 for (bp_balance, delegators) in block_producers {
451 let bp_account = create_account(&mut counter, bp_balance, None, &mut total_balance);
452 let bp_pub_key = bp_account.public_key.clone();
453 accounts.push(Ok(bp_account));
454
455 for balance in delegators {
456 let delegator_account = create_account(
457 &mut counter,
458 balance,
459 Some(bp_pub_key.clone()),
460 &mut total_balance,
461 );
462 accounts.push(Ok(delegator_account));
463 }
464 }
465
466 let remaining_accounts = AccountSecretKey::max_deterministic_count()
467 .checked_sub(counter as usize)
468 .unwrap_or_default();
469
470 let non_staker_count = match non_stakers {
471 NonStakers::Fill => remaining_accounts,
472 NonStakers::None => 0,
473 NonStakers::Count(count) => *std::cmp::min(count, &remaining_accounts),
474 };
475
476 let non_staker_total = total_balance.checked_mul(20).expect("overflow") / 80;
477 let non_staker_balance = non_staker_total
478 .checked_div(non_staker_count as u64)
479 .unwrap_or(0);
480
481 println!("Non staker total balance: {}", non_staker_total);
482
483 if matches!(non_stakers, NonStakers::Fill | NonStakers::Count(_)) {
485 for _ in 0..non_staker_count {
486 let non_staker_account =
487 create_account(&mut counter, non_staker_balance, None, &mut total_balance);
488 accounts.push(Ok(non_staker_account));
489 }
490 }
491
492 Self::build_ledger_from_accounts(accounts)
493 }
494
495 fn build_ledger_from_accounts(
496 accounts: impl IntoIterator<Item = Result<ledger::Account, InvalidBigInt>>,
497 ) -> Result<(ledger::Mask, v2::CurrencyAmountStableV1), InvalidBigInt> {
498 let db =
499 ledger::Database::create_with_token_owners(constraint_constants().ledger_depth as u8);
500 let mask = ledger::Mask::new_root(db);
501 let (mask, total_currency) = accounts.into_iter().try_fold(
502 (mask, 0),
503 |(mut mask, mut total_currency): (ledger::Mask, u64), account| {
504 let account = account?;
505 let account_id = account.id();
506 total_currency = total_currency
507 .checked_add(account.balance.as_u64())
508 .expect("overflow");
509 mask.get_or_create_account(account_id, account).unwrap();
510 Ok((mask, total_currency))
511 },
512 )?;
513
514 Ok((mask, v2::CurrencyAmountStableV1(total_currency.into())))
515 }
516
517 fn build_ledger_from_accounts_and_hashes(
518 accounts: impl IntoIterator<Item = Result<ledger::Account, InvalidBigInt>>,
519 hashes: Vec<(u64, Fp)>,
520 ) -> Result<(ledger::Mask, v2::CurrencyAmountStableV1), InvalidBigInt> {
521 let (mask, total_currency) = Self::build_ledger_from_accounts(accounts)?;
522
523 mask.set_raw_inner_hashes(hashes);
526
527 Ok((mask, total_currency))
528 }
529}
530
531fn ledger_hash(mask: &mut ledger::Mask) -> v2::LedgerHash {
532 v2::MinaBaseLedgerHash0StableV1(mask.merkle_root().into()).into()
533}
534
535fn genesis_account_iter() -> impl Iterator<Item = ledger::Account> {
536 std::iter::once({
537 let pub_key = AccountSecretKey::genesis_producer().public_key();
539 let account_id = ledger::AccountId::new(pub_key.try_into().unwrap(), Default::default()); ledger::Account::create_with(account_id, Balance::from_u64(0))
541 })
542}
543
544fn create_genesis_producer_stake_proof(
545 mask: &ledger::Mask,
546) -> v2::MinaBaseSparseLedgerBaseStableV2 {
547 let producer = AccountSecretKey::genesis_producer().public_key();
548 let producer_id =
549 ledger::AccountId::new(producer.try_into().unwrap(), ledger::TokenId::default()); let sparse_ledger =
551 ledger::sparse_ledger::SparseLedger::of_ledger_subset_exn(mask.clone(), &[producer_id]);
552 (&sparse_ledger).into()
553}
554
555#[derive(Debug, BinProtRead, BinProtWrite)]
556struct LedgerAccountsWithHash {
557 ledger_hash: LedgerHash,
558 accounts: Vec<MinaBaseAccountBinableArgStableV2>,
559 hashes: Vec<(u64, LedgerHash)>,
560}
561
562impl LedgerAccountsWithHash {
563 fn cache(&self) -> Result<(), std::io::Error> {
564 let cache_dir = openmina_cache_path("ledgers").unwrap();
565 let cache_file = cache_dir.join(format!("{}.bin", self.ledger_hash));
566 ensure_path_exists(cache_dir)?;
567 let mut file = File::create(cache_file)?;
568 self.binprot_write(&mut file)
569 }
570
571 fn load(ledger_name: String) -> Result<Option<Self>, binprot::Error> {
572 let cache_filename = openmina_cache_path(format!("ledgers/{}.bin", ledger_name)).unwrap();
573 if cache_filename.is_file() {
574 let mut file = File::open(cache_filename)?;
575 LedgerAccountsWithHash::binprot_read(&mut file).map(Some)
576 } else {
577 Ok(None)
578 }
579 }
580}
581
582use mina_p2p_messages::v2::{LedgerHash, MinaBaseAccountBinableArgStableV2};
583
584#[derive(Debug, Serialize, Deserialize, BinProtRead, BinProtWrite)]
586pub struct PrebuiltGenesisConfig {
587 constants: ProtocolConstants,
588 accounts: Vec<MinaBaseAccountBinableArgStableV2>,
589 ledger_hash: LedgerHash,
590 hashes: Vec<(u64, LedgerHash)>,
591 staking_epoch_data: PrebuiltGenesisEpochData,
592 next_epoch_data: PrebuiltGenesisEpochData,
593}
594
595impl PrebuiltGenesisConfig {
596 pub fn read<R: Read>(mut reader: R) -> Result<Self, binprot::Error> {
597 PrebuiltGenesisConfig::binprot_read(&mut reader)
598 }
599
600 pub fn store<W: Write>(&self, mut writer: W) -> Result<(), std::io::Error> {
601 self.binprot_write(&mut writer)
602 }
603
604 pub fn load(self) -> Result<(Vec<ledger::Mask>, GenesisConfigLoaded), GenesisConfigError> {
605 let mut masks = Vec::new();
606 let (mask, genesis_total_currency) = GenesisConfig::build_ledger_from_accounts_and_hashes(
607 self.accounts.into_iter().map(|acc| (&acc).try_into()),
608 self.hashes
609 .into_iter()
610 .map(|(n, h)| Ok((n, h.to_field()?)))
611 .collect::<Result<Vec<_>, InvalidBigInt>>()?,
612 )?;
613 masks.push(mask);
614 let (staking_ledger_mask, staking_epoch_total_currency) =
615 GenesisConfig::build_ledger_from_accounts_and_hashes(
616 self.staking_epoch_data
617 .accounts
618 .into_iter()
619 .map(|acc| (&acc).try_into()),
620 self.staking_epoch_data
621 .hashes
622 .into_iter()
623 .map(|(n, h)| Ok((n, h.to_field()?)))
624 .collect::<Result<Vec<_>, InvalidBigInt>>()?,
625 )?;
626 let (_mask, next_epoch_total_currency) =
627 GenesisConfig::build_ledger_from_accounts_and_hashes(
628 self.next_epoch_data
629 .accounts
630 .into_iter()
631 .map(|acc| (&acc).try_into()),
632 self.next_epoch_data
633 .hashes
634 .into_iter()
635 .map(|(n, h)| Ok((n, h.to_field()?)))
636 .collect::<Result<Vec<_>, InvalidBigInt>>()?,
637 )?;
638
639 let load_result = GenesisConfigLoaded {
640 constants: self.constants,
641 genesis_ledger_hash: self.ledger_hash,
642 genesis_total_currency,
643 genesis_producer_stake_proof: create_genesis_producer_stake_proof(&staking_ledger_mask),
644 staking_epoch_ledger_hash: self.staking_epoch_data.ledger_hash.clone(),
645 staking_epoch_total_currency,
646 next_epoch_ledger_hash: self.next_epoch_data.ledger_hash.clone(),
647 next_epoch_total_currency,
648 staking_epoch_seed: self.staking_epoch_data.seed,
649 next_epoch_seed: self.next_epoch_data.seed,
650 };
651 masks.push(staking_ledger_mask);
652 Ok((masks, load_result))
653 }
654
655 #[allow(clippy::result_unit_err)]
656 pub fn from_loaded(
657 (masks, data): (Vec<ledger::Mask>, GenesisConfigLoaded),
658 ) -> Result<Self, ()> {
659 let find_mask_by_hash = |hash: &v2::LedgerHash| {
660 masks
661 .iter()
662 .find(|&m| m.clone().merkle_root() == hash.to_field().unwrap())
663 .ok_or(())
664 };
665 let inner_hashes = |mask: &ledger::Mask| {
666 mask.get_raw_inner_hashes()
667 .into_iter()
668 .map(|(idx, hash)| (idx, v2::LedgerHash::from_fp(hash)))
669 .collect()
670 };
671 let genesis_mask = find_mask_by_hash(&data.genesis_ledger_hash)?;
672 let epoch_data = |hash: v2::LedgerHash, seed: v2::EpochSeed| {
673 find_mask_by_hash(&hash).map(|mask| PrebuiltGenesisEpochData {
674 accounts: mask.to_list().into_iter().map(Into::into).collect(),
675 ledger_hash: hash,
676 hashes: inner_hashes(mask),
677 seed,
678 })
679 };
680 Ok(Self {
681 constants: data.constants,
682 accounts: genesis_mask.to_list().into_iter().map(Into::into).collect(),
683 ledger_hash: data.genesis_ledger_hash,
684 hashes: inner_hashes(genesis_mask),
685 staking_epoch_data: epoch_data(
686 data.staking_epoch_ledger_hash,
687 data.staking_epoch_seed,
688 )?,
689 next_epoch_data: epoch_data(data.next_epoch_ledger_hash, data.next_epoch_seed)?,
690 })
691 }
692}
693
694#[derive(Debug, Serialize, Deserialize, BinProtRead, BinProtWrite)]
695pub struct PrebuiltGenesisEpochData {
696 accounts: Vec<MinaBaseAccountBinableArgStableV2>,
697 ledger_hash: LedgerHash,
698 hashes: Vec<(u64, LedgerHash)>,
699 seed: v2::EpochSeed,
700}
701
702impl TryFrom<DaemonJson> for PrebuiltGenesisConfig {
703 type Error = GenesisConfigError;
704
705 fn try_from(config: DaemonJson) -> Result<Self, Self::Error> {
706 let constants = config
707 .genesis
708 .as_ref()
709 .map_or(PROTOCOL_CONSTANTS, |genesis| genesis.protocol_constants());
710 let ledger = config.ledger.as_ref().ok_or(GenesisConfigError::NoLedger)?;
711 let ledger_accounts = ledger
712 .accounts_with_genesis_winner()
713 .iter()
714 .map(daemon_json::Account::to_account)
715 .collect::<Result<Vec<_>, _>>()?;
716 let accounts = ledger_accounts.iter().map(Into::into).collect();
717 let (mut mask, _total_currency) =
718 GenesisConfig::build_ledger_from_accounts(ledger_accounts.into_iter().map(Ok))?;
719 let ledger_hash = ledger_hash(&mut mask);
720 let hashes = mask
721 .get_raw_inner_hashes()
722 .into_iter()
723 .map(|(idx, hash)| (idx, v2::LedgerHash::from_fp(hash)))
724 .collect();
725 let daemon_json::Epochs { staking, next } = config.epoch_data.unwrap();
726 let staking_epoch_data = staking.try_into().unwrap();
727 let next_epoch_data = next.unwrap().try_into().unwrap();
728 let result = PrebuiltGenesisConfig {
729 constants,
730 accounts,
731 ledger_hash,
732 hashes,
733 staking_epoch_data,
734 next_epoch_data,
735 };
736 Ok(result)
737 }
738}
739
740impl TryFrom<EpochData> for PrebuiltGenesisEpochData {
741 type Error = GenesisConfigError;
742
743 fn try_from(value: EpochData) -> Result<Self, Self::Error> {
744 let EpochData {
745 accounts,
746 hash,
747 s3_data_hash: _,
748 seed,
749 } = value;
750
751 let expected_ledger_hash = hash.unwrap().parse().unwrap();
752 let seed = seed.parse().unwrap();
753 let ledger_accounts = accounts
754 .unwrap()
755 .iter()
756 .map(daemon_json::Account::to_account)
757 .collect::<Result<Vec<_>, _>>()?;
758 let accounts = ledger_accounts.iter().map(Into::into).collect();
759 let (mut mask, _total_currency) =
760 GenesisConfig::build_ledger_from_accounts(ledger_accounts.into_iter().map(Ok))?;
761 let ledger_hash = ledger_hash(&mut mask);
762
763 if ledger_hash != expected_ledger_hash {
764 return Err(Self::Error::LedgerHashMismatch {
765 expected: expected_ledger_hash,
766 computed: ledger_hash,
767 });
768 }
769
770 let hashes = mask
771 .get_raw_inner_hashes()
772 .into_iter()
773 .map(|(idx, hash)| (idx, v2::LedgerHash::from_fp(hash)))
774 .collect();
775
776 Ok(Self {
777 accounts,
778 ledger_hash,
779 hashes,
780 seed,
781 })
782 }
783}