diff --git a/ledger-tool/tests/basic.rs b/ledger-tool/tests/basic.rs index 388ef3e79e..6a804e442c 100644 --- a/ledger-tool/tests/basic.rs +++ b/ledger-tool/tests/basic.rs @@ -1,5 +1,5 @@ use assert_cmd::prelude::*; -use solana::blocktree::create_tmp_sample_blocktree; +use solana::blocktree::create_new_tmp_ledger; use solana_sdk::genesis_block::GenesisBlock; use solana_sdk::signature::{Keypair, KeypairUtil}; use std::process::Command; @@ -34,12 +34,10 @@ fn nominal() { let keypair = Arc::new(Keypair::new()); let (genesis_block, _mint_keypair) = GenesisBlock::new_with_leader(100, keypair.pubkey(), 50); let ticks_per_slot = genesis_block.ticks_per_slot; - let (ledger_path, tick_height, _last_entry_height, _last_id, _last_entry_id) = - create_tmp_sample_blocktree( - "test_ledger_tool_nominal", - &genesis_block, - ticks_per_slot - 2, - ); + + let (ledger_path, _last_id) = + create_new_tmp_ledger("test_ledger_tool_nominal", &genesis_block).unwrap(); + let ticks = ticks_per_slot as usize; // Basic validation let output = run_ledger_tool(&["-l", &ledger_path, "verify"]); @@ -48,7 +46,7 @@ fn nominal() { // Print everything let output = run_ledger_tool(&["-l", &ledger_path, "print"]); assert!(output.status.success()); - assert_eq!(count_newlines(&output.stdout), tick_height as usize); + assert_eq!(count_newlines(&output.stdout), ticks); // Only print the first 5 items let output = run_ledger_tool(&["-l", &ledger_path, "-n", "5", "print"]); @@ -58,7 +56,7 @@ fn nominal() { // Skip entries with no hashes let output = run_ledger_tool(&["-l", &ledger_path, "-h", "1", "print"]); assert!(output.status.success()); - assert_eq!(count_newlines(&output.stdout), tick_height as usize); + assert_eq!(count_newlines(&output.stdout), ticks); // Skip entries with fewer than 2 hashes (skip everything) let output = run_ledger_tool(&["-l", &ledger_path, "-h", "2", "print"]); diff --git a/src/blocktree.rs b/src/blocktree.rs index f1ba0f687d..e819f6c00d 100644 --- a/src/blocktree.rs +++ b/src/blocktree.rs @@ -1237,23 +1237,20 @@ impl Iterator for EntryIterator { } } -// Returns a tuple (entry_height, tick_height, last_id), corresponding to the -// total number of entries, the number of ticks, and the last id generated in the -// new ledger -pub fn create_new_ledger( - ledger_path: &str, - genesis_block: &GenesisBlock, -) -> Result<(u64, u64, Hash)> { +// Creates a new ledger with slot 0 full of ticks (and only ticks). +// +// Returns the last_id that can be used to start slot 1 entries with. +pub fn create_new_ledger(ledger_path: &str, genesis_block: &GenesisBlock) -> Result { let ticks_per_slot = genesis_block.ticks_per_slot; Blocktree::destroy(ledger_path)?; genesis_block.write(&ledger_path)?; - // Add a single tick linked back to the genesis_block to bootstrap the ledger + // Fill slot 0 with ticks that link back to the genesis_block to bootstrap the ledger. let blocktree = Blocktree::open_config(ledger_path, ticks_per_slot)?; - let entries = crate::entry::create_ticks(1, genesis_block.last_id()); + let entries = crate::entry::create_ticks(genesis_block.ticks_per_slot, genesis_block.last_id()); blocktree.write_entries(0, 0, 0, &entries)?; - Ok((1, 1, entries[0].id)) + Ok(entries.last().unwrap().id) } pub fn genesis<'a, I>(ledger_path: &str, entries: I) -> Result<()> @@ -1306,37 +1303,13 @@ pub fn get_tmp_ledger_path(name: &str) -> String { path } -pub fn create_tmp_sample_blocktree( - name: &str, - genesis_block: &GenesisBlock, - num_extra_ticks: u64, -) -> (String, u64, u64, Hash, Hash) { - let ticks_per_slot = genesis_block.ticks_per_slot; - +// Same as `create_new_ledger()` but use a temporary ledger name based on the provided `name` +// +// Note: like `create_new_ledger` the returned ledger will have slot 0 full of ticks (and only +// ticks) +pub fn create_new_tmp_ledger(name: &str, genesis_block: &GenesisBlock) -> Result<(String, Hash)> { let ledger_path = get_tmp_ledger_path(name); - let (mut entry_height, mut tick_height, mut last_entry_id) = - create_new_ledger(&ledger_path, &genesis_block).unwrap(); - - let mut last_id = genesis_block.last_id(); - if num_extra_ticks > 0 { - let entries = crate::entry::create_ticks(num_extra_ticks, last_entry_id); - - let blocktree = Blocktree::open_config(&ledger_path, ticks_per_slot).unwrap(); - blocktree - .write_entries(0, tick_height, entry_height, &entries) - .unwrap(); - tick_height += num_extra_ticks; - entry_height += entries.len() as u64; - last_id = entries.last().unwrap().id; - last_entry_id = last_id; - } - ( - ledger_path, - tick_height, - entry_height, - last_id, - last_entry_id, - ) + create_new_ledger(&ledger_path, genesis_block).and_then(|last_id| Ok((ledger_path, last_id))) } #[macro_export] diff --git a/src/blocktree_processor.rs b/src/blocktree_processor.rs index 7c0a619cc8..a8b7e7d9a2 100644 --- a/src/blocktree_processor.rs +++ b/src/blocktree_processor.rs @@ -149,7 +149,7 @@ pub fn process_blocktree( if slot == 0 { // The first entry in the ledger is a pseudo-tick used only to ensure the number of ticks // in slot 0 is the same as the number of ticks in all subsequent slots. It is not - // registered as a tick and thus cannot be used as a last_id + // processed by the bank, skip over it. if entries.is_empty() { warn!("entry0 not present"); return Err(BankError::LedgerVerificationFailed); @@ -164,7 +164,6 @@ pub fn process_blocktree( entries = entries.drain(1..).collect(); } - // Feed the entries into the bank for this slot if !entries.is_empty() { if !entries.verify(&last_entry_id) { warn!("Ledger proof of history failed at entry: {}", entry_height); @@ -228,7 +227,7 @@ pub fn process_blocktree( #[cfg(test)] mod tests { use super::*; - use crate::blocktree::create_tmp_sample_blocktree; + use crate::blocktree::create_new_tmp_ledger; use crate::blocktree::tests::entries_to_blobs; use crate::entry::{create_ticks, next_entry, Entry}; use solana_sdk::genesis_block::GenesisBlock; @@ -258,32 +257,45 @@ mod tests { let (genesis_block, _mint_keypair) = GenesisBlock::new(10_000); let ticks_per_slot = genesis_block.ticks_per_slot; - // Create a new ledger with slot 0 full of ticks - let (ledger_path, tick_height, _entry_height, _last_id, last_entry_id) = - create_tmp_sample_blocktree( - "blocktree_with_incomplete_slot", - &genesis_block, - ticks_per_slot - 2, // last tick missing in slot 0 - ); - debug!("ledger_path: {:?}", ledger_path); - assert_eq!(tick_height, ticks_per_slot - 1); - /* Build a blocktree in the ledger with the following fork structure: slot 0 | slot 1 + | + slot 2 - where slot 0 is incomplete + where slot 1 is incomplete (missing 1 tick at the end) */ + // Create a new ledger with slot 0 full of ticks + let (ledger_path, mut last_id) = + create_new_tmp_ledger("blocktree_with_two_forks", &genesis_block).unwrap(); + debug!("ledger_path: {:?}", ledger_path); + let blocktree = Blocktree::open_config(&ledger_path, ticks_per_slot) .expect("Expected to successfully open database ledger"); - // slot 1, points at slot 0 - let _last_slot1_entry_id = - fill_blocktree_slot_with_ticks(&blocktree, ticks_per_slot, 1, 0, last_entry_id); + let expected_last_entry_id; + + // Write slot 1 + // slot 1, points at slot 0. Missing one tick + { + let parent_slot = 0; + let slot = 1; + let mut entries = create_ticks(ticks_per_slot, last_id); + last_id = entries.last().unwrap().id; + + entries.pop(); + expected_last_entry_id = entries.last().unwrap().id; + + let blobs = entries_to_blobs(&entries, slot, parent_slot); + blocktree.insert_data_blobs(blobs.iter()).unwrap(); + } + + // slot 2, points at slot 1 + fill_blocktree_slot_with_ticks(&blocktree, ticks_per_slot, 2, 1, last_id); let (mut _bank_forks, bank_forks_info) = process_blocktree(&genesis_block, &blocktree).unwrap(); @@ -292,9 +304,9 @@ mod tests { assert_eq!( bank_forks_info[0], BankForksInfo { - bank_id: 0, // never finished first slot - entry_height: ticks_per_slot - 1, - last_entry_id: last_entry_id, + bank_id: 1, // never finished first slot + entry_height: 2 * ticks_per_slot - 1, + last_entry_id: expected_last_entry_id, next_blob_index: ticks_per_slot - 1, } ); @@ -308,14 +320,10 @@ mod tests { let ticks_per_slot = genesis_block.ticks_per_slot; // Create a new ledger with slot 0 full of ticks - let (ledger_path, tick_height, _entry_height, _last_id, mut last_entry_id) = - create_tmp_sample_blocktree( - "blocktree_with_two_forks", - &genesis_block, - ticks_per_slot - 1, - ); + let (ledger_path, last_id) = + create_new_tmp_ledger("blocktree_with_two_forks", &genesis_block).unwrap(); debug!("ledger_path: {:?}", ledger_path); - assert_eq!(tick_height, ticks_per_slot); + let mut last_entry_id = last_id; /* Build a blocktree in the ledger with the following fork structure: @@ -439,11 +447,12 @@ mod tests { fn test_process_ledger_simple() { let leader_pubkey = Keypair::new().pubkey(); let (genesis_block, mint_keypair) = GenesisBlock::new_with_leader(100, leader_pubkey, 50); - let (ledger_path, tick_height, mut entry_height, mut last_id, mut last_entry_id) = - create_tmp_sample_blocktree("process_ledger_simple", &genesis_block, 0); + let (ledger_path, last_id) = + create_new_tmp_ledger("process_ledger_simple", &genesis_block).unwrap(); debug!("ledger_path: {:?}", ledger_path); let mut entries = vec![]; + let mut last_entry_id = last_id; for _ in 0..3 { // Transfer one token from the mint to a random account let keypair = Keypair::new(); @@ -461,37 +470,30 @@ mod tests { entries.push(entry); } - // Add a tick for good measure - let tick = Entry::new(&last_entry_id, 1, vec![]); - last_entry_id = tick.id; - last_id = last_entry_id; - entries.push(tick); + // Fill up the rest of slot 1 with ticks + entries.extend(create_ticks(genesis_block.ticks_per_slot, last_entry_id)); let blocktree = Blocktree::open(&ledger_path).expect("Expected to successfully open database ledger"); - - blocktree - .write_entries(0, tick_height, entry_height, &entries) - .unwrap(); - entry_height += entries.len() as u64; - + blocktree.write_entries(1, 0, 0, &entries).unwrap(); + let entry_height = genesis_block.ticks_per_slot + entries.len() as u64; let (bank_forks, bank_forks_info) = process_blocktree(&genesis_block, &blocktree).unwrap(); assert_eq!(bank_forks_info.len(), 1); assert_eq!( bank_forks_info[0], BankForksInfo { - bank_id: 0, + bank_id: 1, entry_height, - last_entry_id, - next_blob_index: entry_height, + last_entry_id: entries.last().unwrap().id, + next_blob_index: entries.len() as u64, } ); let bank = bank_forks.working_bank(); assert_eq!(bank.get_balance(&mint_keypair.pubkey()), 50 - 3); - assert_eq!(bank.tick_height(), 1); - assert_eq!(bank.last_id(), last_id); + assert_eq!(bank.tick_height(), 2 * genesis_block.ticks_per_slot - 1); + assert_eq!(bank.last_id(), entries.last().unwrap().id); } #[test] diff --git a/src/fullnode.rs b/src/fullnode.rs index fdc66dd780..11f8591a8a 100644 --- a/src/fullnode.rs +++ b/src/fullnode.rs @@ -445,28 +445,20 @@ impl Service for Fullnode { } } -// Create entries such the node identified by active_keypair -// will be added to the active set for leader selection: -// 1) Give the node a nonzero number of tokens, -// 2) A vote from the validator +// Create entries such the node identified by active_keypair will be added to the active set for +// leader selection, and append `num_ending_ticks` empty tick entries. pub fn make_active_set_entries( active_keypair: &Arc, token_source: &Keypair, stake: u64, slot_height_to_vote_on: u64, - last_entry_id: &Hash, - last_tick_id: &Hash, + last_id: &Hash, num_ending_ticks: u64, ) -> (Vec, VotingKeypair) { // 1) Assume the active_keypair node has no tokens staked - let transfer_tx = SystemTransaction::new_account( - &token_source, - active_keypair.pubkey(), - stake, - *last_tick_id, - 0, - ); - let mut last_entry_id = *last_entry_id; + let transfer_tx = + SystemTransaction::new_account(&token_source, active_keypair.pubkey(), stake, *last_id, 0); + let mut last_entry_id = *last_id; let transfer_entry = next_entry_mut(&mut last_entry_id, 1, vec![transfer_tx]); // 2) Create and register a vote account for active_keypair @@ -474,25 +466,25 @@ pub fn make_active_set_entries( let vote_account_id = voting_keypair.pubkey(); let new_vote_account_tx = - VoteTransaction::new_account(active_keypair, vote_account_id, *last_tick_id, 1, 1); + VoteTransaction::new_account(active_keypair, vote_account_id, *last_id, 1, 1); let new_vote_account_entry = next_entry_mut(&mut last_entry_id, 1, vec![new_vote_account_tx]); // 3) Create vote entry - let vote_tx = - VoteTransaction::new_vote(&voting_keypair, slot_height_to_vote_on, *last_tick_id, 0); + let vote_tx = VoteTransaction::new_vote(&voting_keypair, slot_height_to_vote_on, *last_id, 0); let vote_entry = next_entry_mut(&mut last_entry_id, 1, vec![vote_tx]); - // 4) Create the ending empty ticks - let mut txs = vec![transfer_entry, new_vote_account_entry, vote_entry]; + // 4) Create `num_ending_ticks` empty ticks + let mut entries = vec![transfer_entry, new_vote_account_entry, vote_entry]; let empty_ticks = create_ticks(num_ending_ticks, last_entry_id); - txs.extend(empty_ticks); - (txs, voting_keypair) + entries.extend(empty_ticks); + + (entries, voting_keypair) } #[cfg(test)] mod tests { use super::*; - use crate::blocktree::{create_tmp_sample_blocktree, tmp_copy_blocktree}; + use crate::blocktree::{create_new_tmp_ledger, tmp_copy_blocktree}; use crate::entry::make_consecutive_blobs; use crate::streamer::responder; use solana_sdk::hash::Hash; @@ -509,8 +501,8 @@ mod tests { let validator_node = Node::new_localhost_with_pubkey(validator_keypair.pubkey()); let (genesis_block, _mint_keypair) = GenesisBlock::new_with_leader(10_000, leader_keypair.pubkey(), 1000); - let (validator_ledger_path, _tick_height, _last_entry_height, _last_id, _last_entry_id) = - create_tmp_sample_blocktree("validator_exit", &genesis_block, 0); + let (validator_ledger_path, _last_id) = + create_new_tmp_ledger("validator_exit", &genesis_block).unwrap(); let validator = Fullnode::new( validator_node, @@ -536,17 +528,11 @@ mod tests { let validator_node = Node::new_localhost_with_pubkey(validator_keypair.pubkey()); let (genesis_block, _mint_keypair) = GenesisBlock::new_with_leader(10_000, leader_keypair.pubkey(), 1000); - let ( - validator_ledger_path, - _tick_height, - _last_entry_height, - _last_id, - _last_entry_id, - ) = create_tmp_sample_blocktree( + let (validator_ledger_path, _last_id) = create_new_tmp_ledger( &format!("validator_parallel_exit_{}", i), &genesis_block, - 0, - ); + ) + .unwrap(); ledger_paths.push(validator_ledger_path.clone()); Fullnode::new( validator_node, @@ -594,13 +580,8 @@ mod tests { genesis_block.ticks_per_slot = ticks_per_slot; genesis_block.slots_per_epoch = slots_per_epoch; - let ( - bootstrap_leader_ledger_path, - _tick_height, - _genesis_entry_height, - _last_id, - _last_entry_id, - ) = create_tmp_sample_blocktree("test_leader_to_leader_transition", &genesis_block, 1); + let (bootstrap_leader_ledger_path, _last_id) = + create_new_tmp_ledger("test_leader_to_leader_transition", &genesis_block).unwrap(); // Start the bootstrap leader let bootstrap_leader = Fullnode::new( @@ -617,10 +598,6 @@ mod tests { // Wait for the bootstrap leader to transition. Since there are no other nodes in the // cluster it will continue to be the leader - assert_eq!( - rotation_receiver.recv().unwrap(), - (FullnodeReturnType::LeaderToLeaderRotation, 0) - ); assert_eq!( rotation_receiver.recv().unwrap(), (FullnodeReturnType::LeaderToLeaderRotation, 1) @@ -630,7 +607,7 @@ mod tests { #[test] #[ignore] - fn test_wrong_role_transition() { + fn test_ledger_role_transition() { solana_logger::setup(); let fullnode_config = FullnodeConfig::default(); @@ -642,14 +619,11 @@ mod tests { let validator_keypair = Arc::new(Keypair::new()); let (bootstrap_leader_node, validator_node, bootstrap_leader_ledger_path, _, _) = setup_leader_validator( + "test_wrong_role_transition", &bootstrap_leader_keypair, &validator_keypair, - 0, - // Generate enough ticks for an epochs to flush the bootstrap_leader's vote at - // tick_height = 0 from the leader scheduler's active window - ticks_per_slot * slots_per_epoch, - "test_wrong_role_transition", ticks_per_slot, + 0, ); let bootstrap_leader_info = bootstrap_leader_node.info.clone(); @@ -723,12 +697,11 @@ mod tests { let fullnode_config = FullnodeConfig::default(); let (leader_node, validator_node, validator_ledger_path, ledger_initial_len, last_id) = setup_leader_validator( + "test_validator_to_leader_transition", &leader_keypair, &validator_keypair, - 0, - 0, - "test_validator_to_leader_transition", ticks_per_slot, + 0, ); let leader_id = leader_keypair.pubkey(); @@ -800,50 +773,39 @@ mod tests { } fn setup_leader_validator( + test_name: &str, leader_keypair: &Arc, validator_keypair: &Arc, - num_genesis_ticks: u64, - num_ending_ticks: u64, - test_name: &str, ticks_per_slot: u64, + num_ending_slots: u64, ) -> (Node, Node, String, u64, Hash) { info!("validator: {}", validator_keypair.pubkey()); info!("leader: {}", leader_keypair.pubkey()); - // Make a leader identity - let leader_node = Node::new_localhost_with_pubkey(leader_keypair.pubkey()); - // Create validator identity - assert!(num_genesis_ticks <= ticks_per_slot); + let leader_node = Node::new_localhost_with_pubkey(leader_keypair.pubkey()); + let validator_node = Node::new_localhost_with_pubkey(validator_keypair.pubkey()); let (mut genesis_block, mint_keypair) = GenesisBlock::new_with_leader(10_000, leader_node.info.id, 500); genesis_block.ticks_per_slot = ticks_per_slot; - let (ledger_path, tick_height, mut entry_height, last_id, last_entry_id) = - create_tmp_sample_blocktree(test_name, &genesis_block, num_genesis_ticks); + let (ledger_path, last_id) = create_new_tmp_ledger(test_name, &genesis_block).unwrap(); - let validator_node = Node::new_localhost_with_pubkey(validator_keypair.pubkey()); - - // Write two entries so that the validator is in the active set: - let (active_set_entries, _) = make_active_set_entries( + // Add entries so that the validator is in the active set, then finish up the slot with + // ticks (and maybe add extra slots full of empty ticks) + let (entries, _) = make_active_set_entries( validator_keypair, &mint_keypair, 10, 0, - &last_entry_id, &last_id, - num_ending_ticks, + ticks_per_slot * (num_ending_slots + 1), ); let blocktree = Blocktree::open_config(&ledger_path, ticks_per_slot).unwrap(); - let active_set_entries_len = active_set_entries.len() as u64; - let last_id = active_set_entries.last().unwrap().id; - - blocktree - .write_entries(0, tick_height, entry_height, active_set_entries) - .unwrap(); - - entry_height += active_set_entries_len; + let last_id = entries.last().unwrap().id; + let entry_height = ticks_per_slot + entries.len() as u64; + blocktree.write_entries(1, 0, 0, entries).unwrap(); ( leader_node, diff --git a/src/replay_stage.rs b/src/replay_stage.rs index a876880a0a..469d452510 100644 --- a/src/replay_stage.rs +++ b/src/replay_stage.rs @@ -462,7 +462,7 @@ impl Service for ReplayStage { #[cfg(test)] mod test { use super::*; - use crate::blocktree::{create_tmp_sample_blocktree, Blocktree}; + use crate::blocktree::create_new_tmp_ledger; use crate::cluster_info::{ClusterInfo, Node}; use crate::entry::create_ticks; use crate::entry::{next_entry_mut, Entry}; @@ -473,134 +473,10 @@ mod test { use solana_sdk::hash::Hash; use solana_sdk::signature::{Keypair, KeypairUtil}; use std::fs::remove_dir_all; - use std::sync::atomic::{AtomicBool, Ordering}; + use std::sync::atomic::AtomicBool; use std::sync::mpsc::channel; use std::sync::{Arc, RwLock}; - #[test] - #[ignore] // TODO: Fix this test to not send all entries in slot 0 - pub fn test_replay_stage_leader_rotation_exit() { - solana_logger::setup(); - - // Set up dummy node to host a ReplayStage - let my_keypair = Keypair::new(); - let my_id = my_keypair.pubkey(); - let my_node = Node::new_localhost_with_pubkey(my_id); - let cluster_info_me = ClusterInfo::new(my_node.info.clone()); - - // Create keypair for the old leader - let old_leader_id = Keypair::new().pubkey(); - - // Set up the LeaderScheduler so that my_id becomes the leader for epoch 1 - let ticks_per_slot = 16; - let (mut genesis_block, mint_keypair) = - GenesisBlock::new_with_leader(10_000, old_leader_id, 500); - genesis_block.ticks_per_slot = ticks_per_slot; - genesis_block.slots_per_epoch = 1; - - // Create a ledger - let (my_ledger_path, mut tick_height, entry_height, mut last_id, last_entry_id) = - create_tmp_sample_blocktree( - "test_replay_stage_leader_rotation_exit", - &genesis_block, - 0, - ); - - info!("my_id: {:?}", my_id); - info!("old_leader_id: {:?}", old_leader_id); - - let my_keypair = Arc::new(my_keypair); - let num_ending_ticks = 0; - let (active_set_entries, voting_keypair) = make_active_set_entries( - &my_keypair, - &mint_keypair, - 100, - 1, // add a vote for tick_height = ticks_per_slot - &last_entry_id, - &last_id, - num_ending_ticks, - ); - last_id = active_set_entries.last().unwrap().id; - - { - let blocktree = Blocktree::open(&my_ledger_path).unwrap(); - blocktree - .write_entries(0, tick_height, entry_height, active_set_entries) - .unwrap(); - tick_height += num_ending_ticks; - } - - { - // Set up the bank - let (bank_forks, bank_forks_info, blocktree, ledger_signal_receiver) = - new_banks_from_blocktree(&my_ledger_path); - - // Set up the replay stage - let (rotation_sender, rotation_receiver) = channel(); - let exit = Arc::new(AtomicBool::new(false)); - let blocktree = Arc::new(blocktree); - let (replay_stage, ledger_writer_recv) = ReplayStage::new( - my_id, - Some(Arc::new(voting_keypair)), - blocktree.clone(), - &Arc::new(RwLock::new(bank_forks)), - &bank_forks_info, - Arc::new(RwLock::new(cluster_info_me)), - exit.clone(), - &rotation_sender, - ledger_signal_receiver, - &Arc::new(RpcSubscriptions::default()), - ); - - let total_entries_to_send = 2 * ticks_per_slot as usize - 2; - let mut entries_to_send = vec![]; - while entries_to_send.len() < total_entries_to_send { - let entry = next_entry_mut(&mut last_id, 1, vec![]); - entries_to_send.push(entry); - } - - // Write the entries to the ledger, replay_stage should get notified of changes - let meta = blocktree.meta(0).unwrap().unwrap(); - blocktree - .write_entries(0, tick_height, meta.consumed, &entries_to_send) - .unwrap(); - - info!("Wait for replay_stage to exit and check return value is correct"); - let rotation_info = rotation_receiver - .recv() - .expect("should have signaled leader rotation"); - assert_eq!( - rotation_info.last_entry_id, - bank_forks_info[0].last_entry_id - ); - assert_eq!(rotation_info.slot, 2); - assert_eq!(rotation_info.leader_id, my_keypair.pubkey()); - - info!("Check that the entries on the ledger writer channel are correct"); - let mut received_ticks = ledger_writer_recv - .recv() - .expect("Expected to receive an entry on the ledger writer receiver"); - - while let Ok(entries) = ledger_writer_recv.try_recv() { - received_ticks.extend(entries); - } - let received_ticks_entries: Vec = received_ticks - .iter() - .map(|entry_meta| entry_meta.entry.clone()) - .collect(); - assert_eq!(&received_ticks_entries[..], &entries_to_send[..]); - - // Replay stage should continue running even after rotation has happened (tvu never goes down) - assert_eq!(exit.load(Ordering::Relaxed), false); - - info!("Close replay_stage"); - replay_stage - .close() - .expect("Expect successful ReplayStage exit"); - } - let _ignored = remove_dir_all(&my_ledger_path); - } - #[test] fn test_vote_error_replay_stage_correctness() { // Set up dummy node to host a ReplayStage @@ -613,12 +489,9 @@ mod test { let (genesis_block, _mint_keypair) = GenesisBlock::new_with_leader(10_000, leader_id, 500); - let (my_ledger_path, tick_height, _last_entry_height, _last_id, _last_entry_id) = - create_tmp_sample_blocktree( - "test_vote_error_replay_stage_correctness", - &genesis_block, - 1, - ); + let (my_ledger_path, _last_id) = + create_new_tmp_ledger("test_vote_error_replay_stage_correctness", &genesis_block) + .unwrap(); // Set up the cluster info let cluster_info_me = Arc::new(RwLock::new(ClusterInfo::new(my_node.info.clone()))); @@ -631,7 +504,6 @@ mod test { let (bank_forks, bank_forks_info, blocktree, l_receiver) = new_banks_from_blocktree(&my_ledger_path); let bank = bank_forks.working_bank(); - let entry_height = bank_forks_info[0].entry_height; let last_entry_id = bank_forks_info[0].last_entry_id; let blocktree = Arc::new(blocktree); @@ -654,9 +526,7 @@ mod test { info!("Send ReplayStage an entry, should see it on the ledger writer receiver"); let next_tick = create_ticks(1, last_entry_id); - blocktree - .write_entries(0, tick_height, entry_height, next_tick.clone()) - .unwrap(); + blocktree.write_entries(1, 0, 0, next_tick.clone()).unwrap(); let received_tick = ledger_writer_recv .recv() @@ -671,137 +541,6 @@ mod test { let _ignored = remove_dir_all(&my_ledger_path); } - #[test] - #[ignore] - fn test_vote_error_replay_stage_leader_rotation() { - solana_logger::setup(); - - let ticks_per_slot = 10; - let slots_per_epoch = 2; - let active_window_tick_length = ticks_per_slot * slots_per_epoch; - - // Set up dummy node to host a ReplayStage - let my_keypair = Keypair::new(); - let my_id = my_keypair.pubkey(); - let my_node = Node::new_localhost_with_pubkey(my_id); - - // Create keypair for the leader - let leader_id = Keypair::new().pubkey(); - - let (mut genesis_block, mint_keypair) = - GenesisBlock::new_with_leader(10_000, leader_id, 500); - genesis_block.ticks_per_slot = ticks_per_slot; - - // Create the ledger - let (my_ledger_path, tick_height, genesis_entry_height, last_id, last_entry_id) = - create_tmp_sample_blocktree( - "test_vote_error_replay_stage_leader_rotation", - &genesis_block, - 1, - ); - - let my_keypair = Arc::new(my_keypair); - // Write two entries to the ledger so that the validator is in the active set: - // 1) Give the validator a nonzero number of tokens 2) A vote from the validator. - // This will cause leader rotation after the bootstrap height - let (active_set_entries, voting_keypair) = make_active_set_entries( - &my_keypair, - &mint_keypair, - 100, - 1, - &last_entry_id, - &last_id, - 0, - ); - let mut last_id = active_set_entries.last().unwrap().id; - { - let blocktree = Blocktree::open_config(&my_ledger_path, ticks_per_slot).unwrap(); - blocktree - .write_entries(0, tick_height, genesis_entry_height, &active_set_entries) - .unwrap(); - } - - // Set up the cluster info - let cluster_info_me = Arc::new(RwLock::new(ClusterInfo::new(my_node.info.clone()))); - - // Set up the replay stage - let (rotation_sender, rotation_receiver) = channel(); - let exit = Arc::new(AtomicBool::new(false)); - { - let (bank_forks, bank_forks_info, blocktree, l_receiver) = - new_banks_from_blocktree(&my_ledger_path); - let bank = bank_forks.working_bank(); - let meta = blocktree - .meta(0) - .unwrap() - .expect("First slot metadata must exist"); - - let voting_keypair = Arc::new(voting_keypair); - let blocktree = Arc::new(blocktree); - let (replay_stage, ledger_writer_recv) = ReplayStage::new( - my_keypair.pubkey(), - Some(voting_keypair.clone()), - blocktree.clone(), - &Arc::new(RwLock::new(bank_forks)), - &bank_forks_info, - cluster_info_me.clone(), - exit.clone(), - &rotation_sender, - l_receiver, - &Arc::new(RpcSubscriptions::default()), - ); - - let keypair = voting_keypair.as_ref(); - let vote = VoteTransaction::new_vote(keypair, 0, bank.last_id(), 0); - cluster_info_me.write().unwrap().push_vote(vote); - - // Send enough ticks to trigger leader rotation - let total_entries_to_send = (active_window_tick_length - tick_height) as usize; - let num_hashes = 1; - - let leader_rotation_index = (active_window_tick_length - tick_height - 1) as usize; - let mut expected_last_id = Hash::default(); - for i in 0..total_entries_to_send { - let entry = next_entry_mut(&mut last_id, num_hashes, vec![]); - blocktree - .write_entries( - 0, - tick_height + i as u64, - meta.consumed + i as u64, - vec![entry.clone()], - ) - .expect("Expected successful database write"); - // Check that the entries on the ledger writer channel are correct - let received_entry = ledger_writer_recv - .recv() - .expect("Expected to recieve an entry on the ledger writer receiver"); - assert_eq!(received_entry[0].entry, entry); - - if i == leader_rotation_index { - expected_last_id = entry.id; - } - } - - // Wait for replay_stage to exit and check return value is correct - let rotation_info = rotation_receiver - .recv() - .expect("should have signaled leader rotation"); - assert_eq!( - rotation_info.last_entry_id, - bank_forks_info[0].last_entry_id - ); - assert_eq!(rotation_info.slot, 1); - assert_eq!(rotation_info.leader_id, my_keypair.pubkey()); - - assert_ne!(expected_last_id, Hash::default()); - //replay stage should continue running even after rotation has happened (tvu never goes down) - replay_stage - .close() - .expect("Expect successful ReplayStage exit"); - } - let _ignored = remove_dir_all(&my_ledger_path); - } - #[test] fn test_replay_stage_poh_error_entry_receiver() { // Set up dummy node to host a ReplayStage diff --git a/src/storage_stage.rs b/src/storage_stage.rs index 9c45066c44..4260a1a327 100644 --- a/src/storage_stage.rs +++ b/src/storage_stage.rs @@ -448,7 +448,7 @@ impl Service for StorageStage { #[cfg(test)] mod tests { - use crate::blocktree::{create_tmp_sample_blocktree, Blocktree}; + use crate::blocktree::{create_new_tmp_ledger, Blocktree}; use crate::cluster_info::{ClusterInfo, NodeInfo}; use crate::entry::{make_tiny_test_entries, Entry, EntryMeta}; use crate::service::Service; @@ -509,14 +509,12 @@ mod tests { let (genesis_block, _mint_keypair) = GenesisBlock::new(1000); let ticks_per_slot = genesis_block.ticks_per_slot; - let (ledger_path, tick_height, genesis_entry_height, _last_id, _last_entry_id) = - create_tmp_sample_blocktree("storage_stage_process_entries", &genesis_block, 1); + let (ledger_path, _last_id) = + create_new_tmp_ledger("storage_stage_process_entries", &genesis_block).unwrap(); let entries = make_tiny_test_entries(64); let blocktree = Blocktree::open_config(&ledger_path, ticks_per_slot).unwrap(); - blocktree - .write_entries(0, tick_height, genesis_entry_height, &entries) - .unwrap(); + blocktree.write_entries(1, 0, 0, &entries).unwrap(); let cluster_info = test_cluster_info(keypair.pubkey()); @@ -575,14 +573,12 @@ mod tests { let (genesis_block, _mint_keypair) = GenesisBlock::new(1000); let ticks_per_slot = genesis_block.ticks_per_slot;; - let (ledger_path, tick_height, genesis_entry_height, _last_id, _last_entry_id) = - create_tmp_sample_blocktree("storage_stage_process_entries", &genesis_block, 1); + let (ledger_path, _last_id) = + create_new_tmp_ledger("storage_stage_process_entries", &genesis_block).unwrap(); let entries = make_tiny_test_entries(128); let blocktree = Blocktree::open_config(&ledger_path, ticks_per_slot).unwrap(); - blocktree - .write_entries(0, tick_height, genesis_entry_height, &entries) - .unwrap(); + blocktree.write_entries(1, 0, 0, &entries).unwrap(); let cluster_info = test_cluster_info(keypair.pubkey()); diff --git a/src/thin_client.rs b/src/thin_client.rs index ea30671888..301b9df211 100644 --- a/src/thin_client.rs +++ b/src/thin_client.rs @@ -457,7 +457,7 @@ pub fn retry_get_balance( } pub fn new_fullnode(ledger_name: &'static str) -> (Fullnode, NodeInfo, Keypair, String) { - use crate::blocktree::create_tmp_sample_blocktree; + use crate::blocktree::create_new_tmp_ledger; use crate::cluster_info::Node; use crate::fullnode::Fullnode; use crate::voting_keypair::VotingKeypair; @@ -469,9 +469,7 @@ pub fn new_fullnode(ledger_name: &'static str) -> (Fullnode, NodeInfo, Keypair, let node_info = node.info.clone(); let (genesis_block, mint_keypair) = GenesisBlock::new_with_leader(10_000, node_info.id, 42); - - let (ledger_path, _tick_height, _last_entry_height, _last_id, _last_entry_id) = - create_tmp_sample_blocktree(ledger_name, &genesis_block, genesis_block.ticks_per_slot); + let (ledger_path, _last_id) = create_new_tmp_ledger(ledger_name, &genesis_block).unwrap(); let vote_account_keypair = Arc::new(Keypair::new()); let voting_keypair = VotingKeypair::new_local(&vote_account_keypair); @@ -651,8 +649,10 @@ mod tests { let mut client = mk_client(&leader_data); let last_id = client.get_last_id(); + info!("test_thin_client last_id: {:?}", last_id); - let starting_balance = client.poll_get_balance(&alice.pubkey()).unwrap(); + let starting_alice_balance = client.poll_get_balance(&alice.pubkey()).unwrap(); + info!("Alice has {} tokens", starting_alice_balance); info!("Give Bob 500 tokens"); let signature = client @@ -660,20 +660,21 @@ mod tests { .unwrap(); client.poll_for_signature(&signature).unwrap(); - let balance = client.poll_get_balance(&bob_keypair.pubkey()); - assert_eq!(balance.unwrap(), 500); + let bob_balance = client.poll_get_balance(&bob_keypair.pubkey()); + assert_eq!(bob_balance.unwrap(), 500); info!("Take Bob's 500 tokens away"); let signature = client .transfer(500, &bob_keypair, alice.pubkey(), &last_id) .unwrap(); client.poll_for_signature(&signature).unwrap(); - let balance = client.poll_get_balance(&alice.pubkey()).unwrap(); - assert_eq!(balance, starting_balance); + let alice_balance = client.poll_get_balance(&alice.pubkey()).unwrap(); + assert_eq!(alice_balance, starting_alice_balance); info!("Should get an error when Bob's balance hits zero and is purged"); - let balance = client.poll_get_balance(&bob_keypair.pubkey()); - assert!(balance.is_err()); + let bob_balance = client.poll_get_balance(&bob_keypair.pubkey()); + info!("Bob's balance is {:?}", bob_balance); + assert!(bob_balance.is_err(),); server_exit(); remove_dir_all(ledger_path).unwrap(); diff --git a/tests/multinode.rs b/tests/multinode.rs index e5e698d4d5..c032a1ffa2 100644 --- a/tests/multinode.rs +++ b/tests/multinode.rs @@ -3,7 +3,7 @@ extern crate solana; use log::*; use solana::blob_fetch_stage::BlobFetchStage; -use solana::blocktree::{create_tmp_sample_blocktree, tmp_copy_blocktree, Blocktree}; +use solana::blocktree::{create_new_tmp_ledger, tmp_copy_blocktree, Blocktree}; use solana::client::mk_client; use solana::cluster_info::{Node, NodeInfo}; use solana::entry::{reconstruct_entries_from_blobs, Entry}; @@ -52,8 +52,8 @@ fn test_multi_node_ledger_window() -> result::Result<()> { let ticks_per_slot = genesis_block.ticks_per_slot; info!("ticks_per_slot: {}", ticks_per_slot); - let (leader_ledger_path, tick_height, mut last_entry_height, _last_id, mut last_entry_id) = - create_tmp_sample_blocktree("multi_node_ledger_window", &genesis_block, 0); + let (leader_ledger_path, last_id) = + create_new_tmp_ledger("multi_node_ledger_window", &genesis_block).unwrap(); ledger_paths.push(leader_ledger_path.clone()); // make a copy at zero @@ -62,25 +62,10 @@ fn test_multi_node_ledger_window() -> result::Result<()> { // Write some into leader's ledger, this should populate the leader's window // and force it to respond to repair from the ledger window - // TODO: write out more than slot 0 { let blocktree = Blocktree::open_config(&leader_ledger_path, ticks_per_slot).unwrap(); - - let entries = solana::entry::create_ticks( - genesis_block.ticks_per_slot - last_entry_height - 1, - last_entry_id, - ); - blocktree - .write_entries(0, tick_height, last_entry_height, &entries) - .unwrap(); - - last_entry_height += entries.len() as u64; - last_entry_id = entries.last().unwrap().id; - - info!( - "Final last_entry_height: {}, last_entry_id: {:?}", - last_entry_height, last_entry_id - ); + let entries = solana::entry::create_ticks(genesis_block.ticks_per_slot, last_id); + blocktree.write_entries(1, 0, 0, &entries).unwrap(); } let fullnode_config = FullnodeConfig::default(); @@ -168,9 +153,8 @@ fn test_multi_node_validator_catchup_from_zero() -> result::Result<()> { let mut ledger_paths = Vec::new(); let (genesis_block, alice) = GenesisBlock::new_with_leader(10_000, leader_data.id, 500); - - let (genesis_ledger_path, _tick_height, _last_entry_height, _last_id, _last_entry_id) = - create_tmp_sample_blocktree("multi_node_validator_catchup_from_zero", &genesis_block, 0); + let (genesis_ledger_path, _last_id) = + create_new_tmp_ledger("multi_node_validator_catchup_from_zero", &genesis_block).unwrap(); ledger_paths.push(genesis_ledger_path.clone()); let zero_ledger_path = tmp_copy_blocktree!(&genesis_ledger_path); @@ -346,8 +330,8 @@ fn test_multi_node_basic() { let (genesis_block, alice) = GenesisBlock::new_with_leader(10_000, leader_data.id, 500); - let (genesis_ledger_path, _tick_height, _last_entry_height, _last_id, _last_entry_id) = - create_tmp_sample_blocktree("multi_node_basic", &genesis_block, 0); + let (genesis_ledger_path, _last_id) = + create_new_tmp_ledger("multi_node_basic", &genesis_block).unwrap(); ledger_paths.push(genesis_ledger_path.clone()); let leader_ledger_path = tmp_copy_blocktree!(&genesis_ledger_path); @@ -448,9 +432,8 @@ fn test_boot_validator_from_file() { let mut ledger_paths = Vec::new(); let (genesis_block, alice) = GenesisBlock::new_with_leader(100_000, leader_pubkey, 1000); - - let (genesis_ledger_path, _tick_height, _last_entry_height, _last_id, _last_entry_id) = - create_tmp_sample_blocktree("boot_validator_from_file", &genesis_block, 0); + let (genesis_ledger_path, _last_id) = + create_new_tmp_ledger("boot_validator_from_file", &genesis_block).unwrap(); ledger_paths.push(genesis_ledger_path.clone()); let leader_ledger_path = tmp_copy_blocktree!(&genesis_ledger_path); @@ -500,8 +483,8 @@ fn test_boot_validator_from_file() { // TODO: it would be nice to determine the slot that the leader processed the transactions // in, and only wait for that slot here let expected_rotations = vec![ - (FullnodeReturnType::LeaderToValidatorRotation, 0), (FullnodeReturnType::LeaderToValidatorRotation, 1), + (FullnodeReturnType::LeaderToValidatorRotation, 2), ]; for expected_rotation in expected_rotations { @@ -562,13 +545,11 @@ fn test_leader_restart_validator_start_from_old_ledger() -> result::Result<()> { leader_keypair.pubkey(), initial_leader_balance, ); - - let (ledger_path, _tick_height, _last_entry_height, _last_id, _last_entry_id) = - create_tmp_sample_blocktree( - "leader_restart_validator_start_from_old_ledger", - &genesis_block, - 0, - ); + let (ledger_path, _last_id) = create_new_tmp_ledger( + "leader_restart_validator_start_from_old_ledger", + &genesis_block, + ) + .unwrap(); let bob_pubkey = Keypair::new().pubkey(); @@ -676,9 +657,8 @@ fn test_multi_node_dynamic_network() { let bob_pubkey = Keypair::new().pubkey(); let (genesis_block, alice) = GenesisBlock::new_with_leader(10_000_000, leader_pubkey, 500); - - let (genesis_ledger_path, _tick_height, _last_entry_height, _last_id, _last_entry_id) = - create_tmp_sample_blocktree("multi_node_dynamic_network", &genesis_block, 0); + let (genesis_ledger_path, _last_id) = + create_new_tmp_ledger("multi_node_dynamic_network", &genesis_block).unwrap(); let mut ledger_paths = Vec::new(); ledger_paths.push(genesis_ledger_path.clone()); @@ -891,24 +871,23 @@ fn test_leader_to_validator_transition() { // Initialize the leader ledger. Make a mint and a genesis entry // in the leader ledger - let (leader_ledger_path, tick_height, genesis_entry_height, last_id, last_entry_id) = - create_tmp_sample_blocktree("test_leader_to_validator_transition", &genesis_block, 0); + let (leader_ledger_path, last_id) = + create_new_tmp_ledger("test_leader_to_validator_transition", &genesis_block).unwrap(); // Write the votes entries to the ledger that will cause leader rotation // to validator_keypair at slot 2 - let (active_set_entries, _) = make_active_set_entries( - &validator_keypair, - &mint_keypair, - 100, - 1, - &last_entry_id, - &last_id, - 0, - ); { let blocktree = Blocktree::open_config(&leader_ledger_path, ticks_per_slot).unwrap(); + let (active_set_entries, _) = make_active_set_entries( + &validator_keypair, + &mint_keypair, + 100, + 1, + &last_id, + ticks_per_slot, + ); blocktree - .write_entries(0, tick_height, genesis_entry_height, &active_set_entries) + .write_entries(1, 0, 0, &active_set_entries) .unwrap(); } info!("leader id: {}", leader_keypair.pubkey()); @@ -927,10 +906,7 @@ fn test_leader_to_validator_transition() { let (rotation_sender, rotation_receiver) = channel(); let leader_exit = leader.run(Some(rotation_sender)); - let expected_rotations = vec![ - (FullnodeReturnType::LeaderToLeaderRotation, 0), - (FullnodeReturnType::LeaderToValidatorRotation, 1), - ]; + let expected_rotations = vec![(FullnodeReturnType::LeaderToValidatorRotation, 2)]; for expected_rotation in expected_rotations { loop { @@ -976,23 +952,22 @@ fn test_leader_validator_basic() { genesis_block.ticks_per_slot = ticks_per_slot; // Make a common mint and a genesis entry for both leader + validator ledgers - let (leader_ledger_path, tick_height, genesis_entry_height, last_id, last_entry_id) = - create_tmp_sample_blocktree("test_leader_validator_basic", &genesis_block, 0); + let (leader_ledger_path, last_id) = + create_new_tmp_ledger("test_leader_validator_basic", &genesis_block).unwrap(); // Add validator vote on tick height 1 - let (active_set_entries, _) = make_active_set_entries( - &validator_keypair, - &mint_keypair, - 100, - 0, - &last_entry_id, - &last_id, - 0, - ); { let blocktree = Blocktree::open_config(&leader_ledger_path, ticks_per_slot).unwrap(); + let (active_set_entries, _) = make_active_set_entries( + &validator_keypair, + &mint_keypair, + 100, + 1, + &last_id, + ticks_per_slot, + ); blocktree - .write_entries(0, tick_height, genesis_entry_height, &active_set_entries) + .write_entries(1, 0, 0, &active_set_entries) .unwrap(); } @@ -1030,28 +1005,18 @@ fn test_leader_validator_basic() { converge(&leader_info, 2); - info!("Waiting for slot 0 -> slot 1: bootstrap leader and the validator rotate"); - assert_eq!( - leader_rotation_receiver.recv().unwrap(), - (FullnodeReturnType::LeaderToLeaderRotation, 0), - ); - assert_eq!( - leader_rotation_receiver.recv().unwrap(), - (FullnodeReturnType::LeaderToValidatorRotation, 1) - ); - assert_eq!( - validator_rotation_receiver.recv().unwrap(), - (FullnodeReturnType::LeaderToValidatorRotation, 0) - ); - assert_eq!( - validator_rotation_receiver.recv().unwrap(), - (FullnodeReturnType::ValidatorToLeaderRotation, 1) - ); + // + // The ledger was populated with slot 0 and slot 1, so the first rotation should occur at slot 2 + // - info!("Waiting for slot 1 -> slot 2: validator remains the slot leader due to no votes"); + info!("Waiting for slot 1 -> slot 2: bootstrap leader and the validator rotate"); assert_eq!( validator_rotation_receiver.recv().unwrap(), - (FullnodeReturnType::LeaderToLeaderRotation, 2) + (FullnodeReturnType::ValidatorToLeaderRotation, 2) + ); + assert_eq!( + leader_rotation_receiver.recv().unwrap(), + (FullnodeReturnType::LeaderToValidatorRotation, 2), ); info!("Waiting for slot 2 -> slot 3: validator remains the slot leader due to no votes"); @@ -1059,6 +1024,20 @@ fn test_leader_validator_basic() { validator_rotation_receiver.recv().unwrap(), (FullnodeReturnType::LeaderToLeaderRotation, 3) ); + assert_eq!( + leader_rotation_receiver.recv().unwrap(), + (FullnodeReturnType::LeaderToValidatorRotation, 3) + ); + + info!("Waiting for slot 3 -> slot 4: validator remains the slot leader due to no votes"); + assert_eq!( + validator_rotation_receiver.recv().unwrap(), + (FullnodeReturnType::LeaderToLeaderRotation, 4) + ); + assert_eq!( + leader_rotation_receiver.recv().unwrap(), + (FullnodeReturnType::LeaderToValidatorRotation, 4) + ); info!("Shut down"); validator_exit(); @@ -1106,13 +1085,8 @@ fn test_dropped_handoff_recovery() { genesis_block.ticks_per_slot = ticks_per_slot; // Make a common mint and a genesis entry for both leader + validator's ledgers - let num_ending_ticks = 1; - let (genesis_ledger_path, tick_height, genesis_entry_height, last_id, last_entry_id) = - create_tmp_sample_blocktree( - "test_dropped_handoff_recovery", - &genesis_block, - num_ending_ticks, - ); + let (genesis_ledger_path, last_id) = + create_new_tmp_ledger("test_dropped_handoff_recovery", &genesis_block).unwrap(); // Create the validator keypair that will be the next leader in line let next_leader_keypair = Arc::new(Keypair::new()); @@ -1125,21 +1099,18 @@ fn test_dropped_handoff_recovery() { // Make the entries to give the next_leader validator some stake so that they will be in // leader election active set - let (active_set_entries, _) = make_active_set_entries( - &next_leader_keypair, - &mint_keypair, - 100, - 1, - &last_entry_id, - &last_id, - 0, - ); - - // Write the entries { let blocktree = Blocktree::open_config(&genesis_ledger_path, ticks_per_slot).unwrap(); + let (active_set_entries, _) = make_active_set_entries( + &next_leader_keypair, + &mint_keypair, + 100, + 1, + &last_id, + ticks_per_slot, + ); blocktree - .write_entries(0, tick_height, genesis_entry_height, &active_set_entries) + .write_entries(1, 0, 0, &active_set_entries) .unwrap(); } @@ -1264,45 +1235,36 @@ fn test_full_leader_validator_network() { genesis_block.ticks_per_slot = ticks_per_slot; // Make a common mint and a genesis entry for both leader + validator's ledgers - let num_ending_ticks = 1; - let (bootstrap_leader_ledger_path, tick_height, mut entry_height, last_id, mut last_entry_id) = - create_tmp_sample_blocktree( - "test_full_leader_validator_network", - &genesis_block, - num_ending_ticks, - ); + let (bootstrap_leader_ledger_path, mut last_id) = + create_new_tmp_ledger("test_full_leader_validator_network", &genesis_block).unwrap(); // Create a common ledger with entries in the beginnging that will add all the validators // to the active set for leader election. let mut ledger_paths = Vec::new(); ledger_paths.push(bootstrap_leader_ledger_path.clone()); + // Make entries to give each validator node some stake so that they will be in the + // leader election active set + let mut active_set_entries = vec![]; for node_keypair in node_keypairs.iter() { - // Make entries to give each validator node some stake so that they will be in the - // leader election active set - let (active_set_entries, _) = make_active_set_entries( + let (node_active_set_entries, _) = make_active_set_entries( node_keypair, &mint_keypair, 100, 0, - &last_entry_id, &last_id, - 0, + ticks_per_slot, ); + last_id = node_active_set_entries.last().unwrap().id; + active_set_entries.extend(node_active_set_entries); + } - // Write the entries - last_entry_id = active_set_entries - .last() - .expect("expected at least one genesis entry") - .id; - { - let blocktree = - Blocktree::open_config(&bootstrap_leader_ledger_path, ticks_per_slot).unwrap(); - blocktree - .write_entries(0, tick_height, entry_height, &active_set_entries) - .unwrap(); - entry_height += active_set_entries.len() as u64; - } + { + let blocktree = + Blocktree::open_config(&bootstrap_leader_ledger_path, ticks_per_slot).unwrap(); + blocktree + .write_entries(1, 0, 0, &active_set_entries) + .unwrap(); } let mut nodes = vec![]; @@ -1462,16 +1424,9 @@ fn test_broadcast_last_tick() { genesis_block.ticks_per_slot = ticks_per_slot; // Create leader ledger - let ( - bootstrap_leader_ledger_path, - _tick_height, - genesis_entry_height, - _last_id, - _last_entry_id, - ) = create_tmp_sample_blocktree("test_broadcast_last_tick", &genesis_block, 0); + let (bootstrap_leader_ledger_path, _last_id) = + create_new_tmp_ledger("test_broadcast_last_tick", &genesis_block).unwrap(); - let genesis_ledger_len = genesis_entry_height; - debug!("genesis_ledger_len: {}", genesis_ledger_len); let blob_receiver_exit = Arc::new(AtomicBool::new(false)); // Create the listeners @@ -1662,9 +1617,8 @@ fn test_fullnode_rotate( genesis_block.slots_per_epoch = slots_per_epoch; // Make a common mint and a genesis entry for both leader + validator ledgers - let (leader_ledger_path, mut tick_height, mut last_entry_height, last_id, mut last_entry_id) = - create_tmp_sample_blocktree("test_fullnode_rotate", &genesis_block, 0); - assert_eq!(tick_height, 1); + let (leader_ledger_path, mut last_id) = + create_new_tmp_ledger("test_fullnode_rotate", &genesis_block).unwrap(); let mut ledger_paths = Vec::new(); ledger_paths.push(leader_ledger_path.clone()); @@ -1676,6 +1630,19 @@ fn test_fullnode_rotate( let validator_keypair = Arc::new(Keypair::new()); let validator = Node::new_localhost_with_pubkey(validator_keypair.pubkey()); + let mut leader_slot_height_of_next_rotation = 1; + + if fullnode_config.leader_scheduler_config.ticks_per_slot == 1 { + // Add another tick to the ledger if the cluster has been configured for 1 ticks_per_slot. + // The "pseudo-tick" entry0 currently added by bank::process_ledger cannot be rotated on + // since it has no last id (so at 1 ticks_per_slot rotation must start at a tick_height of + // 2) + let tick = solana::entry::create_ticks(1, last_id); + last_id = tick[0].id; + entries.extend(tick); + leader_slot_height_of_next_rotation += 1; + } + // Setup the cluster with a single node if include_validator { // Add validator vote on tick height 1 @@ -1683,40 +1650,22 @@ fn test_fullnode_rotate( &validator_keypair, &mint_keypair, 100, - 0, - &last_entry_id, + 1, &last_id, - 0, + ticks_per_slot, ); + last_id = active_set_entries.last().unwrap().id; entries.extend(active_set_entries); - last_entry_id = entries.last().unwrap().id; - } - - let mut start_slot = 0; - let mut leader_tick_height_of_next_rotation = 2; - if ticks_per_slot == 1 { - // Add another tick to the ledger if the cluster has been configured for 1 tick_per_slot. - // The "pseudo-tick" entry0 currently added by bank::process_ledger cannot be rotated on - // since it has no last id (so at 1 ticks_per_slot rotation must start at a tick_height of - // 2) - let tick = solana::entry::create_ticks(1, last_entry_id); - entries.extend(tick); - last_entry_id = entries.last().unwrap().id; - - start_slot = 1; - tick_height = 0; - last_entry_height = 0; + leader_slot_height_of_next_rotation += 1; } // Write additional ledger entries { - trace!("last_entry_id: {:?}", last_entry_id); + trace!("last_id: {:?}", last_id); trace!("entries: {:?}", entries); let blocktree = Blocktree::open_config(&leader_ledger_path, ticks_per_slot).unwrap(); - blocktree - .write_entries(start_slot, tick_height, last_entry_height, &entries) - .unwrap(); + blocktree.write_entries(1, 0, 0, &entries).unwrap(); } // Start up the node(s) @@ -1762,12 +1711,12 @@ fn test_fullnode_rotate( let mut client_last_id = solana_sdk::hash::Hash::default(); let mut validator_should_be_leader = !leader_should_be_leader; - let mut validator_tick_height_of_next_rotation = leader_tick_height_of_next_rotation; + let mut validator_slot_height_of_next_rotation = leader_slot_height_of_next_rotation; let mut log_spam = 0; - let max_tick_height = 8; - while leader_tick_height_of_next_rotation < max_tick_height - && validator_tick_height_of_next_rotation < max_tick_height + let max_slot_height = 5; + while leader_slot_height_of_next_rotation < max_slot_height + && validator_slot_height_of_next_rotation < max_slot_height { // Check for leader rotation { @@ -1779,7 +1728,7 @@ fn test_fullnode_rotate( } info!("leader rotation event {:?} at slot={}", rotation_type, slot); info!("leader should be leader? {}", leader_should_be_leader); - assert_eq!(slot, leader_tick_height_of_next_rotation / ticks_per_slot); + assert_eq!(slot, leader_slot_height_of_next_rotation); if include_validator { assert_eq!( rotation_type, @@ -1793,7 +1742,7 @@ fn test_fullnode_rotate( } else { assert_eq!(rotation_type, FullnodeReturnType::LeaderToLeaderRotation); } - leader_tick_height_of_next_rotation += ticks_per_slot; + leader_slot_height_of_next_rotation += 1; } Err(TryRecvError::Empty) => {} err => panic!(err), @@ -1810,13 +1759,10 @@ fn test_fullnode_rotate( } info!( "validator rotation event {:?} at slot={} {}", - rotation_type, slot, validator_tick_height_of_next_rotation + rotation_type, slot, validator_slot_height_of_next_rotation ); info!("validator should be leader? {}", validator_should_be_leader); - assert_eq!( - slot, - validator_tick_height_of_next_rotation / ticks_per_slot - ); + assert_eq!(slot, validator_slot_height_of_next_rotation); assert_eq!( rotation_type, if validator_should_be_leader { @@ -1825,7 +1771,7 @@ fn test_fullnode_rotate( FullnodeReturnType::ValidatorToLeaderRotation } ); - validator_tick_height_of_next_rotation += ticks_per_slot; + validator_slot_height_of_next_rotation += 1; validator_should_be_leader = !validator_should_be_leader; } Err(TryRecvError::Empty) => {} @@ -1866,16 +1812,15 @@ fn test_fullnode_rotate( }); } else { log_spam += 1; - if log_spam % 10 == 0 { + if log_spam % 25 == 0 { if include_validator { - trace!("waiting for leader and validator to reach max tick height..."); + trace!("waiting for leader and validator to reach max slot height..."); } else { - trace!("waiting for leader to reach max tick height..."); + trace!("waiting for leader to reach max slot height..."); } } } tick_step_receiver.recv().expect("tick step"); - info!("tick step received"); } if transact { @@ -1896,12 +1841,12 @@ fn test_fullnode_rotate( } trace!( - "final validator_tick_height_of_next_rotation: {}", - validator_tick_height_of_next_rotation + "final validator_slot_height_of_next_rotation: {}", + validator_slot_height_of_next_rotation ); trace!( - "final leader_tick_height_of_next_rotation: {}", - leader_tick_height_of_next_rotation + "final leader_slot_height_of_next_rotation: {}", + leader_slot_height_of_next_rotation ); trace!("final leader_should_be_leader: {}", leader_should_be_leader); trace!( diff --git a/tests/replicator.rs b/tests/replicator.rs index 160b03e5cf..f348902302 100644 --- a/tests/replicator.rs +++ b/tests/replicator.rs @@ -10,7 +10,7 @@ extern crate solana; use bincode::deserialize; use solana::blocktree::{ - create_tmp_sample_blocktree, get_tmp_ledger_path, tmp_copy_blocktree, Blocktree, + create_new_tmp_ledger, get_tmp_ledger_path, tmp_copy_blocktree, Blocktree, }; use solana::client::mk_client; use solana::cluster_info::{ClusterInfo, Node, NodeInfo}; @@ -47,8 +47,8 @@ fn test_replicator_startup_basic() { let (genesis_block, mint_keypair) = GenesisBlock::new_with_leader(1_000_000_000, leader_info.id, 42); - let (leader_ledger_path, _tick_height, _last_entry_height, _last_id, _last_entry_id) = - create_tmp_sample_blocktree(leader_ledger_path, &genesis_block, 0); + let (leader_ledger_path, _last_id) = + create_new_tmp_ledger(leader_ledger_path, &genesis_block).unwrap(); let validator_ledger_path = tmp_copy_blocktree!(&leader_ledger_path); @@ -273,24 +273,22 @@ fn test_replicator_startup_leader_hang() { #[test] fn test_replicator_startup_ledger_hang() { - use std::net::UdpSocket; - solana_logger::setup(); info!("starting replicator test"); let leader_keypair = Arc::new(Keypair::new()); let (genesis_block, _mint_keypair) = GenesisBlock::new_with_leader(100, leader_keypair.pubkey(), 42); - let (replicator_ledger_path, _tick_height, _last_entry_height, _last_id, _last_entry_id) = - create_tmp_sample_blocktree("replicator_test_replicator_ledger", &genesis_block, 0); + let (replicator_test_replicator_ledger, _last_id) = + create_new_tmp_ledger("replicator_test_replicator_ledger", &genesis_block).unwrap(); info!("starting leader node"); let leader_node = Node::new_localhost_with_pubkey(leader_keypair.pubkey()); let leader_info = leader_node.info.clone(); let leader_ledger_path = "replicator_test_leader_ledger"; - let (leader_ledger_path, _tick_height, _last_entry_height, _last_id, _last_entry_id) = - create_tmp_sample_blocktree(leader_ledger_path, &genesis_block, 0); + let (leader_ledger_path, _last_id) = + create_new_tmp_ledger(leader_ledger_path, &genesis_block).unwrap(); let validator_ledger_path = tmp_copy_blocktree!(&leader_ledger_path); @@ -325,7 +323,7 @@ fn test_replicator_startup_ledger_hang() { let mut replicator_node = Node::new_localhost_with_pubkey(bad_keys.pubkey()); // Pass bad TVU sockets to prevent successful ledger download - replicator_node.sockets.tvu = vec![UdpSocket::bind("0.0.0.0:0").unwrap()]; + replicator_node.sockets.tvu = vec![std::net::UdpSocket::bind("0.0.0.0:0").unwrap()]; let leader_info = NodeInfo::new_entry_point(&leader_info.gossip);