Transitions between local states of a single node.

Some auxiliary functions

enterRound : Time → Op₁ LocalState
enterRound currentTime ls = record ls
  { timer          = just (currentTime + τ)
  ; phase          = Proposing
  ; roundAdvanced? = false
  }

propose : Op₁ LocalState
propose ls = record ls
  { phase = Receiving }

recordTimeout : Op₁ LocalState
recordTimeout ls = record ls
  { timer     = nothing -- don't fire again.
  ; timedOut? = true
  ; r-vote    = 1 + ls .r-cur
  }

registerMessage : (ls : LocalState) → m ∈ ls .inbox → LocalState
registerMessage {m} ls m∈ = record ls
  { phase = AdvancingRound
  ; db    = m ∷ ls .db
  ; inbox = L.removeAt (ls .inbox) (L.Any.index m∈)
  }

registerTimeout  = registerMessage
registerVote     = registerMessage
registerProposal = registerMessage

advanceRound : QC ⊎ TC → Op₁ LocalState
advanceRound qtc ls = record ls
  { phase          = AdvancingRound
  ; r-cur          = 1 + qtc ∙round
  ; tc-last        = Sum.isInj₂ qtc
  ; timer          = nothing
  ; timedOut?      = false
  ; roundAdvanced? = true
  }

lock : QC → Op₁ LocalState
lock qc ls = record ls
  { qc-high = qc  -- ⊔qc ls .qc-high
  ; phase   = Committing
  }

commit : Chain → Op₁ LocalState
commit ch ls =  record ls
  { phase      = Voting
  ; finalChain = ch
  }

shouldEnterRound : Op₁ LocalState
shouldEnterRound ls = record ls
  { phase = if ls .roundAdvanced? then EnteringRound else Receiving }

voteProposal : Op₁ LocalState
voteProposal ls = record (shouldEnterRound ls)
  { r-vote = ls .r-cur }

The local step relation

Given a local state a step may produce a list of messages.

data _⦂_⊢_—_—→_ p t ls : Maybe Envelope → LocalState → Type

-- special case of step sending one message to one recipient
_⦂_⊢_—[_∣_⟩—→_ : Pid → Time → LocalState → Pid → Message → LocalState → Type
p ⦂ t ⊢ ls —[ p′ ∣ m ⟩—→ ls′ = p ⦂ t ⊢ ls — just [ p′ ∣ m ⟩ —→ ls′

-- special case of step multicasting one message
_⦂_⊢_—[_]—→_ : Pid → Time → LocalState → Message → LocalState → Type
p ⦂ t ⊢ ls —[ m ]—→ ls′ = p ⦂ t ⊢ ls — just [ m ⟩ —→ ls′

-- special case of step sending no message
_⦂_⊢_——→_ : Pid → Time → LocalState → LocalState → Type
p ⦂ t ⊢ ls ——→ ls′ = p ⦂ t ⊢ ls — nothing —→ ls′

data _⦂_⊢_—_—→_ p t ls where

  --------------------------
  -- * EnteringRound
  --------------------------

  -- Initialisation: replica enters round either with a TC or without one
  InitTC : let L = currentLeader ls; m = TCFormed tc in
    ∙ ls .phase ≡ EnteringRound
    ∙ ls .tc-last ≡ just tc
      ───────────────────────────────────────
      p ⦂ t ⊢ ls —[ L ∣ m ⟩—→ enterRound t ls

  InitNoTC :
    ∙ ls .phase ≡ EnteringRound
    ∙ ls .tc-last ≡ nothing
      ──────────────────────────────
      p ⦂ t ⊢ ls ——→ enterRound t ls

  --------------------------
  -- * Proposing
  --------------------------

  -- Leader proposes a new block.
  ProposeBlock :
    let
      L  = currentLeader ls
      b  = mkBlockForState ls txn
      m  = Propose $ signData L b
    in
    ∙ ls .phase ≡ Proposing
    ∙ p ≡ L
      ──────────────────────────────
      p ⦂ t ⊢ ls —[ m ]—→ propose ls

  -- Non-leader skips proposing.
  ProposeBlockNoOp : let L = currentLeader ls in
    ∙ ls .phase ≡ Proposing
    ∙ p ≢ L
      ─────────────────────────
      p ⦂ t ⊢ ls ——→ propose ls

  --------------------------
  -- * Receiving
  --------------------------

  RegisterProposal :
    let
      m = Propose sb
      b = sb .datum
    in
    ∀ (m∈ : m ∈ ls .inbox) →
    ∙ ls .phase ≡ Receiving
    ∙ ¬ timedOut ls t
    ∙ sb .node ≡ roundLeader (b ∙round)
    ∙ ValidProposal (ls .db) b
      ─────────────────────────────────────
      p ⦂ t ⊢ ls ——→ registerProposal ls m∈

  RegisterVote :
    let
      L′ = roundLeader (1 + r)
      m  = Vote $ signData p′ (b ∙blockId , r)
    in
    ∀ (m∈ : m ∈ ls .inbox) →
    ∙ ls .phase ≡ Receiving
    ∙ ¬ timedOut ls t
    ∙ b ∙∈ ls .db
    ∙ m ∉ ls .db
    ∙ L′ ≡ p
      ─────────────────────────────────
      p ⦂ t ⊢ ls ——→ registerVote ls m∈

  RegisterTimeout : let m = Timeout tm in
    ∀ (m∈ : m ∈ ls .inbox) →
    ∙ ls .phase ≡ Receiving
    ∙ ¬ timedOut ls t
    ∙ m ∉ ls .db
      ────────────────────────────────────
      p ⦂ t ⊢ ls ——→ registerTimeout ls m∈

  RegisterTC : let m = TCFormed tc in
    ∀ (m∈ : m ∈ ls .inbox) →
    ∙ ls .phase ≡ Receiving
    ∙ ¬ timedOut ls t
    ∙ m ∉ ls .db
      ────────────────────────────────────
      p ⦂ t ⊢ ls ——→ registerTimeout ls m∈

  EnoughTimeouts :
    let
      m   = Timeout $ signData p (ls .r-cur , ls .qc-high) , ls .tc-last
      tms = filter (IsTimeoutForRound? r) (ls .db)
    in
    ∙ ls .phase ≡ Receiving
    ∙ ¬ timedOut ls t
    ∙ IncludesHonest tms
    ∙ r ≥ ls .r-cur
      ────────────────────────────────────
      p ⦂ t ⊢ ls —[ m ]—→ recordTimeout ls

  TimerExpired :
    let m = Timeout $ signData p (ls .r-cur , ls .qc-high) , ls .tc-last in
    ∙ ls .phase ≡ Receiving
    ∙ timedOut ls t
      ────────────────────────────────────
      p ⦂ t ⊢ ls —[ m ]—→ recordTimeout ls

  --------------------------
  -- * AdvancingRound
  --------------------------

  AdvanceRoundQC :
    ∙ ls .phase ≡ AdvancingRound
    ∙ qc ∙∈ ls .db
    ∙ qc ∙round ≥ ls .r-cur
      ──────────────────────────────────────────
      p ⦂ t ⊢ ls ——→ advanceRound (inj₁ qc) ls

  AdvanceRoundTC :
    ∙ ls .phase ≡ AdvancingRound
    ∙ tc ∙∈ ls .db
    ∙ tc ∙round ≥ ls .r-cur
      ──────────────────────────────────────────
      p ⦂ t ⊢ ls ——→ advanceRound (inj₂ tc) ls

  AdvanceRoundNoOp : --when no new QC or TC has formed yet.
    ∙ ls .phase ≡ AdvancingRound
    ∙ AllQC (λ qc → qc ∙round < ls .r-cur) (ls .db)
    ∙ AllTC (λ tc → tc ∙round < ls .r-cur) (ls .db)
      ─────────────────────────────────────────────
      p ⦂ t ⊢ ls ——→ record ls { phase = Locking }

  --------------------------
  -- * Locking
  --------------------------

  Lock :
    ∙ ls .phase ≡ Locking
    ∙ qc -highest-qc-∈- ls .db
      ─────────────────────────
      p ⦂ t ⊢ ls ——→ lock qc ls

  --------------------------
  -- * Committing
  --------------------------

  Commit :
    ∙ ls .phase ≡ Committing
    ∙ b ∶ ch longer-final-∈ ls
      ───────────────────────────
      p ⦂ t ⊢ ls ——→ commit ch ls

  CommitNoOp :
    ∙ ls .phase ≡ Committing
    ∙ NoBlock (_longer-final-∈ ls) (ls .db)
      ─────────────────────────────────────────
      p ⦂ t ⊢ ls ——→ record ls {phase = Voting}

  --------------------------
  -- * Voting
  --------------------------

  VoteBlock :
    let
      m  = Vote $ signData p (b ∙blockId , b ∙round)
      L′ = nextLeader ls
    in
    ∙ ls .phase ≡ Voting
    ∙ b ∙∈ ls .db
    ∙ ShouldVote ls b
      ────────────────────────────────────────
      p ⦂ t ⊢ ls —[ L′ ∣ m ⟩—→ voteProposal ls

  VoteBlockNoOp :
    ∙ ls .phase ≡ Voting
    ∙ NoBlock (ShouldVote ls) (ls .db)
      ──────────────────────────────────
      p ⦂ t ⊢ ls ——→ shouldEnterRound ls

A node can also receive messages at their inbox:

receiveMessage : Message → Op₁ LocalState
receiveMessage m ls = record ls
  { inbox = ls .inbox L.∷ʳ m }