Skip to content

Shared State Hash Protocol

user_name edited this page Apr 14, 2023 · 3 revisions

State Hash

This document describes the cross-module communication using the interfaces in ../shared/modules to compute a new state hash. See module specific documentation & implementation details inside each module respectively.

NOTE: The diagrams below use some Hotstuff specific terminology as described in the HotPOKT Consensus Specification but can be adapted to other BFT protocols as well.

Context Management

The Utility and Persistence modules maintain a context (i.e. an ephemeral states) driven by the Consensus module that can be released & reverted (i.e. the block is invalid / no Validator Consensus reached) or can be committed & persisted to disk (i.e. the block is finalized).

On every round of every height:

  1. The Consensus module handles a NEWROUND message
  2. A new UtilityUnitOfWork is initialized at the current height
  3. A new PersistenceRWContext is initialized at the current height
  4. The Block Application flow commences
sequenceDiagram
    title Steps 1-4
    participant B as Bus
    participant C as Consensus
    participant U as Utility
    participant P as Persistence

    %% Handle New Message
    B-->>C: HandleMessage(NEWROUND)

    %% NEWROUND

    activate C
    %% Create Contexts
    C->>+U: NewContext(height)
    U->>+P: NewRWContext(height)
    P->>-U: PersistenceContext
    U->>U: store context<br>locally
    activate U
    deactivate U
    U->>-C: UtilityUnitOfWork
    C->>C: store context<br>locally
    deactivate C

    %% Apply Block
    Note over C, P: 'Block Application'
Loading

The Proposer drives the Validators to agreement via the Consensus Lifecycle (i.e. HotPOKT)


  1. The Consensus module handles the DECIDE message
  2. The commitQC is propagated to the UtilityUnitOfWork & PersistenceContext on Commit
  3. The persistence module's internal implementation for 'Store Block' must execute.
  4. Both the UtilityUnitOfWork and PersistenceContext are released
sequenceDiagram
    title Steps 5-8
    participant B as Bus
    participant C as Consensus
    participant U as Utility
    participant P as Persistence

    %% Handle New Message
    B-->>C: HandleMessage(DECIDE)

    activate C
    %% Commit Context
    C->>+U: Context.Commit(proposer, quorumCert)
    U->>+P: Context.Commit(proposer, quorumCert)
    P->>P: Internal Implementation
    Note over P: Store Block
    P->>-U: err_code
    U->>C: err_code
    deactivate U

    %% Release Context
    C->>+U: Context.Release()
    U->>+P: Context.Release()
    P->>-U: err_code
    U->>-C: err_code
    deactivate C
Loading

Block Application

When applying the block during the NEWROUND message shown above, the majority of the flow is similar between the leader and the replica with one of the major differences being a call to the Utility module as seen below.

  • ApplyBlock - Uses the existing set of transactions to validate & propose
  • CreateProposalBlock - Reaps the mempool for a new set of transaction to validate and propose
graph TD
    B[Should I prepare a new block?] --> |Wait for 2/3+ NEWROUND messages| C

    C[Am I the leader?] --> |Yes| D
    C[Am I the leader?] --> |No| Z

    D[Did I get any prepareQCs?] --> |Find highest valid prepareQC| E
    D[Did I get any prepareQCs?] --> |No| Z

    E[Am I ahead of highPrepareQC?] --> |Yes| G
    E[Am I ahead of highPrepareQC?] --> |No| Z

    G[Do I have a lockedQC] --> |No| H
    G[Do I have a lockedQC] --> |Yes| I

    I[Is prepareQC.view > lockedQC.view] --> |"No<br>(lockedQC.block)"| Z
    I[Is prepareQC.view > lockedQC.view] --> |"Yes<br>(prepareQC.block)"| Z

    H[CreateProposalBlock]
    Z[ApplyBlock]
Loading

As either the leader or replica, the following steps are followed to apply the proposal transactions in the block.

  1. Update the UtilityUnitOfWork with the proposed block
  2. Call either ApplyBlock or CreateProposalBlock based on the flow above
sequenceDiagram
    title Steps 1-2
    participant C as Consensus
    participant U as Utility

        %% Update the proposal in the utility unit of work
        C->>+U: SetProposalBlock(hash, proposer, txs)
        U->>-C: err_code

        %% Apply the block to the local proposal state
        C->>+U: ApplyBlock / CreateProposalBlock
        U->>-C: err_code
Loading
  1. Loop over all transactions proposed
  2. Check if the transaction has already been applied to the local state
  3. Perform the CRUD operation(s) corresponding to each transaction
  4. The persistence module's internal implementation for 'Compute State Hash' must be triggered
  5. Validate that the local state hash computed is the same as that proposed
sequenceDiagram
    title Steps 3-7
    participant C as Consensus
    participant U as Utility
    participant P as Persistence

    loop for each tx in txs
        U->>+P: TransactionExists(txHash)
        P->>-U: false (does not exist)
        loop for each operation in tx
            U->>+P: Get*/Set*/Update*/Insert*
            P->>-U: err_code
            U->>U: Validation logic
            activate U
            deactivate U
        end
    end
    %% TODO: Consolidate AppHash and StateHash
    U->>+P: ComputeStateHash()
    P->>P: Internal Implementation
    Note over P: Compute State Hash
    P->>-U: stateHash
    U->>C: stateHash

    %% Validate the computed hash
    C->>C: Compare local hash<br>against proposed hash
Loading
Clone this wiki locally