proposeBlock : LocalState → Message → LocalState
proposeBlock ls m = record ls
  { phase = Voted
  ; db    = m ∷ ls .db
  }
voteBlock : (ls : LocalState) → AnyFirst (m ≡_) (ls .inbox) → Message → LocalState
voteBlock {m} ls m∈ vote = record ls
  { db    = vote ∷ m ∷ ls .db
  ; phase = Voted
  ; inbox = (ls .inbox) L.Any.─ (AnyFirst⇒Any m∈)
  }
registerVote : (ls : LocalState) → m ∈ ls .inbox → LocalState
registerVote {m} ls m∈ = record ls
  { db    = m ∷ ls .db
  ; inbox = (ls .inbox) L.Any.─ m∈
  }
finalize : Chain → Op₁ LocalState
finalize ch ls = record ls
  { final = ch}
Given a local state a step may produce a message.
data _▷_⊢_—[_]→_
  (p : Pid) (e : Epoch) (ls : LocalState) : Maybe Message → LocalState → Type where
  -- Leader proposes a new block.
  ProposeBlock :
    let
      L = epochLeader e
      h = ch ♯
      b = ⟨ h , e , txs ⟩
      m = Propose $ signBlock p b
    in
    ∙ ls .phase ≡ Ready
    ∙ p ≡ L
    ∙ ch longest-notarized-chain-∈ ls .db
    ∙ ValidChain (b ∷ ch)
      ─────────────────────────────────────────
      p ▷ e ⊢ ls —[ just m ]→ proposeBlock ls m
  -- Upon getting the first valid proposal a node votes for it.
  VoteBlock :
    let
      L  = epochLeader e
      b  = ⟨ ch ♯ , e , txs ⟩
      sᴾ = signBlock L b
      mᵖ = Propose sᴾ
      m  = Vote    $ signBlock p b
    in
    ∀ (m∈ : AnyFirst (mᵖ ≡_) (ls .inbox)) →
    ∙ sᴾ ∉ map _∙signedBlock (ls .db) -- an alternative is to change voteBlock, accept the proposal but erase the existing vote.
    ∙ ls .phase ≡ Ready
    ∙ p ≢ L
    ∙ ch longest-notarized-chain-∈ ls .db
    ∙ ValidChain (b ∷ ch)
      ─────────────────────────────────────────
      p ▷ e ⊢ ls —[ just m ]→ voteBlock ls m∈ m
  -- NB: could a node get a proposal for a future epoch?
  -- Whenever other nodes vote, register their vote.
  RegisterVote : let m = Vote sb in
    -- TODO: discard dishonest votes
    ∀ (m∈ : m ∈ ls .inbox) →
    ∙ sb ∉ map _∙signedBlock (ls .db)
           -- don't count a vote twice
           -- even if one is Propose sb and the other is Vote sb
      ───────────────────────────────────────────
      p ▷ e ⊢ ls —[ nothing ]→ registerVote ls m∈
  -- Node finalizes a block (in their view).
  FinalizeBlock : ∀ ch b →
    ∙ ValidChain (b ∷ ch)
    ∙ FinalizedChain (ls .db) ch b
      ───────────────────────────────────────
      p ▷ e ⊢ ls —[ nothing ]→ finalize ch ls
When the epoch advances, reset all phases to Ready:
epochChange : Op₁ LocalState
epochChange ls = record ls
  { phase = Ready }
A node can also receive messages at their inbox:
receiveMessage : Message → Op₁ LocalState
receiveMessage m ls = record ls
  { inbox = ls .inbox L.∷ʳ m }
We consider traces of the (local) step relation, defined as its reflexive-transitive closure:
private module ∣LOCAL∣ (p : Pid) where
  StateProperty = LocalState → Type
  _—→_ : Rel LocalState _
  ls —→ ls′ = ∃ λ e → ∃ λ mm → p ▷ e ⊢ ls —[ mm ]→ ls′
  open import Prelude.Closures _—→_ public
  -- step incorporating global changes as well (currently unused)
  data _—→⁺_ : Rel LocalState 0ℓ where
    LocalStep      : ls —→ ls′ → ls —→⁺ ls′
    EpochChange    : ls —→⁺ epochChange ls
    ReceiveMessage : ls —→⁺ receiveMessage m ls
open ∣LOCAL∣ public using () renaming
  ( _—→_ to _▷_—→_; _—↠_ to _▷_—↠_; _—→⁺_ to _▷_—→⁺_
  ; Reachable to LocalReachable; Invariant to LocalInvariant
  ; StateProperty to LocalStateProperty
  )