Move virtual genesis tick into the ledger proper as entry 0
This commit is contained in:
parent
9e9c82869a
commit
c01290438f
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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"]);
|
||||
|
|
77
src/bank.rs
77
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<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());
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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!(
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue