Move virtual genesis tick into the ledger proper as entry 0

This commit is contained in:
Michael Vines 2019-01-29 15:49:29 -08:00
parent 9e9c82869a
commit c01290438f
7 changed files with 90 additions and 68 deletions

View File

@ -2,7 +2,7 @@
use clap::{crate_version, value_t_or_exit, App, Arg}; use clap::{crate_version, value_t_or_exit, App, Arg};
use serde_json; use serde_json;
use solana::db_ledger::create_empty_ledger; use solana::db_ledger::create_new_ledger;
use solana::genesis_block::GenesisBlock; use solana::genesis_block::GenesisBlock;
use solana_sdk::signature::{read_keypair, KeypairUtil}; use solana_sdk::signature::{read_keypair, KeypairUtil};
use std::error; use std::error;
@ -74,6 +74,6 @@ fn main() -> Result<(), Box<dyn error::Error>> {
bootstrap_leader_tokens: BOOTSTRAP_LEADER_TOKENS, bootstrap_leader_tokens: BOOTSTRAP_LEADER_TOKENS,
}; };
create_empty_ledger(ledger_path, &genesis_block)?; create_new_ledger(ledger_path, &genesis_block)?;
Ok(()) Ok(())
} }

View File

@ -33,7 +33,7 @@ fn bad_arguments() {
fn nominal() { fn nominal() {
let keypair = Arc::new(Keypair::new()); let keypair = Arc::new(Keypair::new());
let (_, ledger_path, _, _) = 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 // Basic validation
let output = run_ledger_tool(&["-l", &ledger_path, "verify"]); let output = run_ledger_tool(&["-l", &ledger_path, "verify"]);

View File

@ -724,15 +724,19 @@ impl Bank {
Ok(()) Ok(())
} }
/// Append entry blocks to the ledger, verifying them along the way. /// Starting from the genesis block, append the provided entries to the ledger verifying them
pub fn process_ledger<I>(&self, entries: I) -> Result<(u64, Hash)> /// along the way.
pub fn process_ledger<I>(
&mut self,
genesis_block: &GenesisBlock,
entries: I,
) -> Result<(u64, Hash)>
where where
I: IntoIterator<Item = Entry>, I: IntoIterator<Item = Entry>,
{ {
// these magic numbers are from genesis of the mint, could pull them
// back out of this loop.
let mut entry_height = 0; 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 // Ledger verification needs to be parallelized, but we can't pull the whole
// thing into memory. We therefore chunk it. // thing into memory. We therefore chunk it.
@ -1162,9 +1166,14 @@ mod tests {
mint_keypair: &Keypair, mint_keypair: &Keypair,
keypairs: &[Keypair], keypairs: &[Keypair],
) -> impl Iterator<Item = Entry> { ) -> impl Iterator<Item = Entry> {
let mut last_id = genesis_block.last_id();
let mut hash = genesis_block.last_id();
let mut entries: Vec<Entry> = vec![]; let mut entries: Vec<Entry> = 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; let num_hashes = 1;
for k in keypairs { for k in keypairs {
let txs = vec![Transaction::system_new( let txs = vec![Transaction::system_new(
@ -1184,18 +1193,24 @@ mod tests {
entries.into_iter() 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( fn create_sample_block_with_ticks(
genesis_block: &GenesisBlock, genesis_block: &GenesisBlock,
mint_keypair: &Keypair, mint_keypair: &Keypair,
length: usize, num_entries: usize,
ticks: usize, tick_interval: usize,
) -> impl Iterator<Item = Entry> { ) -> impl Iterator<Item = Entry> {
let mut entries = Vec::with_capacity(length); assert!(num_entries > 0);
let mut last_id = genesis_block.last_id(); let mut entries = Vec::with_capacity(num_entries);
let mut hash = genesis_block.last_id();
// 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; let num_hashes = 1;
for i in 0..length { for i in 1..num_entries {
let keypair = Keypair::new(); let keypair = Keypair::new();
let tx = Transaction::system_new(mint_keypair, keypair.pubkey(), 1, last_id); let tx = Transaction::system_new(mint_keypair, keypair.pubkey(), 1, last_id);
let entry = Entry::new(&hash, 0, num_hashes, vec![tx]); let entry = Entry::new(&hash, 0, num_hashes, vec![tx]);
@ -1210,7 +1225,7 @@ mod tests {
hash = entry.id; hash = entry.id;
entries.push(entry); entries.push(entry);
if (i + 1) % ticks == 0 { if (i + 1) % tick_interval == 0 {
let tick = Entry::new(&hash, 0, num_hashes, vec![]); let tick = Entry::new(&hash, 0, num_hashes, vec![]);
hash = tick.id; hash = tick.id;
last_id = hash; last_id = hash;
@ -1220,27 +1235,31 @@ mod tests {
entries.into_iter() entries.into_iter()
} }
fn create_sample_ledger(length: usize) -> (GenesisBlock, Keypair, impl Iterator<Item = Entry>) { fn create_sample_ledger(
tokens: u64,
num_entries: usize,
) -> (GenesisBlock, Keypair, impl Iterator<Item = Entry>) {
let mint_keypair = Keypair::new(); let mint_keypair = Keypair::new();
let genesis_block = GenesisBlock { let genesis_block = GenesisBlock {
bootstrap_leader_id: Keypair::new().pubkey(), bootstrap_leader_id: Keypair::new().pubkey(),
bootstrap_leader_tokens: 1, bootstrap_leader_tokens: 1,
mint_id: mint_keypair.pubkey(), 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) (genesis_block, mint_keypair, block)
} }
#[test] #[test]
fn test_process_ledger_simple() { fn test_process_ledger_simple() {
let (genesis_block, mint_keypair, ledger) = create_sample_ledger(1); let (genesis_block, mint_keypair, ledger) = create_sample_ledger(100, 2);
let bank = Bank::default(); let mut bank = Bank::default();
bank.process_genesis_block(&genesis_block); bank.process_genesis_block(&genesis_block);
bank.add_system_program(); bank.add_system_program();
let (ledger_height, last_id) = bank.process_ledger(ledger).unwrap(); let (ledger_height, last_id) = bank.process_ledger(&genesis_block, ledger).unwrap();
assert_eq!(bank.get_balance(&mint_keypair.pubkey()), 1); assert_eq!(bank.get_balance(&mint_keypair.pubkey()), 98);
assert_eq!(ledger_height, 3); assert_eq!(ledger_height, 4);
assert_eq!(bank.tick_height(), 2); assert_eq!(bank.tick_height(), 2);
assert_eq!(bank.last_id(), last_id); assert_eq!(bank.last_id(), last_id);
} }
@ -1268,14 +1287,14 @@ mod tests {
&keypairs, &keypairs,
); );
let bank0 = Bank::default(); let mut bank0 = Bank::default();
bank0.add_system_program(); bank0.add_system_program();
bank0.process_genesis_block(&genesis_block); bank0.process_genesis_block(&genesis_block);
bank0.process_ledger(ledger0).unwrap(); bank0.process_ledger(&genesis_block, ledger0).unwrap();
let bank1 = Bank::default(); let mut bank1 = Bank::default();
bank1.add_system_program(); bank1.add_system_program();
bank1.process_genesis_block(&genesis_block); 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(); let initial_state = bank0.hash_internal_state();
@ -1283,11 +1302,11 @@ mod tests {
let pubkey = keypairs[0].pubkey(); let pubkey = keypairs[0].pubkey();
bank0 bank0
.transfer(1_000, &mint_keypair, pubkey, genesis_block.last_id()) .transfer(1_000, &mint_keypair, pubkey, bank0.last_id())
.unwrap(); .unwrap();
assert_ne!(bank0.hash_internal_state(), initial_state); assert_ne!(bank0.hash_internal_state(), initial_state);
bank1 bank1
.transfer(1_000, &mint_keypair, pubkey, genesis_block.last_id()) .transfer(1_000, &mint_keypair, pubkey, bank1.last_id())
.unwrap(); .unwrap();
assert_eq!(bank0.hash_internal_state(), bank1.hash_internal_state()); assert_eq!(bank0.hash_internal_state(), bank1.hash_internal_state());
} }

View File

@ -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::destroy(ledger_path)?;
DbLedger::open(ledger_path)?;
genesis_block.write(&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<()> 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 { pub fn create_tmp_ledger(name: &str, genesis_block: &GenesisBlock) -> String {
let ledger_path = get_tmp_ledger_path(name); 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 ledger_path
} }

View File

@ -384,7 +384,9 @@ impl Fullnode {
let entries = db_ledger.read_ledger().expect("opening ledger"); let entries = db_ledger.read_ledger().expect("opening ledger");
info!("processing 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. // entry_height is the network-wide agreed height of the ledger.
// initialize it from the input ledger // initialize it from the input ledger
info!( info!(

View File

@ -347,7 +347,6 @@ mod test {
let initial_tick_height = genesis_entry_height; let initial_tick_height = genesis_entry_height;
let active_set_entries_len = active_set_entries.len() as u64; let active_set_entries_len = active_set_entries.len() as u64;
let initial_non_tick_height = genesis_entry_height - initial_tick_height; 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(); 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 // 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 leader_rotation_interval = 16;
let num_bootstrap_slots = 2; let bootstrap_height = 2 * leader_rotation_interval;
let bootstrap_height = num_bootstrap_slots * leader_rotation_interval; assert!((num_ending_ticks as u64) < bootstrap_height);
let leader_scheduler_config = LeaderSchedulerConfig::new( let leader_scheduler_config = LeaderSchedulerConfig::new(
bootstrap_height, bootstrap_height,
leader_rotation_interval, leader_rotation_interval,
@ -376,7 +375,7 @@ mod test {
Arc::new(RwLock::new(LeaderScheduler::new(&leader_scheduler_config))); Arc::new(RwLock::new(LeaderScheduler::new(&leader_scheduler_config)));
// Set up the bank // 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); Fullnode::new_bank_from_ledger(&my_ledger_path, leader_scheduler);
// Set up the replay stage // Set up the replay stage
@ -390,31 +389,27 @@ mod test {
Arc::new(RwLock::new(cluster_info_me)), Arc::new(RwLock::new(cluster_info_me)),
entry_receiver, entry_receiver,
exit.clone(), exit.clone(),
Arc::new(RwLock::new(initial_entry_len)), Arc::new(RwLock::new(entry_height)),
Arc::new(RwLock::new(last_entry_id)), Arc::new(RwLock::new(last_entry_id)),
rotation_sender, rotation_sender,
None, None,
); );
// Send enough ticks to trigger leader rotation // Send enough ticks to trigger leader rotation
let extra_entries = leader_rotation_interval; let total_entries_to_send = (bootstrap_height + leader_rotation_interval) as usize;
let total_entries_to_send = (bootstrap_height + extra_entries) as usize;
let num_hashes = 1;
let mut entries_to_send = vec![]; let mut entries_to_send = vec![];
while entries_to_send.len() < total_entries_to_send { 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; last_id = entry.id;
entries_to_send.push(entry); 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 // Add on the only entries that weren't ticks to the bootstrap height to get the
// total expected entry length // total expected entry length
let leader_rotation_index = (bootstrap_height - initial_tick_height) as usize; let leader_rotation_index = (bootstrap_height - initial_tick_height) as usize;
let expected_entry_height = let expected_entry_height =
bootstrap_height + initial_non_tick_height + active_set_entries_len - 1; bootstrap_height + initial_non_tick_height + active_set_entries_len;
let expected_last_id = entries_to_send[leader_rotation_index - 2].id; let expected_last_id = entries_to_send[leader_rotation_index - 1].id;
entry_sender.send(entries_to_send.clone()).unwrap(); entry_sender.send(entries_to_send.clone()).unwrap();
// Wait for replay_stage to exit and check return value is correct // Wait for replay_stage to exit and check return value is correct
@ -440,7 +435,7 @@ mod test {
assert_eq!( assert_eq!(
&received_ticks[..], &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) //replay stage should continue running even after rotation has happened (tvu never goes down)
assert_eq!(exit.load(Ordering::Relaxed), false); assert_eq!(exit.load(Ordering::Relaxed), false);
@ -470,7 +465,7 @@ mod test {
); );
// Set up the bank // 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); Fullnode::new_bank_from_ledger(&my_ledger_path, leader_scheduler);
// Set up the cluster info // Set up the cluster info
@ -491,7 +486,7 @@ mod test {
entry_receiver, entry_receiver,
exit.clone(), exit.clone(),
Arc::new(RwLock::new(entry_height)), Arc::new(RwLock::new(entry_height)),
Arc::new(RwLock::new(last_id)), Arc::new(RwLock::new(last_entry_id)),
to_leader_sender, to_leader_sender,
None, None,
); );
@ -503,7 +498,7 @@ mod test {
vote_signer_proxy.send_validator_vote(&bank, &cluster_info_me, &mock_sender); vote_signer_proxy.send_validator_vote(&bank, &cluster_info_me, &mock_sender);
// Send ReplayStage an entry, should see it on the ledger writer receiver // 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 entry_sender
.send(next_tick.clone()) .send(next_tick.clone())
.expect("Error sending entry to ReplayStage"); .expect("Error sending entry to ReplayStage");
@ -521,6 +516,8 @@ mod test {
#[test] #[test]
fn test_vote_error_replay_stage_leader_rotation() { fn test_vote_error_replay_stage_leader_rotation() {
solana_logger::setup();
// Set up dummy node to host a ReplayStage // Set up dummy node to host a ReplayStage
let my_keypair = Keypair::new(); let my_keypair = Keypair::new();
let my_id = my_keypair.pubkey(); let my_id = my_keypair.pubkey();
@ -549,7 +546,6 @@ mod test {
let initial_tick_height = genesis_entry_height; let initial_tick_height = genesis_entry_height;
let active_set_entries_len = active_set_entries.len() as u64; let active_set_entries_len = active_set_entries.len() as u64;
let initial_non_tick_height = genesis_entry_height - initial_tick_height; 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(); let db_ledger = DbLedger::open(&my_ledger_path).unwrap();
@ -578,7 +574,7 @@ mod test {
Arc::new(RwLock::new(LeaderScheduler::new(&leader_scheduler_config))); Arc::new(RwLock::new(LeaderScheduler::new(&leader_scheduler_config)));
// Set up the bank // 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); Fullnode::new_bank_from_ledger(&my_ledger_path, leader_scheduler);
// Set up the cluster info // Set up the cluster info
@ -597,7 +593,7 @@ mod test {
cluster_info_me.clone(), cluster_info_me.clone(),
entry_receiver, entry_receiver,
exit.clone(), exit.clone(),
Arc::new(RwLock::new(initial_entry_len as u64)), Arc::new(RwLock::new(entry_height)),
Arc::new(RwLock::new(last_entry_id)), Arc::new(RwLock::new(last_entry_id)),
rotation_tx, rotation_tx,
None, None,
@ -611,16 +607,15 @@ mod test {
// Send enough ticks to trigger leader rotation // Send enough ticks to trigger leader rotation
let total_entries_to_send = (bootstrap_height - initial_tick_height) as usize; 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 // Add on the only entries that weren't ticks to the bootstrap height to get the
// total expected entry length // total expected entry length
let expected_entry_height = let expected_entry_height =
bootstrap_height + initial_non_tick_height + active_set_entries_len - 1; bootstrap_height + initial_non_tick_height + active_set_entries_len;
let leader_rotation_index = (bootstrap_height - initial_tick_height - 2) as usize; let leader_rotation_index = (bootstrap_height - initial_tick_height - 1) as usize;
let mut expected_last_id = Hash::default(); let mut expected_last_id = Hash::default();
for i in 0..total_entries_to_send - 1 { for i in 0..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; last_id = entry.id;
entry_sender entry_sender
.send(vec![entry.clone()]) .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!( assert_eq!(
Some(TvuReturnType::LeaderRotation( Some(TvuReturnType::LeaderRotation(
bootstrap_height, bootstrap_height,
@ -656,7 +651,8 @@ mod test {
} }
); );
assert_ne!(expected_last_id, Hash::default()); 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); assert_eq!(exit.load(Ordering::Relaxed), false);
let _ignored = remove_dir_all(&my_ledger_path); let _ignored = remove_dir_all(&my_ledger_path);
} }

View File

@ -135,7 +135,7 @@ fn test_multi_node_ledger_window() -> result::Result<()> {
let bob_pubkey = Keypair::new().pubkey(); let bob_pubkey = Keypair::new().pubkey();
let mut ledger_paths = Vec::new(); 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); create_tmp_sample_ledger("multi_node_ledger_window", 10_000, 0, leader_data.id, 500);
ledger_paths.push(leader_ledger_path.clone()); 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 // 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 // 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(); let db_ledger = DbLedger::open(&leader_ledger_path).unwrap();
db_ledger db_ledger
.write_entries(DEFAULT_SLOT_HEIGHT, 0, &entries) .write_entries(DEFAULT_SLOT_HEIGHT, last_entry_height, &entries)
.unwrap(); .unwrap();
} }