2020-03-30 16:53:25 -07:00
|
|
|
use crate::consensus::VOTE_THRESHOLD_SIZE;
|
2020-04-22 11:22:09 -07:00
|
|
|
use solana_ledger::blockstore::Blockstore;
|
2020-03-30 16:53:25 -07:00
|
|
|
use solana_measure::measure::Measure;
|
|
|
|
use solana_metrics::inc_new_counter_info;
|
2019-09-20 19:38:56 -07:00
|
|
|
use solana_runtime::bank::Bank;
|
2019-11-02 00:38:30 -07:00
|
|
|
use solana_sdk::clock::Slot;
|
2019-11-20 10:12:43 -08:00
|
|
|
use solana_vote_program::{vote_state::VoteState, vote_state::MAX_LOCKOUT_HISTORY};
|
2019-11-02 00:38:30 -07:00
|
|
|
use std::{
|
|
|
|
collections::HashMap,
|
|
|
|
sync::atomic::{AtomicBool, Ordering},
|
|
|
|
sync::mpsc::{channel, Receiver, RecvTimeoutError, Sender},
|
|
|
|
sync::{Arc, RwLock},
|
|
|
|
thread::{self, Builder, JoinHandle},
|
|
|
|
time::Duration,
|
|
|
|
};
|
2019-09-04 23:10:25 -07:00
|
|
|
|
2020-04-20 22:25:49 -07:00
|
|
|
pub type BlockCommitmentArray = [u64; MAX_LOCKOUT_HISTORY + 1];
|
|
|
|
|
2019-10-14 15:24:10 -07:00
|
|
|
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
2019-11-04 15:44:27 -08:00
|
|
|
pub struct BlockCommitment {
|
2020-04-20 22:25:49 -07:00
|
|
|
pub commitment: BlockCommitmentArray,
|
2019-09-04 23:10:25 -07:00
|
|
|
}
|
|
|
|
|
2019-11-04 15:44:27 -08:00
|
|
|
impl BlockCommitment {
|
2019-09-20 19:38:56 -07:00
|
|
|
pub fn increase_confirmation_stake(&mut self, confirmation_count: usize, stake: u64) {
|
|
|
|
assert!(confirmation_count > 0 && confirmation_count <= MAX_LOCKOUT_HISTORY);
|
2019-11-04 15:44:27 -08:00
|
|
|
self.commitment[confirmation_count - 1] += stake;
|
2019-09-04 23:10:25 -07:00
|
|
|
}
|
2019-09-20 19:38:56 -07:00
|
|
|
|
|
|
|
pub fn get_confirmation_stake(&mut self, confirmation_count: usize) -> u64 {
|
|
|
|
assert!(confirmation_count > 0 && confirmation_count <= MAX_LOCKOUT_HISTORY);
|
2019-11-04 15:44:27 -08:00
|
|
|
self.commitment[confirmation_count - 1]
|
2019-09-04 23:10:25 -07:00
|
|
|
}
|
2020-04-20 22:25:49 -07:00
|
|
|
|
|
|
|
pub fn increase_rooted_stake(&mut self, stake: u64) {
|
|
|
|
self.commitment[MAX_LOCKOUT_HISTORY] += stake;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_rooted_stake(&self) -> u64 {
|
|
|
|
self.commitment[MAX_LOCKOUT_HISTORY]
|
|
|
|
}
|
|
|
|
|
2019-10-14 15:24:10 -07:00
|
|
|
#[cfg(test)]
|
2020-04-20 22:25:49 -07:00
|
|
|
pub(crate) fn new(commitment: BlockCommitmentArray) -> Self {
|
2019-11-04 15:44:27 -08:00
|
|
|
Self { commitment }
|
2019-10-14 15:24:10 -07:00
|
|
|
}
|
2019-09-04 23:10:25 -07:00
|
|
|
}
|
|
|
|
|
2019-11-04 15:44:27 -08:00
|
|
|
pub struct BlockCommitmentCache {
|
|
|
|
block_commitment: HashMap<Slot, BlockCommitment>,
|
2020-04-22 11:22:09 -07:00
|
|
|
largest_confirmed_root: Slot,
|
2019-10-14 15:24:10 -07:00
|
|
|
total_stake: u64,
|
2020-03-30 10:29:30 -07:00
|
|
|
bank: Arc<Bank>,
|
2020-04-22 11:22:09 -07:00
|
|
|
blockstore: Arc<Blockstore>,
|
2020-03-30 10:29:30 -07:00
|
|
|
root: Slot,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::fmt::Debug for BlockCommitmentCache {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
f.debug_struct("BlockCommitmentCache")
|
|
|
|
.field("block_commitment", &self.block_commitment)
|
|
|
|
.field("total_stake", &self.total_stake)
|
|
|
|
.field(
|
|
|
|
"bank",
|
|
|
|
&format_args!("Bank({{current_slot: {:?}}})", self.bank.slot()),
|
|
|
|
)
|
|
|
|
.field("root", &self.root)
|
|
|
|
.finish()
|
|
|
|
}
|
2019-09-04 23:10:25 -07:00
|
|
|
}
|
|
|
|
|
2019-11-04 15:44:27 -08:00
|
|
|
impl BlockCommitmentCache {
|
2020-03-30 10:29:30 -07:00
|
|
|
pub fn new(
|
|
|
|
block_commitment: HashMap<Slot, BlockCommitment>,
|
2020-04-22 11:22:09 -07:00
|
|
|
largest_confirmed_root: Slot,
|
2020-03-30 10:29:30 -07:00
|
|
|
total_stake: u64,
|
|
|
|
bank: Arc<Bank>,
|
2020-04-22 11:22:09 -07:00
|
|
|
blockstore: Arc<Blockstore>,
|
2020-03-30 10:29:30 -07:00
|
|
|
root: Slot,
|
|
|
|
) -> Self {
|
2019-09-20 19:38:56 -07:00
|
|
|
Self {
|
2019-11-04 15:44:27 -08:00
|
|
|
block_commitment,
|
2020-04-22 11:22:09 -07:00
|
|
|
largest_confirmed_root,
|
2019-10-14 15:24:10 -07:00
|
|
|
total_stake,
|
2020-03-30 10:29:30 -07:00
|
|
|
bank,
|
2020-04-22 11:22:09 -07:00
|
|
|
blockstore,
|
2020-03-30 10:29:30 -07:00
|
|
|
root,
|
2019-09-20 19:38:56 -07:00
|
|
|
}
|
2019-09-04 23:10:25 -07:00
|
|
|
}
|
|
|
|
|
2020-04-22 11:22:09 -07:00
|
|
|
pub fn default_with_blockstore(blockstore: Arc<Blockstore>) -> Self {
|
|
|
|
Self {
|
|
|
|
block_commitment: HashMap::default(),
|
|
|
|
largest_confirmed_root: Slot::default(),
|
|
|
|
total_stake: u64::default(),
|
|
|
|
bank: Arc::new(Bank::default()),
|
|
|
|
blockstore,
|
|
|
|
root: Slot::default(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-04 15:44:27 -08:00
|
|
|
pub fn get_block_commitment(&self, slot: Slot) -> Option<&BlockCommitment> {
|
|
|
|
self.block_commitment.get(&slot)
|
2019-09-04 23:10:25 -07:00
|
|
|
}
|
2019-10-14 15:24:10 -07:00
|
|
|
|
2020-04-22 11:22:09 -07:00
|
|
|
pub fn largest_confirmed_root(&self) -> Slot {
|
|
|
|
self.largest_confirmed_root
|
|
|
|
}
|
|
|
|
|
2019-10-14 15:24:10 -07:00
|
|
|
pub fn total_stake(&self) -> u64 {
|
|
|
|
self.total_stake
|
|
|
|
}
|
2019-10-14 22:14:20 -07:00
|
|
|
|
2020-03-30 10:29:30 -07:00
|
|
|
pub fn bank(&self) -> Arc<Bank> {
|
|
|
|
self.bank.clone()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn slot(&self) -> Slot {
|
|
|
|
self.bank.slot()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn root(&self) -> Slot {
|
|
|
|
self.root
|
|
|
|
}
|
|
|
|
|
2020-03-30 16:53:25 -07:00
|
|
|
pub fn get_confirmation_count(&self, slot: Slot) -> Option<usize> {
|
|
|
|
self.get_lockout_count(slot, VOTE_THRESHOLD_SIZE)
|
2019-10-14 22:14:20 -07:00
|
|
|
}
|
|
|
|
|
2020-03-30 16:53:25 -07:00
|
|
|
// Returns the lowest level at which at least `minimum_stake_percentage` of the total epoch
|
|
|
|
// stake is locked out
|
|
|
|
fn get_lockout_count(&self, slot: Slot, minimum_stake_percentage: f64) -> Option<usize> {
|
|
|
|
self.get_block_commitment(slot).map(|block_commitment| {
|
|
|
|
let iterator = block_commitment.commitment.iter().enumerate().rev();
|
|
|
|
let mut sum = 0;
|
|
|
|
for (i, stake) in iterator {
|
|
|
|
sum += stake;
|
|
|
|
if (sum as f64 / self.total_stake as f64) > minimum_stake_percentage {
|
|
|
|
return i + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
0
|
|
|
|
})
|
|
|
|
}
|
2020-04-20 22:25:49 -07:00
|
|
|
|
|
|
|
pub fn is_confirmed_rooted(&self, slot: Slot) -> bool {
|
2020-04-22 11:22:09 -07:00
|
|
|
slot <= self.largest_confirmed_root()
|
|
|
|
&& (self.blockstore.is_root(slot) || self.bank.status_cache_ancestors().contains(&slot))
|
2020-04-20 22:25:49 -07:00
|
|
|
}
|
|
|
|
|
2020-03-30 16:53:25 -07:00
|
|
|
#[cfg(test)]
|
2020-04-22 11:22:09 -07:00
|
|
|
pub fn new_for_tests_with_blockstore(blockstore: Arc<Blockstore>) -> Self {
|
2020-03-30 16:53:25 -07:00
|
|
|
let mut block_commitment: HashMap<Slot, BlockCommitment> = HashMap::new();
|
|
|
|
block_commitment.insert(0, BlockCommitment::default());
|
|
|
|
Self {
|
|
|
|
block_commitment,
|
2020-04-22 11:22:09 -07:00
|
|
|
blockstore,
|
2020-03-30 16:53:25 -07:00
|
|
|
total_stake: 42,
|
2020-04-22 11:22:09 -07:00
|
|
|
largest_confirmed_root: Slot::default(),
|
|
|
|
bank: Arc::new(Bank::default()),
|
|
|
|
root: Slot::default(),
|
2020-03-30 16:53:25 -07:00
|
|
|
}
|
2019-10-14 22:14:20 -07:00
|
|
|
}
|
2019-09-04 23:10:25 -07:00
|
|
|
}
|
|
|
|
|
2019-11-04 15:44:27 -08:00
|
|
|
pub struct CommitmentAggregationData {
|
2019-09-20 19:38:56 -07:00
|
|
|
bank: Arc<Bank>,
|
2020-03-30 10:29:30 -07:00
|
|
|
root: Slot,
|
2019-09-17 19:43:40 -07:00
|
|
|
total_staked: u64,
|
|
|
|
}
|
|
|
|
|
2019-11-04 15:44:27 -08:00
|
|
|
impl CommitmentAggregationData {
|
2020-03-30 10:29:30 -07:00
|
|
|
pub fn new(bank: Arc<Bank>, root: Slot, total_staked: u64) -> Self {
|
|
|
|
Self {
|
|
|
|
bank,
|
|
|
|
root,
|
|
|
|
total_staked,
|
|
|
|
}
|
2019-09-17 19:43:40 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-22 11:22:09 -07:00
|
|
|
fn get_largest_confirmed_root(mut rooted_stake: Vec<(Slot, u64)>, total_stake: u64) -> Slot {
|
|
|
|
rooted_stake.sort_by(|a, b| a.0.cmp(&b.0).reverse());
|
|
|
|
let mut stake_sum = 0;
|
|
|
|
for (root, stake) in rooted_stake {
|
|
|
|
stake_sum += stake;
|
|
|
|
if (stake_sum as f64 / total_stake as f64) > VOTE_THRESHOLD_SIZE {
|
|
|
|
return root;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
0
|
|
|
|
}
|
|
|
|
|
2019-11-04 15:44:27 -08:00
|
|
|
pub struct AggregateCommitmentService {
|
|
|
|
t_commitment: JoinHandle<()>,
|
2019-09-17 19:43:40 -07:00
|
|
|
}
|
|
|
|
|
2019-11-04 15:44:27 -08:00
|
|
|
impl AggregateCommitmentService {
|
2019-09-17 19:43:40 -07:00
|
|
|
pub fn new(
|
|
|
|
exit: &Arc<AtomicBool>,
|
2019-11-04 15:44:27 -08:00
|
|
|
block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
|
|
|
|
) -> (Sender<CommitmentAggregationData>, Self) {
|
2019-09-20 19:38:56 -07:00
|
|
|
let (sender, receiver): (
|
2019-11-04 15:44:27 -08:00
|
|
|
Sender<CommitmentAggregationData>,
|
|
|
|
Receiver<CommitmentAggregationData>,
|
2019-09-17 19:43:40 -07:00
|
|
|
) = channel();
|
|
|
|
let exit_ = exit.clone();
|
|
|
|
(
|
2019-09-20 19:38:56 -07:00
|
|
|
sender,
|
2019-09-17 19:43:40 -07:00
|
|
|
Self {
|
2019-11-04 15:44:27 -08:00
|
|
|
t_commitment: Builder::new()
|
2019-09-17 19:43:40 -07:00
|
|
|
.name("solana-aggregate-stake-lockouts".to_string())
|
|
|
|
.spawn(move || loop {
|
|
|
|
if exit_.load(Ordering::Relaxed) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-01-02 19:50:43 -08:00
|
|
|
if let Err(RecvTimeoutError::Disconnected) =
|
|
|
|
Self::run(&receiver, &block_commitment_cache, &exit_)
|
|
|
|
{
|
|
|
|
break;
|
2019-09-17 19:43:40 -07:00
|
|
|
}
|
|
|
|
})
|
|
|
|
.unwrap(),
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
2019-09-20 19:38:56 -07:00
|
|
|
|
|
|
|
fn run(
|
2019-11-04 15:44:27 -08:00
|
|
|
receiver: &Receiver<CommitmentAggregationData>,
|
|
|
|
block_commitment_cache: &RwLock<BlockCommitmentCache>,
|
2019-09-20 19:38:56 -07:00
|
|
|
exit: &Arc<AtomicBool>,
|
2020-01-02 19:50:43 -08:00
|
|
|
) -> Result<(), RecvTimeoutError> {
|
2019-09-20 19:38:56 -07:00
|
|
|
loop {
|
|
|
|
if exit.load(Ordering::Relaxed) {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut aggregation_data = receiver.recv_timeout(Duration::from_secs(1))?;
|
|
|
|
|
|
|
|
while let Ok(new_data) = receiver.try_recv() {
|
|
|
|
aggregation_data = new_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
let ancestors = aggregation_data.bank.status_cache_ancestors();
|
|
|
|
if ancestors.is_empty() {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-03-30 16:53:25 -07:00
|
|
|
let mut aggregate_commitment_time = Measure::start("aggregate-commitment-ms");
|
2020-04-22 11:22:09 -07:00
|
|
|
let (block_commitment, rooted_stake) =
|
|
|
|
Self::aggregate_commitment(&ancestors, &aggregation_data.bank);
|
|
|
|
|
|
|
|
let largest_confirmed_root =
|
|
|
|
get_largest_confirmed_root(rooted_stake, aggregation_data.total_staked);
|
2019-09-20 19:38:56 -07:00
|
|
|
|
2020-03-30 10:29:30 -07:00
|
|
|
let mut new_block_commitment = BlockCommitmentCache::new(
|
|
|
|
block_commitment,
|
2020-04-22 11:22:09 -07:00
|
|
|
largest_confirmed_root,
|
2020-03-30 10:29:30 -07:00
|
|
|
aggregation_data.total_staked,
|
|
|
|
aggregation_data.bank,
|
2020-04-22 11:22:09 -07:00
|
|
|
block_commitment_cache.read().unwrap().blockstore.clone(),
|
2020-03-30 10:29:30 -07:00
|
|
|
aggregation_data.root,
|
|
|
|
);
|
2019-09-20 19:38:56 -07:00
|
|
|
|
2019-11-04 15:44:27 -08:00
|
|
|
let mut w_block_commitment_cache = block_commitment_cache.write().unwrap();
|
2019-09-20 19:38:56 -07:00
|
|
|
|
2019-11-04 15:44:27 -08:00
|
|
|
std::mem::swap(&mut *w_block_commitment_cache, &mut new_block_commitment);
|
2020-03-30 16:53:25 -07:00
|
|
|
aggregate_commitment_time.stop();
|
|
|
|
inc_new_counter_info!(
|
|
|
|
"aggregate-commitment-ms",
|
|
|
|
aggregate_commitment_time.as_ms() as usize
|
|
|
|
);
|
2019-09-20 19:38:56 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-22 11:22:09 -07:00
|
|
|
pub fn aggregate_commitment(
|
|
|
|
ancestors: &[Slot],
|
|
|
|
bank: &Bank,
|
|
|
|
) -> (HashMap<Slot, BlockCommitment>, Vec<(Slot, u64)>) {
|
2019-09-20 19:38:56 -07:00
|
|
|
assert!(!ancestors.is_empty());
|
|
|
|
|
|
|
|
// Check ancestors is sorted
|
|
|
|
for a in ancestors.windows(2) {
|
|
|
|
assert!(a[0] < a[1]);
|
|
|
|
}
|
|
|
|
|
2019-11-04 15:44:27 -08:00
|
|
|
let mut commitment = HashMap::new();
|
2020-04-22 11:22:09 -07:00
|
|
|
let mut rooted_stake: Vec<(Slot, u64)> = Vec::new();
|
2019-09-20 19:38:56 -07:00
|
|
|
for (_, (lamports, account)) in bank.vote_accounts().into_iter() {
|
|
|
|
if lamports == 0 {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
let vote_state = VoteState::from(&account);
|
|
|
|
if vote_state.is_none() {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let vote_state = vote_state.unwrap();
|
2019-11-04 15:44:27 -08:00
|
|
|
Self::aggregate_commitment_for_vote_account(
|
|
|
|
&mut commitment,
|
2020-04-22 11:22:09 -07:00
|
|
|
&mut rooted_stake,
|
2019-09-20 19:38:56 -07:00
|
|
|
&vote_state,
|
|
|
|
ancestors,
|
|
|
|
lamports,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-04-22 11:22:09 -07:00
|
|
|
(commitment, rooted_stake)
|
2019-09-20 19:38:56 -07:00
|
|
|
}
|
|
|
|
|
2019-11-04 15:44:27 -08:00
|
|
|
fn aggregate_commitment_for_vote_account(
|
|
|
|
commitment: &mut HashMap<Slot, BlockCommitment>,
|
2020-04-22 11:22:09 -07:00
|
|
|
rooted_stake: &mut Vec<(Slot, u64)>,
|
2019-09-20 19:38:56 -07:00
|
|
|
vote_state: &VoteState,
|
2019-11-02 00:38:30 -07:00
|
|
|
ancestors: &[Slot],
|
2019-09-20 19:38:56 -07:00
|
|
|
lamports: u64,
|
|
|
|
) {
|
|
|
|
assert!(!ancestors.is_empty());
|
|
|
|
let mut ancestors_index = 0;
|
|
|
|
if let Some(root) = vote_state.root_slot {
|
|
|
|
for (i, a) in ancestors.iter().enumerate() {
|
|
|
|
if *a <= root {
|
2019-11-04 15:44:27 -08:00
|
|
|
commitment
|
2019-09-20 19:38:56 -07:00
|
|
|
.entry(*a)
|
2019-11-04 15:44:27 -08:00
|
|
|
.or_insert_with(BlockCommitment::default)
|
2020-04-20 22:25:49 -07:00
|
|
|
.increase_rooted_stake(lamports);
|
2019-09-20 19:38:56 -07:00
|
|
|
} else {
|
|
|
|
ancestors_index = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2020-04-22 11:22:09 -07:00
|
|
|
rooted_stake.push((root, lamports));
|
2019-09-20 19:38:56 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
for vote in &vote_state.votes {
|
|
|
|
while ancestors[ancestors_index] <= vote.slot {
|
2019-11-04 15:44:27 -08:00
|
|
|
commitment
|
2019-09-20 19:38:56 -07:00
|
|
|
.entry(ancestors[ancestors_index])
|
2019-11-04 15:44:27 -08:00
|
|
|
.or_insert_with(BlockCommitment::default)
|
2019-09-20 19:38:56 -07:00
|
|
|
.increase_confirmation_stake(vote.confirmation_count as usize, lamports);
|
|
|
|
ancestors_index += 1;
|
|
|
|
|
|
|
|
if ancestors_index == ancestors.len() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-09-17 19:43:40 -07:00
|
|
|
|
2019-11-13 10:12:09 -08:00
|
|
|
pub fn join(self) -> thread::Result<()> {
|
2019-11-04 15:44:27 -08:00
|
|
|
self.t_commitment.join()
|
2019-09-17 19:43:40 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-04 23:10:25 -07:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2020-04-22 11:22:09 -07:00
|
|
|
use solana_ledger::{
|
|
|
|
genesis_utils::{create_genesis_config, GenesisConfigInfo},
|
|
|
|
get_tmp_ledger_path,
|
|
|
|
};
|
2019-09-20 19:38:56 -07:00
|
|
|
use solana_sdk::pubkey::Pubkey;
|
2019-11-20 10:12:43 -08:00
|
|
|
use solana_stake_program::stake_state;
|
2020-02-25 17:12:01 -08:00
|
|
|
use solana_vote_program::vote_state::{self, VoteStateVersions};
|
2019-09-04 23:10:25 -07:00
|
|
|
|
|
|
|
#[test]
|
2019-11-04 15:44:27 -08:00
|
|
|
fn test_block_commitment() {
|
|
|
|
let mut cache = BlockCommitment::default();
|
2019-09-20 19:38:56 -07:00
|
|
|
assert_eq!(cache.get_confirmation_stake(1), 0);
|
|
|
|
cache.increase_confirmation_stake(1, 10);
|
|
|
|
assert_eq!(cache.get_confirmation_stake(1), 10);
|
|
|
|
cache.increase_confirmation_stake(1, 20);
|
|
|
|
assert_eq!(cache.get_confirmation_stake(1), 30);
|
|
|
|
}
|
|
|
|
|
2019-10-14 22:14:20 -07:00
|
|
|
#[test]
|
2020-03-30 16:53:25 -07:00
|
|
|
fn test_get_confirmations() {
|
2020-03-30 10:29:30 -07:00
|
|
|
let bank = Arc::new(Bank::default());
|
2020-04-22 11:22:09 -07:00
|
|
|
let ledger_path = get_tmp_ledger_path!();
|
|
|
|
let blockstore = Arc::new(Blockstore::open(&ledger_path).unwrap());
|
2019-11-04 15:44:27 -08:00
|
|
|
// Build BlockCommitmentCache with votes at depths 0 and 1 for 2 slots
|
|
|
|
let mut cache0 = BlockCommitment::default();
|
2020-03-30 16:53:25 -07:00
|
|
|
cache0.increase_confirmation_stake(1, 5);
|
|
|
|
cache0.increase_confirmation_stake(2, 40);
|
2019-10-14 22:14:20 -07:00
|
|
|
|
2019-11-04 15:44:27 -08:00
|
|
|
let mut cache1 = BlockCommitment::default();
|
2020-03-30 16:53:25 -07:00
|
|
|
cache1.increase_confirmation_stake(1, 40);
|
|
|
|
cache1.increase_confirmation_stake(2, 5);
|
2019-10-14 22:14:20 -07:00
|
|
|
|
2020-03-30 16:53:25 -07:00
|
|
|
let mut cache2 = BlockCommitment::default();
|
|
|
|
cache2.increase_confirmation_stake(1, 20);
|
|
|
|
cache2.increase_confirmation_stake(2, 5);
|
2019-10-14 22:14:20 -07:00
|
|
|
|
2019-11-04 15:44:27 -08:00
|
|
|
let mut block_commitment = HashMap::new();
|
|
|
|
block_commitment.entry(0).or_insert(cache0.clone());
|
|
|
|
block_commitment.entry(1).or_insert(cache1.clone());
|
2020-03-30 16:53:25 -07:00
|
|
|
block_commitment.entry(2).or_insert(cache2.clone());
|
2020-04-22 11:22:09 -07:00
|
|
|
let block_commitment_cache =
|
|
|
|
BlockCommitmentCache::new(block_commitment, 0, 50, bank, blockstore, 0);
|
2019-10-14 22:14:20 -07:00
|
|
|
|
2020-03-30 16:53:25 -07:00
|
|
|
assert_eq!(block_commitment_cache.get_confirmation_count(0), Some(2));
|
|
|
|
assert_eq!(block_commitment_cache.get_confirmation_count(1), Some(1));
|
|
|
|
assert_eq!(block_commitment_cache.get_confirmation_count(2), Some(0),);
|
|
|
|
assert_eq!(block_commitment_cache.get_confirmation_count(3), None,);
|
2019-10-14 22:14:20 -07:00
|
|
|
}
|
|
|
|
|
2020-04-22 11:22:09 -07:00
|
|
|
#[test]
|
|
|
|
fn test_is_confirmed_rooted() {
|
|
|
|
let bank = Arc::new(Bank::default());
|
|
|
|
let ledger_path = get_tmp_ledger_path!();
|
|
|
|
let blockstore = Arc::new(Blockstore::open(&ledger_path).unwrap());
|
|
|
|
blockstore.set_roots(&[0, 1]).unwrap();
|
|
|
|
// Build BlockCommitmentCache with rooted slots
|
|
|
|
let mut cache0 = BlockCommitment::default();
|
|
|
|
cache0.increase_rooted_stake(50);
|
|
|
|
let mut cache1 = BlockCommitment::default();
|
|
|
|
cache1.increase_rooted_stake(40);
|
|
|
|
let mut cache2 = BlockCommitment::default();
|
|
|
|
cache2.increase_rooted_stake(20);
|
|
|
|
|
|
|
|
let mut block_commitment = HashMap::new();
|
|
|
|
block_commitment.entry(1).or_insert(cache0.clone());
|
|
|
|
block_commitment.entry(2).or_insert(cache1.clone());
|
|
|
|
block_commitment.entry(3).or_insert(cache2.clone());
|
|
|
|
let largest_confirmed_root = 1;
|
|
|
|
let block_commitment_cache = BlockCommitmentCache::new(
|
|
|
|
block_commitment,
|
|
|
|
largest_confirmed_root,
|
|
|
|
50,
|
|
|
|
bank,
|
|
|
|
blockstore,
|
|
|
|
0,
|
|
|
|
);
|
|
|
|
|
|
|
|
assert!(block_commitment_cache.is_confirmed_rooted(0));
|
|
|
|
assert!(block_commitment_cache.is_confirmed_rooted(1));
|
|
|
|
assert!(!block_commitment_cache.is_confirmed_rooted(2));
|
|
|
|
assert!(!block_commitment_cache.is_confirmed_rooted(3));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_get_largest_confirmed_root() {
|
|
|
|
assert_eq!(get_largest_confirmed_root(vec![], 10), 0);
|
|
|
|
let mut rooted_stake = vec![];
|
|
|
|
rooted_stake.push((0, 5));
|
|
|
|
rooted_stake.push((1, 5));
|
|
|
|
assert_eq!(get_largest_confirmed_root(rooted_stake, 10), 0);
|
|
|
|
let mut rooted_stake = vec![];
|
|
|
|
rooted_stake.push((1, 5));
|
|
|
|
rooted_stake.push((0, 10));
|
|
|
|
rooted_stake.push((2, 5));
|
|
|
|
rooted_stake.push((1, 4));
|
|
|
|
assert_eq!(get_largest_confirmed_root(rooted_stake, 10), 1);
|
|
|
|
}
|
|
|
|
|
2019-09-20 19:38:56 -07:00
|
|
|
#[test]
|
2019-11-04 15:44:27 -08:00
|
|
|
fn test_aggregate_commitment_for_vote_account_1() {
|
2019-09-20 19:38:56 -07:00
|
|
|
let ancestors = vec![3, 4, 5, 7, 9, 11];
|
2019-11-04 15:44:27 -08:00
|
|
|
let mut commitment = HashMap::new();
|
2020-04-22 11:22:09 -07:00
|
|
|
let mut rooted_stake = vec![];
|
2019-09-20 19:38:56 -07:00
|
|
|
let lamports = 5;
|
2019-09-25 13:53:49 -07:00
|
|
|
let mut vote_state = VoteState::default();
|
2019-09-20 19:38:56 -07:00
|
|
|
|
2020-04-22 11:22:09 -07:00
|
|
|
let root = ancestors.last().unwrap().clone();
|
|
|
|
vote_state.root_slot = Some(root);
|
2019-11-04 15:44:27 -08:00
|
|
|
AggregateCommitmentService::aggregate_commitment_for_vote_account(
|
|
|
|
&mut commitment,
|
2020-04-22 11:22:09 -07:00
|
|
|
&mut rooted_stake,
|
2019-09-20 19:38:56 -07:00
|
|
|
&vote_state,
|
|
|
|
&ancestors,
|
|
|
|
lamports,
|
2019-09-04 23:10:25 -07:00
|
|
|
);
|
2019-09-20 19:38:56 -07:00
|
|
|
|
|
|
|
for a in ancestors {
|
2019-11-04 15:44:27 -08:00
|
|
|
let mut expected = BlockCommitment::default();
|
2020-04-20 22:25:49 -07:00
|
|
|
expected.increase_rooted_stake(lamports);
|
2019-11-04 15:44:27 -08:00
|
|
|
assert_eq!(*commitment.get(&a).unwrap(), expected);
|
2019-09-20 19:38:56 -07:00
|
|
|
}
|
2020-04-22 11:22:09 -07:00
|
|
|
assert_eq!(rooted_stake[0], (root, lamports));
|
2019-09-20 19:38:56 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2019-11-04 15:44:27 -08:00
|
|
|
fn test_aggregate_commitment_for_vote_account_2() {
|
2019-09-20 19:38:56 -07:00
|
|
|
let ancestors = vec![3, 4, 5, 7, 9, 11];
|
2019-11-04 15:44:27 -08:00
|
|
|
let mut commitment = HashMap::new();
|
2020-04-22 11:22:09 -07:00
|
|
|
let mut rooted_stake = vec![];
|
2019-09-20 19:38:56 -07:00
|
|
|
let lamports = 5;
|
2019-09-25 13:53:49 -07:00
|
|
|
let mut vote_state = VoteState::default();
|
2019-09-20 19:38:56 -07:00
|
|
|
|
|
|
|
let root = ancestors[2];
|
|
|
|
vote_state.root_slot = Some(root);
|
|
|
|
vote_state.process_slot_vote_unchecked(*ancestors.last().unwrap());
|
2019-11-04 15:44:27 -08:00
|
|
|
AggregateCommitmentService::aggregate_commitment_for_vote_account(
|
|
|
|
&mut commitment,
|
2020-04-22 11:22:09 -07:00
|
|
|
&mut rooted_stake,
|
2019-09-20 19:38:56 -07:00
|
|
|
&vote_state,
|
|
|
|
&ancestors,
|
|
|
|
lamports,
|
2019-09-04 23:10:25 -07:00
|
|
|
);
|
2019-09-20 19:38:56 -07:00
|
|
|
|
|
|
|
for a in ancestors {
|
|
|
|
if a <= root {
|
2019-11-04 15:44:27 -08:00
|
|
|
let mut expected = BlockCommitment::default();
|
2020-04-20 22:25:49 -07:00
|
|
|
expected.increase_rooted_stake(lamports);
|
2019-11-04 15:44:27 -08:00
|
|
|
assert_eq!(*commitment.get(&a).unwrap(), expected);
|
2019-09-20 19:38:56 -07:00
|
|
|
} else {
|
2019-11-04 15:44:27 -08:00
|
|
|
let mut expected = BlockCommitment::default();
|
2019-09-20 19:38:56 -07:00
|
|
|
expected.increase_confirmation_stake(1, lamports);
|
2019-11-04 15:44:27 -08:00
|
|
|
assert_eq!(*commitment.get(&a).unwrap(), expected);
|
2019-09-20 19:38:56 -07:00
|
|
|
}
|
|
|
|
}
|
2020-04-22 11:22:09 -07:00
|
|
|
assert_eq!(rooted_stake[0], (root, lamports));
|
2019-09-20 19:38:56 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2019-11-04 15:44:27 -08:00
|
|
|
fn test_aggregate_commitment_for_vote_account_3() {
|
2019-09-20 19:38:56 -07:00
|
|
|
let ancestors = vec![3, 4, 5, 7, 9, 10, 11];
|
2019-11-04 15:44:27 -08:00
|
|
|
let mut commitment = HashMap::new();
|
2020-04-22 11:22:09 -07:00
|
|
|
let mut rooted_stake = vec![];
|
2019-09-20 19:38:56 -07:00
|
|
|
let lamports = 5;
|
2019-09-25 13:53:49 -07:00
|
|
|
let mut vote_state = VoteState::default();
|
2019-09-20 19:38:56 -07:00
|
|
|
|
|
|
|
let root = ancestors[2];
|
|
|
|
vote_state.root_slot = Some(root);
|
|
|
|
assert!(ancestors[4] + 2 >= ancestors[6]);
|
|
|
|
vote_state.process_slot_vote_unchecked(ancestors[4]);
|
|
|
|
vote_state.process_slot_vote_unchecked(ancestors[6]);
|
2019-11-04 15:44:27 -08:00
|
|
|
AggregateCommitmentService::aggregate_commitment_for_vote_account(
|
|
|
|
&mut commitment,
|
2020-04-22 11:22:09 -07:00
|
|
|
&mut rooted_stake,
|
2019-09-20 19:38:56 -07:00
|
|
|
&vote_state,
|
|
|
|
&ancestors,
|
|
|
|
lamports,
|
2019-09-04 23:10:25 -07:00
|
|
|
);
|
2019-09-20 19:38:56 -07:00
|
|
|
|
|
|
|
for (i, a) in ancestors.iter().enumerate() {
|
|
|
|
if *a <= root {
|
2019-11-04 15:44:27 -08:00
|
|
|
let mut expected = BlockCommitment::default();
|
2020-04-20 22:25:49 -07:00
|
|
|
expected.increase_rooted_stake(lamports);
|
2019-11-04 15:44:27 -08:00
|
|
|
assert_eq!(*commitment.get(&a).unwrap(), expected);
|
2019-09-20 19:38:56 -07:00
|
|
|
} else if i <= 4 {
|
2019-11-04 15:44:27 -08:00
|
|
|
let mut expected = BlockCommitment::default();
|
2019-09-20 19:38:56 -07:00
|
|
|
expected.increase_confirmation_stake(2, lamports);
|
2019-11-04 15:44:27 -08:00
|
|
|
assert_eq!(*commitment.get(&a).unwrap(), expected);
|
2019-09-20 19:38:56 -07:00
|
|
|
} else if i <= 6 {
|
2019-11-04 15:44:27 -08:00
|
|
|
let mut expected = BlockCommitment::default();
|
2019-09-20 19:38:56 -07:00
|
|
|
expected.increase_confirmation_stake(1, lamports);
|
2019-11-04 15:44:27 -08:00
|
|
|
assert_eq!(*commitment.get(&a).unwrap(), expected);
|
2019-09-20 19:38:56 -07:00
|
|
|
}
|
|
|
|
}
|
2020-04-22 11:22:09 -07:00
|
|
|
assert_eq!(rooted_stake[0], (root, lamports));
|
2019-09-04 23:10:25 -07:00
|
|
|
}
|
2019-09-17 19:43:40 -07:00
|
|
|
|
|
|
|
#[test]
|
2019-11-04 15:44:27 -08:00
|
|
|
fn test_aggregate_commitment_validity() {
|
2019-09-20 19:38:56 -07:00
|
|
|
let ancestors = vec![3, 4, 5, 7, 9, 10, 11];
|
2019-11-08 20:56:57 -08:00
|
|
|
let GenesisConfigInfo {
|
|
|
|
mut genesis_config, ..
|
|
|
|
} = create_genesis_config(10_000);
|
2019-09-20 19:38:56 -07:00
|
|
|
|
2020-04-22 11:22:09 -07:00
|
|
|
let rooted_stake_amount = 40;
|
|
|
|
|
2019-09-26 13:29:29 -07:00
|
|
|
let sk1 = Pubkey::new_rand();
|
2019-09-20 19:38:56 -07:00
|
|
|
let pk1 = Pubkey::new_rand();
|
|
|
|
let mut vote_account1 = vote_state::create_account(&pk1, &Pubkey::new_rand(), 0, 100);
|
2019-11-12 12:33:40 -08:00
|
|
|
let stake_account1 =
|
|
|
|
stake_state::create_account(&sk1, &pk1, &vote_account1, &genesis_config.rent, 100);
|
2019-09-26 13:29:29 -07:00
|
|
|
let sk2 = Pubkey::new_rand();
|
2019-09-20 19:38:56 -07:00
|
|
|
let pk2 = Pubkey::new_rand();
|
|
|
|
let mut vote_account2 = vote_state::create_account(&pk2, &Pubkey::new_rand(), 0, 50);
|
2019-11-12 12:33:40 -08:00
|
|
|
let stake_account2 =
|
|
|
|
stake_state::create_account(&sk2, &pk2, &vote_account2, &genesis_config.rent, 50);
|
2020-04-22 11:22:09 -07:00
|
|
|
let sk3 = Pubkey::new_rand();
|
|
|
|
let pk3 = Pubkey::new_rand();
|
|
|
|
let mut vote_account3 = vote_state::create_account(&pk3, &Pubkey::new_rand(), 0, 1);
|
|
|
|
let stake_account3 = stake_state::create_account(
|
|
|
|
&sk3,
|
|
|
|
&pk3,
|
|
|
|
&vote_account3,
|
|
|
|
&genesis_config.rent,
|
|
|
|
rooted_stake_amount,
|
|
|
|
);
|
|
|
|
let sk4 = Pubkey::new_rand();
|
|
|
|
let pk4 = Pubkey::new_rand();
|
|
|
|
let mut vote_account4 = vote_state::create_account(&pk4, &Pubkey::new_rand(), 0, 1);
|
|
|
|
let stake_account4 = stake_state::create_account(
|
|
|
|
&sk4,
|
|
|
|
&pk4,
|
|
|
|
&vote_account4,
|
|
|
|
&genesis_config.rent,
|
|
|
|
rooted_stake_amount,
|
|
|
|
);
|
2019-09-20 19:38:56 -07:00
|
|
|
|
2019-11-08 20:56:57 -08:00
|
|
|
genesis_config.accounts.extend(vec![
|
2019-09-20 19:38:56 -07:00
|
|
|
(pk1, vote_account1.clone()),
|
2019-09-26 13:29:29 -07:00
|
|
|
(sk1, stake_account1),
|
2019-09-20 19:38:56 -07:00
|
|
|
(pk2, vote_account2.clone()),
|
2019-09-26 13:29:29 -07:00
|
|
|
(sk2, stake_account2),
|
2020-04-22 11:22:09 -07:00
|
|
|
(pk3, vote_account3.clone()),
|
|
|
|
(sk3, stake_account3),
|
|
|
|
(pk4, vote_account4.clone()),
|
|
|
|
(sk4, stake_account4),
|
2019-09-20 19:38:56 -07:00
|
|
|
]);
|
|
|
|
|
|
|
|
// Create bank
|
2019-11-08 20:56:57 -08:00
|
|
|
let bank = Arc::new(Bank::new(&genesis_config));
|
2019-09-20 19:38:56 -07:00
|
|
|
|
|
|
|
let mut vote_state1 = VoteState::from(&vote_account1).unwrap();
|
|
|
|
vote_state1.process_slot_vote_unchecked(3);
|
|
|
|
vote_state1.process_slot_vote_unchecked(5);
|
2020-02-25 17:12:01 -08:00
|
|
|
let versioned = VoteStateVersions::Current(Box::new(vote_state1));
|
|
|
|
VoteState::to(&versioned, &mut vote_account1).unwrap();
|
2019-09-20 19:38:56 -07:00
|
|
|
bank.store_account(&pk1, &vote_account1);
|
|
|
|
|
|
|
|
let mut vote_state2 = VoteState::from(&vote_account2).unwrap();
|
|
|
|
vote_state2.process_slot_vote_unchecked(9);
|
|
|
|
vote_state2.process_slot_vote_unchecked(10);
|
2020-02-25 17:12:01 -08:00
|
|
|
let versioned = VoteStateVersions::Current(Box::new(vote_state2));
|
|
|
|
VoteState::to(&versioned, &mut vote_account2).unwrap();
|
2019-09-20 19:38:56 -07:00
|
|
|
bank.store_account(&pk2, &vote_account2);
|
|
|
|
|
2020-04-22 11:22:09 -07:00
|
|
|
let mut vote_state3 = VoteState::from(&vote_account3).unwrap();
|
|
|
|
vote_state3.root_slot = Some(1);
|
|
|
|
let versioned = VoteStateVersions::Current(Box::new(vote_state3));
|
|
|
|
VoteState::to(&versioned, &mut vote_account3).unwrap();
|
|
|
|
bank.store_account(&pk3, &vote_account3);
|
|
|
|
|
|
|
|
let mut vote_state4 = VoteState::from(&vote_account4).unwrap();
|
|
|
|
vote_state4.root_slot = Some(2);
|
|
|
|
let versioned = VoteStateVersions::Current(Box::new(vote_state4));
|
|
|
|
VoteState::to(&versioned, &mut vote_account4).unwrap();
|
|
|
|
bank.store_account(&pk4, &vote_account4);
|
|
|
|
|
|
|
|
let (commitment, rooted_stake) =
|
|
|
|
AggregateCommitmentService::aggregate_commitment(&ancestors, &bank);
|
2019-09-20 19:38:56 -07:00
|
|
|
|
|
|
|
for a in ancestors {
|
|
|
|
if a <= 3 {
|
2019-11-04 15:44:27 -08:00
|
|
|
let mut expected = BlockCommitment::default();
|
2019-09-20 19:38:56 -07:00
|
|
|
expected.increase_confirmation_stake(2, 150);
|
2019-11-04 15:44:27 -08:00
|
|
|
assert_eq!(*commitment.get(&a).unwrap(), expected);
|
2019-09-20 19:38:56 -07:00
|
|
|
} else if a <= 5 {
|
2019-11-04 15:44:27 -08:00
|
|
|
let mut expected = BlockCommitment::default();
|
2019-09-20 19:38:56 -07:00
|
|
|
expected.increase_confirmation_stake(1, 100);
|
|
|
|
expected.increase_confirmation_stake(2, 50);
|
2019-11-04 15:44:27 -08:00
|
|
|
assert_eq!(*commitment.get(&a).unwrap(), expected);
|
2019-09-20 19:38:56 -07:00
|
|
|
} else if a <= 9 {
|
2019-11-04 15:44:27 -08:00
|
|
|
let mut expected = BlockCommitment::default();
|
2019-09-20 19:38:56 -07:00
|
|
|
expected.increase_confirmation_stake(2, 50);
|
2019-11-04 15:44:27 -08:00
|
|
|
assert_eq!(*commitment.get(&a).unwrap(), expected);
|
2019-09-20 19:38:56 -07:00
|
|
|
} else if a <= 10 {
|
2019-11-04 15:44:27 -08:00
|
|
|
let mut expected = BlockCommitment::default();
|
2019-09-20 19:38:56 -07:00
|
|
|
expected.increase_confirmation_stake(1, 50);
|
2019-11-04 15:44:27 -08:00
|
|
|
assert_eq!(*commitment.get(&a).unwrap(), expected);
|
2019-09-20 19:38:56 -07:00
|
|
|
} else {
|
2019-11-04 15:44:27 -08:00
|
|
|
assert!(commitment.get(&a).is_none());
|
2019-09-20 19:38:56 -07:00
|
|
|
}
|
|
|
|
}
|
2020-04-22 11:22:09 -07:00
|
|
|
assert_eq!(rooted_stake.len(), 2);
|
|
|
|
assert_eq!(get_largest_confirmed_root(rooted_stake, 100), 1)
|
2019-09-17 19:43:40 -07:00
|
|
|
}
|
2019-09-04 23:10:25 -07:00
|
|
|
}
|