2019-01-24 12:04:04 -08:00
|
|
|
//! The `genesis_block` module is a library for generating the chain's genesis block.
|
|
|
|
|
2019-05-07 11:16:22 -07:00
|
|
|
use crate::account::Account;
|
2019-09-06 15:33:58 -07:00
|
|
|
use crate::clock::{DEFAULT_SLOTS_PER_EPOCH, DEFAULT_SLOTS_PER_SEGMENT, DEFAULT_TICKS_PER_SLOT};
|
2019-05-07 20:28:41 -07:00
|
|
|
use crate::fee_calculator::FeeCalculator;
|
2019-02-18 22:26:22 -08:00
|
|
|
use crate::hash::{hash, Hash};
|
2019-06-11 21:42:31 -07:00
|
|
|
use crate::inflation::Inflation;
|
2019-05-18 14:01:36 -07:00
|
|
|
use crate::poh_config::PohConfig;
|
2019-02-18 22:26:22 -08:00
|
|
|
use crate::pubkey::Pubkey;
|
2019-09-17 04:42:55 -07:00
|
|
|
use crate::rent_calculator::RentCalculator;
|
2019-02-18 22:26:22 -08:00
|
|
|
use crate::signature::{Keypair, KeypairUtil};
|
2019-08-15 21:07:00 -07:00
|
|
|
use crate::system_program::{self, solana_system_program};
|
2019-06-14 14:22:52 -07:00
|
|
|
use bincode::{deserialize, serialize};
|
|
|
|
use memmap::Mmap;
|
|
|
|
use std::fs::{File, OpenOptions};
|
2019-01-24 12:04:04 -08:00
|
|
|
use std::io::Write;
|
|
|
|
use std::path::Path;
|
|
|
|
|
2019-06-19 15:40:39 -07:00
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
2019-01-24 12:04:04 -08:00
|
|
|
pub struct GenesisBlock {
|
2019-05-07 20:28:41 -07:00
|
|
|
pub accounts: Vec<(Pubkey, Account)>,
|
2019-04-02 07:57:00 -07:00
|
|
|
pub native_instruction_processors: Vec<(String, Pubkey)>,
|
2019-06-19 15:40:39 -07:00
|
|
|
pub rewards_pools: Vec<(Pubkey, Account)>,
|
2019-05-07 20:28:41 -07:00
|
|
|
pub slots_per_epoch: u64,
|
|
|
|
pub stakers_slot_offset: u64,
|
2019-06-11 17:04:13 -07:00
|
|
|
pub epoch_warmup: bool,
|
2019-06-19 15:40:39 -07:00
|
|
|
pub ticks_per_slot: u64,
|
2019-07-09 16:48:40 -07:00
|
|
|
pub slots_per_segment: u64,
|
2019-05-18 14:01:36 -07:00
|
|
|
pub poh_config: PohConfig,
|
2019-06-19 15:40:39 -07:00
|
|
|
pub fee_calculator: FeeCalculator,
|
2019-09-17 04:42:55 -07:00
|
|
|
pub rent_calculator: RentCalculator,
|
2019-06-11 21:42:31 -07:00
|
|
|
pub inflation: Inflation,
|
2019-01-24 12:04:04 -08:00
|
|
|
}
|
|
|
|
|
2019-05-07 11:16:22 -07:00
|
|
|
// useful for basic tests
|
|
|
|
pub fn create_genesis_block(lamports: u64) -> (GenesisBlock, Keypair) {
|
|
|
|
let mint_keypair = Keypair::new();
|
|
|
|
(
|
|
|
|
GenesisBlock::new(
|
|
|
|
&[(
|
|
|
|
mint_keypair.pubkey(),
|
|
|
|
Account::new(lamports, 0, &system_program::id()),
|
|
|
|
)],
|
2019-08-15 21:07:00 -07:00
|
|
|
&[solana_system_program()],
|
2019-05-07 11:16:22 -07:00
|
|
|
),
|
|
|
|
mint_keypair,
|
|
|
|
)
|
|
|
|
}
|
2019-01-24 12:04:04 -08:00
|
|
|
|
2019-06-11 17:04:13 -07:00
|
|
|
impl Default for GenesisBlock {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
accounts: Vec::new(),
|
|
|
|
native_instruction_processors: Vec::new(),
|
2019-06-19 15:40:39 -07:00
|
|
|
rewards_pools: Vec::new(),
|
|
|
|
epoch_warmup: true,
|
2019-06-11 17:04:13 -07:00
|
|
|
slots_per_epoch: DEFAULT_SLOTS_PER_EPOCH,
|
|
|
|
stakers_slot_offset: DEFAULT_SLOTS_PER_EPOCH,
|
|
|
|
ticks_per_slot: DEFAULT_TICKS_PER_SLOT,
|
2019-07-09 16:48:40 -07:00
|
|
|
slots_per_segment: DEFAULT_SLOTS_PER_SEGMENT,
|
2019-06-11 17:04:13 -07:00
|
|
|
poh_config: PohConfig::default(),
|
2019-06-11 21:42:31 -07:00
|
|
|
inflation: Inflation::default(),
|
2019-06-19 15:40:39 -07:00
|
|
|
fee_calculator: FeeCalculator::default(),
|
2019-09-17 04:42:55 -07:00
|
|
|
rent_calculator: RentCalculator::default(),
|
2019-06-11 17:04:13 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-19 15:40:39 -07:00
|
|
|
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
|
|
|
pub struct Builder {
|
|
|
|
genesis_block: GenesisBlock,
|
|
|
|
already_have_stakers_slot_offset: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Builder {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Builder::default()
|
|
|
|
}
|
|
|
|
// consuming builder because I don't want to clone all the accounts
|
|
|
|
pub fn build(self) -> GenesisBlock {
|
|
|
|
self.genesis_block
|
|
|
|
}
|
|
|
|
|
|
|
|
fn append<T: Clone>(items: &[T], mut dest: Vec<T>) -> Vec<T> {
|
|
|
|
items.iter().cloned().for_each(|item| dest.push(item));
|
|
|
|
dest
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn account(self, pubkey: Pubkey, account: Account) -> Self {
|
|
|
|
self.accounts(&[(pubkey, account)])
|
|
|
|
}
|
|
|
|
pub fn accounts(mut self, accounts: &[(Pubkey, Account)]) -> Self {
|
|
|
|
self.genesis_block.accounts = Self::append(accounts, self.genesis_block.accounts);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
pub fn native_instruction_processor(self, name: &str, pubkey: Pubkey) -> Self {
|
|
|
|
self.native_instruction_processors(&[(name.to_string(), pubkey)])
|
|
|
|
}
|
|
|
|
pub fn native_instruction_processors(
|
|
|
|
mut self,
|
|
|
|
native_instruction_processors: &[(String, Pubkey)],
|
|
|
|
) -> Self {
|
|
|
|
self.genesis_block.native_instruction_processors = Self::append(
|
|
|
|
native_instruction_processors,
|
|
|
|
self.genesis_block.native_instruction_processors,
|
|
|
|
);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
pub fn rewards_pool(self, pubkey: Pubkey, account: Account) -> Self {
|
|
|
|
self.rewards_pools(&[(pubkey, account)])
|
|
|
|
}
|
|
|
|
pub fn rewards_pools(mut self, rewards_pools: &[(Pubkey, Account)]) -> Self {
|
|
|
|
self.genesis_block.rewards_pools =
|
|
|
|
Self::append(rewards_pools, self.genesis_block.rewards_pools);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
// also sets stakers_slot_offset, unless already set explicitly
|
|
|
|
pub fn slots_per_epoch(mut self, slots_per_epoch: u64) -> Self {
|
|
|
|
self.genesis_block.slots_per_epoch = slots_per_epoch;
|
|
|
|
if !self.already_have_stakers_slot_offset {
|
|
|
|
self.genesis_block.stakers_slot_offset = slots_per_epoch;
|
|
|
|
}
|
|
|
|
self
|
|
|
|
}
|
|
|
|
pub fn stakers_slot_offset(mut self, stakers_slot_offset: u64) -> Self {
|
|
|
|
self.genesis_block.stakers_slot_offset = stakers_slot_offset;
|
|
|
|
self.already_have_stakers_slot_offset = true;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
pub fn epoch_warmup(mut self, epoch_warmup: bool) -> Self {
|
|
|
|
self.genesis_block.epoch_warmup = epoch_warmup;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
pub fn ticks_per_slot(mut self, ticks_per_slot: u64) -> Self {
|
|
|
|
self.genesis_block.ticks_per_slot = ticks_per_slot;
|
|
|
|
self
|
|
|
|
}
|
2019-06-26 10:13:21 -07:00
|
|
|
pub fn poh_config(mut self, poh_config: PohConfig) -> Self {
|
|
|
|
self.genesis_block.poh_config = poh_config;
|
2019-06-19 15:40:39 -07:00
|
|
|
self
|
|
|
|
}
|
2019-06-26 10:13:21 -07:00
|
|
|
pub fn fee_calculator(mut self, fee_calculator: FeeCalculator) -> Self {
|
|
|
|
self.genesis_block.fee_calculator = fee_calculator;
|
2019-06-19 15:40:39 -07:00
|
|
|
self
|
|
|
|
}
|
2019-06-26 10:13:21 -07:00
|
|
|
pub fn inflation(mut self, inflation: Inflation) -> Self {
|
|
|
|
self.genesis_block.inflation = inflation;
|
2019-06-19 15:40:39 -07:00
|
|
|
self
|
|
|
|
}
|
2019-09-17 04:42:55 -07:00
|
|
|
pub fn rent_calculator(mut self, rent_calculator: RentCalculator) -> Self {
|
|
|
|
self.genesis_block.rent_calculator = rent_calculator;
|
|
|
|
self
|
|
|
|
}
|
2019-06-19 15:40:39 -07:00
|
|
|
}
|
|
|
|
|
2019-05-07 11:16:22 -07:00
|
|
|
impl GenesisBlock {
|
|
|
|
pub fn new(
|
|
|
|
accounts: &[(Pubkey, Account)],
|
|
|
|
native_instruction_processors: &[(String, Pubkey)],
|
|
|
|
) -> Self {
|
|
|
|
Self {
|
2019-05-07 20:28:41 -07:00
|
|
|
accounts: accounts.to_vec(),
|
2019-05-07 11:16:22 -07:00
|
|
|
native_instruction_processors: native_instruction_processors.to_vec(),
|
2019-06-11 17:04:13 -07:00
|
|
|
..GenesisBlock::default()
|
2019-05-07 11:16:22 -07:00
|
|
|
}
|
2019-01-24 12:04:04 -08:00
|
|
|
}
|
|
|
|
|
2019-03-01 09:49:37 -08:00
|
|
|
pub fn hash(&self) -> Hash {
|
2019-01-24 12:04:04 -08:00
|
|
|
let serialized = serde_json::to_string(self).unwrap();
|
|
|
|
hash(&serialized.into_bytes())
|
|
|
|
}
|
|
|
|
|
2019-07-30 15:53:41 -07:00
|
|
|
pub fn load(ledger_path: &Path) -> Result<Self, std::io::Error> {
|
2019-06-14 14:22:52 -07:00
|
|
|
let file = OpenOptions::new()
|
|
|
|
.read(true)
|
|
|
|
.open(&Path::new(ledger_path).join("genesis.bin"))
|
|
|
|
.expect("Unable to open genesis file");
|
|
|
|
|
|
|
|
//UNSAFE: Required to create a Mmap
|
|
|
|
let mem = unsafe { Mmap::map(&file).expect("failed to map the genesis file") };
|
|
|
|
let genesis_block = deserialize(&mem)
|
|
|
|
.map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, format!("{:?}", err)))?;
|
2019-01-24 12:04:04 -08:00
|
|
|
Ok(genesis_block)
|
|
|
|
}
|
|
|
|
|
2019-07-30 15:53:41 -07:00
|
|
|
pub fn write(&self, ledger_path: &Path) -> Result<(), std::io::Error> {
|
2019-06-14 14:22:52 -07:00
|
|
|
let serialized = serialize(&self)
|
|
|
|
.map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, format!("{:?}", err)))?;
|
2019-05-07 11:16:22 -07:00
|
|
|
|
2019-07-30 15:53:41 -07:00
|
|
|
std::fs::create_dir_all(&ledger_path)?;
|
2019-05-07 11:16:22 -07:00
|
|
|
|
2019-07-30 15:53:41 -07:00
|
|
|
let mut file = File::create(&ledger_path.join("genesis.bin"))?;
|
2019-06-14 14:22:52 -07:00
|
|
|
file.write_all(&serialized)
|
2019-01-24 12:04:04 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2019-05-07 11:16:22 -07:00
|
|
|
use crate::signature::{Keypair, KeypairUtil};
|
2019-07-30 15:53:41 -07:00
|
|
|
use std::path::PathBuf;
|
2019-01-24 12:04:04 -08:00
|
|
|
|
2019-07-30 15:53:41 -07:00
|
|
|
fn make_tmp_path(name: &str) -> PathBuf {
|
2019-07-17 14:27:58 -07:00
|
|
|
let out_dir = std::env::var("FARF_DIR").unwrap_or_else(|_| "farf".to_string());
|
2019-05-07 11:16:22 -07:00
|
|
|
let keypair = Keypair::new();
|
|
|
|
|
2019-07-30 15:53:41 -07:00
|
|
|
let path = [
|
|
|
|
out_dir,
|
|
|
|
"tmp".to_string(),
|
|
|
|
format!("{}-{}", name, keypair.pubkey()),
|
|
|
|
]
|
|
|
|
.iter()
|
|
|
|
.collect();
|
2019-05-07 11:16:22 -07:00
|
|
|
|
|
|
|
// whack any possible collision
|
|
|
|
let _ignored = std::fs::remove_dir_all(&path);
|
|
|
|
// whack any possible collision
|
|
|
|
let _ignored = std::fs::remove_file(&path);
|
|
|
|
|
|
|
|
path
|
2019-01-24 12:04:04 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2019-05-07 11:16:22 -07:00
|
|
|
fn test_genesis_block() {
|
|
|
|
let mint_keypair = Keypair::new();
|
2019-06-19 15:40:39 -07:00
|
|
|
let block = Builder::new()
|
|
|
|
.account(
|
|
|
|
mint_keypair.pubkey(),
|
|
|
|
Account::new(10_000, 0, &Pubkey::default()),
|
|
|
|
)
|
|
|
|
.accounts(&[(Pubkey::new_rand(), Account::new(1, 0, &Pubkey::default()))])
|
|
|
|
.native_instruction_processor("hi", Pubkey::new_rand())
|
|
|
|
.build();
|
|
|
|
|
2019-05-07 11:16:22 -07:00
|
|
|
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);
|
2019-01-24 12:04:04 -08:00
|
|
|
}
|
|
|
|
}
|