From bd9b487558af96c54ec2690b3434f1732972baa3 Mon Sep 17 00:00:00 2001 From: Anatoly Yakovenko Date: Sat, 2 Feb 2019 14:50:38 -0800 Subject: [PATCH 01/21] proposal for the leader validator loop --- book/src/leader-validator-transition.md | 96 +++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 book/src/leader-validator-transition.md diff --git a/book/src/leader-validator-transition.md b/book/src/leader-validator-transition.md new file mode 100644 index 00000000000000..efdef393b53a38 --- /dev/null +++ b/book/src/leader-validator-transition.md @@ -0,0 +1,96 @@ +# Leader and Validator Transition + +A fullnode operates in two modes, leader and validator. The modes overlap in code and in function, but have different behaviors. The goal for this design is to allow the two modes to transition between states while reusing the computed bank state. + + +## Validator + +A validator operates on may different concurrent forks of the bank state until it can prove a PoH record at a height that is the scheduled slot to be a leader. + +## Leader + +A leader operates on a single fork for a specific PoH slot. + + +## PoH + +Two different objects for managing PoH for leaders and validators. Both only work for a specific range of ticks and will error out and send an exit signal when they are done. + +### PoH Generator + +This object handles PoH generation for a Validator. + +``` +PohGenerator { + /// Start the PoH thread, and compute it until end_ticks. + /// Once computed, the exit_signal is sent + pub fn new(start: &Hash, start_ticks: u64, end_ticks: u64, exit_signal: Sender<()>); + + /// The is called when a validator votes and needs to reset PoH with its vote + /// Error is returned if end_ticks is reached first + pub fn reset(last_id: &Hash, reset_height: u64) -> Result<()>; + + /// The list of entries from `start` or `reset_height` to `end_ticks` + /// Error is returned if end_ticks has not been reached yet + pub fn final_entries() -> Result>; +} +``` + +### PoH Recorder + +This object handles PoH recording for a Leader + ``` +PohRecorder { + /// Start the PoH thread, and compute it until end_ticks. + /// The recorder will continue to call register tick on the BankState (TODO: we can remove this if the slot ticks are the only valid ticks) + /// Once computed, the exit_signal is sent + pub fn new( + entries: &[Entry], + end_ticks: u64, + sender: Sender>, + bank_state: Arc, exit_signal: Sender<()>); + + /// Record transactions + /// Error is returned if end_ticks is reached first + pub fn record(&self, mixin: Hash, txs: Vec) -> Result<()>; +} +``` + + +## Fullnode Loop + +This object manages the transition between modes. The main idea is that once a ledger is replayed, the validator can run until there is proof that it can be a leader. The leader can then run and record its transactions. + +``` +let (exit_receiver, exit_signal) = channel(); +loop { + // run the validator loop first + let my_slot = leader_scheduler.get_scheduled_tick_height(my_id); + let last_fork = forks.get_latest_fork(); + + // the generator will exit immediatly if tick_height == my_slot.start + assert!(last_fork.tick_height <= my_slot.start, "start is out of sync, replay the ledger!"); + let generator = PohGenerator::new(last_fork.id, last_fork.tick_height, my_slot.start, exit_signal); + + // operate the validator, this runs until my_slot.start is reached by the generator + let validator = Validator::new(forks, bank, generator); + + // wait for validator to finish + exit_receiver.recv(); + validator.exit(). + + // these entries connect to my_slot + let poh_entries = generator.entries(); + + // make sure + assert!(poh_entries.last().unwrap().tick_height == my_slot.start, "generator didn't reach my scheduled height, abort!"); + + // operate as leader + let bank_state = forks.get(my_slot.fork_id); + let recorder = PohRecorder::new(poh_entries, my_slot.end, bank_state, exit_signal); + let leader = Leader::new(bank_state); + exit_receiver.recv(); + // finalize the bank_state and send the vote as a validator + leader.exit(); +} +``` From 0f2a8bbc40bf86783864c277688963d4dae07a36 Mon Sep 17 00:00:00 2001 From: Anatoly Yakovenko Date: Sat, 2 Feb 2019 15:17:50 -0800 Subject: [PATCH 02/21] more design --- book/src/leader-validator-transition.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/book/src/leader-validator-transition.md b/book/src/leader-validator-transition.md index efdef393b53a38..8de7f156ef034d 100644 --- a/book/src/leader-validator-transition.md +++ b/book/src/leader-validator-transition.md @@ -59,7 +59,7 @@ PohRecorder { ## Fullnode Loop -This object manages the transition between modes. The main idea is that once a ledger is replayed, the validator can run until there is proof that it can be a leader. The leader can then run and record its transactions. +This object manages the transition between modes. The main idea is that once a ledger is replayed, the validator can run until there is proof that it can be a leader. The Leader can then run and record its transactions. ``` let (exit_receiver, exit_signal) = channel(); @@ -94,3 +94,7 @@ loop { leader.exit(); } ``` + +## BankState + +BankState operates over a specific slot. Once the final tick has been registered the state becomes finalized and further writes will error out. Validators operate over a bunch of different BankStates that represent live active chains. A Leader only operates over a single BankState. From 2831df50e2ff305f331eff0271baea919cf64c13 Mon Sep 17 00:00:00 2001 From: Anatoly Yakovenko Date: Sat, 2 Feb 2019 15:28:45 -0800 Subject: [PATCH 03/21] more docs --- book/src/leader-validator-transition.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/book/src/leader-validator-transition.md b/book/src/leader-validator-transition.md index 8de7f156ef034d..58484587a338ac 100644 --- a/book/src/leader-validator-transition.md +++ b/book/src/leader-validator-transition.md @@ -85,13 +85,20 @@ loop { // make sure assert!(poh_entries.last().unwrap().tick_height == my_slot.start, "generator didn't reach my scheduled height, abort!"); + // get the slot these entries connect to + let starting_slot = poh_entries.first().unwrap().slot_index() - 1; + + // create a fork from the start to my slot + let bank_state = forks.init(my_slot.slot_id, starting_slot); + // operate as leader - let bank_state = forks.get(my_slot.fork_id); let recorder = PohRecorder::new(poh_entries, my_slot.end, bank_state, exit_signal); let leader = Leader::new(bank_state); exit_receiver.recv(); - // finalize the bank_state and send the vote as a validator leader.exit(); + + // Finalize the bank_state, send the vote as a validator. + bank_state.finalize(); } ``` From 311bc671094f4739cf84dd22e77308f7eaba0f71 Mon Sep 17 00:00:00 2001 From: Anatoly Yakovenko Date: Sun, 3 Feb 2019 12:11:05 -0800 Subject: [PATCH 04/21] docs --- book/src/leader-validator-transition.md | 112 +++++++++++++++--------- 1 file changed, 73 insertions(+), 39 deletions(-) diff --git a/book/src/leader-validator-transition.md b/book/src/leader-validator-transition.md index 58484587a338ac..a1d4cbee0f46c4 100644 --- a/book/src/leader-validator-transition.md +++ b/book/src/leader-validator-transition.md @@ -1,11 +1,15 @@ # Leader and Validator Transition -A fullnode operates in two modes, leader and validator. The modes overlap in code and in function, but have different behaviors. The goal for this design is to allow the two modes to transition between states while reusing the computed bank state. +A fullnode operates in two modes, leader and validator. The modes overlap in +code and in function, but have different behaviors. The goal for this design is +to allow the two modes to transition between states while reusing the computed +bank state. ## Validator -A validator operates on may different concurrent forks of the bank state until it can prove a PoH record at a height that is the scheduled slot to be a leader. +A validator operates on may different concurrent forks of the bank state until +it can prove a PoH record at a height that is the scheduled slot to be a leader. ## Leader @@ -14,76 +18,104 @@ A leader operates on a single fork for a specific PoH slot. ## PoH -Two different objects for managing PoH for leaders and validators. Both only work for a specific range of ticks and will error out and send an exit signal when they are done. +Two different objects for managing PoH for leaders and validators. Both only +work for a specific range of ticks and will error out and send an exit signal +when they are done. ### PoH Generator -This object handles PoH generation for a Validator. +This object handles PoH generation for a Validator. It solves the following +problems for validators -``` -PohGenerator { +1. Keep track of *time* as defined by PoH height, and stop the validator when it +hits the scheduled leader slot. + +2. Reset the clock whenever a validator votes. The last validator vote is the +validators most recent commitment. It is therefore the closest fork to the +scheduled slot. + +3. Provide the entries that connect the last vote to the scheduled slot. + +``` PohGenerator { /// Start the PoH thread, and compute it until end_ticks. /// Once computed, the exit_signal is sent - pub fn new(start: &Hash, start_ticks: u64, end_ticks: u64, exit_signal: Sender<()>); + pub fn new( + start: &Hash, + start_ticks: u64, + end_ticks: u64, + exit_signal: Sender<()>); - /// The is called when a validator votes and needs to reset PoH with its vote - /// Error is returned if end_ticks is reached first + /// The is called when a validator votes and needs to reset PoH with its + /// vote. Error is returned if end_ticks is reached first. pub fn reset(last_id: &Hash, reset_height: u64) -> Result<()>; - /// The list of entries from `start` or `reset_height` to `end_ticks` - /// Error is returned if end_ticks has not been reached yet + /// The list of entries from `start` or `reset_height` to `end_ticks`. + /// Error is returned if end_ticks has not been reached yet. pub fn final_entries() -> Result>; -} -``` +} ``` ### PoH Recorder -This object handles PoH recording for a Leader - ``` +This object handles PoH recording for a Leader. It solves the following +problems: + +1. Keep track of *time* as defined by PoH height, and stop the leader when it +gets to the end of its scheduled leader slot. + +2. Register ticks with the BankState as they are produced by PoH. + +3. Record entries that are produced by the TPU into PoH. + +``` PohRecorder { /// Start the PoH thread, and compute it until end_ticks. - /// The recorder will continue to call register tick on the BankState (TODO: we can remove this if the slot ticks are the only valid ticks) - /// Once computed, the exit_signal is sent + /// The recorder will continue to call register tick on the BankState. + /// TODO: we can remove `register_ticks` on the bank if the slot ticks are + /// the only valid ticks. + /// Once computed, the exit_signal is sent. pub fn new( - entries: &[Entry], + entries: &[Entry], end_ticks: u64, sender: Sender>, - bank_state: Arc, exit_signal: Sender<()>); + bank_state: Arc, + exit_signal: Sender<()>); - /// Record transactions - /// Error is returned if end_ticks is reached first + /// Record transactions /// Error is returned if end_ticks is reached first pub fn record(&self, mixin: Hash, txs: Vec) -> Result<()>; -} +} ``` ## Fullnode Loop -This object manages the transition between modes. The main idea is that once a ledger is replayed, the validator can run until there is proof that it can be a leader. The Leader can then run and record its transactions. +This object manages the transition between modes. The main idea is that once a +ledger is replayed, the validator can run until there is proof that it can be a +leader. The Leader can then run and record its transactions. -``` -let (exit_receiver, exit_signal) = channel(); +``` +let (exit_receiver, exit_signal) = channel(); loop { // run the validator loop first let my_slot = leader_scheduler.get_scheduled_tick_height(my_id); let last_fork = forks.get_latest_fork(); - // the generator will exit immediatly if tick_height == my_slot.start - assert!(last_fork.tick_height <= my_slot.start, "start is out of sync, replay the ledger!"); + // the generator will exit immediatly if tick_height == my_slot.start + assert!(last_fork.tick_height <= my_slot.start, + "start is out of sync, replay the ledger!"); let generator = PohGenerator::new(last_fork.id, last_fork.tick_height, my_slot.start, exit_signal); - // operate the validator, this runs until my_slot.start is reached by the generator - let validator = Validator::new(forks, bank, generator); + // operate the validator, this runs until my_slot.start is reached by the + // generator + let validator = Validator::new(forks, bank, generator, //other params); + + // wait for validator to finish exit_receiver.recv(); validator.exit(). - // wait for validator to finish - exit_receiver.recv(); - validator.exit(). - // these entries connect to my_slot let poh_entries = generator.entries(); - // make sure - assert!(poh_entries.last().unwrap().tick_height == my_slot.start, "generator didn't reach my scheduled height, abort!"); + // make sure this is true + assert!(poh_entries.last().unwrap().tick_height == my_slot.start, + "generator didn't reach my scheduled height, abort!"); // get the slot these entries connect to let starting_slot = poh_entries.first().unwrap().slot_index() - 1; @@ -93,9 +125,8 @@ loop { // operate as leader let recorder = PohRecorder::new(poh_entries, my_slot.end, bank_state, exit_signal); - let leader = Leader::new(bank_state); - exit_receiver.recv(); - leader.exit(); + let leader = Leader::new(bank_state, //other params); + exit_receiver.recv(); leader.exit(); // Finalize the bank_state, send the vote as a validator. bank_state.finalize(); @@ -104,4 +135,7 @@ loop { ## BankState -BankState operates over a specific slot. Once the final tick has been registered the state becomes finalized and further writes will error out. Validators operate over a bunch of different BankStates that represent live active chains. A Leader only operates over a single BankState. +BankState operates over a specific slot. Once the final tick has been +registered the state becomes finalized and further writes will error out. +Validators operate over a bunch of different BankStates that represent live +active chains. A Leader only operates over a single BankState. From 63402144d6b7348d9b218bfd10bc79f35146e3f9 Mon Sep 17 00:00:00 2001 From: Anatoly Yakovenko Date: Sun, 3 Feb 2019 12:43:38 -0800 Subject: [PATCH 05/21] fmt --- book/src/leader-validator-transition.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/book/src/leader-validator-transition.md b/book/src/leader-validator-transition.md index a1d4cbee0f46c4..50eea087638bb3 100644 --- a/book/src/leader-validator-transition.md +++ b/book/src/leader-validator-transition.md @@ -102,13 +102,15 @@ loop { // the generator will exit immediatly if tick_height == my_slot.start assert!(last_fork.tick_height <= my_slot.start, "start is out of sync, replay the ledger!"); - let generator = PohGenerator::new(last_fork.id, last_fork.tick_height, my_slot.start, exit_signal); + let generator = PohGenerator::new(last_fork.id, last_fork.tick_height, + my_slot.start, exit_signal); // operate the validator, this runs until my_slot.start is reached by the // generator let validator = Validator::new(forks, bank, generator, //other params); - // wait for validator to finish exit_receiver.recv(); validator.exit(). + // wait for validator to finish exit_receiver.recv(); + validator.exit(). // these entries connect to my_slot let poh_entries = generator.entries(); @@ -124,9 +126,11 @@ loop { let bank_state = forks.init(my_slot.slot_id, starting_slot); // operate as leader - let recorder = PohRecorder::new(poh_entries, my_slot.end, bank_state, exit_signal); + let recorder = PohRecorder::new(poh_entries, my_slot.end, bank_state, + exit_signal); let leader = Leader::new(bank_state, //other params); - exit_receiver.recv(); leader.exit(); + exit_receiver.recv(); + leader.exit(); // Finalize the bank_state, send the vote as a validator. bank_state.finalize(); From 8b48c3f44208206a6c277faf6e23771c405404a6 Mon Sep 17 00:00:00 2001 From: Anatoly Yakovenko Date: Sun, 3 Feb 2019 16:26:25 -0800 Subject: [PATCH 06/21] more docs --- book/src/leader-validator-transition.md | 94 ++++++++++++++----------- 1 file changed, 53 insertions(+), 41 deletions(-) diff --git a/book/src/leader-validator-transition.md b/book/src/leader-validator-transition.md index 50eea087638bb3..db12277ed91752 100644 --- a/book/src/leader-validator-transition.md +++ b/book/src/leader-validator-transition.md @@ -93,47 +93,59 @@ ledger is replayed, the validator can run until there is proof that it can be a leader. The Leader can then run and record its transactions. ``` -let (exit_receiver, exit_signal) = channel(); -loop { - // run the validator loop first - let my_slot = leader_scheduler.get_scheduled_tick_height(my_id); - let last_fork = forks.get_latest_fork(); - - // the generator will exit immediatly if tick_height == my_slot.start - assert!(last_fork.tick_height <= my_slot.start, - "start is out of sync, replay the ledger!"); - let generator = PohGenerator::new(last_fork.id, last_fork.tick_height, - my_slot.start, exit_signal); - - // operate the validator, this runs until my_slot.start is reached by the - // generator - let validator = Validator::new(forks, bank, generator, //other params); - - // wait for validator to finish exit_receiver.recv(); - validator.exit(). - - // these entries connect to my_slot - let poh_entries = generator.entries(); - - // make sure this is true - assert!(poh_entries.last().unwrap().tick_height == my_slot.start, - "generator didn't reach my scheduled height, abort!"); - - // get the slot these entries connect to - let starting_slot = poh_entries.first().unwrap().slot_index() - 1; - - // create a fork from the start to my slot - let bank_state = forks.init(my_slot.slot_id, starting_slot); - - // operate as leader - let recorder = PohRecorder::new(poh_entries, my_slot.end, bank_state, - exit_signal); - let leader = Leader::new(bank_state, //other params); - exit_receiver.recv(); - leader.exit(); - - // Finalize the bank_state, send the vote as a validator. - bank_state.finalize(); +/// * forks - the Forks generated by replaying the ledger +/// * leader_scheduler - the leader_scheduler generated by replaying the ledger +/// * ncp - the network + +let main(forks: Forks, leader_scheduler: LeaderScheduler, ncp: Ncp) { + let (exit_receiver, exit_signal) = channel(); + loop { + // run the validator loop first + let my_slot = leader_scheduler.get_scheduled_tick_height(my_id); + let last_fork = forks.latest_fork(); + + // the generator will exit immediatly if tick_height == my_slot.start + assert!(last_fork.tick_height <= my_slot.start, + "start is out of sync, replay the ledger!"); + let generator = PohGenerator::new(last_fork.last_id, last_fork.tick_height, + my_slot.start, exit_signal); + + // operate the validator, this runs until my_slot.start is reached by the + // generator + let validator = Validator::new(forks, leader_scheduler, generator, ncp); + + // wait for validator to finish + exit_receiver.recv(); + // the bank and forked state is outside thie loop; + validator.exit(). + + // these entries connect to my_slot + let poh_entries = generator.entries(); + + // make sure this is true + assert!(poh_entries.last().unwrap().tick_height == my_slot.start, + "generator didn't reach my scheduled height, abort!"); + + // get the slot these entries connect to + let starting_slot = poh_entries + .first() + .map(|e| e.slot_index() - 1) + .unwrap_or(my_slot.slot_id - 1); + + // create a fork from the start to my slot + let bank_state = forks.init(my_slot.slot_id, starting_slot); + + // operate as leader + // recorder will register ticks into the bank_state + let recorder = PohRecorder::new(poh_entries, my_slot.end, bank_state, + exit_signal); + let leader = Leader::new(bank_state, recorder, ncp); + exit_receiver.recv(); + leader.exit(); + + // Finalize the bank_state, send the vote as a validator. + bank_state.finalize(); + } } ``` From 48c6ef4029d17e5dbfa8520308517d39f4669c14 Mon Sep 17 00:00:00 2001 From: Anatoly Yakovenko Date: Mon, 4 Feb 2019 07:41:16 -0800 Subject: [PATCH 07/21] comments --- book/src/leader-validator-transition.md | 36 +++++++++++++++---------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/book/src/leader-validator-transition.md b/book/src/leader-validator-transition.md index db12277ed91752..959aef3ffad8ed 100644 --- a/book/src/leader-validator-transition.md +++ b/book/src/leader-validator-transition.md @@ -8,19 +8,23 @@ bank state. ## Validator -A validator operates on may different concurrent forks of the bank state until +A validator operates on many different concurrent forks of the bank state until it can prove a PoH record at a height that is the scheduled slot to be a leader. +The TVU won't operate on concurrent forks during its slot and that it doesn't +return to validating after its slot is over. + ## Leader -A leader operates on a single fork for a specific PoH slot. +A leader operates on a single fork for a specific PoH slot as +described in the [leader rotation](leader-rotation.md) document. ## PoH -Two different objects for managing PoH for leaders and validators. Both only -work for a specific range of ticks and will error out and send an exit signal -when they are done. +Leaders and Validators use a different object for managing PoH. Each object only +work for a specific range of ticks and will error out and will notify the loop +when it is done. ### PoH Generator @@ -92,12 +96,14 @@ This object manages the transition between modes. The main idea is that once a ledger is replayed, the validator can run until there is proof that it can be a leader. The Leader can then run and record its transactions. -``` -/// * forks - the Forks generated by replaying the ledger -/// * leader_scheduler - the leader_scheduler generated by replaying the ledger -/// * ncp - the network +Some psuedocode describing the loop: + +```rust +/// * forks - The Forks generated by replaying the ledger. +/// * leader_scheduler - The leader_scheduler generated by replaying the ledger. +/// * cluster_info - The network. -let main(forks: Forks, leader_scheduler: LeaderScheduler, ncp: Ncp) { +let main(forks: Forks, leader_scheduler: LeaderScheduler, cluster_info: Arc>) { let (exit_receiver, exit_signal) = channel(); loop { // run the validator loop first @@ -112,7 +118,7 @@ let main(forks: Forks, leader_scheduler: LeaderScheduler, ncp: Ncp) { // operate the validator, this runs until my_slot.start is reached by the // generator - let validator = Validator::new(forks, leader_scheduler, generator, ncp); + let validator = Validator::new(forks, leader_scheduler, generator, cluster_info); // wait for validator to finish exit_receiver.recv(); @@ -139,7 +145,7 @@ let main(forks: Forks, leader_scheduler: LeaderScheduler, ncp: Ncp) { // recorder will register ticks into the bank_state let recorder = PohRecorder::new(poh_entries, my_slot.end, bank_state, exit_signal); - let leader = Leader::new(bank_state, recorder, ncp); + let leader = Leader::new(bank_state, recorder, cluster_info); exit_receiver.recv(); leader.exit(); @@ -151,7 +157,9 @@ let main(forks: Forks, leader_scheduler: LeaderScheduler, ncp: Ncp) { ## BankState -BankState operates over a specific slot. Once the final tick has been -registered the state becomes finalized and further writes will error out. +BankState is tracking changes to the runtime over a specific slot. Once the +final tick has been registered the state becomes finalized and further writes +will error out. + Validators operate over a bunch of different BankStates that represent live active chains. A Leader only operates over a single BankState. From 877521364ee080e2e6a7559a44b1993775ea1d33 Mon Sep 17 00:00:00 2001 From: Anatoly Yakovenko Date: Mon, 4 Feb 2019 07:44:33 -0800 Subject: [PATCH 08/21] docs --- book/src/leader-validator-transition.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/book/src/leader-validator-transition.md b/book/src/leader-validator-transition.md index 959aef3ffad8ed..3a026282ce3fbb 100644 --- a/book/src/leader-validator-transition.md +++ b/book/src/leader-validator-transition.md @@ -89,14 +89,16 @@ PohRecorder { } ``` - ## Fullnode Loop This object manages the transition between modes. The main idea is that once a ledger is replayed, the validator can run until there is proof that it can be a leader. The Leader can then run and record its transactions. -Some psuedocode describing the loop: +The core idea is that the loop is synchronized to PoH, and does a synchronous +start and stop of a validator and leader while having a reference to all the +bank forks and the leader_scheduler. The following is psuedocode for the +loop: ```rust /// * forks - The Forks generated by replaying the ledger. From 53a211d1b306615c65aeac8932edeade649498a0 Mon Sep 17 00:00:00 2001 From: Anatoly Yakovenko Date: Mon, 4 Feb 2019 07:45:23 -0800 Subject: [PATCH 09/21] s/finalized/frozen/ --- book/src/leader-validator-transition.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/src/leader-validator-transition.md b/book/src/leader-validator-transition.md index 3a026282ce3fbb..4944c1b0eb67b0 100644 --- a/book/src/leader-validator-transition.md +++ b/book/src/leader-validator-transition.md @@ -160,7 +160,7 @@ let main(forks: Forks, leader_scheduler: LeaderScheduler, cluster_info: Arc Date: Mon, 4 Feb 2019 08:00:09 -0800 Subject: [PATCH 10/21] more docs --- book/src/leader-validator-transition.md | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/book/src/leader-validator-transition.md b/book/src/leader-validator-transition.md index 4944c1b0eb67b0..ca966d9de2eb88 100644 --- a/book/src/leader-validator-transition.md +++ b/book/src/leader-validator-transition.md @@ -5,6 +5,9 @@ code and in function, but have different behaviors. The goal for this design is to allow the two modes to transition between states while reusing the computed bank state. +The main design idea is centered around a synchronous loop that creates and +destroys Validator or Leader based on what slot was generated by PoH. + ## Validator @@ -101,15 +104,23 @@ bank forks and the leader_scheduler. The following is psuedocode for the loop: ```rust +//// The arguments supplied to the loop persist through the transitions. +/// /// * forks - The Forks generated by replaying the ledger. /// * leader_scheduler - The leader_scheduler generated by replaying the ledger. /// * cluster_info - The network. - -let main(forks: Forks, leader_scheduler: LeaderScheduler, cluster_info: Arc>) { +/// * entry_tree - The ledger. + +let main( + forks: Forks, + leader_scheduler: Arc>, + cluster_info: Arc>, + entry_tree: Arc) +{ let (exit_receiver, exit_signal) = channel(); loop { // run the validator loop first - let my_slot = leader_scheduler.get_scheduled_tick_height(my_id); + let my_slot = leader_scheduler.read().unlock().get_scheduled_tick_height(my_id); let last_fork = forks.latest_fork(); // the generator will exit immediatly if tick_height == my_slot.start @@ -120,7 +131,7 @@ let main(forks: Forks, leader_scheduler: LeaderScheduler, cluster_info: Arc Date: Mon, 4 Feb 2019 10:13:48 -0800 Subject: [PATCH 11/21] update --- book/src/leader-validator-transition.md | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/book/src/leader-validator-transition.md b/book/src/leader-validator-transition.md index ca966d9de2eb88..12628105c6546a 100644 --- a/book/src/leader-validator-transition.md +++ b/book/src/leader-validator-transition.md @@ -43,7 +43,8 @@ scheduled slot. 3. Provide the entries that connect the last vote to the scheduled slot. -``` PohGenerator { +```rust +struct PohGenerator { /// Start the PoH thread, and compute it until end_ticks. /// Once computed, the exit_signal is sent pub fn new( @@ -59,7 +60,8 @@ scheduled slot. /// The list of entries from `start` or `reset_height` to `end_ticks`. /// Error is returned if end_ticks has not been reached yet. pub fn final_entries() -> Result>; -} ``` +} +``` ### PoH Recorder @@ -73,8 +75,8 @@ gets to the end of its scheduled leader slot. 3. Record entries that are produced by the TPU into PoH. -``` -PohRecorder { +```rust +struct PohRecorder { /// Start the PoH thread, and compute it until end_ticks. /// The recorder will continue to call register tick on the BankState. /// TODO: we can remove `register_ticks` on the bank if the slot ticks are @@ -87,7 +89,8 @@ PohRecorder { bank_state: Arc, exit_signal: Sender<()>); - /// Record transactions /// Error is returned if end_ticks is reached first + /// Record transactions + /// Error is returned if end_ticks is reached first pub fn record(&self, mixin: Hash, txs: Vec) -> Result<()>; } ``` @@ -100,7 +103,7 @@ leader. The Leader can then run and record its transactions. The core idea is that the loop is synchronized to PoH, and does a synchronous start and stop of a validator and leader while having a reference to all the -bank forks and the leader_scheduler. The following is psuedocode for the +bank forks and the leader\_scheduler. The following is pseudocode for the loop: ```rust @@ -108,14 +111,14 @@ loop: /// /// * forks - The Forks generated by replaying the ledger. /// * leader_scheduler - The leader_scheduler generated by replaying the ledger. -/// * cluster_info - The network. -/// * entry_tree - The ledger. +/// * cluster_info - The network. The code to continuosly receive and retransmit blocks is owned by this. +/// * block_tree - The ledger. let main( forks: Forks, leader_scheduler: Arc>, cluster_info: Arc>, - entry_tree: Arc) + block_tree: Arc) { let (exit_receiver, exit_signal) = channel(); loop { @@ -131,7 +134,7 @@ let main( // operate the validator, this runs until my_slot.start is reached by the // generator - let validator = Validator::new(&forks, &leader_scheduler, generator, &cluster_info, &entry_tree); + let validator = Validator::new(&forks, &leader_scheduler, generator, &cluster_info, &block_tree); // wait for validator to finish exit_receiver.recv(); @@ -158,7 +161,7 @@ let main( // recorder will register ticks into the bank_state let recorder = PohRecorder::new(poh_entries, my_slot.end, bank_state, exit_signal); - let leader = Leader::new(&bank_state, recorder, &cluster_info, &leader_scheduler, &entry_tree); + let leader = Leader::new(&bank_state, recorder, &cluster_info, &leader_scheduler, &block_tree); exit_receiver.recv(); leader.exit(); From 8b858e06ca2e44033fb0a7181464aacc30fb685c Mon Sep 17 00:00:00 2001 From: Anatoly Yakovenko Date: Mon, 4 Feb 2019 15:03:27 -0800 Subject: [PATCH 12/21] removed bad rust and replaced with psudocode --- book/src/leader-validator-transition.md | 156 ++++++++---------------- 1 file changed, 54 insertions(+), 102 deletions(-) diff --git a/book/src/leader-validator-transition.md b/book/src/leader-validator-transition.md index 12628105c6546a..1b145544a4108a 100644 --- a/book/src/leader-validator-transition.md +++ b/book/src/leader-validator-transition.md @@ -43,25 +43,17 @@ scheduled slot. 3. Provide the entries that connect the last vote to the scheduled slot. -```rust -struct PohGenerator { - /// Start the PoH thread, and compute it until end_ticks. - /// Once computed, the exit_signal is sent - pub fn new( - start: &Hash, - start_ticks: u64, - end_ticks: u64, - exit_signal: Sender<()>); - - /// The is called when a validator votes and needs to reset PoH with its - /// vote. Error is returned if end_ticks is reached first. - pub fn reset(last_id: &Hash, reset_height: u64) -> Result<()>; - - /// The list of entries from `start` or `reset_height` to `end_ticks`. - /// Error is returned if end_ticks has not been reached yet. - pub fn final_entries() -> Result>; -} -``` +The interface to this object implements the following methods: + +* new - Start the PoH thread, and compute it until end\_ticks. This method +would take the starting hash the starting tick height and ending tick height and +create a PoH thread that runs until the end. + +* restart - Restart the PoH thread at a new hash and reset height. It still +runs until the configured ending tick height. + +* final\_entries - Get the entries that connect the set of entries from the +restart point to the final tick as specified by new. ### PoH Recorder @@ -75,25 +67,11 @@ gets to the end of its scheduled leader slot. 3. Record entries that are produced by the TPU into PoH. -```rust -struct PohRecorder { - /// Start the PoH thread, and compute it until end_ticks. - /// The recorder will continue to call register tick on the BankState. - /// TODO: we can remove `register_ticks` on the bank if the slot ticks are - /// the only valid ticks. - /// Once computed, the exit_signal is sent. - pub fn new( - entries: &[Entry], - end_ticks: u64, - sender: Sender>, - bank_state: Arc, - exit_signal: Sender<()>); - - /// Record transactions - /// Error is returned if end_ticks is reached first - pub fn record(&self, mixin: Hash, txs: Vec) -> Result<()>; -} -``` +* new - Create a new recorder, with the starting entries as generated by the +Generator. The recorder runs until the ending tick height, and registers ticks +with a BankState. + +* record - Adds a vector of transactions to PoH, and register the tick with BankState. ## Fullnode Loop @@ -106,70 +84,44 @@ start and stop of a validator and leader while having a reference to all the bank forks and the leader\_scheduler. The following is pseudocode for the loop: -```rust -//// The arguments supplied to the loop persist through the transitions. -/// -/// * forks - The Forks generated by replaying the ledger. -/// * leader_scheduler - The leader_scheduler generated by replaying the ledger. -/// * cluster_info - The network. The code to continuosly receive and retransmit blocks is owned by this. -/// * block_tree - The ledger. - -let main( - forks: Forks, - leader_scheduler: Arc>, - cluster_info: Arc>, - block_tree: Arc) -{ - let (exit_receiver, exit_signal) = channel(); - loop { - // run the validator loop first - let my_slot = leader_scheduler.read().unlock().get_scheduled_tick_height(my_id); - let last_fork = forks.latest_fork(); - - // the generator will exit immediatly if tick_height == my_slot.start - assert!(last_fork.tick_height <= my_slot.start, - "start is out of sync, replay the ledger!"); - let generator = PohGenerator::new(last_fork.last_id, last_fork.tick_height, - my_slot.start, exit_signal); - - // operate the validator, this runs until my_slot.start is reached by the - // generator - let validator = Validator::new(&forks, &leader_scheduler, generator, &cluster_info, &block_tree); - - // wait for validator to finish - exit_receiver.recv(); - // the bank and forked state is outside thie loop; - validator.exit(). - - // these entries connect to my_slot - let poh_entries = generator.entries(); - - // make sure this is true - assert!(poh_entries.last().unwrap().tick_height == my_slot.start, - "generator didn't reach my scheduled height, abort!"); - - // get the slot these entries connect to - let starting_slot = poh_entries - .first() - .map(|e| e.slot_index() - 1) - .unwrap_or(my_slot.slot_id - 1); - - // create a fork from the start to my slot - let bank_state = forks.init(my_slot.slot_id, starting_slot); - - // operate as leader - // recorder will register ticks into the bank_state - let recorder = PohRecorder::new(poh_entries, my_slot.end, bank_state, - exit_signal); - let leader = Leader::new(&bank_state, recorder, &cluster_info, &leader_scheduler, &block_tree); - exit_receiver.recv(); - leader.exit(); - - // Finalize the bank_state, send the vote as a validator. - bank_state.finalize(); - } -} -``` +The fullnode loop: + +These components are passed in into the loop from the outside. + +* forks - The Forks generated by replaying the ledger. + +* leader\_scheduler - The leader\_scheduler generated by replaying the ledger. + +* cluster\_info - The network. The code to continuously receive and retransmit +blocks is owned by this. + +* block\_tree - The ledger. + +loop: + +1. Get my next scheduled slot. + +2. Start the PoH Generator to run until the next scheduled slot. + +3. Run the TVU over all the forks. + + a. TVU will send votes for votable forks. + +4. Wait for PoH generator to finish. + +5. Stop the TVU. + +6. Get the entries connecting a voted slot in Forks to the scheduled leader slot. + +7. Start a PoH recorder for the leader slot range of ticks. + +8. Start the TPU over a BankState checkpoint for the leader slot. + +9. Wait for the PoH recorder to finish. + + a. TPU will freeze BankState. The vote will be sent in 3. + +10. Goto 1. ## BankState From 6c70204e606075dd08b78143d329871f81623fae Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Wed, 6 Feb 2019 16:20:49 -0700 Subject: [PATCH 13/21] Cleanup --- book/src/leader-validator-transition.md | 35 ++++++++++++------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/book/src/leader-validator-transition.md b/book/src/leader-validator-transition.md index 1b145544a4108a..bd96e91d0ffa55 100644 --- a/book/src/leader-validator-transition.md +++ b/book/src/leader-validator-transition.md @@ -25,16 +25,15 @@ described in the [leader rotation](leader-rotation.md) document. ## PoH -Leaders and Validators use a different object for managing PoH. Each object only -work for a specific range of ticks and will error out and will notify the loop -when it is done. +Leaders and Validators use different objects for managing PoH. Each object only +works for a specific range of ticks and will exit the loop when it is done. ### PoH Generator -This object handles PoH generation for a Validator. It solves the following -problems for validators +The PoH Generator generates a PoH stream for a Validator. It solves the following +problems for validators: -1. Keep track of *time* as defined by PoH height, and stop the validator when it +1. Keep track of *time* as defined by PoH height and stop the validator when it hits the scheduled leader slot. 2. Reset the clock whenever a validator votes. The last validator vote is the @@ -63,15 +62,15 @@ problems: 1. Keep track of *time* as defined by PoH height, and stop the leader when it gets to the end of its scheduled leader slot. -2. Register ticks with the BankState as they are produced by PoH. +2. Register ticks with the BankFork as they are produced by PoH. 3. Record entries that are produced by the TPU into PoH. * new - Create a new recorder, with the starting entries as generated by the Generator. The recorder runs until the ending tick height, and registers ticks -with a BankState. +with a BankFork. -* record - Adds a vector of transactions to PoH, and register the tick with BankState. +* record - Adds a vector of transactions to PoH, and register the tick with BankFork. ## Fullnode Loop @@ -105,7 +104,7 @@ loop: 3. Run the TVU over all the forks. - a. TVU will send votes for votable forks. + 1. TVU will send votes for votable forks. 4. Wait for PoH generator to finish. @@ -115,19 +114,19 @@ loop: 7. Start a PoH recorder for the leader slot range of ticks. -8. Start the TPU over a BankState checkpoint for the leader slot. +8. Start the TPU over a BankFork checkpoint for the leader slot. 9. Wait for the PoH recorder to finish. - a. TPU will freeze BankState. The vote will be sent in 3. + 1. TPU will freeze BankFork. The vote will be sent in 3. 10. Goto 1. -## BankState +## BankFork -BankState is tracking changes to the runtime over a specific slot. Once the -final tick has been registered the state becomes frozen and further writes -will error out. +BankFork tracks changes to the bank state over a specific slot. Once the +final tick has been registered the state is frozen. Any attempts to write +to are rejected. -Validators operate over a bunch of different BankStates that represent live -active chains. A Leader only operates over a single BankState. +Validators operate over multiple BankForks, each reprsenting a live +active chain. A Leader only operates over a single BankFork. From ba1ecd2cda65646b04d531596fde418e96244bea Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Wed, 6 Feb 2019 16:22:36 -0700 Subject: [PATCH 14/21] Add proposal to SUMMARY.md --- book/src/SUMMARY.md | 1 + book/src/leader-validator-transition.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index a0178d752da55a..e2cbc9901ea507 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -48,3 +48,4 @@ - [Economic Sustainability](ed_economic_sustainability.md) - [Attack Vectors](ed_attack_vectors.md) - [References](ed_references.md) + - [Leader-to-Validator Transition](leader-validator-transition.md) diff --git a/book/src/leader-validator-transition.md b/book/src/leader-validator-transition.md index bd96e91d0ffa55..de6c460906cfac 100644 --- a/book/src/leader-validator-transition.md +++ b/book/src/leader-validator-transition.md @@ -1,4 +1,4 @@ -# Leader and Validator Transition +# Leader-to-Validator Transition A fullnode operates in two modes, leader and validator. The modes overlap in code and in function, but have different behaviors. The goal for this design is From f3d2d7171641ca4fcf42c71cff5c95592922dc9a Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Thu, 7 Feb 2019 10:32:51 -0700 Subject: [PATCH 15/21] Delete references to terminology defined in the Fork Deltas proposal ...where "live active chain" was changed to "active fork". --- book/src/leader-validator-transition.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/book/src/leader-validator-transition.md b/book/src/leader-validator-transition.md index de6c460906cfac..d6e56c6a981b18 100644 --- a/book/src/leader-validator-transition.md +++ b/book/src/leader-validator-transition.md @@ -127,6 +127,3 @@ loop: BankFork tracks changes to the bank state over a specific slot. Once the final tick has been registered the state is frozen. Any attempts to write to are rejected. - -Validators operate over multiple BankForks, each reprsenting a live -active chain. A Leader only operates over a single BankFork. From 496eac572c86d16897fa9d28662ed6b6893fe800 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Thu, 7 Feb 2019 16:05:33 -0700 Subject: [PATCH 16/21] Attempt to simplify the algorithm --- book/src/leader-validator-transition.md | 72 +++++++++++-------------- 1 file changed, 31 insertions(+), 41 deletions(-) diff --git a/book/src/leader-validator-transition.md b/book/src/leader-validator-transition.md index d6e56c6a981b18..b43b21c2433d27 100644 --- a/book/src/leader-validator-transition.md +++ b/book/src/leader-validator-transition.md @@ -1,31 +1,31 @@ # Leader-to-Validator Transition -A fullnode operates in two modes, leader and validator. The modes overlap in -code and in function, but have different behaviors. The goal for this design is -to allow the two modes to transition between states while reusing the computed -bank state. +A fullnode typically operates as a validator. If, however, a staker delegates +its stake to a fullnode, it will occationally be selected as a *slot leader*. +As a slot leader, the fullnode is resposible for producing blocks during an +assigned *slot*. A slot has a duration of some number of preconfigured +*ticks*. The duration of those ticks are estimated with a *PoH Generator* +described later in this document. -The main design idea is centered around a synchronous loop that creates and -destroys Validator or Leader based on what slot was generated by PoH. +## BankFork +BankFork tracks changes to the bank state over a specific slot. Once the +final tick has been registered the state is frozen. Any attempts to write +to are rejected. ## Validator A validator operates on many different concurrent forks of the bank state until -it can prove a PoH record at a height that is the scheduled slot to be a leader. +it can prove a PoH record at a height within the slot it is scheduled to be +the slot leader. -The TVU won't operate on concurrent forks during its slot and that it doesn't -return to validating after its slot is over. - -## Leader - -A leader operates on a single fork for a specific PoH slot as -described in the [leader rotation](leader-rotation.md) document. +## Slot Leader +A slot leader builds blocks on top of only one fork, the one it last voted on. ## PoH -Leaders and Validators use different objects for managing PoH. Each object only +Slot leaders and validators use different objects for managing PoH. Each object only works for a specific range of ticks and will exit the loop when it is done. ### PoH Generator @@ -56,7 +56,7 @@ restart point to the final tick as specified by new. ### PoH Recorder -This object handles PoH recording for a Leader. It solves the following +This object handles PoH recording for a slot leader. It solves the following problems: 1. Keep track of *time* as defined by PoH height, and stop the leader when it @@ -76,12 +76,12 @@ with a BankFork. This object manages the transition between modes. The main idea is that once a ledger is replayed, the validator can run until there is proof that it can be a -leader. The Leader can then run and record its transactions. +leader. The slot leader can then execute and record its transactions. -The core idea is that the loop is synchronized to PoH, and does a synchronous -start and stop of a validator and leader while having a reference to all the -bank forks and the leader\_scheduler. The following is pseudocode for the -loop: +The core idea is that the loop is synchronized to PoH and does a synchronous +start and stop of the slot leader functionality. After stopping, the validator's +TVU should find itself in the same state as if a different leader had sent it +the same block. The following is pseudocode for the loop: The fullnode loop: @@ -98,32 +98,22 @@ blocks is owned by this. loop: -1. Get my next scheduled slot. - -2. Start the PoH Generator to run until the next scheduled slot. +1. Query the leader\_schedular for the next assigned slot. -3. Run the TVU over all the forks. +2. Run the TVU over all the forks. - 1. TVU will send votes for votable forks. + 1. TVU will send votes to what it believes is the "best" fork. + 2. After each vote, restart the PoH Generator to run until the next assigned slot. -4. Wait for PoH generator to finish. +3. Wait for PoH generator to finish. -5. Stop the TVU. +4. Start a PoH recorder at the last hash of the PoH Generator. -6. Get the entries connecting a voted slot in Forks to the scheduled leader slot. +5. Start the TPU. Point it to the last fork the TVU voted on. -7. Start a PoH recorder for the leader slot range of ticks. +7. Wait for the PoH recorder to finish. -8. Start the TPU over a BankFork checkpoint for the leader slot. + 1. TPU will freeze BankFork. The vote will be sent in 2. -9. Wait for the PoH recorder to finish. +8. Goto 1. - 1. TPU will freeze BankFork. The vote will be sent in 3. - -10. Goto 1. - -## BankFork - -BankFork tracks changes to the bank state over a specific slot. Once the -final tick has been registered the state is frozen. Any attempts to write -to are rejected. From 49c5b12ac7f0ce4077a3ee7d33f0a0ab19980b05 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Thu, 7 Feb 2019 16:59:53 -0700 Subject: [PATCH 17/21] Fix numbers --- book/src/leader-validator-transition.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/src/leader-validator-transition.md b/book/src/leader-validator-transition.md index b43b21c2433d27..c5fb4545d120cb 100644 --- a/book/src/leader-validator-transition.md +++ b/book/src/leader-validator-transition.md @@ -111,9 +111,9 @@ loop: 5. Start the TPU. Point it to the last fork the TVU voted on. -7. Wait for the PoH recorder to finish. +6. Wait for the PoH recorder to finish. 1. TPU will freeze BankFork. The vote will be sent in 2. -8. Goto 1. +7. Goto 1. From 685bd90e843284b103f3927c579c28a199b1bf62 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Fri, 8 Feb 2019 17:16:20 -0700 Subject: [PATCH 18/21] Rewrite with just one PoH Recorder --- book/src/leader-validator-transition.md | 121 +++++++++++------------- 1 file changed, 53 insertions(+), 68 deletions(-) diff --git a/book/src/leader-validator-transition.md b/book/src/leader-validator-transition.md index c5fb4545d120cb..15e62fb09ea231 100644 --- a/book/src/leader-validator-transition.md +++ b/book/src/leader-validator-transition.md @@ -1,87 +1,74 @@ # Leader-to-Validator Transition A fullnode typically operates as a validator. If, however, a staker delegates -its stake to a fullnode, it will occationally be selected as a *slot leader*. -As a slot leader, the fullnode is resposible for producing blocks during an -assigned *slot*. A slot has a duration of some number of preconfigured -*ticks*. The duration of those ticks are estimated with a *PoH Generator* -described later in this document. +its stake to a fullnode, it will occasionally be selected as a *slot leader*. +As a slot leader, the fullnode is responsible for producing blocks during an +assigned *slot*. A slot has a duration of some number of preconfigured *ticks*. +The duration of those ticks are estimated with a *PoH Recorder* described later +in this document. ## BankFork -BankFork tracks changes to the bank state over a specific slot. Once the -final tick has been registered the state is frozen. Any attempts to write -to are rejected. +BankFork tracks changes to the bank state over a specific slot. Once the final +tick has been registered the state is frozen. Any attempts to write to are +rejected. ## Validator A validator operates on many different concurrent forks of the bank state until -it can prove a PoH record at a height within the slot it is scheduled to be -the slot leader. +it can prove a PoH record at a height within the slot it is scheduled to be the +slot leader. ## Slot Leader A slot leader builds blocks on top of only one fork, the one it last voted on. -## PoH +## PoH Recorder -Slot leaders and validators use different objects for managing PoH. Each object only -works for a specific range of ticks and will exit the loop when it is done. +Slot leaders and validators use a PoH Recorder for both estimating slot height +and for recording transactions. -### PoH Generator +### PoH as a Validators -The PoH Generator generates a PoH stream for a Validator. It solves the following -problems for validators: +The PoH Recorder acts as a simple VDF when validating. It tells the validator +when it needs to switch to the slot leader role. Every time the validator votes +on a fork, it should use the fork's latest block id to re-seed the VDF. +Re-seeding solves two problems. First, it synchronizes its VDF to the leader's, +allowing it to more accurately determine when its leader slot begins. Second, +if the previous leader goes down, all wallclock time is accounted for in the +next leader's PoH stream. For example, if one block is missing when the leader +starts, the block it produces should have a PoH duration of two blocks. The +longer duration ensures the following leader isn't attempting to snip all the +transactions from the previous leader's slot. -1. Keep track of *time* as defined by PoH height and stop the validator when it -hits the scheduled leader slot. +### PoH as a Slot Leader -2. Reset the clock whenever a validator votes. The last validator vote is the -validators most recent commitment. It is therefore the closest fork to the -scheduled slot. +A slot leader use the PoH Recorder to record transactions, locking their +positions in time. The PoH hash must be derived from a previous leader's last +block. If it isn't, its block will fail PoH verification and be rejected by +the cluster. -3. Provide the entries that connect the last vote to the scheduled slot. +The PoH Recorder also serves to inform the slot leader when its slot is over. +The leader needs to take care not to modify its bank if recording the +transaction would generate a PoH height outside its designated slot. The +leader, therefore, should not commit account changes until after it generates +the entry's PoH hash. When the PoH height falls outside its slot any +transactions in its pipeline may be dropped or forwarded to the next leader. +Forwarding is preferred, as it would minimize network congestion, allowing the +cluster to advertise higher TPS capacity. -The interface to this object implements the following methods: - -* new - Start the PoH thread, and compute it until end\_ticks. This method -would take the starting hash the starting tick height and ending tick height and -create a PoH thread that runs until the end. - -* restart - Restart the PoH thread at a new hash and reset height. It still -runs until the configured ending tick height. - -* final\_entries - Get the entries that connect the set of entries from the -restart point to the final tick as specified by new. - -### PoH Recorder - -This object handles PoH recording for a slot leader. It solves the following -problems: - -1. Keep track of *time* as defined by PoH height, and stop the leader when it -gets to the end of its scheduled leader slot. - -2. Register ticks with the BankFork as they are produced by PoH. - -3. Record entries that are produced by the TPU into PoH. - -* new - Create a new recorder, with the starting entries as generated by the -Generator. The recorder runs until the ending tick height, and registers ticks -with a BankFork. - -* record - Adds a vector of transactions to PoH, and register the tick with BankFork. ## Fullnode Loop -This object manages the transition between modes. The main idea is that once a -ledger is replayed, the validator can run until there is proof that it can be a -leader. The slot leader can then execute and record its transactions. +The PoH Recorder manages the transition between modes. Once a ledger is +replayed, the validator can run until the recorder indicates it should be +the slot leader. As a slot leader, the node can then execute and record +transactions. -The core idea is that the loop is synchronized to PoH and does a synchronous -start and stop of the slot leader functionality. After stopping, the validator's -TVU should find itself in the same state as if a different leader had sent it -the same block. The following is pseudocode for the loop: +The loop is synchronized to PoH and does a synchronous start and stop of the +slot leader functionality. After stopping, the validator's TVU should find +itself in the same state as if a different leader had sent it the same block. +The following is pseudocode for the loop: The fullnode loop: @@ -92,28 +79,26 @@ These components are passed in into the loop from the outside. * leader\_scheduler - The leader\_scheduler generated by replaying the ledger. * cluster\_info - The network. The code to continuously receive and retransmit -blocks is owned by this. + blocks is owned by this. * block\_tree - The ledger. loop: -1. Query the leader\_schedular for the next assigned slot. +1. Query the leader\_scheduler for the next assigned slot. 2. Run the TVU over all the forks. 1. TVU will send votes to what it believes is the "best" fork. - 2. After each vote, restart the PoH Generator to run until the next assigned slot. - -3. Wait for PoH generator to finish. - -4. Start a PoH recorder at the last hash of the PoH Generator. + 2. After each vote, restart the PoH Recorder to run until the next assigned +slot. -5. Start the TPU. Point it to the last fork the TVU voted on. +3. When time to be a slot leader, start the TPU. Point it to the last fork the + TVU voted on. -6. Wait for the PoH recorder to finish. +4. Produce entries until the end of the slot. 1. TPU will freeze BankFork. The vote will be sent in 2. -7. Goto 1. +5. Goto 1. From a8456d97e34e76d91ad1b632fc1a9d35ee90dd8e Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Fri, 8 Feb 2019 20:20:27 -0700 Subject: [PATCH 19/21] Review feedback --- book/src/leader-validator-transition.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/book/src/leader-validator-transition.md b/book/src/leader-validator-transition.md index 15e62fb09ea231..4a0386f39e946e 100644 --- a/book/src/leader-validator-transition.md +++ b/book/src/leader-validator-transition.md @@ -86,19 +86,15 @@ These components are passed in into the loop from the outside. loop: 1. Query the leader\_scheduler for the next assigned slot. - 2. Run the TVU over all the forks. - 1. TVU will send votes to what it believes is the "best" fork. 2. After each vote, restart the PoH Recorder to run until the next assigned slot. - 3. When time to be a slot leader, start the TPU. Point it to the last fork the TVU voted on. - 4. Produce entries until the end of the slot. - - 1. TPU will freeze BankFork. The vote will be sent in 2. - + 1. For the duration of the slot, the TVU must not vote on other forks. + 2. After the slot ends, the TPU freezes its BankFork. After freezing, + the TVU may resume voting. 5. Goto 1. From 79ed8881a819a92bd8e706509a4afbee77b5920a Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Fri, 8 Feb 2019 20:26:08 -0700 Subject: [PATCH 20/21] Delete the mostly unused list of components --- book/src/leader-validator-transition.md | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/book/src/leader-validator-transition.md b/book/src/leader-validator-transition.md index 4a0386f39e946e..df036e50ba5152 100644 --- a/book/src/leader-validator-transition.md +++ b/book/src/leader-validator-transition.md @@ -70,22 +70,7 @@ slot leader functionality. After stopping, the validator's TVU should find itself in the same state as if a different leader had sent it the same block. The following is pseudocode for the loop: -The fullnode loop: - -These components are passed in into the loop from the outside. - -* forks - The Forks generated by replaying the ledger. - -* leader\_scheduler - The leader\_scheduler generated by replaying the ledger. - -* cluster\_info - The network. The code to continuously receive and retransmit - blocks is owned by this. - -* block\_tree - The ledger. - -loop: - -1. Query the leader\_scheduler for the next assigned slot. +1. Query the LeaderScheduler for the next assigned slot. 2. Run the TVU over all the forks. 1. TVU will send votes to what it believes is the "best" fork. 2. After each vote, restart the PoH Recorder to run until the next assigned From 704d3ec410c952a12690621c7e6d5bab96e43cd1 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Wed, 13 Feb 2019 11:53:11 -0700 Subject: [PATCH 21/21] Apply review feedback --- book/src/leader-validator-transition.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/book/src/leader-validator-transition.md b/book/src/leader-validator-transition.md index df036e50ba5152..b5e1056df4afa3 100644 --- a/book/src/leader-validator-transition.md +++ b/book/src/leader-validator-transition.md @@ -16,8 +16,7 @@ rejected. ## Validator A validator operates on many different concurrent forks of the bank state until -it can prove a PoH record at a height within the slot it is scheduled to be the -slot leader. +it generates a PoH hash with a height within its leader slot. ## Slot Leader @@ -28,7 +27,7 @@ A slot leader builds blocks on top of only one fork, the one it last voted on. Slot leaders and validators use a PoH Recorder for both estimating slot height and for recording transactions. -### PoH as a Validators +### PoH Recorder when Validating The PoH Recorder acts as a simple VDF when validating. It tells the validator when it needs to switch to the slot leader role. Every time the validator votes @@ -41,7 +40,7 @@ starts, the block it produces should have a PoH duration of two blocks. The longer duration ensures the following leader isn't attempting to snip all the transactions from the previous leader's slot. -### PoH as a Slot Leader +### PoH Recorder when Leading A slot leader use the PoH Recorder to record transactions, locking their positions in time. The PoH hash must be derived from a previous leader's last