add epoch_schedule sysvar (#6256)
* add epoch_schedule sysvar * book sheesh!
This commit is contained in:
@ -17,6 +17,7 @@ pub const DEFAULT_SLOTS_PER_SEGMENT: u64 = 1024;
|
||||
// 4 times longer than the max_lockout to allow enough time for PoRep (128 slots)
|
||||
pub const DEFAULT_SLOTS_PER_TURN: u64 = 32 * 4;
|
||||
|
||||
// leader schedule is governed by this
|
||||
pub const NUM_CONSECUTIVE_LEADER_SLOTS: u64 = 4;
|
||||
|
||||
/// The time window of recent block hash values that the bank will track the signatures
|
||||
|
198
sdk/src/epoch_schedule.rs
Normal file
198
sdk/src/epoch_schedule.rs
Normal file
@ -0,0 +1,198 @@
|
||||
//! configuration for epochs, slots
|
||||
|
||||
/// 1 Epoch = 400 * 8192 ms ~= 55 minutes
|
||||
pub use crate::clock::{Epoch, Slot, DEFAULT_SLOTS_PER_EPOCH};
|
||||
|
||||
/// The number of slots before an epoch starts to calculate the leader schedule.
|
||||
/// Default is an entire epoch, i.e. leader schedule for epoch X is calculated at
|
||||
/// the beginning of epoch X - 1.
|
||||
pub const DEFAULT_LEADER_SCHEDULE_SLOT_OFFSET: u64 = DEFAULT_SLOTS_PER_EPOCH;
|
||||
|
||||
/// based on MAX_LOCKOUT_HISTORY from vote_api
|
||||
pub const MINIMUM_SLOTS_PER_EPOCH: u64 = 32;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize)]
|
||||
pub struct EpochSchedule {
|
||||
/// The maximum number of slots in each epoch.
|
||||
pub slots_per_epoch: u64,
|
||||
|
||||
/// A number of slots before beginning of an epoch to calculate
|
||||
/// a leader schedule for that epoch
|
||||
pub leader_schedule_slot_offset: u64,
|
||||
|
||||
/// whether epochs start short and grow
|
||||
pub warmup: bool,
|
||||
|
||||
/// basically: log2(slots_per_epoch) - log2(MINIMUM_SLOT_LEN)
|
||||
pub first_normal_epoch: Epoch,
|
||||
|
||||
/// basically: 2.pow(first_normal_epoch) - MINIMUM_SLOT_LEN
|
||||
pub first_normal_slot: Slot,
|
||||
}
|
||||
|
||||
impl Default for EpochSchedule {
|
||||
fn default() -> Self {
|
||||
Self::custom(
|
||||
DEFAULT_SLOTS_PER_EPOCH,
|
||||
DEFAULT_LEADER_SCHEDULE_SLOT_OFFSET,
|
||||
true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
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: 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();
|
||||
let log2_slots_per_epoch = next_power_of_two
|
||||
.trailing_zeros()
|
||||
.saturating_sub(MINIMUM_SLOTS_PER_EPOCH.trailing_zeros());
|
||||
|
||||
(
|
||||
u64::from(log2_slots_per_epoch),
|
||||
next_power_of_two.saturating_sub(MINIMUM_SLOTS_PER_EPOCH),
|
||||
)
|
||||
} else {
|
||||
(0, 0)
|
||||
};
|
||||
EpochSchedule {
|
||||
slots_per_epoch,
|
||||
leader_schedule_slot_offset,
|
||||
warmup,
|
||||
first_normal_epoch,
|
||||
first_normal_slot,
|
||||
}
|
||||
}
|
||||
|
||||
/// get the length of the given epoch (in slots)
|
||||
pub fn get_slots_in_epoch(&self, epoch: u64) -> u64 {
|
||||
if epoch < self.first_normal_epoch {
|
||||
2u64.pow(epoch as u32 + MINIMUM_SLOTS_PER_EPOCH.trailing_zeros() as u32)
|
||||
} else {
|
||||
self.slots_per_epoch
|
||||
}
|
||||
}
|
||||
|
||||
/// get the epoch for which the given slot should save off
|
||||
/// information about stakers
|
||||
pub fn get_leader_schedule_epoch(&self, slot: u64) -> u64 {
|
||||
if slot < self.first_normal_slot {
|
||||
// until we get to normal slots, behave as if leader_schedule_slot_offset == slots_per_epoch
|
||||
self.get_epoch_and_slot_index(slot).0 + 1
|
||||
} else {
|
||||
self.first_normal_epoch
|
||||
+ (slot - self.first_normal_slot + self.leader_schedule_slot_offset)
|
||||
/ self.slots_per_epoch
|
||||
}
|
||||
}
|
||||
|
||||
/// get epoch for the given slot
|
||||
pub fn get_epoch(&self, slot: u64) -> u64 {
|
||||
self.get_epoch_and_slot_index(slot).0
|
||||
}
|
||||
|
||||
/// get epoch and offset into the epoch for the given slot
|
||||
pub fn get_epoch_and_slot_index(&self, slot: u64) -> (u64, u64) {
|
||||
if slot < self.first_normal_slot {
|
||||
let epoch = (slot + MINIMUM_SLOTS_PER_EPOCH + 1)
|
||||
.next_power_of_two()
|
||||
.trailing_zeros()
|
||||
- MINIMUM_SLOTS_PER_EPOCH.trailing_zeros()
|
||||
- 1;
|
||||
|
||||
let epoch_len = 2u64.pow(epoch + MINIMUM_SLOTS_PER_EPOCH.trailing_zeros());
|
||||
|
||||
(
|
||||
u64::from(epoch),
|
||||
slot - (epoch_len - MINIMUM_SLOTS_PER_EPOCH),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
self.first_normal_epoch + ((slot - self.first_normal_slot) / self.slots_per_epoch),
|
||||
(slot - self.first_normal_slot) % self.slots_per_epoch,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_first_slot_in_epoch(&self, epoch: u64) -> u64 {
|
||||
if epoch <= self.first_normal_epoch {
|
||||
(2u64.pow(epoch as u32) - 1) * MINIMUM_SLOTS_PER_EPOCH
|
||||
} else {
|
||||
(epoch - self.first_normal_epoch) * self.slots_per_epoch + self.first_normal_slot
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_last_slot_in_epoch(&self, epoch: u64) -> u64 {
|
||||
self.get_first_slot_in_epoch(epoch) + self.get_slots_in_epoch(epoch) - 1
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_epoch_schedule() {
|
||||
// one week of slots at 8 ticks/slot, 10 ticks/sec is
|
||||
// (1 * 7 * 24 * 4500u64).next_power_of_two();
|
||||
|
||||
// test values between MINIMUM_SLOT_LEN and MINIMUM_SLOT_LEN * 16, should cover a good mix
|
||||
for slots_per_epoch in MINIMUM_SLOTS_PER_EPOCH..=MINIMUM_SLOTS_PER_EPOCH * 16 {
|
||||
let epoch_schedule = EpochSchedule::custom(slots_per_epoch, slots_per_epoch / 2, true);
|
||||
|
||||
assert_eq!(epoch_schedule.get_first_slot_in_epoch(0), 0);
|
||||
assert_eq!(
|
||||
epoch_schedule.get_last_slot_in_epoch(0),
|
||||
MINIMUM_SLOTS_PER_EPOCH - 1
|
||||
);
|
||||
|
||||
let mut last_leader_schedule = 0;
|
||||
let mut last_epoch = 0;
|
||||
let mut last_slots_in_epoch = MINIMUM_SLOTS_PER_EPOCH;
|
||||
for slot in 0..(2 * slots_per_epoch) {
|
||||
// verify that leader_schedule_epoch is continuous over the warmup
|
||||
// and into the first normal epoch
|
||||
|
||||
let leader_schedule = epoch_schedule.get_leader_schedule_epoch(slot);
|
||||
if leader_schedule != last_leader_schedule {
|
||||
assert_eq!(leader_schedule, last_leader_schedule + 1);
|
||||
last_leader_schedule = leader_schedule;
|
||||
}
|
||||
|
||||
let (epoch, offset) = epoch_schedule.get_epoch_and_slot_index(slot);
|
||||
|
||||
// verify that epoch increases continuously
|
||||
if epoch != last_epoch {
|
||||
assert_eq!(epoch, last_epoch + 1);
|
||||
last_epoch = epoch;
|
||||
assert_eq!(epoch_schedule.get_first_slot_in_epoch(epoch), slot);
|
||||
assert_eq!(epoch_schedule.get_last_slot_in_epoch(epoch - 1), slot - 1);
|
||||
|
||||
// verify that slots in an epoch double continuously
|
||||
// until they reach slots_per_epoch
|
||||
|
||||
let slots_in_epoch = epoch_schedule.get_slots_in_epoch(epoch);
|
||||
if slots_in_epoch != last_slots_in_epoch {
|
||||
if slots_in_epoch != slots_per_epoch {
|
||||
assert_eq!(slots_in_epoch, last_slots_in_epoch * 2);
|
||||
}
|
||||
}
|
||||
last_slots_in_epoch = slots_in_epoch;
|
||||
}
|
||||
// verify that the slot offset is less than slots_in_epoch
|
||||
assert!(offset < last_slots_in_epoch);
|
||||
}
|
||||
|
||||
// assert that these changed ;)
|
||||
assert!(last_leader_schedule != 0); // t
|
||||
assert!(last_epoch != 0);
|
||||
// assert that we got to "normal" mode
|
||||
assert!(last_slots_in_epoch == slots_per_epoch);
|
||||
}
|
||||
}
|
||||
}
|
@ -45,11 +45,11 @@ impl Default for FeeCalculator {
|
||||
}
|
||||
|
||||
impl FeeCalculator {
|
||||
pub fn new(target_lamports_per_signature: u64) -> Self {
|
||||
pub fn new(target_lamports_per_signature: u64, target_signatures_per_slot: usize) -> Self {
|
||||
let base_fee_calculator = Self {
|
||||
target_lamports_per_signature,
|
||||
lamports_per_signature: target_lamports_per_signature,
|
||||
target_signatures_per_slot: 0,
|
||||
target_signatures_per_slot,
|
||||
..FeeCalculator::default()
|
||||
};
|
||||
|
||||
@ -160,20 +160,20 @@ mod tests {
|
||||
assert_eq!(FeeCalculator::default().calculate_fee(&message), 0);
|
||||
|
||||
// No signature, no fee.
|
||||
assert_eq!(FeeCalculator::new(1).calculate_fee(&message), 0);
|
||||
assert_eq!(FeeCalculator::new(1, 0).calculate_fee(&message), 0);
|
||||
|
||||
// One signature, a fee.
|
||||
let pubkey0 = Pubkey::new(&[0; 32]);
|
||||
let pubkey1 = Pubkey::new(&[1; 32]);
|
||||
let ix0 = system_instruction::transfer(&pubkey0, &pubkey1, 1);
|
||||
let message = Message::new(vec![ix0]);
|
||||
assert_eq!(FeeCalculator::new(2).calculate_fee(&message), 2);
|
||||
assert_eq!(FeeCalculator::new(2, 0).calculate_fee(&message), 2);
|
||||
|
||||
// Two signatures, double the fee.
|
||||
let ix0 = system_instruction::transfer(&pubkey0, &pubkey1, 1);
|
||||
let ix1 = system_instruction::transfer(&pubkey1, &pubkey0, 1);
|
||||
let message = Message::new(vec![ix0, ix1]);
|
||||
assert_eq!(FeeCalculator::new(2).calculate_fee(&message), 4);
|
||||
assert_eq!(FeeCalculator::new(2, 0).calculate_fee(&message), 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1,35 +1,38 @@
|
||||
//! The `genesis_block` module is a library for generating the chain's genesis block.
|
||||
|
||||
use crate::account::Account;
|
||||
use crate::clock::{DEFAULT_SLOTS_PER_EPOCH, DEFAULT_SLOTS_PER_SEGMENT, DEFAULT_TICKS_PER_SLOT};
|
||||
use crate::fee_calculator::FeeCalculator;
|
||||
use crate::hash::{hash, Hash};
|
||||
use crate::inflation::Inflation;
|
||||
use crate::poh_config::PohConfig;
|
||||
use crate::pubkey::Pubkey;
|
||||
use crate::rent_calculator::RentCalculator;
|
||||
use crate::signature::{Keypair, KeypairUtil};
|
||||
use crate::system_program::{self, solana_system_program};
|
||||
use crate::{
|
||||
account::Account,
|
||||
clock::{DEFAULT_SLOTS_PER_SEGMENT, DEFAULT_TICKS_PER_SLOT},
|
||||
epoch_schedule::EpochSchedule,
|
||||
fee_calculator::FeeCalculator,
|
||||
hash::{hash, Hash},
|
||||
inflation::Inflation,
|
||||
poh_config::PohConfig,
|
||||
pubkey::Pubkey,
|
||||
rent_calculator::RentCalculator,
|
||||
signature::{Keypair, KeypairUtil},
|
||||
system_program::{self, solana_system_program},
|
||||
};
|
||||
use bincode::{deserialize, serialize};
|
||||
use memmap::Mmap;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::{
|
||||
fs::{File, OpenOptions},
|
||||
io::Write,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct GenesisBlock {
|
||||
pub accounts: Vec<(Pubkey, Account)>,
|
||||
pub native_instruction_processors: Vec<(String, Pubkey)>,
|
||||
pub rewards_pools: Vec<(Pubkey, Account)>,
|
||||
pub slots_per_epoch: u64,
|
||||
pub stakers_slot_offset: u64,
|
||||
pub epoch_warmup: bool,
|
||||
pub ticks_per_slot: u64,
|
||||
pub slots_per_segment: u64,
|
||||
pub poh_config: PohConfig,
|
||||
pub fee_calculator: FeeCalculator,
|
||||
pub rent_calculator: RentCalculator,
|
||||
pub inflation: Inflation,
|
||||
pub epoch_schedule: EpochSchedule,
|
||||
}
|
||||
|
||||
// useful for basic tests
|
||||
@ -53,15 +56,13 @@ impl Default for GenesisBlock {
|
||||
accounts: Vec::new(),
|
||||
native_instruction_processors: Vec::new(),
|
||||
rewards_pools: Vec::new(),
|
||||
epoch_warmup: true,
|
||||
slots_per_epoch: DEFAULT_SLOTS_PER_EPOCH,
|
||||
stakers_slot_offset: DEFAULT_SLOTS_PER_EPOCH,
|
||||
ticks_per_slot: DEFAULT_TICKS_PER_SLOT,
|
||||
slots_per_segment: DEFAULT_SLOTS_PER_SEGMENT,
|
||||
poh_config: PohConfig::default(),
|
||||
inflation: Inflation::default(),
|
||||
fee_calculator: FeeCalculator::default(),
|
||||
rent_calculator: RentCalculator::default(),
|
||||
epoch_schedule: EpochSchedule::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -69,7 +70,6 @@ impl Default for GenesisBlock {
|
||||
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
||||
pub struct Builder {
|
||||
genesis_block: GenesisBlock,
|
||||
already_have_stakers_slot_offset: bool,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
@ -114,21 +114,8 @@ impl Builder {
|
||||
Self::append(rewards_pools, self.genesis_block.rewards_pools);
|
||||
self
|
||||
}
|
||||
// also sets stakers_slot_offset, unless already set explicitly
|
||||
pub fn slots_per_epoch(mut self, slots_per_epoch: u64) -> Self {
|
||||
self.genesis_block.slots_per_epoch = slots_per_epoch;
|
||||
if !self.already_have_stakers_slot_offset {
|
||||
self.genesis_block.stakers_slot_offset = slots_per_epoch;
|
||||
}
|
||||
self
|
||||
}
|
||||
pub fn stakers_slot_offset(mut self, stakers_slot_offset: u64) -> Self {
|
||||
self.genesis_block.stakers_slot_offset = stakers_slot_offset;
|
||||
self.already_have_stakers_slot_offset = true;
|
||||
self
|
||||
}
|
||||
pub fn epoch_warmup(mut self, epoch_warmup: bool) -> Self {
|
||||
self.genesis_block.epoch_warmup = epoch_warmup;
|
||||
pub fn epoch_schedule(mut self, epoch_schedule: EpochSchedule) -> Self {
|
||||
self.genesis_block.epoch_schedule = epoch_schedule;
|
||||
self
|
||||
}
|
||||
pub fn ticks_per_slot(mut self, ticks_per_slot: u64) -> Self {
|
||||
|
@ -2,6 +2,7 @@ pub mod account;
|
||||
pub mod account_utils;
|
||||
pub mod bpf_loader;
|
||||
pub mod clock;
|
||||
pub mod epoch_schedule;
|
||||
pub mod fee_calculator;
|
||||
pub mod hash;
|
||||
pub mod inflation;
|
||||
@ -17,6 +18,7 @@ pub mod pubkey;
|
||||
pub mod rent_calculator;
|
||||
pub mod rpc_port;
|
||||
pub mod short_vec;
|
||||
pub mod slot_hashes;
|
||||
pub mod system_instruction;
|
||||
pub mod system_program;
|
||||
pub mod sysvar;
|
||||
|
77
sdk/src/slot_hashes.rs
Normal file
77
sdk/src/slot_hashes.rs
Normal file
@ -0,0 +1,77 @@
|
||||
//! named accounts for synthesized data accounts for bank state, etc.
|
||||
//!
|
||||
//! this account carries the Bank's most recent blockhashes for some N parents
|
||||
//!
|
||||
use crate::hash::Hash;
|
||||
use std::ops::Deref;
|
||||
|
||||
pub const MAX_SLOT_HASHES: usize = 512; // 512 slots to get your vote in
|
||||
|
||||
pub use crate::clock::Slot;
|
||||
|
||||
pub type SlotHash = (Slot, Hash);
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug, Default)]
|
||||
pub struct SlotHashes(Vec<SlotHash>);
|
||||
|
||||
impl SlotHashes {
|
||||
pub fn add(&mut self, slot: Slot, hash: Hash) {
|
||||
match self.binary_search_by(|probe| slot.cmp(&probe.0)) {
|
||||
Ok(index) => (self.0)[index] = (slot, hash),
|
||||
Err(index) => (self.0).insert(index, (slot, hash)),
|
||||
}
|
||||
(self.0).truncate(MAX_SLOT_HASHES);
|
||||
}
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
pub fn get(&self, slot: &Slot) -> Option<&Hash> {
|
||||
self.binary_search_by(|probe| slot.cmp(&probe.0))
|
||||
.ok()
|
||||
.map(|index| &self[index].1)
|
||||
}
|
||||
pub fn new(slot_hashes: &[SlotHash]) -> Self {
|
||||
let mut slot_hashes = slot_hashes.to_vec();
|
||||
slot_hashes.sort_by(|a, b| b.0.cmp(&a.0)); // reverse order
|
||||
Self(slot_hashes)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for SlotHashes {
|
||||
type Target = Vec<SlotHash>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::hash::hash;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut slot_hashes = SlotHashes::new(&[(1, Hash::default()), (3, Hash::default())]);
|
||||
slot_hashes.add(2, Hash::default());
|
||||
assert_eq!(
|
||||
slot_hashes,
|
||||
SlotHashes(vec![
|
||||
(3, Hash::default()),
|
||||
(2, Hash::default()),
|
||||
(1, Hash::default()),
|
||||
])
|
||||
);
|
||||
|
||||
let mut slot_hashes = SlotHashes::new(&[]);
|
||||
for i in 0..MAX_SLOT_HASHES + 1 {
|
||||
slot_hashes.add(
|
||||
i as u64,
|
||||
hash(&[(i >> 24) as u8, (i >> 16) as u8, (i >> 8) as u8, i as u8]),
|
||||
);
|
||||
}
|
||||
for i in 0..MAX_SLOT_HASHES {
|
||||
assert_eq!(slot_hashes[i].0, (MAX_SLOT_HASHES - i) as u64);
|
||||
}
|
||||
|
||||
assert_eq!(slot_hashes.len(), MAX_SLOT_HASHES);
|
||||
}
|
||||
}
|
57
sdk/src/sysvar/epoch_schedule.rs
Normal file
57
sdk/src/sysvar/epoch_schedule.rs
Normal file
@ -0,0 +1,57 @@
|
||||
//! This account contains the current cluster rent
|
||||
//!
|
||||
use crate::{
|
||||
account::{Account, KeyedAccount},
|
||||
account_info::AccountInfo,
|
||||
epoch_schedule::EpochSchedule,
|
||||
instruction::InstructionError,
|
||||
sysvar,
|
||||
};
|
||||
|
||||
/// epoch_schedule account pubkey
|
||||
const ID: [u8; 32] = [
|
||||
6, 167, 213, 23, 24, 220, 63, 238, 2, 211, 228, 127, 1, 0, 248, 176, 84, 247, 148, 46, 96, 89,
|
||||
30, 63, 80, 135, 25, 168, 5, 0, 0, 0,
|
||||
];
|
||||
|
||||
crate::solana_name_id!(ID, "SysvarEpochSchedu1e111111111111111111111111");
|
||||
|
||||
impl EpochSchedule {
|
||||
pub fn deserialize(account: &Account) -> Result<Self, bincode::Error> {
|
||||
account.deserialize_data()
|
||||
}
|
||||
pub fn from_account(account: &Account) -> Option<Self> {
|
||||
account.deserialize_data().ok()
|
||||
}
|
||||
pub fn to_account(&self, account: &mut Account) -> Option<()> {
|
||||
account.serialize_data(self).ok()
|
||||
}
|
||||
pub fn from_account_info(account: &AccountInfo) -> Option<Self> {
|
||||
account.deserialize_data().ok()
|
||||
}
|
||||
pub fn to_account_info(&self, account: &mut AccountInfo) -> Option<()> {
|
||||
account.serialize_data(self).ok()
|
||||
}
|
||||
pub fn from_keyed_account(account: &KeyedAccount) -> Result<EpochSchedule, InstructionError> {
|
||||
if !check_id(account.unsigned_key()) {
|
||||
return Err(InstructionError::InvalidArgument);
|
||||
}
|
||||
EpochSchedule::from_account(account.account).ok_or(InstructionError::InvalidArgument)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_account(lamports: u64, epoch_schedule: &EpochSchedule) -> Account {
|
||||
Account::new_data(lamports, epoch_schedule, &sysvar::id()).unwrap()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_create_account() {
|
||||
let account = create_account(42, &EpochSchedule::default());
|
||||
let epoch_schedule = EpochSchedule::from_account(&account).unwrap();
|
||||
assert_eq!(epoch_schedule, EpochSchedule::default());
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
use crate::pubkey::Pubkey;
|
||||
|
||||
pub mod clock;
|
||||
pub mod epoch_schedule;
|
||||
pub mod fees;
|
||||
pub mod rent;
|
||||
pub mod rewards;
|
||||
|
@ -1,9 +1,12 @@
|
||||
//! This account contains the current cluster rent
|
||||
//!
|
||||
use crate::account::Account;
|
||||
use crate::account_info::AccountInfo;
|
||||
use crate::rent_calculator::RentCalculator;
|
||||
use crate::sysvar;
|
||||
use crate::{
|
||||
account::{Account, KeyedAccount},
|
||||
account_info::AccountInfo,
|
||||
instruction::InstructionError,
|
||||
rent_calculator::RentCalculator,
|
||||
sysvar,
|
||||
};
|
||||
use bincode::serialized_size;
|
||||
|
||||
/// rent account pubkey
|
||||
@ -49,9 +52,6 @@ pub fn create_account(lamports: u64, rent_calculator: &RentCalculator) -> Accoun
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
use crate::account::KeyedAccount;
|
||||
use crate::instruction::InstructionError;
|
||||
|
||||
pub fn from_keyed_account(account: &KeyedAccount) -> Result<Rent, InstructionError> {
|
||||
if !check_id(account.unsigned_key()) {
|
||||
return Err(InstructionError::InvalidArgument);
|
||||
|
@ -58,7 +58,6 @@ use crate::account::KeyedAccount;
|
||||
use crate::instruction::InstructionError;
|
||||
pub fn from_keyed_account(account: &KeyedAccount) -> Result<Rewards, InstructionError> {
|
||||
if !check_id(account.unsigned_key()) {
|
||||
dbg!(account.unsigned_key());
|
||||
return Err(InstructionError::InvalidArgument);
|
||||
}
|
||||
Rewards::from_account(account.account).ok_or(InstructionError::InvalidAccountData)
|
||||
|
@ -2,14 +2,9 @@
|
||||
//!
|
||||
//! this account carries the Bank's most recent blockhashes for some N parents
|
||||
//!
|
||||
use crate::account::Account;
|
||||
use crate::account_info::AccountInfo;
|
||||
use crate::hash::Hash;
|
||||
use crate::sysvar;
|
||||
pub use crate::slot_hashes::{SlotHash, SlotHashes};
|
||||
use crate::{account::Account, account_info::AccountInfo, sysvar};
|
||||
use bincode::serialized_size;
|
||||
use std::ops::Deref;
|
||||
|
||||
pub use crate::clock::Slot;
|
||||
|
||||
const ID: [u8; 32] = [
|
||||
6, 167, 213, 23, 25, 47, 10, 175, 198, 242, 101, 227, 251, 119, 204, 122, 218, 130, 197, 41,
|
||||
@ -20,11 +15,6 @@ crate::solana_name_id!(ID, "SysvarS1otHashes111111111111111111111111111");
|
||||
|
||||
pub const MAX_SLOT_HASHES: usize = 512; // 512 slots to get your vote in
|
||||
|
||||
pub type SlotHash = (Slot, Hash);
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
pub struct SlotHashes(Vec<SlotHash>);
|
||||
|
||||
impl SlotHashes {
|
||||
pub fn from_account(account: &Account) -> Option<Self> {
|
||||
account.deserialize_data().ok()
|
||||
@ -39,30 +29,7 @@ impl SlotHashes {
|
||||
account.serialize_data(self).ok()
|
||||
}
|
||||
pub fn size_of() -> usize {
|
||||
serialized_size(&SlotHashes(vec![(0, Hash::default()); MAX_SLOT_HASHES])).unwrap() as usize
|
||||
}
|
||||
pub fn add(&mut self, slot: Slot, hash: Hash) {
|
||||
match self.binary_search_by(|probe| slot.cmp(&probe.0)) {
|
||||
Ok(index) => (self.0)[index] = (slot, hash),
|
||||
Err(index) => (self.0).insert(index, (slot, hash)),
|
||||
}
|
||||
(self.0).truncate(MAX_SLOT_HASHES);
|
||||
}
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
pub fn get(&self, slot: &Slot) -> Option<&Hash> {
|
||||
self.binary_search_by(|probe| slot.cmp(&probe.0))
|
||||
.ok()
|
||||
.map(|index| &self[index].1)
|
||||
}
|
||||
pub fn new(slot_hashes: &[SlotHash]) -> Self {
|
||||
Self(slot_hashes.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for SlotHashes {
|
||||
type Target = Vec<SlotHash>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
serialized_size(&SlotHashes::new(&[SlotHash::default(); MAX_SLOT_HASHES])).unwrap() as usize
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,7 +53,6 @@ pub fn from_keyed_account(account: &KeyedAccount) -> Result<SlotHashes, Instruct
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::hash::hash;
|
||||
|
||||
#[test]
|
||||
fn test_create_account() {
|
||||
@ -94,18 +60,6 @@ mod tests {
|
||||
let account = create_account(lamports, &[]);
|
||||
assert_eq!(account.data.len(), SlotHashes::size_of());
|
||||
let slot_hashes = SlotHashes::from_account(&account);
|
||||
assert_eq!(slot_hashes, Some(SlotHashes(vec![])));
|
||||
let mut slot_hashes = slot_hashes.unwrap();
|
||||
for i in 0..MAX_SLOT_HASHES + 1 {
|
||||
slot_hashes.add(
|
||||
i as u64,
|
||||
hash(&[(i >> 24) as u8, (i >> 16) as u8, (i >> 8) as u8, i as u8]),
|
||||
);
|
||||
}
|
||||
for i in 0..MAX_SLOT_HASHES {
|
||||
assert_eq!(slot_hashes[i].0, (MAX_SLOT_HASHES - i) as u64);
|
||||
}
|
||||
|
||||
assert_eq!(slot_hashes.len(), MAX_SLOT_HASHES);
|
||||
assert_eq!(slot_hashes, Some(SlotHashes::default()));
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ pub struct StakeHistoryEntry {
|
||||
pub deactivating: u64, // requested to be cooled down, not fully deactivated yet
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Default, Clone)]
|
||||
pub struct StakeHistory(Vec<(Epoch, StakeHistoryEntry)>);
|
||||
|
||||
|
Reference in New Issue
Block a user