karolinagrzeszkiewicz
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # hotstuff-rs v. 0.4 changelog The changes can be grouped as follows: 1. Consensus for validator-set-updating blocks, 2. Validator set speculation, 3. View synchronization and block sync trigger, 4. Related refactoring and restructuring for the sake of integrating the above-mentioned features, 5. Bug fixes: - pending validator sets, - commit rule, - locking precommit and commit QCs 6. Enhancements: - allow replicas to vote for a known block - wrapper types to replace type aliasing, e.g., with `CryptoHash` or `ViewNumber` (optional) # Changes to the `BlockTree` fields New fields: 1. `resigning_validator_set`, deserialized into `ValidatorSet` type, 2. `locked_validator_set_updating_block`, deserialized into `CryptoHash` type, 3. `locked_validator_set_updating_view`, deserialized into `ViewNumber` type, 4. `highest_block_justify`, deserialized into `QuorumCertificate` type, 5. `highest_tc`, deserialized into `TimeoutCertificate` type. Modified fields: 1. Replace `pending_validator_set_updates` with `validator_set_updates_info` (or `attached_validator_set_updates`), deserialized into `ValidatorSetUpdatesInfo` type # Consensus for validator-set-updating blocks ## New `Validators` module This module regulates what validators can do in the speculation vs. "normal" periods, for instance when they can act as leaders, when they can vote and what they can vote for. The predicates contained in this module implement the validator set speculation protocol for the speculation period, and for the "normal" period they generalize predicates that are already found in the code of the `Algorithm` module. ### `ValidatorSetState` struct ```rust enum ValidatorSetState = { StableValidatorSet { validator_set: ValidatorSet}, ValidatorSetUpdate { new_validator_set: ValidatorSet, old_validator_set: ValidatorSet, block: CryptoHash, } } ``` **Methods:** #### `i_am_voter` This method generalizes the predicate from [this line](https://github.com/parallelchain-io/hotstuff_rs/blob/1f4352b9941ffa0deaa5192e7388361b39575017/src/algorithm.rs#L356) and contains an additional check that implements the validator set speculation protocol. - For `StableValidatorSet{vs}`, a validator from `vs` is always a voter (unless the vote is for the "decide" phase in which case the validator set state should have been updated to `ValidatorSetUpdate{vs', vs, b}`). - For `ValidatorSetUpdate{vs', vs, b}` a validator can be a voter for a given nudge or proposal depending on its membership in `vs` or `vs'` and the `justify` QC of the proposal or the nudge. ```rust fn i_am_voter(self, cur_view: ViewNumber, justify: QuorumCertificate) -> bool { match self { StableValidatorSet(_) => true, ValidatorSetUpdate{vs', vs, b} => { if (vs.contains(me) && !vs'.contains(me) { justify.block == b && justify.phase <= Commit } else if (vs'.contains(me) && !vs.contains(me) { justify.block == b && qc.phase >= Commit } else { vs.contains(me) && vs'.contains(me) } } } } ``` #### `i_am_leader` This method generalizes the predicate from [this line](https://github.com/parallelchain-io/hotstuff_rs/blob/1f4352b9941ffa0deaa5192e7388361b39575017/src/algorithm.rs#L142) and contains an additional check that implements the validator set speculation protocol. For `StableValidatorSet{vs}`, a validator is a leader of the current view if it is the leader of the current view for`vs`. For `ValidatorSetUpdate{vs', vs, b}` a validator is a leader of the current view if and only if: 1. The validator is a leader of the current view for `vs` or `vs'`, and 2. The validator has a `highest_qc` on top of which it can propose or nudge given its membership in the committed or resigning validator set. ```rust fn i_am_leader(self, cur_view: ViewNumber, highest_qc: QuorumCertificate, pacemaker: impl Pacemaker) -> (bool, Option<Justify>) { match self { StableValidatorSet(vs) => { pacemaker.view_leader(cur_view, &vs) == me.public() }, ValidatorSetUpdate{vs', vs, b} => { let i_am_leader_of_vs = pacemaker.view_leader(cur_view, &vs) == me.public(); let i_am_leader_of_vs' = pacemaker.view_leader(cur_view, &vs') == me.public(); if i_am_leader_of_vs { highest_qc.block == b && highest_qc.phase = Commit) } else if i_am_leader_of_vs' { highest_qc.block == b && highest_qc.phase >= Commit } else { false } } } ``` #### `next_leader` Generalizes [this line](https://github.com/parallelchain-io/hotstuff_rs/blob/1f4352b9941ffa0deaa5192e7388361b39575017/src/algorithm.rs#L357). By `next_leader` we mean the intended recipient of a given `Vote` message. For `StableValidatorSet{vs}`, the next leader is simply the leader of the next view for `vs`. For `ValidatorSetUpdate{vs', vs, b}` the next leader, to whom the vote is addressed, depends on the phase of the vote. ```rust fn next_leader(self, cur_view, vote: &Vote, pacemaker: impl Pacemaker) -> VerifyingKey { match self { StableValidatorSet(vs) => { pacemaker.view_leader(cur_view+1, &vs) }, ValidatorSetUpdate{vs', vs, b} => { if vote.block == b { match vote.phase { Prepare | Precommit(_) | Commit(_) => pacemaker.view_leader(cur_view+1, &vs), Decide(_) => pacemaker.view_leader(cur_view+1, &vs') } } else { panic!("During the Validator Set Update phase a validator can only vote for the validator-set-updating block!") } } } ``` #### Optional: `i_am_next_leader` Generalizes [this predicate](https://github.com/parallelchain-io/hotstuff_rs/blob/1f4352b9941ffa0deaa5192e7388361b39575017/src/algorithm.rs#L411) used to determine whether a replica is the next leader. ```rust fn i_am_next_leader(self, cur_view) -> bool { match self { StableValidatorSet(vs) => { me.public() == pacemaker.view_leader(cur_view + 1, vs) }, ValidatorSetUpdate{vs', vs, b} => { me.public() == pacemaker.view_leader(cur_view + 1, vs) || me.public() == pacemaker.view_leader(cur_view + 1, vs') } } ``` ## Safety-related changes in `Types` and `State` With the validator set speculation and the new consensus protocol for validator-set-updating blocks, as well as some of the bug fixes, we have plenty of changes to the safety predicates, which calls for neat and type-safe abstractions. ### `Justify` enum This struct is a wrapper for the `QuorumCertificate` type that introduces more type safety. The idea is that only some `QuorumCertificate`s, i.e., the ones from `Generic` or `Decide` phase, can serve as the `justify` of a block, and only some i.e, the ones from `Prepare`, `Precommit` or `Commit` phase, can serve as the `justify` of a `Nudge`. ```rust pub(crate) enum Justify { BlockJustify(QuorumCertificate), NudgeJustify(QuorumCertificate) ``` This is merely for the sake of code readibility and in order to avoid listing all acceptable phases of a given QC, like we do in `safe_qc`. Instead, we will: - Define `From` and `Into` instances to convert between the `Justify` and `QuorumCertificate` types, - Change the type of the `justify` fields of a `Block` and a `Nudge` to the `Justify` type, - In `safe_block` and on receiving a `Nudge`, check if its `justify` is a `BlockJustify` and a `NudgeJustify` respectively, - in `safe_qc` pattern match on the `Justify::from(qc)` ### `safe_qc` New requirements: 1. If a validator set updating block is locked, then the `NudgeJustify` must be for that block, and(! or have higher view than the lock) 2. Consecutive views requirement: `prepareQC.view + 1 = precommitQC.view` and `precommitQC.view + 1 = commitQC.view`. ```rust pub fn safe_qc(&self, qc: &QuorumCertificate, chain_id: ChainID) -> bool { /* 1 */ (qc.chain_id == chain_id || qc.is_genesis_qc()) && /* 2 */ (self.contains(&qc.block) || block_justify.qc.is_genesis_qc()) && /* 3 */ qc.view >= self.locked_view() && match Justify::from(qc) { BlockJustify(qc) => { /* 4 */ (qc.phase.is_generic() && !self.contains_validator_set_updates(&qc.block) || qc.phase.is_decide() && self.contains_validator_set_updates(&qc.block)) }, NudgeJustify(qc) => { /* 4 */ self.contains_validator_set_updates(&qc.block) && /* 5 */ (self.locked_validator_set_updating_block().is_none() || qc.block == self.locked_validator_set_updating_block().unwrap() || qc.view > self.locked_validator_set_updating_view().unwrap()) /* 6 */ match qc.phase { Precommit(prepare_qc_view) => qc.view == prepare_qc_view + 1, Commit(precommit_qc_view) => qc.view == precommit_qc_view + 1, _ => true } } } } ``` **Note:** on receiving any nudge or proposal, a replica should check if it should unlock `self.locked_validator_set_updating_block()` (because a QC with a view higher than the `locked_validator_set_updating_block_view` was seen) before checking `safe_qc`! **Note:** `safe_qc` (along with `correct_qc` and `safe_block`) may not be sufficient cobdition for a replica to vote for a given nudge or proposal. For prepare and precommit QCs, a replica should check if `cur_view = qc.view + 1` before voting. **Also note** that after checking `safe_qc` on receiving a nudge, we should also check if the lock on a validator-set-updating block should be released and/or another block should be locked. This is different from the order as specified in the protocol spec, where the lock should be checked and possibly released before checking `safe_qc`. This is why the line `qc.view > self.locked_validator_set_updating_view().unwrap()` is included in condition 4. ### New `correct_qc` method of the `BlockTree` This method serves to check the cryptographic correctness of a `BlockJustify` against an appropriate validator set, depending on where the replica is in the protocol, i.e., depending on the state of the `BlockTree`. The assumption behind this implementation is that a replica may need to verify the correctness of a QC for a parent block more than once. Likewise, it may need to validate a parent QC, for example after seeing a `decideQC`, a replica may need to validate a commitQC since other replicas have not received the `decideQC` and are nuding commitQC. In general, a replica may need to go one step back in validating QCs - since seeing a QC is generally an evidence that a quorum is at most one step behind. Hence, how a QC for a validator-set-updating block should be validated depends on whether the validator set updates have already been applied or are still pending. ```rust pub fn correct_qc(&self, qc: &QuorumCertificate) -> bool { match qc.phase { Generic => qc.is_correct(self.committed_validator_set()), Prepare | Precommit(_) | Commit(_) => { match self.validator_set_updates(qc.block) { Pending(vs_updates) => qc.is_correct(self.committed_validator_set()), Committed(vs_updates) => { let prev_vs = self.committed_validator_set().apply(vs_updates.inverse()); qc.is_correct(prev_vs) }, _ => false // The block must have associated validator set updates! } }, Decide(_) => { match self.validator_set_updates(qc.block) { Pending(vs_updates) => { let vs = self.committed_validator_set(); let vs' = self.committed_validator_set().apply(vs_updates); let (vs'_qc, vs_qc) = self.qc.split(vs', vs); vs'_qc.is_correct(vs') && vs_qc.is_correct(vs) }, Committed(vs_updates) => { let vs' = self.committed_validator_set(); let vs = self.committed_validator_set().apply(vs_updates.inverse()); let (vs'_qc, vs_qc) = self.qc.split(vs', vs); vs'_qc.is_correct(vs') && vs_qc.is_correct(vs) } } } } } ``` ### Modified `safe_block` Two changes: 1. New requirement: If a block is locked, then to be `safe_block` either the block must be the locked block, i.e., have the same hash, or its `justify.view` must be greater than the view of the locked block 2. Instead of `(block.justify.phase.is_generic() || block.justify.phase.is_commit())`, check if the `justify` is of the `BlockJustify` type 4. Removed requirement: `!self.contains(&block.hash)`. **Note** that this requirement is not entirely removed, rather **we will check if the block is already in the `BlockTree` before inserting it to the `BlockTree`**. However, removing the requirement from `safe_block` allows us to vote for a block that is already in the block tree - this is essential fro the liveness of the consensus protocol for validator set updating blocks. **Also note** that after checking `safe_block` on receiving a proposal, we should also check if the lock on a validator-set-updating block should be released and/or another block should be locked. This is different from the order as specified in the protocol spec, where the lock should be checked and possibly released before checking `safe_block`. This is why the line `qc.view > self.locked_validator_set_updating_view().unwrap()` is included in condition 4. ```rust pub fn safe_block(&self, block: &Block, chain_id: ChainID) -> bool { /* 1 */ self.safe_qc(&block.justify, chain_id) && /* 2 */ (self.locked_validator_set_updating_block.is_none() || block.hash == self.locked_validator_set_updating_block().unwrap() || block.justify.view > self.locked_validator_set_updating_view().unwrap() ) } ``` ### Commit rule A method of the `BlockTree`. We check if the justify of a block is a valid commitQC for the block's great-grandparent, i.e., if it satisfies the commit rule. This is meant to be applied only to a genericQC. ```rust let parent_justify = self.block_justify(block_justify.qc.block); let grandparent_justify = self.block_justify(parent_justify.qc.block); block_justify.qc.view == parent_justify.qc.view + 1 && parent_justify.qc.view == granparent_justify.qc.view + 1 ``` This check shall be integrated into [these lines](https://github.com/parallelchain-io/hotstuff_rs/blob/1f4352b9941ffa0deaa5192e7388361b39575017/src/state.rs#L192), and divided into two short-circuiting checks. ### `insert_block` and `commit_block` TODO: make sure `insert_block` is self-sufficient for committing and deciding blocks, as it is the only function that writes to the `BlockTree` when in sync. #### `insert_block` Outline of changes: 1. Check if the lock on a validator-set-updating block should be removed or updated, 2. Replace the `Commit` phase justify, with a `Decide` phase justify, 3. For a `Decide` justify, still call `commit_block` (in case the block has not been committed yet, for instance if `insert_block` is called from `sync`), 4. For a `Decide` justify, set `resigning_validator_set = None`, i.e., forget about the old validator set. 5. For a `Decide(v)` justify, set `locked_view = v` (`v` is the view of the `commitQC`). #### `commit_block` Outline of changes: 1. For a validator-set-updating block, set the newly committed and resigning validator sets accordingly. <!-- 2. For a validatir-set-updating block, update `locked_view = commitQC.view`. --> ## Algorithm 1. `on_receive_nudge` and `on_receive_proposal` must incorporate changes regarding the locking behaviour, extra collectors, and forgetting the resigning validator set. 2. `on_receive_proposal` should also check if the block exists before inserting. 3. `on_receive_nudge` should call `commit_block` on receiving a commitQC. 4. `on_receive_nudge` should not update `locked_view`, unless it is a commitQC, in which case update `locked_view = commitQC.view`. ## Types module (updates) The changes in the module concern adding a new `decideQC`, they mostly apply to the `QuorumCertificate` struct and its methods, as well as the `VoteCollector`. ### `ValidatorSetUpdatesInfo` Define a new `ValidatorSetUpdatesInfo` enum, which can be used to map blocks to their associated validator set updates (if any), containing information on whether the updates have been applied or not. Introducing this enum is part of the solution to [issue 28](https://github.com/parallelchain-io/hotstuff_rs/issues/28). TODO: Find a more better name for this enum. ```rust enum ValidatorSetUpdatesInfo { NoUpdates, Pending(ValidatorSetUpdates), Committed(ValidatorSetUpdates) } ``` Then the BlockTree should store a mapping from a block (hash) to its ValidatorSetUpdatesInfo, where: - a non-validator-set-updating block maps to `NoUpdates`, - a uncommitted validator-set-updating block maps to `Pending(vs_updates)`, - a committed validator-set-updating block maps to `Committed(vs_updates)`. We also define a `BlockTree` method called `contains_validator_set_updates` which takes a block as an argument and returns true if and only if the block maps to `Pending(vs_updates)` or `Committed(vs_updates)`. ### Extra "decide" phase: ```rust pub enum Phase { // ↓↓↓ For pipelined flow ↓↓↓ // Generic, // ↓↓↓ For phased flow ↓↓↓ // Prepare, // The inner view number is the view number of the *prepare* qc contained in the nudge which triggered the // vote containing this phase. Precommit(ViewNumber), // The inner view number is the view number of the *precommit* qc contained in the nudge which triggered the // vote containing this phase. Commit(ViewNumber), // The inner view number is the view number of the *commit* qc contained in the nudge which triggered the // vote containing this phase. Decide(ViewNumber), } ``` We also define the `Ord` trait for `Phase`. ### New methods of `QuorumCertificate` The idea is that a decideQC contains a vector of signatures that can be split into two vectors of signatures, from the new and resigning validator sets respectively. Likewise, two QCs from `Decide` phase can be merged into a `decideQC`. New methods of the `QuorumCertificate`: ```rust fn merge(self, qc: QuorumCertificate) -> QuorumCertificate { } ``` ```rust fn split(self, index: u64) -> (QuorumCertificate, QuorumCertificate) { } ``` # View and Block Sync ## `Algorithm` module ### Modified flow of a `view` The following changes concern the body of the `start_algorithm` loop: 1. Before setting `cur_view`, we check which view (`>= cur_view`) is the minimal view that has not timed out yet, and we set cur_view to that view. We also check if we have just entered a new epoch, and if so reset the timers. - if we entered the first view of a new epoch, we allocate `Pacemaker.view_length` to each view starting from the first view, - if we entered another view of the epoch, we allocate `Pacemaker.view_length` to each view starting from the view we have entered (TODO: think of a better mechanism). 2. After setting `cur_view`, we check if we have just entered an epoch view, and if so we sync with a random peer. 3. The return type of `execute_view` will be `Result<(), EpochEnd>` - `ShouldSync` error is not needed anymore, since block sync should be called from `execute_view`, thus counting towards view execution time, - New `EpochEnd` error which signals that the epoch view has timed out, and the replica should enter `epoch_sync`. 4. If the `execute_view` returns `Err(EpochEnd)`, then call `epoch_sync`, as part of which `view_deadline` is called and the timer for the view is reset. ### New `epoch_sync` method When called, `epoch_sync` will: 1. Set `ready_to_advance = false`, 2. Reset the timer for the current epoch view to `now()` + $\Delta_{view}$ (via `view_deadline` call), and 3. Broadcast a `NewEpoch` message, and 4. Enter a loop, bounded by the timeout of the current epoch view, where: - `NewView`, `NewEpoch`, `AdvanceView`, `AdvanceEpoch` messages are processed, including collecting a TC, - On seeing a QC or TC for `>= cur_view` (received via `ShouldAdvanceView` or `ShouldAdvanceEpoch` error) we process the QC/TC as usual - if safe and correct, set `highest_qc`/`highest_tc`, `ready_to_advance = true` and exit the loop, if for unknown block then sync, else ignore. 5. If `ready_to_advance = false`, then go back to step 2. The purpose of resetting the timer and periodically sending `NewEpoch` until a TC is received, is to nudge any lagging replicas in case there is no quorum in the epoch view, so that eventually a quorum is formed. ### Conditions on advancing to a new view For non-epoch v: 1. On getting `Err(ShouldAdvanceView{qc, sender})` from `pm_stub.recv`, with a correct and safe qc. For an epoch view v: 1. On seeing `Err(ShouldAdvanceEpoch{c, sender})` from `pm_stub.recv`. ### `on_receive_advance_view` This method shall be called after `Err(ShouldAdvanceView{qc, sender})` is seen. In reality, this doesn't have to be abstracted into a new method. When `on_receive_advance_view` is called: 1. Check if the `qc` is `is_correct` and `safe_qc`, also if it is from the leader of `qc.view` and if it is for a non-epoch view, if yes then terminate this view and advance to a new view. 2. If the `qc` is not `safe_qc`, but is `is_correct`, and satisfies the two other conditions above, then sync with the `sender`, and afterwards re-evaluate the `qc`. **Note:** This can happen only once, no recursion! 3. Else, ignore the error. ### `on_receive_advance_epoch` This method shall be called after `Err(ShouldAdvanceEpoch{qc, sender})` is seen. Again, in reality, this doesn't have to be abstracted into a new method. When `on_receive_advance_epoch` is called on a `qc`: 1. Check if the qc is `is_correct` and `safe_qc`, also if it is for an epoch view, if yes then: - broadcast a `AdvanceEpoch{qc}` message, - terminate this view and advance to new view. 3. If the `qc` is not `safe_qc`, but is `is_correct`, and is from the corrrect sender, then sync with the `sender`, and re-evaluate the qc. 4. Else, ignore the error. When `on_receive_advance_epoch` is called on a `tc`: 1. Check if the `tc` is `is_correct` and if it is for an epoch view, if yes then: - broadcast a `AdvanceEpoch{tc}` message, - terminate this view and advance to a new view. 2. Else, ignore the error. ### `on_receive_new_epoch` 1. Verify the signature and collect with the `NewEpochCollector`, 2. If a TC collected, then broadcast it. ### New `TimeoutCertificate` type ```rust #[derive(Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)] pub struct TimeoutCertificate { pub chain_id: ChainID, pub view: ViewNumber, pub signatures: SignatureSet, } ``` Just like for `QuorumCertificate`, there will be an `is_correct` method for `TimeoutCertificate` to check if the certificate is cryptographically correct i.e, correctly signed by: - validators from the `committed_validator_set` - or validator from the `resigning_validator_set` (if `resigning_validator_set.is_some()`) with total power greater or equal to the quorum threshold of $2f+1$. ### New `highest_tc` field for the `BlockTree` ```rust mod paths { \\ All other fields... pub(super) const HIGHEST_TC: [u8; 1] = [12]; } ``` ### New `ProgressMessage` instances ```rust enum ProgressMessage { Proposal(Proposal), Nudge(Nudge), Vote(Vote), NewView(NewView), NewEpoch(NewEpoch), // new: signal the desire to advance to a new epoch AdvanceView(QuorumCertificate), // new: inform others about non-epoch-view QC AdvanceEpoch(Certificate), // new: inform others about the epoch-view TC or QC } ``` where ```rust struct NewEpoch { chain_id: ChainID view: ViewNumber signature: Signature highest_c: Certificate } ``` ```rust enum Certificate { QuorumCertificate{QuorumCertificate}, TimeoutCertificate{TimeoutCertificate}, } ``` and `AdvanceEpoch` simply wraps around a `Certificate` type, but has the same APIs as other `ProgressMessage`s: * The `view()` of a `AdvanceEpoch(tc)` message is the view of the `tc` or `qc` respectively, * The `chain_id()` of a `AdvanceEpoch(tc)` message is the chain id of the `tc` or `qc` respectively. Alternatively, define: ```rust struct AdvanceEpoch { chain_id: ChainID view: ViewNumber certificate: Certificate } ``` and ```rust struct AdvanceView { chain_id: ChainID view: ViewNumber qc: QuorumCertificate } ``` ### `ProgressMessageBuffer` updates ```rust enum ProgressMessageReceiveError { Timeout, ReceivedQCFromTheFuture, // TODO: delete ShouldAdvanceView{qc: QuorumCertificate, sender: VerifyingKey}, // new ShouldAdvanceEpoch{c: Certificate, sender: VerifyingKey}, // new } ``` The high-level idea is that: - `ShouldAdvanceView` is thrown when the message type suggests that the quorum has advanced from or is ready to advance from a non-epoch view, even if it is a non-epoch view of another epoch (and hence requires advancing an epoch) and the replica can simply advance to the view where the quorum is. - `ShouldAdvanceEpoch` is thrown when the message type suggests that the quorum is ready to advance or in the process of advancing to a new epoch, and there is evidence that replicas can advance, and in addition to advancing to a new epoch the replica should re-broadcast this evidence to everyone. As for the `recv` method: 1. On seeing a `AdvanceView(qc)` message (picked up from the poller or cache) with `qc.view >= cur_view` and `qc.view` not epoch view **or** `NewEpoch` or `NewView` message with a certificate for (epoch or non-epoch) `view >= cur_view`: - the message is cached, - `Err(ShouldAdvanceView{c, sender})` is thrown, 2. On seeing a `AdvanceEpoch(c)` message (picked up from the poller or cache) with a QC or TC for `view >= cur_view`: - the message is cached, - `Err(ShouldAdvanceEpoch{c, sender})` is thrown. The idea is that TC or QC for current or future view are immediately returned in the corresponding error instances. Upon getting one of these errors the algorithm thread will check if the QC or TC is `correct` (and if it is `safe_qc` for a QC), and if so it will update its `highest_tc` or `highest_qc` field and return in `execute_view`. In case of an epoch view QC or TC, the algorithm thread will also re-broadcast it. ### Keeping track of whether a replica has voted It is essential for the safety of the protocol that a replica votes only once per view. This can be put into question as we introduce the validator set speculation period, but the validator set speculation period is designed in such a way that the invariant that a replica votes at most once per view is preserved. Hence, we store an `i_have_voted` variable, initiated as false on entering each view, and once it becomes true a replica can't vote anymore. **Exception:** we do not set the variable to equal to `true` after voting "decide", and we do not check if the variable is `false` before voting "decide". This is to let the replica vote again in the exceptional scenario when the leader of `vs` broadcasts a `Nudge(commitQC)`, not knowing about the newly collected `decideQC`, before the leader of `vs'` broadcasts a proposal supported by a `decideQC`. This is safe because the "decide" vote is merely a finalizing vote for a validator-set-updating block that has already been committed. ### Modular `Pacemaker` implementation New methods for the trait: ```rust pub trait Pacemaker: Send { fn view_timeout( &mut self, cur_view: ViewNumber, highest_qc_view_number: ViewNumber, // TODO: delete, no longer needed. ) -> Duration; fn view_leader(&mut self, cur_view: ViewNumber, validator_set: &ValidatorSet) -> VerifyingKey; } ``` `DefaultPacemaker` implements the trait by storing, reading from, and writing to a `timeout` vector: ```rust pub struct DefaultPacemaker { view_time: Duration, epoch_length: u64, cur_epoch: u64, timeout: BTreeMap<ViewNumber, Instant>, } impl DefaultPacemaker { /// # Safety /// `view_timeout` must not be larger than [u32::MAX] seconds for reasons explained in [Pacemaker]. pub fn new(view_timeout: Duration) -> DefaultPacemaker { Self { view_timeout, 0, // timeout = [..., now()] } } // new provided method. fn reset_epoch_view_timeout( &mut self, epoch_number: EpochNumber ) -> () // new provided method. // sets the timers for a new epoch. fn set_epoch_timers( &mut self, epoch_number: EpochNumber, start_view: ViewNumber, ) -> () } impl Pacemaker for DefaultPacemaker { // ... } ``` To keep the `Pacemaker` API simple and consistent with the Pacemaker module API specification from the HotStuff paper, the `DefaultPacemaker` will implement `view_timeout` such that if needed the timers are reset on calling `view_timeout`. For the implementation of the `view_leader` method, check [this design document](https://hackmd.io/FS7UmWUGT-qDdrxetGVB2w). ### `NewEpochCollector` Now stores signatures, so that a TC can be returned from the `collect` method. ```rust struct NewEpochCollector { chain_id: ChainID, view: ViewNumber, validator_set: ValidatorSet, validator_set_total_power: TotalPower, signature_set: SignatureSet, } ```

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully