Revert "Revert "add genesis stake placeholders (#6969)" (#7109)" (#7124)

This reverts commit 702f7cc51d.
This commit is contained in:
Rob Walker
2019-11-25 15:11:55 -08:00
committed by GitHub
parent acbe89a159
commit ef64f00cbb
18 changed files with 1322 additions and 105 deletions

View File

@ -11,6 +11,7 @@ homepage = "https://solana.com/"
[dependencies]
base64 = "0.11.0"
clap = "2.33.0"
hex = "0.4.0"
serde = "1.0.102"
serde_derive = "1.0.102"
serde_json = "1.0.41"

View File

@ -0,0 +1,32 @@
use solana_sdk::{hash::hashv, pubkey::Pubkey};
#[derive(Default)]
pub struct AddressGenerator {
base_pubkey: Pubkey,
base_name: String,
nth: usize,
}
impl AddressGenerator {
pub fn new(base_pubkey: &Pubkey, base_name: &str) -> Self {
Self {
base_pubkey: *base_pubkey,
base_name: base_name.to_string(),
nth: 0,
}
}
pub fn nth(&self, nth: usize) -> Pubkey {
Pubkey::new(
hashv(&[
self.base_pubkey.as_ref(),
format!("{}:{}", self.base_name, nth).as_bytes(),
])
.as_ref(),
)
}
pub fn next(&mut self) -> Pubkey {
let nth = self.nth;
self.nth += 1;
self.nth(nth)
}
}

View File

@ -1,7 +1,636 @@
use solana_sdk::{account::Account, pubkey::Pubkey};
use crate::{
stakes::{create_and_add_stakes, StakerInfo},
unlocks::UnlockInfo,
};
use solana_sdk::{genesis_config::GenesisConfig, native_token::sol_to_lamports};
pub(crate) fn create_genesis_accounts() -> Vec<(Pubkey, Account)> {
vec![]
// 30 "month" schedule is 1/5th at 6 months
// 1/24 at each 1/12 of a year thereafter
const BATCH_ONE_UNLOCK_INFO: UnlockInfo = UnlockInfo {
cliff_fraction: 0.2,
cliff_years: 0.5,
unlocks: 24,
unlock_years: 1.0 / 12.0,
};
// 1st batch
const BATCH_ONE_STAKER_INFOS: &[StakerInfo] = &[
StakerInfo {
name: "diligent bridge",
staker: "ab22196afde08a090a3721eb20e3e1ea84d36e14d1a3f0815b236b300d9d33ef",
withdrawer: "a2a7ae9098f862f4b3ba7d102d174de5e84a560444c39c035f3eeecce442eadc",
sol: 6_250_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "four wish",
staker: "6a56514c29f6b1de4d46164621d6bd25b337a711f569f9283c1143c7e8fb546e",
withdrawer: "b420af728f58d9f269d6e07fbbaecf6ed6535e5348538e3f39f2710351f2b940",
sol: 10_000_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "simple friends",
staker: "ddf2e4c81eafae2d68ac99171b066c87bddb168d6b7c07333cd951f36640163d",
withdrawer: "312fa06ccf1b671b26404a34136161ed2aba9e66f248441b4fddb5c592fde560",
sol: 1_250_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "noxious leather",
staker: "0cbf98cd35ceff84ca72b752c32cc3eeee4f765ca1bef1140927ebf5c6e74339",
withdrawer: "467e06fa25a9e06824eedc926ce431947ed99c728bed36be54561354c1330959",
sol: 6_250_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "worthless direction",
staker: "ef1562bf9edfd0f5e62530cce4244e8de544a3a30075a2cd5c9074edfbcbe78a",
withdrawer: "2ab26abb9d8131a30a4a63446125cf961ece4b926c31cce0eb84da4eac3f836e",
sol: 12_500_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "historical company",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 322_850.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "callous money",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 5_927_155.25,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "outstanding jump",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 625_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "feeble toes",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 750_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "disillusioned deer",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 1_250_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "unwritten songs",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 4_250_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "overt dime",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 500_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "slow committee",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 625_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "curvy twig",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 625_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "gamy scissors",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 250_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "mushy key",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 1_250_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "marked silver",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 250_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "free sock",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 625_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "tremendous meeting",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 1_250_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "panoramic cloth",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 625_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "normal kick",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 2_500_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "unbecoming observation",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 250_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "cut beginner",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 250_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "alcoholic button",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 625_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "old-fashioned clover",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 750_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "expensive underwear",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 2_500_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "like dust",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 5_000_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "rapid straw",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 5_850_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "windy trousers",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 2_579_350.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "dramatic veil",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 3_611_110.50,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "incandescent skin",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 3_000_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "spiky love",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 3_250_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
];
// 30 "month" schedule is 1/5th at 6 months
// 1/24 at each 1/12 of a year thereafter
const BATCH_TWO_UNLOCK_INFO: UnlockInfo = UnlockInfo {
cliff_fraction: 0.2,
cliff_years: 0.5,
unlocks: 24,
unlock_years: 1.0 / 12.0,
};
const BATCH_TWO_STAKER_INFOS: &[StakerInfo] = &[
// 2nd batch
StakerInfo {
name: "macabre note",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 4_000_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "alcoholic letter",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 4_000_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "heady trucks",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 4_000_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "ten support",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 1_000_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "foregoing middle",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 800_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "ludicrous destruction",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 4_000_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "numberless wheel",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 4_000_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "short powder",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 4_000_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "cut name",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 4_000_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "six fly",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 4_000_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "mindless pickle",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 100_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "marked rabbit",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 38_741.36,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "jagged doctor",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 711_258.64,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "truthful pollution",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 1_587_300.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "unkempt activity",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 2_222_220.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "ritzy view",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 40_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "remarkable plant",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 300_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "busy value",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 100_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "imperfect slave",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 222_065.84,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "uneven drawer",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 400_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "far behavior",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 4_000_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "abaft memory",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 400_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "poor glove",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 2_000_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "strange iron",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 2_000_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "nonstop rail",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 1_000_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "milky bait",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 400_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "wandering start",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 1_200_000.0,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
];
// 30 "month" schedule is 1/5th at 6 months
// 1/24 at each 1/12 of a year thereafter
pub const BATCH_THREE_UNLOCK_INFO: UnlockInfo = UnlockInfo {
cliff_fraction: 0.2,
cliff_years: 0.5,
unlocks: 24,
unlock_years: 1.0 / 12.0,
};
pub const BATCH_THREE_STAKER_INFOS: &[StakerInfo] = &[
// 3rd batch
StakerInfo {
name: "dusty dress",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 1_212_121.21,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "godly bed",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 151_515.15,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "innocent property",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 227_272.73,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "responsible bikes",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 3_030_303.03,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "learned market",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 3_030_303.03,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "jumpy school",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 303_030.30,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "sticky houses",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 1_515_151.52,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "bustling basketball",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 1_515_152.52,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "ordinary dad",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 606_060.61,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "absurd bat",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 90_909.09,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "cloudy ocean",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 67_945.45,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "black-and-white fold",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 757_575.76,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "stale part",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 45_454.55,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "available health",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 2_797_575.76,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "afraid visitor",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 481_818.18,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "arrogant front",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 151_515.15,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "juvenile zinc",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 151_515.15,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "disturbed box",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 303_030.30,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "disagreeable skate",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 454_545.45,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "miscreant sidewalk",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 75_757.58,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
StakerInfo {
name: "shy play",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: 303_030.30,
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
];
fn add_stakes(
genesis_config: &mut GenesisConfig,
staker_infos: &[StakerInfo],
unlock_info: &UnlockInfo,
granularity: u64,
) -> u64 {
staker_infos
.iter()
.map(|staker_info| {
create_and_add_stakes(genesis_config, staker_info, unlock_info, granularity)
})
.sum::<u64>()
}
pub(crate) fn add_genesis_accounts(genesis_config: &mut GenesisConfig) -> u64 {
add_stakes(
genesis_config,
&BATCH_ONE_STAKER_INFOS,
&BATCH_ONE_UNLOCK_INFO,
sol_to_lamports(1_000_000.0),
) + add_stakes(
genesis_config,
&BATCH_TWO_STAKER_INFOS,
&BATCH_TWO_UNLOCK_INFO,
sol_to_lamports(1_000_000.0),
) + add_stakes(
genesis_config,
&BATCH_THREE_STAKER_INFOS,
&BATCH_THREE_UNLOCK_INFO,
sol_to_lamports(1_000_000.0),
)
}
#[cfg(test)]
@ -9,7 +638,27 @@ mod tests {
use super::*;
#[test]
fn test_create_genesis_accounts() {
assert_eq!(create_genesis_accounts(), vec![]);
fn test_add_genesis_accounts() {
let mut genesis_config = GenesisConfig::default();
let issued_lamports = add_genesis_accounts(&mut genesis_config);
let lamports = genesis_config
.accounts
.iter()
.map(|(_, account)| account.lamports)
.sum::<u64>();
assert_eq!(issued_lamports, lamports);
genesis_config
.accounts
.sort_by(|(ka, _), (kb, _)| ka.cmp(kb));
let len = genesis_config.accounts.len();
genesis_config
.accounts
.dedup_by(|(ka, _), (kb, _)| ka == kb);
assert_eq!(genesis_config.accounts.len(), len);
}
}

View File

@ -1,19 +1,22 @@
//! A command-line executable for generating the chain's genesis config.
mod address_generator;
mod genesis_accounts;
mod stakes;
mod unlocks;
use crate::genesis_accounts::create_genesis_accounts;
use crate::genesis_accounts::add_genesis_accounts;
use clap::{crate_description, crate_name, value_t, value_t_or_exit, App, Arg, ArgMatches};
use solana_clap_utils::input_parsers::pubkey_of;
use solana_genesis::Base64Account;
use solana_ledger::blocktree::create_new_ledger;
use solana_ledger::poh::compute_hashes_per_tick;
use solana_ledger::{blocktree::create_new_ledger, poh::compute_hashes_per_tick};
use solana_sdk::{
account::Account,
clock,
epoch_schedule::EpochSchedule,
fee_calculator::FeeCalculator,
genesis_config::{GenesisConfig, OperatingMode},
native_token::lamports_to_sol,
native_token::sol_to_lamports,
poh_config::PohConfig,
pubkey::Pubkey,
@ -50,7 +53,8 @@ fn pubkey_from_str(key_str: &str) -> Result<Pubkey, Box<dyn error::Error>> {
})
}
pub fn add_genesis_accounts(file: &str, genesis_config: &mut GenesisConfig) -> io::Result<()> {
pub fn load_genesis_accounts(file: &str, genesis_config: &mut GenesisConfig) -> io::Result<u64> {
let mut lamports = 0;
let accounts_file = File::open(file.to_string())?;
let genesis_accounts: HashMap<String, Base64Account> =
@ -82,11 +86,11 @@ pub fn add_genesis_accounts(file: &str, genesis_config: &mut GenesisConfig) -> i
})?;
}
account.executable = account_details.executable;
lamports += account.lamports;
genesis_config.add_account(pubkey, account);
}
Ok(())
Ok(lamports)
}
#[allow(clippy::cognitive_complexity)]
@ -322,15 +326,15 @@ fn main() -> Result<(), Box<dyn error::Error>> {
let bootstrap_storage_pubkey = pubkey_of(&matches, "bootstrap_storage_pubkey_file");
let faucet_pubkey = pubkey_of(&matches, "faucet_pubkey_file");
let bootstrap_leader_vote_account =
vote_state::create_account(&bootstrap_vote_pubkey, &bootstrap_leader_pubkey, 0, 1);
let rent = Rent {
lamports_per_byte_year: value_t_or_exit!(matches, "lamports_per_byte_year", u64),
exemption_threshold: value_t_or_exit!(matches, "rent_exemption_threshold", f64),
burn_percent: value_t_or_exit!(matches, "rent_burn_percentage", u8),
};
let bootstrap_leader_vote_account =
vote_state::create_account(&bootstrap_vote_pubkey, &bootstrap_leader_pubkey, 0, 1);
let bootstrap_leader_stake_account = stake_state::create_account(
&bootstrap_leader_pubkey,
&bootstrap_vote_pubkey,
@ -358,14 +362,6 @@ fn main() -> Result<(), Box<dyn error::Error>> {
));
}
if let Some(faucet_pubkey) = faucet_pubkey {
accounts.push((
faucet_pubkey,
Account::new(faucet_lamports.unwrap(), 0, &system_program::id()),
));
}
accounts.append(&mut create_genesis_accounts());
let ticks_per_slot = value_t_or_exit!(matches, "ticks_per_slot", u64);
let fee_calculator = FeeCalculator::new(
@ -388,7 +384,6 @@ fn main() -> Result<(), Box<dyn error::Error>> {
OperatingMode::Development => {
let hashes_per_tick =
compute_hashes_per_tick(poh_config.target_tick_duration, 1_000_000);
println!("Hashes per tick: {}", hashes_per_tick);
poh_config.hashes_per_tick = Some(hashes_per_tick);
}
OperatingMode::SoftLaunch => {
@ -413,14 +408,11 @@ fn main() -> Result<(), Box<dyn error::Error>> {
}
};
let epoch_schedule = EpochSchedule::new(slots_per_epoch);
println!(
"Genesis mode: {:?} hashes per tick: {:?} slots_per_epoch: {}",
operating_mode, poh_config.hashes_per_tick, slots_per_epoch
);
let native_instruction_processors =
solana_genesis_programs::get_programs(operating_mode, 0).unwrap();
let inflation = solana_genesis_programs::get_inflation(operating_mode, 0).unwrap();
let mut genesis_config = GenesisConfig {
accounts,
native_instruction_processors,
@ -434,17 +426,46 @@ fn main() -> Result<(), Box<dyn error::Error>> {
..GenesisConfig::default()
};
if let Some(files) = matches.values_of("primordial_accounts_file") {
for file in files {
add_genesis_accounts(file, &mut genesis_config)?;
}
if let Some(faucet_pubkey) = faucet_pubkey {
genesis_config.add_account(
faucet_pubkey,
Account::new(faucet_lamports.unwrap(), 0, &system_program::id()),
);
}
// add genesis stuff from storage and stake
solana_storage_program::rewards_pools::add_genesis_accounts(&mut genesis_config);
solana_stake_program::add_genesis_accounts(&mut genesis_config);
if let Some(files) = matches.values_of("primordial_accounts_file") {
for file in files {
load_genesis_accounts(file, &mut genesis_config)?;
}
}
add_genesis_accounts(&mut genesis_config);
create_new_ledger(&ledger_path, &genesis_config)?;
println!(
"Genesis mode: {:?} hashes per tick: {:?} slots_per_epoch: {} capitalization: {}SOL in {} accounts",
operating_mode,
genesis_config.poh_config.hashes_per_tick,
slots_per_epoch,
lamports_to_sol(
genesis_config
.accounts
.iter()
.map(|(pubkey, account)| {
if account.lamports == 0 {
panic!("{:?}", (pubkey, account));
}
account.lamports
})
.sum::<u64>()),
genesis_config.accounts.len()
);
Ok(())
}
@ -462,7 +483,7 @@ mod tests {
#[test]
fn test_append_primordial_accounts_to_genesis() {
// Test invalid file returns error
assert!(add_genesis_accounts("unknownfile", &mut GenesisConfig::default()).is_err());
assert!(load_genesis_accounts("unknownfile", &mut GenesisConfig::default()).is_err());
let mut genesis_config = GenesisConfig::default();
@ -500,7 +521,7 @@ mod tests {
let mut file = File::create(path).unwrap();
file.write_all(&serialized.into_bytes()).unwrap();
add_genesis_accounts(
load_genesis_accounts(
"test_append_primordial_accounts_to_genesis.yml",
&mut genesis_config,
)
@ -572,7 +593,7 @@ mod tests {
let mut file = File::create(path).unwrap();
file.write_all(&serialized.into_bytes()).unwrap();
add_genesis_accounts(
load_genesis_accounts(
"test_append_primordial_accounts_to_genesis.yml",
&mut genesis_config,
)
@ -672,7 +693,7 @@ mod tests {
let mut file = File::create(path).unwrap();
file.write_all(&serialized.into_bytes()).unwrap();
add_genesis_accounts(
load_genesis_accounts(
"test_append_primordial_accounts_to_genesis.yml",
&mut genesis_config,
)
@ -806,7 +827,7 @@ mod tests {
file.write_all(yaml_string_pubkey.as_bytes()).unwrap();
let mut genesis_config = GenesisConfig::default();
add_genesis_accounts(path.to_str().unwrap(), &mut genesis_config).expect("genesis");
load_genesis_accounts(path.to_str().unwrap(), &mut genesis_config).expect("genesis");
remove_file(path).unwrap();
assert_eq!(genesis_config.accounts.len(), 4);
@ -834,7 +855,7 @@ mod tests {
file.write_all(yaml_string_keypair.as_bytes()).unwrap();
let mut genesis_config = GenesisConfig::default();
add_genesis_accounts(path.to_str().unwrap(), &mut genesis_config).expect("genesis");
load_genesis_accounts(path.to_str().unwrap(), &mut genesis_config).expect("genesis");
remove_file(path).unwrap();
assert_eq!(genesis_config.accounts.len(), 3);

265
genesis/src/stakes.rs Normal file
View File

@ -0,0 +1,265 @@
//! stakes generator
use crate::{
address_generator::AddressGenerator,
unlocks::{UnlockInfo, Unlocks},
};
use solana_sdk::{
account::Account, clock::Slot, genesis_config::GenesisConfig, native_token::sol_to_lamports,
pubkey::Pubkey, system_program, timing::years_as_slots,
};
use solana_stake_program::stake_state::{
create_lockup_stake_account, get_stake_rent_exempt_reserve, Authorized, Lockup,
};
#[derive(Debug)]
pub struct StakerInfo {
pub name: &'static str,
pub staker: &'static str,
pub withdrawer: &'static str,
pub sol: f64,
pub custodian: &'static str,
}
// lamports required to run staking operations for one year
// the staker account needs to be rent exempt *and* carry enough
// lamports to cover TX fees (delegation) for one year,
// and we support one delegation per epoch
fn calculate_staker_lamports(genesis_config: &GenesisConfig) -> u64 {
genesis_config.rent.minimum_balance(0).max(1)
+ genesis_config.fee_calculator.max_lamports_per_signature
* genesis_config.epoch_schedule.get_epoch(years_as_slots(
1.0,
&genesis_config.poh_config.target_tick_duration,
genesis_config.ticks_per_slot,
) as Slot)
}
/// create stake accounts for lamports with at most stake_granularity in each
/// account
pub fn create_and_add_stakes(
genesis_config: &mut GenesisConfig,
// information about this staker for this group of stakes
staker_info: &StakerInfo,
// description of how the stakes' lockups will expire
unlock_info: &UnlockInfo,
// the largest each stake account should be, in lamports
granularity: u64,
) -> u64 {
let authorized = Authorized {
staker: Pubkey::new(&hex::decode(staker_info.staker).expect("hex")),
withdrawer: Pubkey::new(&hex::decode(staker_info.withdrawer).expect("hex")),
};
let custodian = Pubkey::new(&hex::decode(staker_info.custodian).expect("hex"));
let total_lamports = sol_to_lamports(staker_info.sol);
let staker_lamports = calculate_staker_lamports(genesis_config);
let staker_account = (
authorized.staker,
Account::new(staker_lamports, 0, &system_program::id()),
);
let stakes_lamports = if !genesis_config.accounts.contains(&staker_account) {
genesis_config.accounts.push(staker_account);
total_lamports - staker_lamports
} else {
total_lamports
};
// the staker account needs to be rent exempt *and* carry enough
// lamports to cover TX fees (delegation) for one year
// as we support one re-delegation per epoch
let unlocks = Unlocks::new(
unlock_info.cliff_fraction,
unlock_info.cliff_years,
unlock_info.unlocks,
unlock_info.unlock_years,
&genesis_config.epoch_schedule,
&genesis_config.poh_config.target_tick_duration,
genesis_config.ticks_per_slot,
);
let mut address_generator = AddressGenerator::new(&authorized.staker, staker_info.name);
let stake_rent_exempt_reserve = get_stake_rent_exempt_reserve(&genesis_config.rent);
for unlock in unlocks {
let lamports = unlock.amount(stakes_lamports);
let (granularity, remainder) = if granularity < lamports {
(granularity, lamports % granularity)
} else {
(lamports, 0)
};
let lockup = Lockup {
epoch: unlock.epoch,
custodian,
};
for _ in 0..(lamports / granularity).saturating_sub(1) {
genesis_config.add_account(
address_generator.next(),
create_lockup_stake_account(
&authorized,
&lockup,
&genesis_config.rent,
granularity,
),
);
}
if remainder <= stake_rent_exempt_reserve {
genesis_config.add_account(
address_generator.next(),
create_lockup_stake_account(
&authorized,
&lockup,
&genesis_config.rent,
granularity + remainder,
),
);
} else {
genesis_config.add_account(
address_generator.next(),
create_lockup_stake_account(
&authorized,
&lockup,
&genesis_config.rent,
granularity,
),
);
genesis_config.add_account(
address_generator.next(),
create_lockup_stake_account(&authorized, &lockup, &genesis_config.rent, remainder),
);
}
}
total_lamports
}
#[cfg(test)]
mod tests {
use super::*;
use solana_sdk::{native_token::lamports_to_sol, rent::Rent};
fn create_and_check_stakes(
genesis_config: &mut GenesisConfig,
staker_info: &StakerInfo,
unlock_info: &UnlockInfo,
total_lamports: u64,
granularity: u64,
len: usize,
) {
assert!(
total_lamports
== create_and_add_stakes(genesis_config, staker_info, unlock_info, granularity)
);
assert_eq!(genesis_config.accounts.len(), len);
assert_eq!(
genesis_config
.accounts
.iter()
.map(|(_pubkey, account)| account.lamports)
.sum::<u64>(),
total_lamports,
);
assert!(genesis_config
.accounts
.iter()
.all(|(_pubkey, account)| account.lamports <= granularity
|| account.lamports - granularity
< get_stake_rent_exempt_reserve(&genesis_config.rent)));
}
#[test]
fn test_create_stakes() {
// 2 unlocks
let rent = Rent {
lamports_per_byte_year: 1,
exemption_threshold: 1.0,
..Rent::default()
};
let reserve = get_stake_rent_exempt_reserve(&rent);
// verify that a small remainder ends up in the last stake
let granularity = reserve;
let total_lamports = reserve + reserve * 2 + 1;
create_and_check_stakes(
&mut GenesisConfig {
rent,
..GenesisConfig::default()
},
&StakerInfo {
name: "fun",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: lamports_to_sol(total_lamports),
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
&UnlockInfo {
cliff_fraction: 0.5,
cliff_years: 0.5,
unlocks: 1,
unlock_years: 0.5,
},
total_lamports,
granularity,
2 + 1,
);
// huge granularity doesn't blow up
let granularity = std::u64::MAX;
let total_lamports = reserve + reserve * 2 + 1;
create_and_check_stakes(
&mut GenesisConfig {
rent,
..GenesisConfig::default()
},
&StakerInfo {
name: "fun",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: lamports_to_sol(total_lamports),
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
&UnlockInfo {
cliff_fraction: 0.5,
cliff_years: 0.5,
unlocks: 1,
unlock_years: 0.5,
},
total_lamports,
granularity,
2 + 1,
);
// exactly reserve as a remainder
let granularity = reserve * 3;
let total_lamports = reserve + (granularity + reserve) * 2;
create_and_check_stakes(
&mut GenesisConfig {
rent,
..GenesisConfig::default()
},
&StakerInfo {
name: "fun",
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
sol: lamports_to_sol(total_lamports),
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
},
&UnlockInfo {
cliff_fraction: 0.5,
cliff_years: 0.5,
unlocks: 1,
unlock_years: 0.5,
},
total_lamports,
granularity,
4 + 1,
);
}
}

210
genesis/src/unlocks.rs Normal file
View File

@ -0,0 +1,210 @@
//! lockups generator
use solana_sdk::{clock::Epoch, epoch_schedule::EpochSchedule, timing::years_as_slots};
use std::time::Duration;
#[derive(Debug)]
pub struct UnlockInfo {
pub cliff_fraction: f64,
pub cliff_years: f64,
pub unlocks: usize,
pub unlock_years: f64,
}
#[derive(Debug, Default, Clone)]
pub struct Unlocks {
/// where in iteration over unlocks, loop var
i: usize,
/// number of unlocks after the first cliff
unlocks: usize,
/// fraction unlocked as of last event
prev_fraction: f64,
/// first cliff
/// fraction of unlocked at first cliff
cliff_fraction: f64,
/// time of cliff, in epochs, 0-based
cliff_epoch: Epoch,
/// post cliff
/// fraction unlocked at each post-cliff unlock
unlock_fraction: f64,
/// time between each post-cliff unlock, in Epochs
unlock_epochs: Epoch,
}
impl Unlocks {
pub fn new(
cliff_fraction: f64, // first cliff fraction
cliff_year: f64, // first cliff time, starting from genesis, in years
unlocks: usize, // number of follow-on unlocks
unlock_years: f64, // years between each following unlock
epoch_schedule: &EpochSchedule,
tick_duration: &Duration,
ticks_per_slot: u64,
) -> Self {
// convert cliff year to a slot height, as the cliff_year is considered from genesis
let cliff_slot = years_as_slots(cliff_year, tick_duration, ticks_per_slot) as u64;
// get the first cliff epoch from that slot height
let cliff_epoch = epoch_schedule.get_epoch(cliff_slot);
// assumes that the first cliff is after any epoch warmup and that follow-on
// epochs are uniform in length
let first_unlock_slot =
years_as_slots(cliff_year + unlock_years, tick_duration, ticks_per_slot) as u64;
let unlock_epochs = epoch_schedule.get_epoch(first_unlock_slot) - cliff_epoch;
Self::from_epochs(cliff_fraction, cliff_epoch, unlocks, unlock_epochs)
}
pub fn from_epochs(
cliff_fraction: f64, // first cliff fraction
cliff_epoch: Epoch, // first cliff epoch
unlocks: usize, // number of follow-on unlocks
unlock_epochs: Epoch, // epochs between each following unlock
) -> Self {
let unlock_fraction = if unlocks != 0 {
(1.0 - cliff_fraction) / unlocks as f64
} else {
0.0
};
Self {
prev_fraction: 0.0,
i: 0,
unlocks,
cliff_fraction,
cliff_epoch,
unlock_fraction,
unlock_epochs,
}
}
}
impl Iterator for Unlocks {
type Item = Unlock;
fn next(&mut self) -> Option<Self::Item> {
let i = self.i;
if i == 0 {
self.i += 1;
self.prev_fraction = self.cliff_fraction;
Some(Unlock {
prev_fraction: 0.0,
fraction: self.cliff_fraction,
epoch: self.cliff_epoch,
})
} else if i <= self.unlocks {
self.i += 1;
let prev_fraction = self.prev_fraction;
// move forward, tortured-looking math comes from wanting to reach 1.0 by the last
// unlock
self.prev_fraction = 1.0 - (self.unlocks - i) as f64 * self.unlock_fraction;
Some(Unlock {
prev_fraction,
fraction: self.prev_fraction,
epoch: self.cliff_epoch + i as u64 * self.unlock_epochs,
})
} else {
None
}
}
}
/// describes an unlock event
#[derive(Debug, Default)]
pub struct Unlock {
/// the epoch height at which this unlock occurs
pub epoch: Epoch,
/// the fraction that was unlocked last iteration
pub prev_fraction: f64,
/// the fraction unlocked this iteration
pub fraction: f64,
}
impl Unlock {
/// the number of lamports unlocked at this event
#[allow(clippy::float_cmp)]
pub fn amount(&self, total: u64) -> u64 {
if self.fraction == 1.0 {
total - (self.prev_fraction * total as f64) as u64
} else {
(self.fraction * total as f64) as u64 - (self.prev_fraction * total as f64) as u64
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_make_lockups() {
// this number just a random val
let total_lamports: u64 = 1725987234408923;
// expected config
const EPOCHS_PER_MONTH: Epoch = 2;
assert_eq!(
Unlocks::from_epochs(0.20, 6 * EPOCHS_PER_MONTH, 24, EPOCHS_PER_MONTH)
.map(|unlock| unlock.amount(total_lamports))
.sum::<u64>(),
total_lamports
);
// one tick/sec
let tick_duration = Duration::new(1, 0);
// one tick per slot
let ticks_per_slot = 1;
// two-week epochs at one second per slot
let epoch_schedule = EpochSchedule::custom(14 * 24 * 60 * 60, 0, false);
assert_eq!(
// 30 "month" schedule is 1/5th at 6 months
// 1/24 at each 1/12 of a year thereafter
Unlocks::new(
0.20,
0.5,
24,
1.0 / 12.0,
&epoch_schedule,
&tick_duration,
ticks_per_slot,
)
.map(|unlock| {
if unlock.prev_fraction == 0.0 {
assert_eq!(unlock.epoch, 13); // 26 weeks is 1/2 year, first cliff
} else if unlock.prev_fraction == 0.2 {
assert_eq!(unlock.epoch, 15); // subsequent unlocks are separated by 2 weeks
}
unlock.amount(total_lamports)
})
.sum::<u64>(),
total_lamports
);
assert_eq!(
Unlocks::new(
0.20,
1.5, // start 1.5 years after genesis
24,
1.0 / 12.0,
&epoch_schedule,
&tick_duration,
ticks_per_slot,
)
.map(|unlock| {
if unlock.prev_fraction == 0.0 {
assert_eq!(unlock.epoch, 26 + 13); // 26 weeks is 1/2 year, first cliff is 1.5 years
} else if unlock.prev_fraction == 0.2 {
assert_eq!(unlock.epoch, 26 + 15); // subsequent unlocks are separated by 2 weeks
}
unlock.amount(total_lamports)
})
.sum::<u64>(),
total_lamports
);
}
}