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 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<dyn error::Error>> {
bootstrap_leader_tokens: BOOTSTRAP_LEADER_TOKENS,
};
create_empty_ledger(ledger_path, &genesis_block)?;
create_new_ledger(ledger_path, &genesis_block)?;
Ok(())
}

View File

@ -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"]);

View File

@ -724,15 +724,19 @@ impl Bank {
Ok(())
}
/// Append entry blocks to the ledger, verifying them along the way.
pub fn process_ledger<I>(&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<I>(
&mut self,
genesis_block: &GenesisBlock,
entries: I,
) -> Result<(u64, Hash)>
where
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 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<Item = Entry> {
let mut last_id = genesis_block.last_id();
let mut hash = genesis_block.last_id();
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;
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<Item = Entry> {
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<Item = Entry>) {
fn create_sample_ledger(
tokens: u64,
num_entries: usize,
) -> (GenesisBlock, Keypair, impl Iterator<Item = Entry>) {
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());
}

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::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
}

View File

@ -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!(

View File

@ -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);
}

View File

@ -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();
}