Source code on Github
module UTxO.Validity where

open import Data.List.Membership.Propositional.Properties using (∈-map⁺; ∈-map⁻)
open import Data.List.Relation.Binary.Suffix.Heterogeneous using (tail)
open import Data.List.Relation.Binary.Pointwise            using (Pointwise-≡⇒≡)
import Data.Maybe.Relation.Unary.Any as M

open import Prelude.Init hiding (module M; _∋_)
open import Prelude.Lists
open import Prelude.Lists.Dec
open import Prelude.Lists.Postulates
open import Prelude.Decidable
open import Prelude.DecEq
open import Prelude.Functor
open import Prelude.Sets
-- open import Prelude.Membership
open L.Mem
open import Prelude.Ord

open import UTxO.Hashing
open import UTxO.Value
open import UTxO.Types
open import UTxO.TxUtilities

-- The definitions here make use of `All` for lists, i.e. that every
-- element in a list satisifies a particular property and `AllN` which
-- gives the predicate access to the position of the element in the
-- list ass well as the element itself.

-- Additionally we make use of `M.Any`. `Any` and `All` for `Maybe` are
-- analogous to the same notions for lists. We can consider a `Maybe` as
-- a zero or one element list. `Any` gives us the notion of a property
-- that holds when we have a `just`, it cannot hold when we have
-- `nothing`. In contrast `All` for Maybe holds vacuously for nothing.

record IsValidTx (tx : Tx) (l : Ledger) {- (vl : ValidLedger l) -} : Set where
  field
    withinInterval :
      T (range tx  length l)

    validOutputRefs :
      outputRefs tx  map outRef (utxo l)

    preservesValues :
      M.Any  q  forge tx +ᶜ q   (outputs tx) value)
            (∑M (map (getSpentOutput l) (inputs tx)) value)

    noDoubleSpending :
      Unique (outputRefs tx)

    allInputsValidate :
      All (λ{ (n , i)  T (validator i (toPendingTx l tx n) (redeemer i) (datum i)) })
          (enumerate (inputs tx))

    allPoliciesValidate :
      All  f  T (f (toPendingMPS l tx (f ))))
          (policies tx)

    validateValidHashes :
      All  i  M.Any  o  (address o  validator i ) × (datumHash o  datum i ♯ᵈ)) (getSpentOutput l i))
          (inputs tx)

    forging :
      All  c  Any  f  c  f ) (policies tx))
          (currencies (forge tx))

open IsValidTx public

data ValidLedger : Ledger  Set where

   : ValidLedger []

  _⊕_∶-_ :  {l}
     ValidLedger l
     (t : Tx)
     IsValidTx t l
      -------------------
     ValidLedger (t  l)

infixl 5 _⊕_∶-_

----------------------
-- Decision Procedure.

infixl 5 _⊕_
_⊕_ :  {l}
   ValidLedger l
   (tx : Tx)
   {wi  : True (T? (range tx  length l))}
   {vor : True (outputRefs tx ⊆? map outRef (utxo l))}
   {pv  : True (M.dec  q  forge tx +ᶜ q   (outputs tx) value)
                       (∑M (map (getSpentOutput l) (inputs tx)) value))}
   {ndp : True (unique? (outputRefs tx))}
   {aiv : True (all? (λ{ (n , i)  T? (validator i (toPendingTx l tx n) (redeemer i) (datum i))})
                      (enumerate (inputs tx)))}
   {apv : True (all?  f  T? (f (toPendingMPS l tx (f ))))
                      (policies tx))}
   {vvh : True (all?  i  M.dec  o  (address o  validator i ) ×-dec (datumHash o  datum i ♯ᵈ))
                                   (getSpentOutput l i))
                     (inputs tx))}
   {frg : True (all?  c  any?  f  c  f ) (policies tx))
                      (currencies (forge tx)))}
   ValidLedger (tx  l)
(vl  tx) {wi} {vor} {pv} {ndp} {aiv} {apv} {vvh} {frg}
  = vl  tx ∶- record { withinInterval      = toWitness wi
                      ; validOutputRefs     = toWitness vor
                      ; preservesValues     = toWitness pv
                      ; noDoubleSpending    = toWitness ndp
                      ; allInputsValidate   = toWitness aiv
                      ; allPoliciesValidate = toWitness apv
                      ; validateValidHashes = toWitness vvh
                      ; forging             = toWitness frg }

----------------------
-- Utilities.

outputsₘ :  ValidLedger  List TxOutput
outputsₘ ([]     , _) = []
outputsₘ (tx  _ , _) = outputs tx

_∈′_ : Tx   ValidLedger  Set
tx ∈′ (l , _) = tx  l

--------------------------------------------------------
-- Well-founded recursion on suffixes of the ledger.

infix 4 _≼_ _≼′_ _≺_ _≺′_

_≼_ : Rel Ledger 0ℓ
_≼_ = Suffix≡

_≺_ : Rel Ledger 0ℓ
l′  l =  λ tx  tx  l′  l

_≼′_ : Rel ( ValidLedger) 0ℓ
(l′ , _) ≼′ (l , _) = l′  l

_≺′_ : Rel ( ValidLedger) 0ℓ
(l , _) ≺′ (l′ , _) = l  l′

postulate
  ≺-trans  : Transitive _≺_
  ≺-transˡ :  {x y z}  x  y  y  z  x  z
  ≺-∑forge :  {l′ l}  l′  l  T $  l forge ≥ᶜ  l′ forge

≺′⇒≼′ :  {l l′}  l′ ≺′ l  l′ ≼′ l
≺′⇒≼′ (_ , p) = tail p

≺-wf : WellFounded _≺_
≺-wf l = acc $ go l
  where
    go :  l l′  l′  l  Acc _≺_ l′
    go (_  l) l′ (_ , here (refl  eq)) = acc λ y y<l′  go l y (subst (y ≺_) (Pointwise-≡⇒≡ eq) y<l′)
    go (_  l) l′ (_ , there l′<l)       = acc λ y y<l′  go l y (≺-trans y<l′ (_ , l′<l))

≺′-wf : WellFounded _≺′_
≺′-wf vl = vl ≺′⇒≺ ≺-wf (proj₁ vl)
  where
    _≺′⇒≺_ :  vl  Acc _≺_ (proj₁ vl)  Acc _≺′_ vl
    (l , _) ≺′⇒≺ acc w = acc λ{ (l′ , _) l′<l  (l′ , _) ≺′⇒≺ w l′ l′<l }

≺′-rec = WF.All.wfRec ≺′-wf 0ℓ

----------------------
-- Properties.

≼⇒valid :  {l l′}
   ValidLedger l
   l′  l
   ValidLedger l′
≼⇒valid vl            (here eq)   rewrite Pointwise-≡⇒≡ eq = vl
≼⇒valid (vl  t ∶- x) (there suf) = ≼⇒valid vl suf

tx∈⇒valid :  {L tx}
   (tx∈ : tx ∈′ L)
   IsValidTx tx (proj₁ $ ∈⇒Suffix tx∈)
tx∈⇒valid {L = _ , vl} {tx = tx} tx∈
  with l , l≺∈⇒Suffix tx∈
  with _  .tx ∶- vtx≼⇒valid vl l≺
     = vtx

-- An output spent in a previous transaction cannot be spent again.
postulate
  suf-utxo :  {tx l l′ x}
     ValidLedger l
     Suffix≡ (tx  l′) l
     x  map outRef (utxo l′)
     x  outputRefs tx
     x  map outRef (utxo l)
-- suf-utxo {tx} {l = x ∷ l} vl (here (refl ∷ p)) x∈′ x∈refs x∈
--   rewrite Pointwise-≡⇒≡ p
--         = {!!}
-- suf-utxo {tx} {l = x ∷ l} vl (there suf) x∈′ x∈refs x∈ = {!!}

-- traceRef : ∀ {tx l}
--   → ValidLedger (tx ∷ l)
--   → ∀ o → o ∈ outputRefs tx
--   → ∃[ tx′ ] ( (tx′ ∈ l)
--              × (tx′ ♯ ≡ hid o) )
-- traceRef {tx} {l} (vl ⊕ .tx ∶- vtx) o o∈ = {!!}


--------------------------------------------------------
-- Packing up useful information about all predecessors
-- (i.e. the inputs of a transaction)

record Res {tx : Tx} {l : Ledger} (vl : ValidLedger l) (vtx : IsValidTx tx l) : Set where
  field
    prevTx   : Tx
    l′       : Ledger
    prevOut  : TxOutput
    vl′      : ValidLedger (prevTx  l′)
    prevOut∈ : prevOut  outputs prevTx
    vl′≺vl   : (prevTx  l′ , vl′) ≺′ (tx  l , (vl  tx ∶- vtx))
    spent≡   :  λ i  (i  inputs tx) × (getSpentOutput l i  just prevOut)

    -- ≈ prevTx ↝⟦ {-value prevOut ◆-} ⟧ tx
    or∈      : Any ((prevTx ♯ₜₓ ≡_)  hid) (outputRefs tx)
    ⁉≡just   : outputs prevTx  index (L.Any.lookup or∈)   just prevOut

resValue :  {tx l} {vl : ValidLedger l} {vtx : IsValidTx tx l}  Res vl vtx  Value
resValue = value  Res.prevOut

prevs :  {tx l} (vl : ValidLedger l) (vtx : IsValidTx tx l)  List (Res vl vtx)
prevs {tx} {l} vl vtx
  = mapWith∈ (inputs tx) go
  module P₀ where
    go :  {i}  i  inputs tx  Res vl vtx
    go {i} i∈
      with outRef∈validOutputRefs vtx (∈-map⁺ outputRef i∈)
      with u , u∈ , refl∈-map⁻ outRef outRef∈
      with prev∈ , prevOut∈ , refl∈utxo⇒outRef≡ {l = l} u∈
      with l′ , suf∈⇒Suffix prev∈
        = record { prevTx   = prevTx u
                 ; l′       = l′
                 ; prevOut  = out u
                 ; vl′      = vl′
                 ; prevOut∈ = prevOut∈
                 ; vl′≺vl   = vl′≺vl
                 ; spent≡   = i , i∈ , utxo-getSpent {l = l} u∈
                 ; or∈      = or∈
                 ; ⁉≡just   = ⁉≡just
                 }
        where
          id≡ : prevTx u ♯ₜₓ  hid (outputRef i)
          id≡ = sym $ ⟨⟩≡just {l}{outputRef i}{prevTx u} (utxo-[] {l} u∈)

          P⊆Q :  {or}  outputRef i  or  prevTx u ♯ₜₓ  hid or
          P⊆Q refl = id≡

          or∈ : Any ((prevTx u ♯ₜₓ ≡_)  hid) (outputRefs tx)
          or∈ = L.Any.map P⊆Q (∈-map⁺ outputRef i∈)

          outRef≡ : L.Any.lookup or∈  outputRef i
          outRef≡ = begin L.Any.lookup or∈                   ≡⟨ Any-lookup∘map P⊆Q (∈-map⁺ outputRef i∈) 
                          L.Any.lookup (∈-map⁺ outputRef i∈) ≡⟨ lookup∘∈-map⁺ {f = outputRef} i∈ 
                          outputRef i                      
                    where open ≡-Reasoning

          ⁉≡just : (outputs (prevTx u)  index (L.Any.lookup or∈))  just (out u)
          ⁉≡just rewrite outRef≡ = utxo-⟨⟩ {l} u∈

          v   = value $ out u
          vl′ = ≼⇒valid vl suf

          vl′≺vl : (prevTx u  l′ , vl′) ≺′ (tx  l , (vl  tx ∶- vtx))
          vl′≺vl = ≺-transˡ suf (tx , suffix-refl (tx  l))
          -- NB. suf ≈ (prevTx u ∷ l′) ≼ l

∑prevs≡ :  {tx l} (vl : ValidLedger l) (vtx : IsValidTx tx l)
         ∑M (map (getSpentOutput l) (inputs tx)) value  just ( (prevs vl vtx) resValue)
∑prevs≡ {tx} {l} vl vtx = ∑M-help {A = TxInput} {xs = inputs tx} {f = getSpentOutput l} {g = value}
                                  {R = Res vl vtx} {go = go} {r = resValue} (cong (_<$>_ value)  s≡)
                                  -- BUG: `cong (value <$>_)` results in unresolved metas...
  where
    open P₀ vl vtx

    i′≡ :  {i} i∈  proj₁ (Res.spent≡ $ go {i} i∈)  i
    i′≡ {i} i∈
      with u , u∈ , refl∈-map⁻ outRef (validOutputRefs vtx (∈-map⁺ outputRef i∈))
      with prev∈ , prevOut∈ , refl∈utxo⇒outRef≡ {l = l} u∈
      with l′ , suf∈⇒Suffix prev∈
         = refl

    s≡ :  {i} i∈  getSpentOutput l i  just (Res.prevOut $ go {i} i∈)
    s≡ {i} i∈
      with Res.spent≡ (go {i} i∈) | inspect Res.spent≡ (go {i} i∈)
    ... | i′ , _ , spent≡ | ≡[ go≡ ]
         = trans (cong (getSpentOutput l) i≡) spent≡
      where
        i≡ : i  i′
        i≡ = trans (sym $ i′≡ {i} i∈) (cong proj₁ go≡)

prevs⊆utxo :  {tx l} {vl : ValidLedger l} {vtx : IsValidTx tx l}
   map resValue (prevs vl vtx)
   map (value  out) (utxo l)
prevs⊆utxo {tx}{l}{vl}{vtx} v∈
  with _ , pr∈ , refl∈-map⁻ resValue v∈
  with _ , i∈  , refl∈-mapWith∈⁻ {f = P₀.go vl vtx} pr∈
  with _ , u∈  , refl∈-map⁻ outRef (validOutputRefs vtx (∈-map⁺ outputRef i∈))
  with _ , _   , refl∈utxo⇒outRef≡ {l = l} u∈
  = ∈-map⁺ (value  out) u∈

-- Non-fungibility
NonFungible :  ValidLedger  TokenClass  Set
NonFungible (l , _) nft =  l forge  nft  1

NF-weaken :  {nft l l′}  l′ ≺′ l  NonFungible l nft  NonFungible l′ nft
NF-weaken {nft}{l , _}{l′ , _} vl′≺ = Nat.≤-trans (≥ᶜ-◆ {x =  l forge} {y =  l′ forge} $ ≺-∑forge vl′≺)
  where open FocusTokenClass nft