Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow voting on empty banks #6719

Merged
merged 5 commits into from
Nov 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 3 additions & 5 deletions core/src/replay_stage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,9 @@ impl ReplayStage {
where
T: 'static + KeypairUtil + Send + Sync,
{
if bank.is_empty() {
inc_new_counter_info!("replay_stage-voted_empty_bank", 1);
}
trace!("handle votable bank {}", bank.slot());
let (vote, tower_index) = tower.new_vote_from_bank(bank, vote_account);
if let Some(new_root) = tower.record_bank_vote(vote) {
Expand Down Expand Up @@ -650,11 +653,6 @@ impl ReplayStage {
trace!("frozen_banks {}", frozen_banks.len());
let mut votable: Vec<(u128, Arc<Bank>, HashMap<u64, StakeLockout>, u64)> = frozen_banks
.values()
.filter(|b| {
let is_votable = b.is_votable();
trace!("bank is votable: {} {}", b.slot(), is_votable);
is_votable
})
.filter(|b| {
let has_voted = tower.has_voted(b.slot());
trace!("bank has_voted: {} {}", b.slot(), has_voted);
Expand Down
2 changes: 1 addition & 1 deletion ledger-tool/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ fn graph_forks(
"".to_string()
},
if first { "filled," } else { "" },
if !bank.is_votable() { "dotted," } else { "" }
""
));
styled_slots.insert(bank.slot());
}
Expand Down
46 changes: 43 additions & 3 deletions local_cluster/src/tests/local_cluster.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use log::*;
use serial_test_derive::serial;
use solana_client::thin_client::create_client;
use solana_core::{
broadcast_stage::BroadcastStageType, gossip_service::discover_cluster,
validator::ValidatorConfig,
broadcast_stage::BroadcastStageType, consensus::VOTE_THRESHOLD_DEPTH,
gossip_service::discover_cluster, validator::ValidatorConfig,
};
use solana_ledger::{bank_forks::SnapshotConfig, blocktree::Blocktree, snapshot_utils};
use solana_runtime::accounts_db::AccountsDB;
Expand Down Expand Up @@ -626,7 +626,47 @@ fn test_faulty_node(faulty_node_type: BroadcastStageType) {
}

#[test]
#[serial]
// Test that when a leader is leader for banks B_i..B_{i+n}, and B_i is not
// votable, then B_{i+1} still chains to B_i
fn test_no_voting() {
solana_logger::setup();
let mut validator_config = ValidatorConfig::default();
validator_config.rpc_config.enable_validator_exit = true;
validator_config.voting_disabled = true;
let config = ClusterConfig {
cluster_lamports: 10_000,
node_stakes: vec![100],
validator_configs: vec![validator_config.clone()],
..ClusterConfig::default()
};
let mut cluster = LocalCluster::new(&config);
let client = cluster
.get_validator_client(&cluster.entry_point_info.id)
.unwrap();
loop {
let last_slot = client.get_slot().expect("Couldn't get slot");
if last_slot > 4 * VOTE_THRESHOLD_DEPTH as u64 {
break;
}
sleep(Duration::from_secs(1));
}

cluster.close_preserve_ledgers();
let leader_pubkey = cluster.entry_point_info.id;
let ledger_path = cluster.validator_infos[&leader_pubkey]
.info
.ledger_path
.clone();
let ledger = Blocktree::open(&ledger_path).unwrap();
for i in 0..2 * VOTE_THRESHOLD_DEPTH {
let meta = ledger.meta(i as u64).unwrap().unwrap();
let parent = meta.parent_slot;
let expected_parent = i.saturating_sub(1);
assert_eq!(parent, expected_parent as u64);
}
}

#[test]
fn test_repairman_catchup() {
solana_logger::setup();
error!("test_repairman_catchup");
Expand Down
53 changes: 10 additions & 43 deletions runtime/src/bank.rs
Original file line number Diff line number Diff line change
Expand Up @@ -693,9 +693,6 @@ impl Bank {
// / ticks/slot
/ self.ticks_per_slot as f64;

// make bank 0 votable
self.is_delta.store(true, Ordering::Relaxed);

self.epoch_schedule = genesis_block.epoch_schedule;

self.inflation = genesis_block.inflation;
Expand Down Expand Up @@ -1529,8 +1526,8 @@ impl Bank {
self.epoch_schedule.get_epoch_and_slot_index(slot)
}

pub fn is_votable(&self) -> bool {
self.is_delta.load(Ordering::Relaxed) && self.tick_height() == self.max_tick_height
pub fn is_empty(&self) -> bool {
!self.is_delta.load(Ordering::Relaxed)
}

/// Add an instruction processor to intercept instructions before the dynamic loader.
Expand Down Expand Up @@ -1633,7 +1630,6 @@ mod tests {
clock::DEFAULT_TICKS_PER_SLOT,
epoch_schedule::MINIMUM_SLOTS_PER_EPOCH,
genesis_block::create_genesis_block,
hash,
instruction::InstructionError,
message::{Message, MessageHeader},
poh_config::PohConfig,
Expand Down Expand Up @@ -2935,38 +2931,19 @@ mod tests {
}

#[test]
fn test_is_votable() {
// test normal case
fn test_is_empty() {
let (genesis_block, mint_keypair) = create_genesis_block(500);
let bank = Arc::new(Bank::new(&genesis_block));
let bank0 = Arc::new(Bank::new(&genesis_block));
let key1 = Keypair::new();
assert_eq!(bank.is_votable(), false);

// Set is_delta to true
// The zeroth bank is empty becasue there are no transactions
assert_eq!(bank0.is_empty(), true);

// Set is_delta to true, bank is no longer empty
let tx_transfer_mint_to_1 =
system_transaction::transfer(&mint_keypair, &key1.pubkey(), 1, genesis_block.hash());
assert_eq!(bank.process_transaction(&tx_transfer_mint_to_1), Ok(()));
assert_eq!(bank.is_votable(), false);

// Register enough ticks to hit max tick height
for i in 0..genesis_block.ticks_per_slot {
bank.register_tick(&hash::hash(format!("hello world {}", i).as_bytes()));
}

assert_eq!(bank.is_votable(), true);

// test empty bank with ticks
let (genesis_block, _mint_keypair) = create_genesis_block(500);
// make an empty bank at slot 1
let bank = new_from_parent(&Arc::new(Bank::new(&genesis_block)));
assert_eq!(bank.is_votable(), false);

// Register enough ticks to hit max tick height
for i in 0..genesis_block.ticks_per_slot {
bank.register_tick(&hash::hash(format!("hello world {}", i).as_bytes()));
}
// empty banks aren't votable even at max tick height
assert_eq!(bank.is_votable(), false);
assert_eq!(bank0.process_transaction(&tx_transfer_mint_to_1), Ok(()));
assert_eq!(bank0.is_empty(), false);
}

#[test]
Expand Down Expand Up @@ -3068,16 +3045,6 @@ mod tests {
assert_eq!(vote_accounts.len(), 1);
}

#[test]
fn test_bank_0_votable() {
let (genesis_block, _) = create_genesis_block(500);
let bank = Arc::new(Bank::new(&genesis_block));
//set tick height to max
let max_tick_height = (bank.slot + 1) * bank.ticks_per_slot;
bank.tick_height.store(max_tick_height, Ordering::Relaxed);
assert!(bank.is_votable());
}

#[test]
fn test_bank_fees_account() {
let (mut genesis_block, _) = create_genesis_block(500);
Expand Down