2019-02-17 19:22:29 -08:00
|
|
|
//! The `leader_confirmation_service` module implements the tools necessary
|
2018-12-20 15:47:48 -08:00
|
|
|
//! to generate a thread which regularly calculates the last confirmation times
|
2018-11-02 15:49:14 -07:00
|
|
|
//! observed by the leader
|
|
|
|
|
2019-03-03 16:44:06 -08:00
|
|
|
use crate::poh_recorder::PohRecorder;
|
2018-11-16 08:45:59 -08:00
|
|
|
use solana_metrics::{influxdb, submit};
|
2019-02-18 22:26:22 -08:00
|
|
|
use solana_runtime::bank::Bank;
|
2018-11-16 08:45:59 -08:00
|
|
|
use solana_sdk::timing;
|
2019-03-02 13:51:26 -08:00
|
|
|
use solana_vote_api::vote_state::VoteState;
|
2018-11-02 15:49:14 -07:00
|
|
|
use std::result;
|
|
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
2019-03-03 16:44:06 -08:00
|
|
|
use std::sync::{Arc, Mutex};
|
2018-11-02 15:49:14 -07:00
|
|
|
use std::thread::sleep;
|
2019-03-03 16:44:06 -08:00
|
|
|
use std::thread::{Builder, JoinHandle};
|
2018-11-02 15:49:14 -07:00
|
|
|
use std::time::Duration;
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq)]
|
2018-12-20 15:47:48 -08:00
|
|
|
pub enum ConfirmationError {
|
2018-11-02 15:49:14 -07:00
|
|
|
NoValidSupermajority,
|
|
|
|
}
|
|
|
|
|
2018-12-20 15:47:48 -08:00
|
|
|
pub const COMPUTE_CONFIRMATION_MS: u64 = 100;
|
2019-03-03 16:44:06 -08:00
|
|
|
pub struct LeaderConfirmationService {}
|
2018-11-02 15:49:14 -07:00
|
|
|
|
2019-02-17 19:22:29 -08:00
|
|
|
impl LeaderConfirmationService {
|
2018-11-02 15:49:14 -07:00
|
|
|
fn get_last_supermajority_timestamp(
|
2019-03-03 16:44:06 -08:00
|
|
|
bank: &Bank,
|
2018-11-02 15:49:14 -07:00
|
|
|
last_valid_validator_timestamp: u64,
|
2018-12-20 15:47:48 -08:00
|
|
|
) -> result::Result<u64, ConfirmationError> {
|
2018-11-02 15:49:14 -07:00
|
|
|
let mut total_stake = 0;
|
2019-03-02 13:23:55 -08:00
|
|
|
let mut slots_and_stakes: Vec<(u64, u64)> = vec![];
|
2019-01-13 10:47:29 -08:00
|
|
|
// Hold an accounts_db read lock as briefly as possible, just long enough to collect all
|
|
|
|
// the vote states
|
2019-03-02 13:23:55 -08:00
|
|
|
bank.vote_accounts().for_each(|(_, account)| {
|
2019-03-05 16:28:14 -08:00
|
|
|
total_stake += account.lamports;
|
2019-03-02 13:23:55 -08:00
|
|
|
let vote_state = VoteState::deserialize(&account.userdata).unwrap();
|
|
|
|
if let Some(stake_and_state) = vote_state
|
|
|
|
.votes
|
|
|
|
.back()
|
2019-03-05 16:28:14 -08:00
|
|
|
.map(|vote| (vote.slot, account.lamports))
|
2019-03-02 13:23:55 -08:00
|
|
|
{
|
|
|
|
slots_and_stakes.push(stake_and_state);
|
|
|
|
}
|
|
|
|
});
|
2018-11-02 15:49:14 -07:00
|
|
|
|
|
|
|
let super_majority_stake = (2 * total_stake) / 3;
|
|
|
|
|
|
|
|
if let Some(last_valid_validator_timestamp) =
|
2019-03-01 13:13:32 -08:00
|
|
|
bank.get_confirmation_timestamp(slots_and_stakes, super_majority_stake)
|
2018-11-02 15:49:14 -07:00
|
|
|
{
|
|
|
|
return Ok(last_valid_validator_timestamp);
|
|
|
|
}
|
|
|
|
|
|
|
|
if last_valid_validator_timestamp != 0 {
|
2019-02-08 13:23:28 -08:00
|
|
|
let now = timing::timestamp();
|
2018-11-16 08:45:59 -08:00
|
|
|
submit(
|
2018-12-20 15:47:48 -08:00
|
|
|
influxdb::Point::new(&"leader-confirmation")
|
2018-11-02 15:49:14 -07:00
|
|
|
.add_field(
|
|
|
|
"duration_ms",
|
|
|
|
influxdb::Value::Integer((now - last_valid_validator_timestamp) as i64),
|
2018-12-07 19:01:28 -08:00
|
|
|
)
|
|
|
|
.to_owned(),
|
2018-11-02 15:49:14 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-12-20 15:47:48 -08:00
|
|
|
Err(ConfirmationError::NoValidSupermajority)
|
2018-11-02 15:49:14 -07:00
|
|
|
}
|
|
|
|
|
2019-03-03 16:44:06 -08:00
|
|
|
pub fn compute_confirmation(bank: &Bank, last_valid_validator_timestamp: &mut u64) {
|
2019-02-21 01:43:57 -08:00
|
|
|
if let Ok(super_majority_timestamp) =
|
2019-03-02 13:23:55 -08:00
|
|
|
Self::get_last_supermajority_timestamp(bank, *last_valid_validator_timestamp)
|
2019-02-21 01:43:57 -08:00
|
|
|
{
|
2019-02-08 13:23:28 -08:00
|
|
|
let now = timing::timestamp();
|
2018-12-20 15:47:48 -08:00
|
|
|
let confirmation_ms = now - super_majority_timestamp;
|
2018-11-02 15:49:14 -07:00
|
|
|
|
|
|
|
*last_valid_validator_timestamp = super_majority_timestamp;
|
|
|
|
|
2018-11-16 08:45:59 -08:00
|
|
|
submit(
|
2018-12-20 15:47:48 -08:00
|
|
|
influxdb::Point::new(&"leader-confirmation")
|
|
|
|
.add_field(
|
|
|
|
"duration_ms",
|
|
|
|
influxdb::Value::Integer(confirmation_ms as i64),
|
|
|
|
)
|
2018-11-02 15:49:14 -07:00
|
|
|
.to_owned(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-17 19:22:29 -08:00
|
|
|
/// Create a new LeaderConfirmationService for computing confirmation.
|
2019-03-03 16:44:06 -08:00
|
|
|
pub fn start(poh_recorder: &Arc<Mutex<PohRecorder>>, exit: Arc<AtomicBool>) -> JoinHandle<()> {
|
|
|
|
let poh_recorder = poh_recorder.clone();
|
|
|
|
Builder::new()
|
2019-02-21 00:44:37 -08:00
|
|
|
.name("solana-leader-confirmation-service".to_string())
|
2018-11-02 15:49:14 -07:00
|
|
|
.spawn(move || {
|
|
|
|
let mut last_valid_validator_timestamp = 0;
|
|
|
|
loop {
|
|
|
|
if exit.load(Ordering::Relaxed) {
|
|
|
|
break;
|
|
|
|
}
|
2019-03-03 16:44:06 -08:00
|
|
|
// dont hold this lock too long
|
|
|
|
let maybe_bank = poh_recorder.lock().unwrap().bank();
|
|
|
|
if let Some(ref bank) = maybe_bank {
|
|
|
|
Self::compute_confirmation(bank, &mut last_valid_validator_timestamp);
|
|
|
|
}
|
2018-12-20 15:47:48 -08:00
|
|
|
sleep(Duration::from_millis(COMPUTE_CONFIRMATION_MS));
|
2018-11-02 15:49:14 -07:00
|
|
|
}
|
2018-12-07 19:01:28 -08:00
|
|
|
})
|
2019-03-03 16:44:06 -08:00
|
|
|
.unwrap()
|
2018-11-02 15:49:14 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
2019-02-23 21:00:55 -08:00
|
|
|
mod tests {
|
2019-02-17 19:22:29 -08:00
|
|
|
use super::*;
|
2019-02-23 21:00:55 -08:00
|
|
|
use crate::voting_keypair::tests::{new_vote_account, push_vote};
|
2018-11-02 15:49:14 -07:00
|
|
|
use bincode::serialize;
|
2019-02-18 22:26:22 -08:00
|
|
|
use solana_sdk::genesis_block::GenesisBlock;
|
2018-11-16 08:04:46 -08:00
|
|
|
use solana_sdk::hash::hash;
|
2019-03-05 14:18:29 -08:00
|
|
|
use solana_sdk::pubkey::Pubkey;
|
2018-12-03 10:26:28 -08:00
|
|
|
use solana_sdk::signature::{Keypair, KeypairUtil};
|
2019-03-02 13:51:26 -08:00
|
|
|
use solana_vote_api::vote_transaction::VoteTransaction;
|
2018-11-02 15:49:14 -07:00
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
|
|
#[test]
|
2018-12-20 15:47:48 -08:00
|
|
|
fn test_compute_confirmation() {
|
2018-12-14 12:36:50 -08:00
|
|
|
solana_logger::setup();
|
2018-11-02 15:49:14 -07:00
|
|
|
|
2019-01-24 12:04:04 -08:00
|
|
|
let (genesis_block, mint_keypair) = GenesisBlock::new(1234);
|
2019-03-05 14:18:29 -08:00
|
|
|
let mut tick_hash = genesis_block.hash();
|
|
|
|
|
|
|
|
let mut bank = Arc::new(Bank::new(&genesis_block));
|
2019-03-01 14:52:27 -08:00
|
|
|
|
|
|
|
// Move the bank up 10 slots
|
2019-03-05 14:18:29 -08:00
|
|
|
for slot in 1..=10 {
|
|
|
|
let max_tick_height = slot * bank.ticks_per_slot() - 1;
|
|
|
|
|
|
|
|
while bank.tick_height() != max_tick_height {
|
|
|
|
tick_hash = hash(&serialize(&tick_hash).unwrap());
|
|
|
|
bank.register_tick(&tick_hash);
|
|
|
|
}
|
|
|
|
|
2019-03-09 19:28:43 -08:00
|
|
|
bank = Arc::new(Bank::new_from_parent(&bank, &Pubkey::default(), slot));
|
2019-03-01 14:52:27 -08:00
|
|
|
}
|
2019-03-05 14:18:29 -08:00
|
|
|
|
2019-03-02 10:25:16 -08:00
|
|
|
let blockhash = bank.last_blockhash();
|
2018-11-02 15:49:14 -07:00
|
|
|
|
|
|
|
// Create a total of 10 vote accounts, each will have a balance of 1 (after giving 1 to
|
2019-03-05 16:28:14 -08:00
|
|
|
// their vote account), for a total staking pool of 10 lamports.
|
2018-11-02 15:49:14 -07:00
|
|
|
let vote_accounts: Vec<_> = (0..10)
|
|
|
|
.map(|i| {
|
|
|
|
// Create new validator to vote
|
2019-01-10 09:21:38 -08:00
|
|
|
let validator_keypair = Arc::new(Keypair::new());
|
2019-03-05 07:52:53 -08:00
|
|
|
let voting_keypair = Keypair::new();
|
2019-02-20 09:29:29 -08:00
|
|
|
let voting_pubkey = voting_keypair.pubkey();
|
2018-11-02 15:49:14 -07:00
|
|
|
|
2019-03-05 16:28:14 -08:00
|
|
|
// Give the validator some lamports
|
2019-03-09 19:28:43 -08:00
|
|
|
bank.transfer(2, &mint_keypair, &validator_keypair.pubkey(), blockhash)
|
2018-11-02 15:49:14 -07:00
|
|
|
.unwrap();
|
2019-02-20 09:29:29 -08:00
|
|
|
new_vote_account(&validator_keypair, &voting_pubkey, &bank, 1);
|
2018-11-02 15:49:14 -07:00
|
|
|
|
|
|
|
if i < 6 {
|
2019-02-20 09:29:29 -08:00
|
|
|
push_vote(&voting_keypair, &bank, (i + 1) as u64);
|
2018-11-02 15:49:14 -07:00
|
|
|
}
|
2019-01-31 20:12:51 -08:00
|
|
|
(voting_keypair, validator_keypair)
|
2018-12-07 19:01:28 -08:00
|
|
|
})
|
|
|
|
.collect();
|
2018-11-02 15:49:14 -07:00
|
|
|
|
2018-12-20 15:47:48 -08:00
|
|
|
// There isn't 2/3 consensus, so the bank's confirmation value should be the default
|
|
|
|
let mut last_confirmation_time = 0;
|
2019-03-02 13:23:55 -08:00
|
|
|
LeaderConfirmationService::compute_confirmation(&bank, &mut last_confirmation_time);
|
2019-03-01 14:52:27 -08:00
|
|
|
assert_eq!(last_confirmation_time, 0);
|
2018-11-02 15:49:14 -07:00
|
|
|
|
|
|
|
// Get another validator to vote, so we now have 2/3 consensus
|
2019-01-31 20:12:51 -08:00
|
|
|
let voting_keypair = &vote_accounts[7].0;
|
2019-03-08 18:29:08 -08:00
|
|
|
let vote_tx =
|
2019-03-09 19:28:43 -08:00
|
|
|
VoteTransaction::new_vote(&voting_keypair.pubkey(), voting_keypair, 7, blockhash, 0);
|
2018-11-02 15:49:14 -07:00
|
|
|
bank.process_transaction(&vote_tx).unwrap();
|
|
|
|
|
2019-03-02 13:23:55 -08:00
|
|
|
LeaderConfirmationService::compute_confirmation(&bank, &mut last_confirmation_time);
|
2018-12-20 15:47:48 -08:00
|
|
|
assert!(last_confirmation_time > 0);
|
2018-11-02 15:49:14 -07:00
|
|
|
}
|
|
|
|
}
|