From c01290438f9c72619baec83cc795443a321e85a2 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Tue, 29 Jan 2019 15:49:29 -0800 Subject: [PATCH] Move virtual genesis tick into the ledger proper as entry 0 --- genesis/src/main.rs | 4 +- ledger-tool/tests/basic.rs | 2 +- src/bank.rs | 77 ++++++++++++++++++++++++-------------- src/db_ledger.rs | 13 +++++-- src/fullnode.rs | 4 +- src/replay_stage.rs | 52 ++++++++++++------------- tests/multinode.rs | 6 +-- 7 files changed, 90 insertions(+), 68 deletions(-) diff --git a/genesis/src/main.rs b/genesis/src/main.rs index 6a2e46a24f..db2045824f 100644 --- a/genesis/src/main.rs +++ b/genesis/src/main.rs @@ -2,7 +2,7 @@ use clap::{crate_version, value_t_or_exit, App, Arg}; use serde_json; -use solana::db_ledger::create_empty_ledger; +use solana::db_ledger::create_new_ledger; use solana::genesis_block::GenesisBlock; use solana_sdk::signature::{read_keypair, KeypairUtil}; use std::error; @@ -74,6 +74,6 @@ fn main() -> Result<(), Box> { bootstrap_leader_tokens: BOOTSTRAP_LEADER_TOKENS, }; - create_empty_ledger(ledger_path, &genesis_block)?; + create_new_ledger(ledger_path, &genesis_block)?; Ok(()) } diff --git a/ledger-tool/tests/basic.rs b/ledger-tool/tests/basic.rs index a01255bd28..20837bbf26 100644 --- a/ledger-tool/tests/basic.rs +++ b/ledger-tool/tests/basic.rs @@ -33,7 +33,7 @@ fn bad_arguments() { fn nominal() { let keypair = Arc::new(Keypair::new()); let (_, ledger_path, _, _) = - create_tmp_sample_ledger("test_ledger_tool_nominal", 100, 10, keypair.pubkey(), 50); + create_tmp_sample_ledger("test_ledger_tool_nominal", 100, 9, keypair.pubkey(), 50); // Basic validation let output = run_ledger_tool(&["-l", &ledger_path, "verify"]); diff --git a/src/bank.rs b/src/bank.rs index 7bfd7eef4f..f65e5e90c5 100644 --- a/src/bank.rs +++ b/src/bank.rs @@ -724,15 +724,19 @@ impl Bank { Ok(()) } - /// Append entry blocks to the ledger, verifying them along the way. - pub fn process_ledger(&self, entries: I) -> Result<(u64, Hash)> + /// Starting from the genesis block, append the provided entries to the ledger verifying them + /// along the way. + pub fn process_ledger( + &mut self, + genesis_block: &GenesisBlock, + entries: I, + ) -> Result<(u64, Hash)> where I: IntoIterator, { - // these magic numbers are from genesis of the mint, could pull them - // back out of this loop. let mut entry_height = 0; - let mut last_id = self.last_id(); + let mut last_id = genesis_block.last_id(); + self.last_ids = RwLock::new(StatusDeque::default()); // Ledger verification needs to be parallelized, but we can't pull the whole // thing into memory. We therefore chunk it. @@ -1162,9 +1166,14 @@ mod tests { mint_keypair: &Keypair, keypairs: &[Keypair], ) -> impl Iterator { - let mut last_id = genesis_block.last_id(); - let mut hash = genesis_block.last_id(); let mut entries: Vec = vec![]; + + // Start off the ledger with a tick linked to the genesis block + let tick = Entry::new(&genesis_block.last_id(), 0, 1, vec![]); + let mut hash = tick.id; + let mut last_id = tick.id; + entries.push(tick); + let num_hashes = 1; for k in keypairs { let txs = vec![Transaction::system_new( @@ -1184,18 +1193,24 @@ mod tests { entries.into_iter() } - // create a ledger with tick entries every `ticks` entries + // create a ledger with a tick every `tick_interval` entries and a couple other transactions fn create_sample_block_with_ticks( genesis_block: &GenesisBlock, mint_keypair: &Keypair, - length: usize, - ticks: usize, + num_entries: usize, + tick_interval: usize, ) -> impl Iterator { - let mut entries = Vec::with_capacity(length); - let mut last_id = genesis_block.last_id(); - let mut hash = genesis_block.last_id(); + assert!(num_entries > 0); + let mut entries = Vec::with_capacity(num_entries); + + // Start off the ledger with a tick linked to the genesis block + let tick = Entry::new(&genesis_block.last_id(), 0, 1, vec![]); + let mut hash = tick.id; + let mut last_id = tick.id; + entries.push(tick); + let num_hashes = 1; - for i in 0..length { + for i in 1..num_entries { let keypair = Keypair::new(); let tx = Transaction::system_new(mint_keypair, keypair.pubkey(), 1, last_id); let entry = Entry::new(&hash, 0, num_hashes, vec![tx]); @@ -1210,7 +1225,7 @@ mod tests { hash = entry.id; entries.push(entry); - if (i + 1) % ticks == 0 { + if (i + 1) % tick_interval == 0 { let tick = Entry::new(&hash, 0, num_hashes, vec![]); hash = tick.id; last_id = hash; @@ -1220,27 +1235,31 @@ mod tests { entries.into_iter() } - fn create_sample_ledger(length: usize) -> (GenesisBlock, Keypair, impl Iterator) { + fn create_sample_ledger( + tokens: u64, + num_entries: usize, + ) -> (GenesisBlock, Keypair, impl Iterator) { let mint_keypair = Keypair::new(); let genesis_block = GenesisBlock { bootstrap_leader_id: Keypair::new().pubkey(), bootstrap_leader_tokens: 1, mint_id: mint_keypair.pubkey(), - tokens: length as u64 + 2, + tokens, }; - let block = create_sample_block_with_ticks(&genesis_block, &mint_keypair, length, length); + let block = + create_sample_block_with_ticks(&genesis_block, &mint_keypair, num_entries, num_entries); (genesis_block, mint_keypair, block) } #[test] fn test_process_ledger_simple() { - let (genesis_block, mint_keypair, ledger) = create_sample_ledger(1); - let bank = Bank::default(); + let (genesis_block, mint_keypair, ledger) = create_sample_ledger(100, 2); + let mut bank = Bank::default(); bank.process_genesis_block(&genesis_block); bank.add_system_program(); - let (ledger_height, last_id) = bank.process_ledger(ledger).unwrap(); - assert_eq!(bank.get_balance(&mint_keypair.pubkey()), 1); - assert_eq!(ledger_height, 3); + let (ledger_height, last_id) = bank.process_ledger(&genesis_block, ledger).unwrap(); + assert_eq!(bank.get_balance(&mint_keypair.pubkey()), 98); + assert_eq!(ledger_height, 4); assert_eq!(bank.tick_height(), 2); assert_eq!(bank.last_id(), last_id); } @@ -1268,14 +1287,14 @@ mod tests { &keypairs, ); - let bank0 = Bank::default(); + let mut bank0 = Bank::default(); bank0.add_system_program(); bank0.process_genesis_block(&genesis_block); - bank0.process_ledger(ledger0).unwrap(); - let bank1 = Bank::default(); + bank0.process_ledger(&genesis_block, ledger0).unwrap(); + let mut bank1 = Bank::default(); bank1.add_system_program(); bank1.process_genesis_block(&genesis_block); - bank1.process_ledger(ledger1).unwrap(); + bank1.process_ledger(&genesis_block, ledger1).unwrap(); let initial_state = bank0.hash_internal_state(); @@ -1283,11 +1302,11 @@ mod tests { let pubkey = keypairs[0].pubkey(); bank0 - .transfer(1_000, &mint_keypair, pubkey, genesis_block.last_id()) + .transfer(1_000, &mint_keypair, pubkey, bank0.last_id()) .unwrap(); assert_ne!(bank0.hash_internal_state(), initial_state); bank1 - .transfer(1_000, &mint_keypair, pubkey, genesis_block.last_id()) + .transfer(1_000, &mint_keypair, pubkey, bank1.last_id()) .unwrap(); assert_eq!(bank0.hash_internal_state(), bank1.hash_internal_state()); } diff --git a/src/db_ledger.rs b/src/db_ledger.rs index fd15a7f9c3..d68954b057 100644 --- a/src/db_ledger.rs +++ b/src/db_ledger.rs @@ -830,11 +830,16 @@ impl Iterator for EntryIterator { } } -pub fn create_empty_ledger(ledger_path: &str, genesis_block: &GenesisBlock) -> Result<(u64, Hash)> { +pub fn create_new_ledger(ledger_path: &str, genesis_block: &GenesisBlock) -> Result<(u64, Hash)> { DbLedger::destroy(ledger_path)?; - DbLedger::open(ledger_path)?; genesis_block.write(&ledger_path)?; - Ok((0, genesis_block.last_id())) + + // Add a single tick linked back to the genesis_block to bootstrap the ledger + let db_ledger = DbLedger::open(ledger_path)?; + let entries = crate::entry::create_ticks(1, genesis_block.last_id()); + db_ledger.write_entries(DEFAULT_SLOT_HEIGHT, 0, &entries)?; + + Ok((1, entries[0].id)) } pub fn genesis<'a, I>(ledger_path: &str, keypair: &Keypair, entries: I) -> Result<()> @@ -875,7 +880,7 @@ pub fn get_tmp_ledger_path(name: &str) -> String { pub fn create_tmp_ledger(name: &str, genesis_block: &GenesisBlock) -> String { let ledger_path = get_tmp_ledger_path(name); - create_empty_ledger(&ledger_path, genesis_block).unwrap(); + create_new_ledger(&ledger_path, genesis_block).unwrap(); ledger_path } diff --git a/src/fullnode.rs b/src/fullnode.rs index 4dba9cd971..a52d1d684b 100644 --- a/src/fullnode.rs +++ b/src/fullnode.rs @@ -384,7 +384,9 @@ impl Fullnode { let entries = db_ledger.read_ledger().expect("opening ledger"); info!("processing ledger..."); - let (entry_height, last_entry_id) = bank.process_ledger(entries).expect("process_ledger"); + let (entry_height, last_entry_id) = bank + .process_ledger(genesis_block, entries) + .expect("process_ledger"); // entry_height is the network-wide agreed height of the ledger. // initialize it from the input ledger info!( diff --git a/src/replay_stage.rs b/src/replay_stage.rs index 897a8b25a9..d4f58e5ec3 100644 --- a/src/replay_stage.rs +++ b/src/replay_stage.rs @@ -347,7 +347,6 @@ mod test { let initial_tick_height = genesis_entry_height; let active_set_entries_len = active_set_entries.len() as u64; let initial_non_tick_height = genesis_entry_height - initial_tick_height; - let initial_entry_len = genesis_entry_height + active_set_entries_len; { let db_ledger = DbLedger::open(&my_ledger_path).unwrap(); @@ -361,10 +360,10 @@ mod test { } // Set up the LeaderScheduler so that this node becomes the leader at - // bootstrap_height = num_bootstrap_slots * leader_rotation_interval + // bootstrap_height let leader_rotation_interval = 16; - let num_bootstrap_slots = 2; - let bootstrap_height = num_bootstrap_slots * leader_rotation_interval; + let bootstrap_height = 2 * leader_rotation_interval; + assert!((num_ending_ticks as u64) < bootstrap_height); let leader_scheduler_config = LeaderSchedulerConfig::new( bootstrap_height, leader_rotation_interval, @@ -376,7 +375,7 @@ mod test { Arc::new(RwLock::new(LeaderScheduler::new(&leader_scheduler_config))); // Set up the bank - let (bank, _, last_entry_id) = + let (bank, entry_height, last_entry_id) = Fullnode::new_bank_from_ledger(&my_ledger_path, leader_scheduler); // Set up the replay stage @@ -390,31 +389,27 @@ mod test { Arc::new(RwLock::new(cluster_info_me)), entry_receiver, exit.clone(), - Arc::new(RwLock::new(initial_entry_len)), + Arc::new(RwLock::new(entry_height)), Arc::new(RwLock::new(last_entry_id)), rotation_sender, None, ); // Send enough ticks to trigger leader rotation - let extra_entries = leader_rotation_interval; - let total_entries_to_send = (bootstrap_height + extra_entries) as usize; - let num_hashes = 1; + let total_entries_to_send = (bootstrap_height + leader_rotation_interval) as usize; let mut entries_to_send = vec![]; while entries_to_send.len() < total_entries_to_send { - let entry = Entry::new(&mut last_id, 0, num_hashes, vec![]); + let entry = Entry::new(&mut last_id, 0, 1, vec![]); last_id = entry.id; entries_to_send.push(entry); } - assert!((num_ending_ticks as u64) < bootstrap_height); - // Add on the only entries that weren't ticks to the bootstrap height to get the // total expected entry length let leader_rotation_index = (bootstrap_height - initial_tick_height) as usize; let expected_entry_height = - bootstrap_height + initial_non_tick_height + active_set_entries_len - 1; - let expected_last_id = entries_to_send[leader_rotation_index - 2].id; + bootstrap_height + initial_non_tick_height + active_set_entries_len; + let expected_last_id = entries_to_send[leader_rotation_index - 1].id; entry_sender.send(entries_to_send.clone()).unwrap(); // Wait for replay_stage to exit and check return value is correct @@ -440,7 +435,7 @@ mod test { assert_eq!( &received_ticks[..], - &entries_to_send[..leader_rotation_index - 1] + &entries_to_send[..leader_rotation_index] ); //replay stage should continue running even after rotation has happened (tvu never goes down) assert_eq!(exit.load(Ordering::Relaxed), false); @@ -470,7 +465,7 @@ mod test { ); // Set up the bank - let (bank, entry_height, last_id) = + let (bank, entry_height, last_entry_id) = Fullnode::new_bank_from_ledger(&my_ledger_path, leader_scheduler); // Set up the cluster info @@ -491,7 +486,7 @@ mod test { entry_receiver, exit.clone(), Arc::new(RwLock::new(entry_height)), - Arc::new(RwLock::new(last_id)), + Arc::new(RwLock::new(last_entry_id)), to_leader_sender, None, ); @@ -503,7 +498,7 @@ mod test { vote_signer_proxy.send_validator_vote(&bank, &cluster_info_me, &mock_sender); // Send ReplayStage an entry, should see it on the ledger writer receiver - let next_tick = create_ticks(1, last_id); + let next_tick = create_ticks(1, last_entry_id); entry_sender .send(next_tick.clone()) .expect("Error sending entry to ReplayStage"); @@ -521,6 +516,8 @@ mod test { #[test] fn test_vote_error_replay_stage_leader_rotation() { + solana_logger::setup(); + // Set up dummy node to host a ReplayStage let my_keypair = Keypair::new(); let my_id = my_keypair.pubkey(); @@ -549,7 +546,6 @@ mod test { let initial_tick_height = genesis_entry_height; let active_set_entries_len = active_set_entries.len() as u64; let initial_non_tick_height = genesis_entry_height - initial_tick_height; - let initial_entry_len = genesis_entry_height + active_set_entries_len; { let db_ledger = DbLedger::open(&my_ledger_path).unwrap(); @@ -578,7 +574,7 @@ mod test { Arc::new(RwLock::new(LeaderScheduler::new(&leader_scheduler_config))); // Set up the bank - let (bank, _, last_entry_id) = + let (bank, entry_height, last_entry_id) = Fullnode::new_bank_from_ledger(&my_ledger_path, leader_scheduler); // Set up the cluster info @@ -597,7 +593,7 @@ mod test { cluster_info_me.clone(), entry_receiver, exit.clone(), - Arc::new(RwLock::new(initial_entry_len as u64)), + Arc::new(RwLock::new(entry_height)), Arc::new(RwLock::new(last_entry_id)), rotation_tx, None, @@ -611,16 +607,15 @@ mod test { // Send enough ticks to trigger leader rotation let total_entries_to_send = (bootstrap_height - initial_tick_height) as usize; - let num_hashes = 1; // Add on the only entries that weren't ticks to the bootstrap height to get the // total expected entry length let expected_entry_height = - bootstrap_height + initial_non_tick_height + active_set_entries_len - 1; - let leader_rotation_index = (bootstrap_height - initial_tick_height - 2) as usize; + bootstrap_height + initial_non_tick_height + active_set_entries_len; + let leader_rotation_index = (bootstrap_height - initial_tick_height - 1) as usize; let mut expected_last_id = Hash::default(); - for i in 0..total_entries_to_send - 1 { - let entry = Entry::new(&mut last_id, 0, num_hashes, vec![]); + for i in 0..total_entries_to_send { + let entry = Entry::new(&mut last_id, 0, 1, vec![]); last_id = entry.id; entry_sender .send(vec![entry.clone()]) @@ -640,7 +635,7 @@ mod test { ); } - // Wait for replay_stage to exit and check return value is correct + info!("Wait for replay_stage to exit and check return value is correct"); assert_eq!( Some(TvuReturnType::LeaderRotation( bootstrap_height, @@ -656,7 +651,8 @@ mod test { } ); assert_ne!(expected_last_id, Hash::default()); - //replay stage should continue running even after rotation has happened (tvu never goes down) + + info!("Replay stage should continue running even after rotation has happened (TVU never goes down)"); assert_eq!(exit.load(Ordering::Relaxed), false); let _ignored = remove_dir_all(&my_ledger_path); } diff --git a/tests/multinode.rs b/tests/multinode.rs index 3374ba94c8..d17a58726d 100644 --- a/tests/multinode.rs +++ b/tests/multinode.rs @@ -135,7 +135,7 @@ fn test_multi_node_ledger_window() -> result::Result<()> { let bob_pubkey = Keypair::new().pubkey(); let mut ledger_paths = Vec::new(); - let (alice, leader_ledger_path, _last_entry_height, _last_entry_id) = + let (alice, leader_ledger_path, last_entry_height, last_entry_id) = create_tmp_sample_ledger("multi_node_ledger_window", 10_000, 0, leader_data.id, 500); ledger_paths.push(leader_ledger_path.clone()); @@ -146,10 +146,10 @@ fn test_multi_node_ledger_window() -> result::Result<()> { // write a bunch more ledger into leader's ledger, this should populate the leader's window // and force it to respond to repair from the ledger window { - let entries = make_tiny_test_entries(genesis_block.last_id(), 100); + let entries = make_tiny_test_entries(last_entry_id, 100); let db_ledger = DbLedger::open(&leader_ledger_path).unwrap(); db_ledger - .write_entries(DEFAULT_SLOT_HEIGHT, 0, &entries) + .write_entries(DEFAULT_SLOT_HEIGHT, last_entry_height, &entries) .unwrap(); }