This reverts commit 702f7cc51d
.
This commit is contained in:
parent
acbe89a159
commit
ef64f00cbb
|
@ -3440,6 +3440,7 @@ version = "0.21.0"
|
|||
dependencies = [
|
||||
"base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
|
@ -8,7 +8,7 @@ rm -f config/run/init-completed
|
|||
timeout 15 ./run.sh &
|
||||
pid=$!
|
||||
|
||||
attempts=10
|
||||
attempts=20
|
||||
while [[ ! -f config/run/init-completed ]]; do
|
||||
sleep 1
|
||||
if ((--attempts == 0)); then
|
||||
|
|
|
@ -1878,7 +1878,10 @@ mod tests {
|
|||
stake_account: bob_keypair.into(),
|
||||
staker: None,
|
||||
withdrawer: None,
|
||||
lockup: Lockup { slot: 0, custodian },
|
||||
lockup: Lockup {
|
||||
epoch: 0,
|
||||
custodian,
|
||||
},
|
||||
lamports: 1234,
|
||||
};
|
||||
let signature = process_command(&config);
|
||||
|
|
|
@ -271,7 +271,7 @@ impl StakeSubCommands for App<'_, '_> {
|
|||
|
||||
pub fn parse_stake_create_account(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||
let stake_account = keypair_of(matches, "stake_account").unwrap();
|
||||
let slot = value_of(&matches, "lockup").unwrap_or(0);
|
||||
let epoch = value_of(&matches, "lockup").unwrap_or(0);
|
||||
let custodian = pubkey_of(matches, "custodian").unwrap_or_default();
|
||||
let staker = pubkey_of(matches, "authorized_staker");
|
||||
let withdrawer = pubkey_of(matches, "authorized_withdrawer");
|
||||
|
@ -282,7 +282,7 @@ pub fn parse_stake_create_account(matches: &ArgMatches<'_>) -> Result<CliCommand
|
|||
stake_account: stake_account.into(),
|
||||
staker,
|
||||
withdrawer,
|
||||
lockup: Lockup { custodian, slot },
|
||||
lockup: Lockup { custodian, epoch },
|
||||
lamports,
|
||||
},
|
||||
require_keypair: true,
|
||||
|
@ -547,7 +547,7 @@ pub fn process_show_stake_account(
|
|||
println!("authorized withdrawer: {}", authorized.staker);
|
||||
}
|
||||
fn show_lockup(lockup: &Lockup) {
|
||||
println!("lockup slot: {}", lockup.slot);
|
||||
println!("lockup epoch: {}", lockup.epoch);
|
||||
println!("lockup custodian: {}", lockup.custodian);
|
||||
}
|
||||
match stake_account.state() {
|
||||
|
@ -793,7 +793,7 @@ mod tests {
|
|||
staker: Some(authorized),
|
||||
withdrawer: Some(authorized),
|
||||
lockup: Lockup {
|
||||
slot: 43,
|
||||
epoch: 43,
|
||||
custodian,
|
||||
},
|
||||
lamports: 50
|
||||
|
@ -823,10 +823,7 @@ mod tests {
|
|||
stake_account: stake_account_keypair.into(),
|
||||
staker: None,
|
||||
withdrawer: None,
|
||||
lockup: Lockup {
|
||||
slot: 0,
|
||||
custodian: Pubkey::default(),
|
||||
},
|
||||
lockup: Lockup::default(),
|
||||
lamports: 50
|
||||
},
|
||||
require_keypair: true
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
|
@ -5,8 +5,8 @@ use serde_derive::{Deserialize, Serialize};
|
|||
use solana_config_program::{create_config_account, get_config_data, ConfigState};
|
||||
use solana_sdk::{
|
||||
account::{Account, KeyedAccount},
|
||||
genesis_config::GenesisConfig,
|
||||
instruction::InstructionError,
|
||||
pubkey::Pubkey,
|
||||
};
|
||||
|
||||
// stake config ID
|
||||
|
@ -48,8 +48,15 @@ impl ConfigState for Config {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn create_genesis_account() -> (Pubkey, Account) {
|
||||
(id(), create_config_account(vec![], &Config::default(), 100))
|
||||
pub fn add_genesis_account(genesis_config: &mut GenesisConfig) -> u64 {
|
||||
let mut account = create_config_account(vec![], &Config::default(), 0);
|
||||
let lamports = genesis_config.rent.minimum_balance(account.data.len());
|
||||
|
||||
account.lamports = lamports.max(1);
|
||||
|
||||
genesis_config.add_account(id(), account);
|
||||
|
||||
lamports
|
||||
}
|
||||
|
||||
pub fn create_account(lamports: u64, config: &Config) -> Account {
|
||||
|
@ -66,19 +73,15 @@ pub fn from_keyed_account(account: &KeyedAccount) -> Result<Config, InstructionE
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut account = create_account(1, &Config::default());
|
||||
let mut account = create_account(0, &Config::default());
|
||||
assert_eq!(Config::from(&account), Some(Config::default()));
|
||||
assert_eq!(
|
||||
from_keyed_account(&KeyedAccount::new(&Pubkey::default(), false, &mut account)),
|
||||
Err(InstructionError::InvalidArgument)
|
||||
);
|
||||
let (pubkey, mut account) = create_genesis_account();
|
||||
assert_eq!(
|
||||
from_keyed_account(&KeyedAccount::new(&pubkey, false, &mut account)),
|
||||
Ok(Config::default())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
use crate::config::create_genesis_account;
|
||||
use crate::rewards_pools::create_rewards_accounts;
|
||||
use crate::stake_instruction::process_instruction;
|
||||
use solana_sdk::genesis_config::GenesisConfig;
|
||||
|
||||
pub mod config;
|
||||
|
@ -11,14 +8,13 @@ pub mod stake_state;
|
|||
solana_sdk::declare_program!(
|
||||
"Stake11111111111111111111111111111111111111",
|
||||
solana_stake_program,
|
||||
process_instruction
|
||||
stake_instruction::process_instruction
|
||||
);
|
||||
|
||||
pub fn add_genesis_accounts(genesis_config: &mut GenesisConfig) {
|
||||
for (pubkey, account) in create_rewards_accounts() {
|
||||
pub fn add_genesis_accounts(genesis_config: &mut GenesisConfig) -> u64 {
|
||||
for (pubkey, account) in rewards_pools::create_genesis_accounts() {
|
||||
genesis_config.add_rewards_pool(pubkey, account);
|
||||
}
|
||||
|
||||
let (pubkey, account) = create_genesis_account();
|
||||
genesis_config.add_account(pubkey, account);
|
||||
config::add_genesis_account(genesis_config)
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ pub fn random_id() -> Pubkey {
|
|||
Pubkey::new(id.as_ref())
|
||||
}
|
||||
|
||||
pub fn create_rewards_accounts() -> Vec<(Pubkey, Account)> {
|
||||
pub fn create_genesis_accounts() -> Vec<(Pubkey, Account)> {
|
||||
let mut accounts = Vec::with_capacity(NUM_REWARDS_POOLS);
|
||||
let mut pubkey = id();
|
||||
|
||||
|
@ -46,7 +46,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test() {
|
||||
let accounts = create_rewards_accounts();
|
||||
let accounts = create_genesis_accounts();
|
||||
|
||||
for _i in 0..NUM_REWARDS_POOLS {
|
||||
let id = random_id();
|
||||
|
|
|
@ -365,7 +365,7 @@ mod tests {
|
|||
} else if sysvar::stake_history::check_id(&meta.pubkey) {
|
||||
sysvar::stake_history::create_account(1, &StakeHistory::default())
|
||||
} else if config::check_id(&meta.pubkey) {
|
||||
config::create_account(1, &config::Config::default())
|
||||
config::create_account(0, &config::Config::default())
|
||||
} else if sysvar::rent::check_id(&meta.pubkey) {
|
||||
sysvar::rent::create_account(1, &Rent::default())
|
||||
} else {
|
||||
|
@ -588,7 +588,7 @@ mod tests {
|
|||
KeyedAccount::new(
|
||||
&config::id(),
|
||||
false,
|
||||
&mut config::create_account(1, &config::Config::default())
|
||||
&mut config::create_account(0, &config::Config::default())
|
||||
),
|
||||
],
|
||||
&serialize(&StakeInstruction::DelegateStake).unwrap(),
|
||||
|
|
|
@ -82,9 +82,9 @@ pub enum StakeAuthorize {
|
|||
|
||||
#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
|
||||
pub struct Lockup {
|
||||
/// slot height at which this stake will allow withdrawal, unless
|
||||
/// epoch at which this stake will allow withdrawal, unless
|
||||
/// to the custodian
|
||||
pub slot: Slot,
|
||||
pub epoch: Epoch,
|
||||
/// custodian account, the only account to which this stake will honor a
|
||||
/// withdrawal before lockup expires. After lockup expires, custodian
|
||||
/// is irrelevant
|
||||
|
@ -104,16 +104,6 @@ pub struct Meta {
|
|||
pub lockup: Lockup,
|
||||
}
|
||||
|
||||
impl Meta {
|
||||
pub fn auto(authorized: &Pubkey) -> Self {
|
||||
Self {
|
||||
authorized: Authorized::auto(authorized),
|
||||
rent_exempt_reserve: Rent::default().minimum_balance(std::mem::size_of::<StakeState>()),
|
||||
..Meta::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
|
||||
pub struct Delegation {
|
||||
/// to whom the stake is delegated
|
||||
|
@ -762,7 +752,7 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
|
|||
|
||||
// verify that lockup has expired or that the withdrawal is going back
|
||||
// to the custodian
|
||||
if lockup.slot > clock.slot && lockup.custodian != *to.unsigned_key() {
|
||||
if lockup.epoch > clock.epoch && lockup.custodian != *to.unsigned_key() {
|
||||
return Err(StakeError::LockupInForce.into());
|
||||
}
|
||||
|
||||
|
@ -811,7 +801,34 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
// utility function, used by Bank, tests, genesis
|
||||
pub fn get_stake_rent_exempt_reserve(rent: &Rent) -> u64 {
|
||||
rent.minimum_balance(std::mem::size_of::<StakeState>())
|
||||
}
|
||||
|
||||
// genesis investor accounts
|
||||
pub fn create_lockup_stake_account(
|
||||
authorized: &Authorized,
|
||||
lockup: &Lockup,
|
||||
rent: &Rent,
|
||||
lamports: u64,
|
||||
) -> Account {
|
||||
let mut stake_account = Account::new(lamports, std::mem::size_of::<StakeState>(), &id());
|
||||
|
||||
let rent_exempt_reserve = rent.minimum_balance(stake_account.data.len());
|
||||
assert!(lamports >= rent_exempt_reserve);
|
||||
|
||||
stake_account
|
||||
.set_state(&StakeState::Initialized(Meta {
|
||||
authorized: *authorized,
|
||||
lockup: *lockup,
|
||||
rent_exempt_reserve,
|
||||
}))
|
||||
.expect("set_state");
|
||||
|
||||
stake_account
|
||||
}
|
||||
|
||||
// utility function, used by Bank, tests, genesis for bootstrap
|
||||
pub fn create_account(
|
||||
authorized: &Pubkey,
|
||||
voter_pubkey: &Pubkey,
|
||||
|
@ -822,19 +839,18 @@ pub fn create_account(
|
|||
let mut stake_account = Account::new(lamports, std::mem::size_of::<StakeState>(), &id());
|
||||
|
||||
let vote_state = VoteState::from(vote_account).expect("vote_state");
|
||||
let rent_exempt_reserve = rent.minimum_balance(std::mem::size_of::<StakeState>());
|
||||
|
||||
let rent_exempt_reserve = rent.minimum_balance(stake_account.data.len());
|
||||
|
||||
stake_account
|
||||
.set_state(&StakeState::Stake(
|
||||
Meta {
|
||||
authorized: Authorized::auto(authorized),
|
||||
rent_exempt_reserve,
|
||||
authorized: Authorized {
|
||||
staker: *authorized,
|
||||
withdrawer: *authorized,
|
||||
},
|
||||
lockup: Lockup::default(),
|
||||
..Meta::default()
|
||||
},
|
||||
Stake::new(
|
||||
lamports - rent_exempt_reserve, // underflow is an error, assert!(lamports> rent_exempt_reserve);
|
||||
lamports - rent_exempt_reserve, // underflow is an error, is basically: assert!(lamports > rent_exempt_reserve);
|
||||
voter_pubkey,
|
||||
&vote_state,
|
||||
std::u64::MAX,
|
||||
|
@ -853,6 +869,15 @@ mod tests {
|
|||
use solana_sdk::{account::Account, pubkey::Pubkey, system_program};
|
||||
use solana_vote_program::vote_state;
|
||||
|
||||
impl Meta {
|
||||
pub fn auto(authorized: &Pubkey) -> Self {
|
||||
Self {
|
||||
authorized: Authorized::auto(authorized),
|
||||
..Meta::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stake_state_stake_from_fail() {
|
||||
let mut stake_account = Account::new(0, std::mem::size_of::<StakeState>(), &id());
|
||||
|
@ -1378,7 +1403,10 @@ mod tests {
|
|||
assert_eq!(
|
||||
stake_keyed_account.initialize(
|
||||
&Authorized::auto(&stake_pubkey),
|
||||
&Lockup { slot: 1, custodian },
|
||||
&Lockup {
|
||||
epoch: 1,
|
||||
custodian
|
||||
},
|
||||
&Rent::default(),
|
||||
),
|
||||
Ok(())
|
||||
|
@ -1387,8 +1415,14 @@ mod tests {
|
|||
assert_eq!(
|
||||
StakeState::from(&stake_keyed_account.account).unwrap(),
|
||||
StakeState::Initialized(Meta {
|
||||
lockup: Lockup { slot: 1, custodian },
|
||||
..Meta::auto(&stake_pubkey)
|
||||
lockup: Lockup {
|
||||
epoch: 1,
|
||||
custodian
|
||||
},
|
||||
..Meta {
|
||||
authorized: Authorized::auto(&stake_pubkey),
|
||||
..Meta::default()
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -1521,7 +1555,10 @@ mod tests {
|
|||
stake_keyed_account
|
||||
.initialize(
|
||||
&Authorized::auto(&stake_pubkey),
|
||||
&Lockup { slot: 0, custodian },
|
||||
&Lockup {
|
||||
epoch: 0,
|
||||
custodian,
|
||||
},
|
||||
&Rent::default(),
|
||||
)
|
||||
.unwrap();
|
||||
|
@ -1722,7 +1759,10 @@ mod tests {
|
|||
let mut stake_account = Account::new_data_with_space(
|
||||
total_lamports,
|
||||
&StakeState::Initialized(Meta {
|
||||
lockup: Lockup { slot: 1, custodian },
|
||||
lockup: Lockup {
|
||||
epoch: 1,
|
||||
custodian,
|
||||
},
|
||||
..Meta::auto(&stake_pubkey)
|
||||
}),
|
||||
std::mem::size_of::<StakeState>(),
|
||||
|
@ -1771,7 +1811,7 @@ mod tests {
|
|||
|
||||
// lockup has expired
|
||||
let mut to_keyed_account = KeyedAccount::new(&to, false, &mut to_account);
|
||||
clock.slot += 1;
|
||||
clock.epoch += 1;
|
||||
assert_eq!(
|
||||
stake_keyed_account.withdraw(
|
||||
total_lamports,
|
||||
|
|
|
@ -15,13 +15,14 @@ solana_sdk::declare_id!("StorageMiningPoo111111111111111111111111111");
|
|||
// to cut down on collisions for redemptions, we make multiple accounts
|
||||
pub const NUM_REWARDS_POOLS: usize = 32;
|
||||
|
||||
pub fn add_genesis_accounts(genesis_config: &mut GenesisConfig) {
|
||||
pub fn add_genesis_accounts(genesis_config: &mut GenesisConfig) -> u64 {
|
||||
let mut pubkey = id();
|
||||
|
||||
for _i in 0..NUM_REWARDS_POOLS {
|
||||
genesis_config.add_rewards_pool(pubkey, create_rewards_pool());
|
||||
pubkey = Pubkey::new(hash(pubkey.as_ref()).as_ref());
|
||||
}
|
||||
0 // didn't consume any lamports
|
||||
}
|
||||
|
||||
pub fn random_id() -> Pubkey {
|
||||
|
|
|
@ -41,7 +41,7 @@ use solana_sdk::{
|
|||
slot_hashes::SlotHashes,
|
||||
system_transaction,
|
||||
sysvar::{self, Sysvar},
|
||||
timing::duration_as_ns,
|
||||
timing::years_as_slots,
|
||||
transaction::{Result, Transaction, TransactionError},
|
||||
};
|
||||
use std::{
|
||||
|
@ -699,12 +699,11 @@ impl Bank {
|
|||
self.ticks_per_slot = genesis_config.ticks_per_slot;
|
||||
self.slots_per_segment = genesis_config.slots_per_segment;
|
||||
self.max_tick_height = (self.slot + 1) * self.ticks_per_slot;
|
||||
// ticks/year = seconds/year ...
|
||||
self.slots_per_year = SECONDS_PER_YEAR
|
||||
// * (ns/s)/(ns/tick) / ticks/slot = 1/s/1/tick = ticks/s
|
||||
*(1_000_000_000.0 / duration_as_ns(&genesis_config.poh_config.target_tick_duration) as f64)
|
||||
// / ticks/slot
|
||||
/ self.ticks_per_slot as f64;
|
||||
self.slots_per_year = years_as_slots(
|
||||
1.0,
|
||||
&genesis_config.poh_config.target_tick_duration,
|
||||
self.ticks_per_slot,
|
||||
);
|
||||
|
||||
self.epoch_schedule = genesis_config.epoch_schedule;
|
||||
|
||||
|
@ -1685,7 +1684,6 @@ mod tests {
|
|||
signature::{Keypair, KeypairUtil},
|
||||
system_instruction,
|
||||
sysvar::{fees::Fees, rewards::Rewards},
|
||||
timing::years_as_slots,
|
||||
};
|
||||
use solana_stake_program::stake_state::{Delegation, Stake};
|
||||
use solana_vote_program::{
|
||||
|
|
|
@ -45,7 +45,7 @@ impl EpochSchedule {
|
|||
pub fn new(slots_per_epoch: u64) -> Self {
|
||||
Self::custom(slots_per_epoch, slots_per_epoch, true)
|
||||
}
|
||||
pub fn custom(slots_per_epoch: Epoch, leader_schedule_slot_offset: u64, warmup: bool) -> Self {
|
||||
pub fn custom(slots_per_epoch: u64, leader_schedule_slot_offset: u64, warmup: bool) -> Self {
|
||||
assert!(slots_per_epoch >= MINIMUM_SLOTS_PER_EPOCH as u64);
|
||||
let (first_normal_epoch, first_normal_slot) = if warmup {
|
||||
let next_power_of_two = slots_per_epoch.next_power_of_two();
|
||||
|
|
Loading…
Reference in New Issue