Move shred_version module to sdk/

This commit is contained in:
Michael Vines
2020-02-24 10:18:08 -07:00
parent 90240bf11d
commit 73063544bd
14 changed files with 89 additions and 82 deletions

View File

@ -7,16 +7,20 @@ use crate::{
fee_calculator::FeeCalculator,
hash::{hash, Hash},
inflation::Inflation,
native_token::lamports_to_sol,
poh_config::PohConfig,
pubkey::Pubkey,
rent::Rent,
shred_version::compute_shred_version,
signature::{Keypair, Signer},
system_program::{self, solana_system_program},
};
use bincode::{deserialize, serialize};
use chrono::{TimeZone, Utc};
use memmap::Mmap;
use std::{
collections::BTreeMap,
fmt,
fs::{File, OpenOptions},
io::Write,
path::{Path, PathBuf},
@ -173,6 +177,51 @@ impl GenesisConfig {
}
}
impl fmt::Display for GenesisConfig {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"\
Creation time: {}\n\
Operating mode: {:?}\n\
Genesis hash: {}\n\
Shred version: {}\n\
Hashes per tick: {:?}\n\
Slots per epoch: {}\n\
Warmup epochs: {}abled\n\
{:?}\n\
{:?}\n\
Capitalization: {} SOL in {} accounts\n\
",
Utc.timestamp(self.creation_time, 0).to_rfc3339(),
self.operating_mode,
self.hash(),
compute_shred_version(&self.hash(), None),
self.poh_config.hashes_per_tick,
self.epoch_schedule.slots_per_epoch,
if self.epoch_schedule.warmup {
"en"
} else {
"dis"
},
self.rent,
self.fee_calculator,
lamports_to_sol(
self.accounts
.iter()
.map(|(pubkey, account)| {
if account.lamports == 0 {
panic!("{:?}", (pubkey, account));
}
account.lamports
})
.sum::<u64>()
),
self.accounts.len(),
)
}
}
#[cfg(test)]
mod tests {
use super::*;

91
sdk/src/hard_forks.rs Normal file
View File

@ -0,0 +1,91 @@
//! The `hard_forks` module is used to maintain the list of slot boundaries for when a hard fork
//! should occur.
use byteorder::{ByteOrder, LittleEndian};
use solana_sdk::clock::Slot;
use std::ops::Add;
#[derive(Default, Clone, Deserialize, Serialize)]
pub struct HardForks {
hard_forks: Vec<(Slot, usize)>,
}
impl HardForks {
// Register a fork to occur at all slots >= `slot` with a parent slot < `slot`
pub fn register(&mut self, new_slot: Slot) {
if let Some(i) = self
.hard_forks
.iter()
.position(|(slot, _)| *slot == new_slot)
{
self.hard_forks[i] = (new_slot, self.hard_forks[i].1 + 1);
} else {
self.hard_forks.push((new_slot, 1));
}
self.hard_forks.sort();
}
// Returns a sorted-by-slot iterator over the registered hark forks
pub fn iter(&self) -> std::slice::Iter<(Slot, usize)> {
self.hard_forks.iter()
}
// Returns data to include in the bank hash for the given slot if a hard fork is scheduled
pub fn get_hash_data(&self, slot: Slot, parent_slot: Slot) -> Option<[u8; 8]> {
// The expected number of hard forks in a cluster is small.
// If this turns out to be false then a more efficient data
// structure may be needed here to avoid this linear search
let fork_count = self
.hard_forks
.iter()
.fold(0, |acc, (fork_slot, fork_count)| {
acc.add(if parent_slot < *fork_slot && slot >= *fork_slot {
*fork_count
} else {
0
})
});
if fork_count > 0 {
let mut buf = [0u8; 8];
LittleEndian::write_u64(&mut buf[..], fork_count as u64);
Some(buf)
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn iter_is_sorted() {
let mut hf = HardForks::default();
hf.register(30);
hf.register(20);
hf.register(10);
hf.register(20);
assert_eq!(
hf.iter().map(|i| *i).collect::<Vec<_>>(),
vec![(10, 1), (20, 2), (30, 1)]
);
}
#[test]
fn multiple_hard_forks_since_parent() {
let mut hf = HardForks::default();
hf.register(10);
hf.register(20);
assert_eq!(hf.get_hash_data(9, 0), None);
assert_eq!(hf.get_hash_data(10, 0), Some([1, 0, 0, 0, 0, 0, 0, 0,]));
assert_eq!(hf.get_hash_data(19, 0), Some([1, 0, 0, 0, 0, 0, 0, 0,]));
assert_eq!(hf.get_hash_data(20, 0), Some([2, 0, 0, 0, 0, 0, 0, 0,]));
assert_eq!(hf.get_hash_data(20, 10), Some([1, 0, 0, 0, 0, 0, 0, 0,]));
assert_eq!(hf.get_hash_data(20, 11), Some([1, 0, 0, 0, 0, 0, 0, 0,]));
assert_eq!(hf.get_hash_data(21, 11), Some([1, 0, 0, 0, 0, 0, 0, 0,]));
assert_eq!(hf.get_hash_data(21, 20), None);
}
}

View File

@ -68,6 +68,10 @@ pub mod client;
#[cfg(not(feature = "program"))]
pub mod genesis_config;
#[cfg(not(feature = "program"))]
pub mod hard_forks;
#[cfg(not(feature = "program"))]
pub mod shred_version;
#[cfg(not(feature = "program"))]
pub mod signature;
#[cfg(not(feature = "program"))]
pub mod signers;

61
sdk/src/shred_version.rs Normal file
View File

@ -0,0 +1,61 @@
use solana_sdk::{
hard_forks::HardForks,
hash::{extend_and_hash, Hash},
};
pub fn version_from_hash(hash: &Hash) -> u16 {
let hash = hash.as_ref();
let mut accum = [0u8; 2];
hash.chunks(2).for_each(|seed| {
accum
.iter_mut()
.zip(seed)
.for_each(|(accum, seed)| *accum ^= *seed)
});
// convert accum into a u16
let version = ((accum[0] as u16) << 8) | accum[1] as u16;
// ensure version is never zero, to avoid looking like an uninitialized version
version.saturating_add(1)
}
pub fn compute_shred_version(genesis_hash: &Hash, hard_forks: Option<&HardForks>) -> u16 {
use byteorder::{ByteOrder, LittleEndian};
let mut hash = *genesis_hash;
if let Some(hard_forks) = hard_forks {
for (slot, count) in hard_forks.iter() {
let mut buf = [0u8; 16];
LittleEndian::write_u64(&mut buf[..8], *slot);
LittleEndian::write_u64(&mut buf[8..], *count as u64);
hash = extend_and_hash(&hash, &buf);
}
}
version_from_hash(&hash)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_compute_shred_version() {
assert_eq!(compute_shred_version(&Hash::default(), None), 1);
let mut hard_forks = HardForks::default();
assert_eq!(
compute_shred_version(&Hash::default(), Some(&hard_forks)),
1
);
hard_forks.register(1);
assert_eq!(
compute_shred_version(&Hash::default(), Some(&hard_forks)),
55551
);
hard_forks.register(1);
assert_eq!(
compute_shred_version(&Hash::default(), Some(&hard_forks)),
46353
);
}
}