-- FVIntmax/AttackGame.lean

import FVIntmax.Wheels.AuthenticatedDictionary

import FVIntmax.Wheels.SignatureAggregation

import FVIntmax.BalanceProof

import FVIntmax.Block

import FVIntmax.Request

import FVIntmax.Wheels

namespace Intmax

noncomputable section Intmax

section RollupContract

open Classical

section

variable {C : Type} [Nonempty C]

         {V : Type} [Lattice V] [AddCommGroup V]
         
         {K₁ : Type} [Finite K₁] [Nonempty K₁]
         {K₂ : Type} [Finite K₂]
         {Sigma : Type}
         [ADScheme K₂ (C × K₁ × ExtraDataT) C Pi]
         [SA : SignatureAggregation (C × K₁ × ExtraDataT) K₂ KₛT Sigma]

end

section

variable {C : Type}
         {V : Type} [Lattice V] [AddCommGroup V]
         {K₁ : Type} [Finite K₁]
         {K₂ : Type}
         {Sigma : Type}
         {block : Block K₁ K₂ C Sigma V}
         {v : V}

section Lemmas

end Lemmas

namespace Scontract

section

variable [LinearOrder K₁] [Nonempty K₁]
         [LinearOrder K₂] [Finite K₂]
         [Nonempty C]
         [CovariantClass V V (· + ·) (· ≤ ·)]
         [CovariantClass V V (Function.swap (· + ·)) (· ≤ ·)]
         [ADScheme K₂ (C × K₁ × ExtraDataT) C Pi]
         [SignatureAggregation (C × K₁ × ExtraDataT) K₂ KₛT Sigma]

section appendBlock

variable {σ : Scontract K₁ K₂ V C Sigma} {request : Request K₁ K₂ C Sigma Pi V}

end appendBlock

end

section

variable [LinearOrder K₁]
         [Finite K₂] [LinearOrder K₂]
         {σ : Scontract K₁ K₂ V C Sigma}
         {request : Request K₁ K₂ C Sigma Pi V}
         [CovariantClass V V (· + ·) (· ≤ ·)]
         [CovariantClass V V (Function.swap (· + ·)) (· ≤ ·)]

end

end Scontract

end

end RollupContract

section AttackGameDefs

variable {K₁ : Type} [Finite K₁] [LinearOrder K₁] [Nonempty K₁]
         {K₂ : Type} [Finite K₂] [LinearOrder K₂]

         {V : Type} [Lattice V] [AddCommGroup V]
         [CovariantClass V V (· + ·) (· ≤ ·)]
         [CovariantClass V V (Function.swap (· + ·)) (· ≤ ·)]

         {Sigma : Type}
         {C : Type} [Nonempty C]

         [ADScheme K₂ (C × K₁ × ExtraDataT) C Pi]
         [SignatureAggregation (C × K₁ × ExtraDataT) K₂ KₛT Sigma]

         (requests : List (Request K₁ K₂ C Sigma Pi V))
         (σ : Scontract K₁ K₂ V C Sigma)

def aggregateDeposits (σ : Scontract K₁ K₂ V C Sigma) : V :=
  ∑ i : Fin σ.length,
    if h : σ[i].isDepositBlock
    then (σ[i.1].getDeposit h).2.1
    else 0

end AttackGameDefs

section AttackGameLemmas

variable {K₁ K₂ Sigma C : Type}
         {V : Type} [Lattice V] [AddCommGroup V]
         [CovariantClass V V (· + ·) (· ≤ ·)]
         [ADScheme K₂ (C × K₁ × ExtraDataT) C Pi]

section computeBalanceSum

variable {k : ℕ}

@[simp]
private def reindex : (a : ℕ) → a ∈ Finset.range (k + 1) \ {0} → ℕ :=
  λ a _ ↦ a.pred

end computeBalanceSum

-- Repository definitions used:
content: class Injective {α ω : Type} (f : α → ω) where
  h : ComputationallyInfeasible (¬ Function.Injective f)

content: abbrev Scontract (K₁ K₂ V : Type) [PreWithZero V] (C Sigma : Type) :=
  List (Block K₁ K₂ C Sigma V)

content: inductive Block (K₁ K₂ : Type) (C Sigma : Type) (V : Type) [PreWithZero V] where
   
  | deposit (recipient : K₂) (amount : V₊)
   
  | transfer (aggregator : K₁) (extradata : ExtraDataT) (commitment : C) (senders : List K₂) (sigma : Sigma)
   
  | withdrawal (withdrawals : K₁ → V₊)

content: abbrev ExtraDataT : Type := ℕ

content: class abbrev PreWithZero (α : Type) := Preorder α, Zero α

content: def NonNeg (α : Type) [PreWithZero α] := { a : α // 0 ≤ a }

content: abbrev isDepositBlock (b : Block K₁ K₂ C Sigma V) := b matches (Block.deposit _ _) 

content: def getDeposit (b : Block K₁ K₂ C Sigma V) (_h : b.isDepositBlock) : K₂ × V₊ :=
  match b with | deposit r v => (r, v)

content: postfix:max "₊" => NonNeg

-- Repository lemmas used:
content: theorem List.getElem_append_left{α : Type u_1} {l₁ l₂ : List α} {i : Nat} (hn : i < l₁.length) :\n(l₁ ++ l₂)[i] = l₁[i]

-- Local definitions used:
content: def aggregateDeposits (σ : Scontract K₁ K₂ V C Sigma) : V :=
  ∑ i : Fin σ.length,
    if h : σ[i].isDepositBlock
    then (σ[i.1].getDeposit h).2.1
    else 0

content: @[simp]
private def reindex : (a : ℕ) → a ∈ Finset.range (k + 1) \ {0} → ℕ :=
  λ a _ ↦ a.pred

-- Local lemmas used:
content: private lemma reindex_mem :
  ∀ (a : ℕ) (ha : a ∈ Finset.range (k + 1) \ {0}), reindex a ha ∈ Finset.range k

content: private lemma reindex_inj :
  ∀ (a₁ : ℕ) (ha₁ : a₁ ∈ Finset.range (k + 1) \ {0})
    (a₂ : ℕ) (ha₂ : a₂ ∈ Finset.range (k + 1) \ {0}),
  reindex a₁ ha₁ = reindex a₂ ha₂ → a₁ = a₂

-- Target theorem and proof:
@[simp]
lemma aggregateDeposits_cons {hd} {tl : List (Block K₁ K₂ C Sigma V)} :
  aggregateDeposits (hd :: tl) =
  (if h : hd.isDepositBlock
   then (hd.getDeposit h).2.1
   else 0) +
  aggregateDeposits tl
:= by
  simp [aggregateDeposits]
  rw [
    Finset.sum_fin_eq_sum_range,
    Finset.sum_eq_sum_diff_singleton_add (i := 0),
    dif_pos (show 0 < tl.length + 1 by omega)
  ]
  simp_rw [List.getElem_cons_zero (h := _)]; case h => exact Finset.mem_range.2 (by omega)
  let F : ℕ → V := λ i ↦
    if h : i < tl.length
    then if h_1 : tl[i].isDepositBlock = true
         then (tl[i].getDeposit h_1).2
         else 0
    else 0
  rw [Finset.sum_bij (t := Finset.range tl.length)
                     (g := F)
                     (i := reindex)
                     (hi := reindex_mem)
                     (i_inj := reindex_inj)
                     (i_surj := λ b hb ↦ by use b.succ; simp; exact Finset.mem_range.1 hb)
                     (h := λ n hn ↦ by simp at hn; simp [hn]
                                       rcases n with _ | n <;> [omega; simp [F, reindex]]
                                       nth_rw 2 [dif_pos (by rcases hn with ⟨hn, _⟩; omega)])]
  unfold F
  rw [Finset.sum_dite_of_true (λ _ ↦ (Finset.mem_range.1 ·)), Finset.sum_fin_eq_sum_range, Finset.univ_eq_attach]
  nth_rw 2 [Finset.sum_dite_of_true]; case h => exact λ _ ↦ (Finset.mem_range.1 ·)
  rw [add_comm]; simp