{-# OPTIONS --safe #-}
open import Prelude
open import Hash
open import Jolteon.Assumptions

module Jolteon.Properties.Steps.CommitPoints ( : _) (open Assumptions ) where

open import Jolteon 
open import Jolteon.Properties.Core 
open import Jolteon.Properties.State 
open import Jolteon.Properties.Steps.Core 

-- ** Relating `StepCommits∈` to a state's `finalChain`.

private
  ¬StepCommits⇒finalChain≡ :   _ : Honest p  (st : p  t  ls  menv —→ ls′) 
     ¬ StepCommits∈ b (_  st)
     b  ls′ .finalChain
      ────────────────────────
      b  ls .finalChain
  ¬StepCommits⇒finalChain≡ {b = b} st b∉ b∈
    with st
  ... | InitTC _ _ = b∈
  ... | InitNoTC _ _ = b∈
  ... | ProposeBlock _ _ = b∈
  ... | ProposeBlockNoOp _ _ = b∈
  ... | RegisterProposal _ _ _ _ _ = b∈
  ... | EnoughTimeouts _ _ _ _ = b∈
  ... | RegisterTimeout _ _ _ _ = b∈
  ... | RegisterTC _ _ _ _ = b∈
  ... | TimerExpired _ _ = b∈
  ... | RegisterVote _ _ _ _ _ _ = b∈
  ... | AdvanceRoundQC _ _ _ = b∈
  ... | AdvanceRoundTC _ _ _ = b∈
  ... | AdvanceRoundNoOp _ _ _ = b∈
  ... | Lock _ _ = b∈
  ... | CommitNoOp _ _ = b∈
  ... | VoteBlock _ _ _ = b∈
  ... | VoteBlockNoOp _ _ = b∈
  ... | Commit _ _ = ⊥-elim $ b∉ b∈

ch∈⇒StepCommits∈ :  {s} (Rs : Reachable s) 
  ( λ p   λ (hp : Honest p)  b  (s @ʰ hp) .finalChain)
  ────────────────────────────────────────────────────────────
  Rs ∋⋯ StepCommits∈ b
ch∈⇒StepCommits∈ (_ , refl , (_ )) (p , hp , b∈)
  rewrite let instance _ = hp in
          pLookup-replicate p initLocalState
  = contradict b∈
ch∈⇒StepCommits∈ {b} (_ , s-init , (_  WaitUntil _ _ _  s ⟩←— tr)) (p , hp , b∈)
  = ch∈⇒StepCommits∈ (_ , s-init , tr) (p , hp , b∈)
ch∈⇒StepCommits∈ {b} (_ , s-init , (_  Deliver {tpm} _  s ⟩←— tr)) (p , hp , b∈′)
  = ch∈⇒StepCommits∈ (_ , s-init , tr) (p , hp , b∈)
  where
  b∈ : b  (s @ʰ hp) .finalChain
  b∈ = subst (b ∈_) (receiveMsg-final  hp  (honestTPMessage tpm) ) b∈′
ch∈⇒StepCommits∈ {b} (_ , s-init , (_  DishonestLocalStep {env = env} _ _  s ⟩←— tr)) (p , hp , b∈′)
  = ch∈⇒StepCommits∈ (_ , s-init , tr) (p , hp , b∈′)
ch∈⇒StepCommits∈ {b = b} (s₀ , s-init , (s'  LocalStep {p = p′} {ls′ = ls′} st  s ⟩←— tr)) (p , hp , b∈′)
  with IHch∈⇒StepCommits∈ {b = b} (s₀ , s-init , tr)
  with p  p′
... | no p≢
  = there $ IH (p , hp , b∈)
  where
  s≡ : s' @ʰ hp  s @ʰ hp
  s≡ = let instance _ = hp in
       pLookup∘updateAt′ p p′ (p≢  ↑-injective) (s .stateMap)

  b∈ : b  (s @ʰ hp) .finalChain
  b∈ = subst    _   .finalChain) s≡ b∈′
... | yes refl
  with StepCommits∈? b (_  st)
... | yes commit-st = here commit-st
... | no ¬commit-st = there $ IH (p , hp , b∈)
  where
  ls′≡ : s'  p  ls′
  ls′≡ = pLookup∘updateAt _ (s .stateMap)

  b∈ : b  (s  p) .finalChain
  b∈ = ¬StepCommits⇒finalChain≡ st ¬commit-st
     $ subst    _   .finalChain) ls′≡ b∈′

getCommitPoint :  {s}  (Rs : Reachable s) 
  Rs ∋⋯ StepCommits∈ b
  ─────────────────────────────
   λ b′  Rs ∋⋯ StepCommits b′
         × b ←—∗ b′
getCommitPoint {b} (_ , s-init , (_  WaitUntil _ _ _ ⟩←— tr)) b∈
  = getCommitPoint (_ , s-init , tr) b∈
getCommitPoint {b} (_ , s-init , (_  Deliver _ ⟩←— tr)) b∈
  = getCommitPoint (_ , s-init , tr) b∈
getCommitPoint {b} (_ , s-init , (_  DishonestLocalStep {env} _ _ ⟩←— tr)) b∈
  = getCommitPoint (_ , s-init , tr) b∈
getCommitPoint (s , s-init , (_  LocalStep p ⟩←— tr))
  (there st∈)
  = let b′ , st∈ , b←b′ = getCommitPoint (s , s-init , tr) st∈
    in b′ , there st∈ , b←b′
getCommitPoint {b}
  (_ , _ , (_  LocalStep (Commit {ch = b′  ch} _ (final∈ _ _ (_  ch∈  _) _ , _)) ⟩←— tr))
  (here b∈ch)
  = b′ , here refl , b←b′
  where
  vch : ValidChain (b′  ch)
  vch = chain-∈⇒ValidChain ch∈

  b∈ : b  b′  ch
  b∈ = b∈ch

  b←b′ : b ←—∗ b′
  b←b′ = b∈⇒b← vch b∈