2018-12-20 15:47:48 -08:00
|
|
|
//! The `compute_leader_confirmation_service` module implements the tools necessary
|
|
|
|
//! to generate a thread which regularly calculates the last confirmation times
|
2018-11-02 15:49:14 -07:00
|
|
|
//! observed by the leader
|
|
|
|
|
2018-12-07 19:16:27 -08:00
|
|
|
use crate::bank::Bank;
|
2018-11-16 08:45:59 -08:00
|
|
|
|
2018-12-07 19:16:27 -08:00
|
|
|
use crate::service::Service;
|
2018-11-16 08:45:59 -08:00
|
|
|
use solana_metrics::{influxdb, submit};
|
2018-12-08 16:54:42 -08:00
|
|
|
use solana_sdk::pubkey::Pubkey;
|
2018-11-16 08:45:59 -08:00
|
|
|
use solana_sdk::timing;
|
2018-12-04 15:05:41 -08:00
|
|
|
use solana_sdk::vote_program::{self, VoteProgram};
|
2018-11-02 15:49:14 -07:00
|
|
|
use std::result;
|
|
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
|
|
|
use std::sync::Arc;
|
|
|
|
use std::thread::sleep;
|
|
|
|
use std::thread::{self, Builder, JoinHandle};
|
|
|
|
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;
|
2018-11-02 15:49:14 -07:00
|
|
|
|
2018-12-20 15:47:48 -08:00
|
|
|
pub struct ComputeLeaderConfirmationService {
|
|
|
|
compute_confirmation_thread: JoinHandle<()>,
|
2018-11-02 15:49:14 -07:00
|
|
|
}
|
|
|
|
|
2018-12-20 15:47:48 -08:00
|
|
|
impl ComputeLeaderConfirmationService {
|
2018-11-02 15:49:14 -07:00
|
|
|
fn get_last_supermajority_timestamp(
|
|
|
|
bank: &Arc<Bank>,
|
2018-12-08 16:54:42 -08:00
|
|
|
leader_id: Pubkey,
|
2018-11-02 15:49:14 -07:00
|
|
|
now: u64,
|
|
|
|
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;
|
|
|
|
|
2018-11-05 08:36:22 -08:00
|
|
|
let mut ticks_and_stakes: Vec<(u64, u64)> = {
|
2018-12-17 12:41:23 -08:00
|
|
|
let bank_accounts = bank.accounts.accounts_db.read().unwrap();
|
2018-11-02 15:49:14 -07:00
|
|
|
// TODO: Doesn't account for duplicates since a single validator could potentially register
|
|
|
|
// multiple vote accounts. Once that is no longer possible (see the TODO in vote_program.rs,
|
|
|
|
// process_transaction(), case VoteInstruction::RegisterAccount), this will be more accurate.
|
|
|
|
// See github issue 1654.
|
|
|
|
bank_accounts
|
2018-12-17 12:41:23 -08:00
|
|
|
.accounts
|
|
|
|
.values()
|
2018-11-02 15:49:14 -07:00
|
|
|
.filter_map(|account| {
|
|
|
|
// Filter out any accounts that don't belong to the VoteProgram
|
|
|
|
// by returning None
|
2018-11-23 12:45:34 -08:00
|
|
|
if vote_program::check_id(&account.owner) {
|
2018-11-02 15:49:14 -07:00
|
|
|
if let Ok(vote_state) = VoteProgram::deserialize(&account.userdata) {
|
2018-12-08 16:54:42 -08:00
|
|
|
if leader_id == vote_state.node_id {
|
|
|
|
return None;
|
|
|
|
}
|
2018-11-02 15:49:14 -07:00
|
|
|
let validator_stake = bank.get_stake(&vote_state.node_id);
|
|
|
|
total_stake += validator_stake;
|
|
|
|
// Filter out any validators that don't have at least one vote
|
|
|
|
// by returning None
|
|
|
|
return vote_state
|
|
|
|
.votes
|
|
|
|
.back()
|
|
|
|
.map(|vote| (vote.tick_height, validator_stake));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
2018-12-07 19:01:28 -08:00
|
|
|
})
|
|
|
|
.collect()
|
2018-11-02 15:49:14 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
let super_majority_stake = (2 * total_stake) / 3;
|
|
|
|
|
|
|
|
if let Some(last_valid_validator_timestamp) =
|
2018-12-20 15:47:48 -08:00
|
|
|
bank.get_confirmation_timestamp(&mut ticks_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 {
|
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
|
|
|
}
|
|
|
|
|
2018-12-20 15:47:48 -08:00
|
|
|
pub fn compute_confirmation(
|
2018-12-08 16:54:42 -08:00
|
|
|
bank: &Arc<Bank>,
|
|
|
|
leader_id: Pubkey,
|
|
|
|
last_valid_validator_timestamp: &mut u64,
|
|
|
|
) {
|
2018-11-02 15:49:14 -07:00
|
|
|
let now = timing::timestamp();
|
2018-12-08 16:54:42 -08:00
|
|
|
if let Ok(super_majority_timestamp) = Self::get_last_supermajority_timestamp(
|
|
|
|
bank,
|
|
|
|
leader_id,
|
|
|
|
now,
|
|
|
|
*last_valid_validator_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-12-21 08:25:07 -08:00
|
|
|
bank.set_confirmation_time((now - *last_valid_validator_timestamp) as usize);
|
2018-11-02 15:49:14 -07:00
|
|
|
|
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(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-20 15:47:48 -08:00
|
|
|
/// Create a new ComputeLeaderConfirmationService for computing confirmation.
|
2018-12-08 16:54:42 -08:00
|
|
|
pub fn new(bank: Arc<Bank>, leader_id: Pubkey, exit: Arc<AtomicBool>) -> Self {
|
2018-12-20 15:47:48 -08:00
|
|
|
let compute_confirmation_thread = Builder::new()
|
|
|
|
.name("solana-leader-confirmation-stage".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;
|
|
|
|
}
|
2018-12-20 15:47:48 -08:00
|
|
|
Self::compute_confirmation(
|
|
|
|
&bank,
|
|
|
|
leader_id,
|
|
|
|
&mut last_valid_validator_timestamp,
|
|
|
|
);
|
|
|
|
sleep(Duration::from_millis(COMPUTE_CONFIRMATION_MS));
|
2018-11-02 15:49:14 -07:00
|
|
|
}
|
2018-12-07 19:01:28 -08:00
|
|
|
})
|
|
|
|
.unwrap();
|
2018-11-02 15:49:14 -07:00
|
|
|
|
2018-12-20 15:47:48 -08:00
|
|
|
(ComputeLeaderConfirmationService {
|
|
|
|
compute_confirmation_thread,
|
2018-11-02 15:49:14 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-20 15:47:48 -08:00
|
|
|
impl Service for ComputeLeaderConfirmationService {
|
2018-11-02 15:49:14 -07:00
|
|
|
type JoinReturnType = ();
|
|
|
|
|
|
|
|
fn join(self) -> thread::Result<()> {
|
2018-12-20 15:47:48 -08:00
|
|
|
self.compute_confirmation_thread.join()
|
2018-11-02 15:49:14 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
pub mod tests {
|
2018-12-07 19:16:27 -08:00
|
|
|
use crate::bank::Bank;
|
2018-12-20 15:47:48 -08:00
|
|
|
use crate::compute_leader_confirmation_service::ComputeLeaderConfirmationService;
|
2018-12-07 19:16:27 -08:00
|
|
|
use crate::create_vote_account::*;
|
2018-12-14 12:36:50 -08:00
|
|
|
|
2018-12-07 19:16:27 -08:00
|
|
|
use crate::mint::Mint;
|
2019-01-05 12:57:52 -08:00
|
|
|
use crate::rpc_request::RpcClient;
|
|
|
|
use crate::vote_stage::create_new_signed_vote_transaction;
|
2018-11-02 15:49:14 -07:00
|
|
|
use bincode::serialize;
|
2018-11-16 08:04:46 -08:00
|
|
|
use solana_sdk::hash::hash;
|
2018-12-03 10:26:28 -08:00
|
|
|
use solana_sdk::signature::{Keypair, KeypairUtil};
|
2018-11-02 15:49:14 -07:00
|
|
|
use std::sync::Arc;
|
|
|
|
use std::thread::sleep;
|
|
|
|
use std::time::Duration;
|
|
|
|
|
|
|
|
#[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
|
|
|
|
|
|
|
let mint = Mint::new(1234);
|
2018-12-08 16:54:42 -08:00
|
|
|
let dummy_leader_id = Keypair::new().pubkey();
|
2018-11-02 15:49:14 -07:00
|
|
|
let bank = Arc::new(Bank::new(&mint));
|
|
|
|
// generate 10 validators, but only vote for the first 6 validators
|
|
|
|
let ids: Vec<_> = (0..10)
|
|
|
|
.map(|i| {
|
|
|
|
let last_id = hash(&serialize(&i).unwrap()); // Unique hash
|
2018-11-05 09:47:41 -08:00
|
|
|
bank.register_tick(&last_id);
|
2018-11-02 15:49:14 -07:00
|
|
|
// sleep to get a different timestamp in the bank
|
|
|
|
sleep(Duration::from_millis(1));
|
|
|
|
last_id
|
2018-12-07 19:01:28 -08:00
|
|
|
})
|
|
|
|
.collect();
|
2018-11-02 15:49:14 -07:00
|
|
|
|
2019-01-05 12:57:52 -08:00
|
|
|
let (signer, t_signer, signer_exit) = local_vote_signer_service().unwrap();
|
|
|
|
let rpc_client = RpcClient::new_from_socket(signer);
|
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
|
|
|
|
// their vote account), for a total staking pool of 10 tokens.
|
|
|
|
let vote_accounts: Vec<_> = (0..10)
|
|
|
|
.map(|i| {
|
|
|
|
// Create new validator to vote
|
|
|
|
let validator_keypair = Keypair::new();
|
|
|
|
let last_id = ids[i];
|
|
|
|
|
|
|
|
// Give the validator some tokens
|
|
|
|
bank.transfer(2, &mint.keypair(), validator_keypair.pubkey(), last_id)
|
|
|
|
.unwrap();
|
2019-01-05 12:57:52 -08:00
|
|
|
let vote_account =
|
|
|
|
create_vote_account(&validator_keypair, &bank, 1, last_id, &rpc_client)
|
|
|
|
.expect("Expected successful creation of account");
|
2018-11-02 15:49:14 -07:00
|
|
|
|
2019-01-05 12:57:52 -08:00
|
|
|
let validator_keypair = Arc::new(validator_keypair);
|
2018-11-02 15:49:14 -07:00
|
|
|
if i < 6 {
|
2019-01-05 12:57:52 -08:00
|
|
|
let vote_tx = create_new_signed_vote_transaction(
|
|
|
|
&last_id,
|
|
|
|
&validator_keypair,
|
|
|
|
(i + 1) as u64,
|
|
|
|
&vote_account,
|
|
|
|
&rpc_client,
|
|
|
|
);
|
2018-11-02 15:49:14 -07:00
|
|
|
bank.process_transaction(&vote_tx).unwrap();
|
|
|
|
}
|
2019-01-05 12:57:52 -08:00
|
|
|
(vote_account, 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;
|
|
|
|
ComputeLeaderConfirmationService::compute_confirmation(
|
2018-12-08 16:54:42 -08:00
|
|
|
&bank,
|
|
|
|
dummy_leader_id,
|
2018-12-20 15:47:48 -08:00
|
|
|
&mut last_confirmation_time,
|
2018-12-08 16:54:42 -08:00
|
|
|
);
|
2018-12-21 08:25:07 -08:00
|
|
|
assert_eq!(bank.confirmation_time(), std::usize::MAX);
|
2018-11-02 15:49:14 -07:00
|
|
|
|
|
|
|
// Get another validator to vote, so we now have 2/3 consensus
|
2019-01-05 12:57:52 -08:00
|
|
|
let vote_account = &vote_accounts[7].0;
|
|
|
|
let vote_tx = create_new_signed_vote_transaction(
|
|
|
|
&ids[6],
|
|
|
|
&vote_accounts[7].1,
|
|
|
|
7,
|
|
|
|
&vote_account,
|
|
|
|
&rpc_client,
|
|
|
|
);
|
2018-11-02 15:49:14 -07:00
|
|
|
bank.process_transaction(&vote_tx).unwrap();
|
|
|
|
|
2018-12-20 15:47:48 -08:00
|
|
|
ComputeLeaderConfirmationService::compute_confirmation(
|
2018-12-08 16:54:42 -08:00
|
|
|
&bank,
|
|
|
|
dummy_leader_id,
|
2018-12-20 15:47:48 -08:00
|
|
|
&mut last_confirmation_time,
|
2018-12-08 16:54:42 -08:00
|
|
|
);
|
2018-12-21 08:25:07 -08:00
|
|
|
assert!(bank.confirmation_time() != std::usize::MAX);
|
2018-12-20 15:47:48 -08:00
|
|
|
assert!(last_confirmation_time > 0);
|
2019-01-05 12:57:52 -08:00
|
|
|
stop_local_vote_signer_service(t_signer, &signer_exit);
|
2018-11-02 15:49:14 -07:00
|
|
|
}
|
|
|
|
}
|