1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//! The `genesis_block` module is a library for generating the chain's genesis block.

use crate::account::Account;
use crate::fee_calculator::FeeCalculator;
use crate::hash::{hash, Hash};
use crate::poh_config::PohConfig;
use crate::pubkey::Pubkey;
use crate::signature::{Keypair, KeypairUtil};
use crate::system_program;
use crate::timing::{DEFAULT_SLOTS_PER_EPOCH, DEFAULT_TICKS_PER_SLOT};
use std::fs::File;
use std::io::Write;
use std::path::Path;

#[derive(Serialize, Deserialize, Debug)]
pub struct GenesisBlock {
    pub accounts: Vec<(Pubkey, Account)>,
    pub bootstrap_leader_pubkey: Pubkey,
    pub epoch_warmup: bool,
    pub fee_calculator: FeeCalculator,
    pub native_instruction_processors: Vec<(String, Pubkey)>,
    pub slots_per_epoch: u64,
    pub stakers_slot_offset: u64,
    pub ticks_per_slot: u64,
    pub poh_config: PohConfig,
}

// useful for basic tests
pub fn create_genesis_block(lamports: u64) -> (GenesisBlock, Keypair) {
    let mint_keypair = Keypair::new();
    (
        GenesisBlock::new(
            &Pubkey::default(),
            &[(
                mint_keypair.pubkey(),
                Account::new(lamports, 0, &system_program::id()),
            )],
            &[],
        ),
        mint_keypair,
    )
}

impl GenesisBlock {
    pub fn new(
        bootstrap_leader_pubkey: &Pubkey,
        accounts: &[(Pubkey, Account)],
        native_instruction_processors: &[(String, Pubkey)],
    ) -> Self {
        Self {
            accounts: accounts.to_vec(),
            bootstrap_leader_pubkey: *bootstrap_leader_pubkey, // TODO: leader_schedule to derive from actual stakes, instead ;)
            epoch_warmup: true,
            fee_calculator: FeeCalculator::default(),
            native_instruction_processors: native_instruction_processors.to_vec(),
            slots_per_epoch: DEFAULT_SLOTS_PER_EPOCH,
            stakers_slot_offset: DEFAULT_SLOTS_PER_EPOCH,
            ticks_per_slot: DEFAULT_TICKS_PER_SLOT,
            poh_config: PohConfig::default(),
        }
    }

    pub fn hash(&self) -> Hash {
        let serialized = serde_json::to_string(self).unwrap();
        hash(&serialized.into_bytes())
    }

    pub fn load(ledger_path: &str) -> Result<Self, std::io::Error> {
        let file = File::open(&Path::new(ledger_path).join("genesis.json"))?;
        let genesis_block = serde_json::from_reader(file)?;
        Ok(genesis_block)
    }

    pub fn write(&self, ledger_path: &str) -> Result<(), std::io::Error> {
        let serialized = serde_json::to_string(self)?;

        let dir = Path::new(ledger_path);
        std::fs::create_dir_all(&dir)?;

        let mut file = File::create(&dir.join("genesis.json"))?;
        file.write_all(&serialized.into_bytes())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::signature::{Keypair, KeypairUtil};

    fn make_tmp_path(name: &str) -> String {
        let out_dir = std::env::var("OUT_DIR").unwrap_or_else(|_| "target".to_string());
        let keypair = Keypair::new();

        let path = format!("{}/tmp/{}-{}", out_dir, name, keypair.pubkey());

        // whack any possible collision
        let _ignored = std::fs::remove_dir_all(&path);
        // whack any possible collision
        let _ignored = std::fs::remove_file(&path);

        path
    }

    #[test]
    fn test_genesis_block() {
        let mint_keypair = Keypair::new();
        let block = GenesisBlock::new(
            &Pubkey::default(),
            &[
                (
                    mint_keypair.pubkey(),
                    Account::new(10_000, 0, &Pubkey::default()),
                ),
                (Pubkey::new_rand(), Account::new(1, 0, &Pubkey::default())),
            ],
            &[("hi".to_string(), Pubkey::new_rand())],
        );
        assert_eq!(block.accounts.len(), 2);
        assert!(block.accounts.iter().any(
            |(pubkey, account)| *pubkey == mint_keypair.pubkey() && account.lamports == 10_000
        ));

        let path = &make_tmp_path("genesis_block");
        block.write(&path).expect("write");
        let loaded_block = GenesisBlock::load(&path).expect("load");
        assert_eq!(block.hash(), loaded_block.hash());
        let _ignored = std::fs::remove_file(&path);
    }

}