Source code on Github
open import Data.Nat.Induction using (<-wellFounded)
open import Data.List.Membership.Propositional.Properties using (∈-filter⁺)

open import Prelude.Init
open import Prelude.General
open import Prelude.Maybes
open import Prelude.Lists
open import Prelude.Lists.Postulates
-- open import Prelude.Membership
open L.Mem
open import Prelude.Nats.Postulates
open import Prelude.Ord

open import UTxO.Hashing
open import UTxO.Value
open import UTxO.Types hiding (I)
open import UTxO.TxUtilities
open import UTxO.Validity
open import UTxO.GlobalPreservation
open import StateMachine.Base hiding (origin)

module StateMachine.Extract
  {S I : Set} {{_ : IsData S}} {{_ : IsData I}} {sm : StateMachine S I}
  {L :  ValidLedger} {jo : Is-just (StateMachine.origin sm)}
  where

open CEM {sm = sm}
open import StateMachine.Properties {sm = sm}
open import StateMachine.Inductive {sm = sm}

open FocusTokenClass nftₛₘ
open import UTxO.TokenProvenance nftₛₘ
open import UTxO.TokenProvenanceNF nftₛₘ
open import StateMachine.Initiality {sm = sm}

private
  variable
    tx : Tx
    n  : Quantity

outputs◆ : Tx  List TxOutput
outputs◆ = filter (◆∈?_  value)  outputs

ams-outputs◆ :  {tx}
   tx ∈′ L
   AtMostSingleton (outputs◆ tx)
ams-outputs◆ {tx} tx∈
  with l′ , l≺∈⇒Suffix tx∈
  = qed
  where
    open ≤-Reasoning

    l  = tx  l′
    vl = ≼⇒valid (proj₂ L) l≺

    ∑forge≤1 : NonFungible (_ , vl) nftₛₘ
    ∑forge≤1 = JustOrigin.nf jo (_ , vl)

    ∑≥ᶜ : T $  l forge ≥ᶜ  (outputs tx) value
    ∑≥ᶜ rewrite globalPreservation {vl = vl} = ∑utxo≥∑out tx l′

    ∑≥ :  l forge    (outputs◆ tx) value 
    ∑≥ = begin  (outputs◆ tx) value  ≡⟨ ∑-filter-◆ {xs = outputs tx} {fv = value} 
                (outputs tx) value   ≤⟨ ≥ᶜ-◆ {x =  l forge} {y =  (outputs tx) value} ∑≥ᶜ 
                l forge              

    qed : AtMostSingleton (outputs◆ tx)
    qed with outputs◆ tx | L.All.all-filter (◆∈?_  value) (outputs tx) | ∑≥
    ... | []         | []            | _   = tt
    ... | _  []     | _  _         | _   = tt
    ... | x  y  os | ◆∈x  ◆∈y  _ | ∑≥′ = ⊥-elim $ ¬i≥x+y ∑forge≤1 ◆∈x ◆∈y i≥x+y
      where
        ⊆-helper : value x   value y   []  value x   value y   map (_◆  value) os
        ⊆-helper (here refl)         = here refl
        ⊆-helper (there (here refl)) = there (here refl)
        ⊆-helper (there (there ()))

        i≥x+y :  l forge   value x  + value y 
        i≥x+y =
          begin
            value x  + value y 
          ≡⟨ cong (value x  +_) $ sym $ Nat.+-identityʳ (value y ) 
            ∑ℕ (value x   value y   [])
          ≤⟨ ∑ℕ-⊆ ⊆-helper 
            ∑ℕ (value x   value y   map (_◆  value) os)
          ≡⟨ sym $ ∑-◆ {xs = x  y  os} {f = value} 
             (x  y  os) value 
          ≤⟨ ∑≥′ 
             l forge 
          

data  : Tx  Tx  Set where

  root :

      (tx : Tx)
     (tx∈ : tx ∈′ L)
     T (policyₛₘ $ record {thisTx = ; txInfo = mkTxInfo (proj₁ $ ∈⇒Suffix tx∈) tx})
      -----------------------------------------------------------------------------
      tx tx

  cons :  {tx tx′ tx″}

      tx tx′
     tx″ ∈′ L
     tx′ ↝⟦ 1  tx″
      --------------
      tx tx″

∣_∣ᵗ : Trace L tx n  
∣_∣ᵗ = L.NE.length  txs

X→X¹ :
    n > 0
   (tr : Trace L tx n)
   T (policyₛₘ $ mkPendingMPS {L = L} tr )
    -----------------------------------------
    (origin tr) tx

X→X¹ {n = n} n>0 tr = go tr (<-wellFounded  tr ∣ᵗ)
  where
    -- NB: well-founded recursion used here, because Agda could not figure out tr′ is structurally smaller!!
    go :  (tr : Trace L tx n)  Acc _<_  tr ∣ᵗ  T (policyₛₘ $ mkPendingMPS {L = L} tr )   (origin tr) tx
    go record {txs = tx  []; trace∈ = tx∈  []; linked = root .tx _; head≡ = refl} _ p≡
      = root tx tx∈ p≡
    go record {txs = tx′  (tx  txs); trace∈ = tx∈  tr∈; linked = cons {tx  txs}{tx′} lnk tx↝; head≡ = refl}
       (acc a) p≡
       rewrite last-∷ {x = tx′}{tx  txs}
       = cons (go tr′ (a _ (s≤s ≤-refl)) p≡) tx∈ tx↝⟦1⟧tx′
       where
         tr′ : Trace L tx n
         tr′ = record {txs = tx  txs; trace∈ = tr∈; linked = lnk; head≡ = refl}

         tx↝⟦1⟧tx′ : tx ↝⟦ 1  tx′
         tx↝⟦1⟧tx′ = weaken-↝ {tx = tx}{tx′} tx↝ n>0

OutputsWith◆ : Tx  S  Set
OutputsWith◆ tx s =
   λ v  outputs◆ tx  [ record {value = v; address = 𝕍; datumHash = toData s ♯ᵈ} ]

record TxS (tx : Tx) (s : S) : Set where
  field
    tx∈ : tx ∈′ L
    s∈  : OutputsWith◆ tx s

∃TxS =  λ tx   λ s  TxS tx s

h₀ :  {tx}
   (tx∈ : tx ∈′ L)
   T (policyₛₘ $ record {thisTx = ; txInfo = mkTxInfo (proj₁ $ ∈⇒Suffix tx∈) tx})
    λ s  Init s × TxS tx s
h₀ {tx = tx} tx∈ p≡
  with v , s , _ , outs≡ , init-sTpolicy⇒ {tx = tx} {l = proj₁ $ ∈⇒Suffix tx∈} refl refl p≡
  = s , init-s , record {tx∈ = tx∈; s∈ = v , outs≡}

hh :  {tx tx′}
   tx ↝⟦ 1  tx′
   (tx∈ : tx′ ∈′ L)
    --------------------------------
   let l = proj₁ $ ∈⇒Suffix tx∈
    in  λ i   λ o  (i  inputs tx′)
                     × (o  outputs tx)
                     × (◆∈ value o)
                     × (getSpentOutput l i  just o)
hh {tx = tx}{tx′} (or∈ , o , ⁉≡just , ◆∈v) tx∈
  = i , o , i∈ , o∈ , ◆∈v , getSpent≡
  where
    l = proj₁ $ ∈⇒Suffix tx∈

    ∃i :  λ i  i  inputs tx′ × (tx ♯ₜₓ  hid (outputRef i))
    ∃i  = find $ L.Any.map⁻ or∈
    i   = proj₁ ∃i
    i∈  = proj₁ $ proj₂ ∃i
    id≡ = proj₂ $ proj₂ ∃i

    o∈ : o  outputs tx
    o∈ = just-⁉⇒∈ {i = index (L.Any.lookup or∈)} ⁉≡just

    index≡ : L.Any.lookup or∈  outputRef i
    index≡ = lookup≡find∘map⁻ {xs = inputs tx′} {f = outputRef} or∈

    ⁉≡just′ : outputs tx  index (outputRef i)   just o
    ⁉≡just′ = subst  x  outputs tx  index x   just o) index≡ ⁉≡just

    vtx : IsValidTx tx′ l
    vtx = tx∈⇒valid {L = L} tx∈

    vvh : Is-just (getSpentOutput l i)
    vvh = Any⇒Is-just {mx = getSpentOutput l i} $ L.All.lookup (validateValidHashes vtx) i∈

    getSpent≡ : getSpentOutput l i  just o
    getSpent≡ = lookup-⟦⟧ {tx = tx}{l}{i}{o} vvh (sym id≡) ⁉≡just′

h :  {tx tx′ s}
   TxS tx s
   tx′ ∈′ L
   tx ↝⟦ 1  tx′
    λ s′  TxS tx′ s′ × (s  s′)
h {tx = tx}{tx′}{s} (record {s∈ = v , outs≡}) tx∈ tx↝
  with txIn , o , txIn∈ , o∈ , ◆∈v , getSpent≡hh {tx = tx}{tx′} tx↝ tx∈
  = qed
  where
    open ≡-Reasoning

    l = proj₁ $ ∈⇒Suffix tx∈

    vtx : IsValidTx tx′ l
    vtx = tx∈⇒valid {L = L} tx∈

    o∈◆ : o  outputs◆ tx
    o∈◆ = ∈-filter⁺ (◆∈?_  value) o∈ ◆∈v

    o≡ : o  record {value = v; address = 𝕍; datumHash = toData s ♯ᵈ}
    o≡ with here eqsubst (o ∈_) outs≡ o∈◆ = eq

    vi = validator txIn
    ri = redeemer txIn
    di = datum txIn
    ds = toData s
    ptx = toPendingTx l tx′ (L.Any.index txIn∈)

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

    aiv′ : T $ vi ptx ri di
    aiv′ = L.All.lookup aiv (x∈→ix∈ txIn∈)

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

    vvh′ : M.Any.Any  o  (address o  vi ) × (datumHash o  di ♯ᵈ)) (getSpentOutput l txIn)
    vvh′ = L.All.lookup vvh txIn∈

    vvh″ : (address o  vi ) × (datumHash o  di ♯ᵈ)
    vvh″ = Any-just {mx = getSpentOutput l txIn} getSpent≡ vvh′

    vi≡ : vi  validatorₛₘ
    vi≡ = ♯-injective
        $ begin vi       ≡⟨ sym (proj₁ vvh″) 
                address o ≡⟨ cong address o≡ 
                𝕍        

    di≡ : di  ds
    di≡ = injective♯ᵈ
        $ begin di ♯ᵈ       ≡⟨ sym (proj₂ vvh″) 
                datumHash o ≡⟨ cong datumHash o≡ 
                ds ♯ᵈ       

    Tval : T (validatorₛₘ ptx ri ds)
    Tval = subst T (begin vi ptx ri di          ≡⟨ cong  x  x ptx ri di) vi≡ 
                          validatorₛₘ ptx ri di ≡⟨ cong (validatorₛₘ ptx ri) di≡ 
                          validatorₛₘ ptx ri ds ) aiv′

    qed :  λ s′  TxS tx′ s′ × (s  s′)
    qed with i , s′ , tx≡ , s→s′ , outsOK , _ , propT-validator {di = ri}{s}{ptx} Tval
        with _ , vout≥T-propagates {ptx} prop
        with o′ , o′∈ , outs≡ , datum≡ , refl , addr≡T-outputsOK {l}{tx′}{di}{ds}{s′}{txIn}{txIn∈} outsOK
      = s′ , record {tx∈ = tx∈; s∈ = value o′ , outs◆≡′} , i , tx≡ , s→s′
      where
        vout◆≥ : value o′   threadₛₘ 
        vout◆≥ = ≥ᶜ-◆ {x = value o′} {y = threadₛₘ} (true⇒T vout≥)

        ◆∈v′ : ◆∈ value o′
        ◆∈v′ = subst (value o′  ≥_) (◆-single {n = 1}) vout◆≥

        o′≡ : o′  record {value = value o′; address = 𝕍; datumHash = toData s′ ♯ᵈ}
        o′≡ rewrite addr≡ | vi≡ | datum≡ = refl

        o′∈outs◆ : o′  outputs◆ tx′
        o′∈outs◆ = ∈-filter⁺ (◆∈?_  value) o′∈ ◆∈v′

        outs◆≡ : outputs◆ tx′  [ o′ ]
        outs◆≡ = ams-∈ {x = o′} {xs = outputs◆ tx′} (ams-outputs◆ tx∈) o′∈outs◆

        outs◆≡′ : outputs◆ tx′  [ record {value = value o′; address = 𝕍; datumHash = toData s′ ♯ᵈ} ]
        outs◆≡′ = trans outs◆≡ (cong [_] o′≡)

data  : ∃TxS  ∃TxS  Set where

  root :  {tx}

     (tx∈ : tx ∈′ L)
     (p≡ : T (policyₛₘ $ record {thisTx = ; txInfo = mkTxInfo (proj₁ $ ∈⇒Suffix tx∈) tx}))
      --------------------------------------------------------
     let s , _ , txs = h₀ tx∈ p≡
      in   (tx , s , txs) (tx , s , txs)

  cons :  {txs₀ tx s tx′} {txs : TxS tx s}

      txs₀ (tx , s , txs)
     (tx∈ : tx′ ∈′ L)
     (tx↝ : tx ↝⟦ 1  tx′)
      -----------------------------------
     let s′ , txs′ , _ = h txs tx∈ tx↝
      in   txs₀ (tx′ , s′ , txs′)


X¹→Xˢ :  {tx tx′}
    tx tx′
    -------------------------------------
    λ s   λ s′   λ txs   λ txs′ 
       (tx , s , txs) (tx′ , s′ , txs′)
X¹→Xˢ {tx = tx} {.tx} (root .tx tx∈ p≡) =
  let s , _ , txs = h₀ tx∈ p≡
  in  _ , _ , _ , _ , root tx∈ p≡
X¹→Xˢ {tx = tx} {tx′} (cons  tx∈ tx↝) =
  let _ , s , _ , txs ,   = X¹→Xˢ 
      s′ , txs′ , _ = h txs tx∈ tx↝
  in  _ , _ , _ , _ , cons  tx∈ tx↝

Xˢ→R :  {tx s tx′ s′} {txs : TxS tx s} {txs′ : TxS tx′ s′}
    (_ , _ , txs) (_ , _ , txs′)
    -------------------------------
   s ↝⋆ s′
Xˢ→R (root {tx = tx} tx∈ p≡) =
  let _ , init-s , _ = h₀ {tx = tx} tx∈ p≡
  in  root init-s
Xˢ→R (cons {txs = txs} x tx∈ tx↝) =
  let _ , _ , s→s′ = h txs tx∈ tx↝
  in  snoc (Xˢ→R x) s→s′

∃Xˢ = ∃[ tx ] ∃[ tx′ ] ∃[ s ] ∃[ s′ ] ∃[ txs ] ∃[ txs′ ]  (tx , s , txs) (tx′ , s′ , txs′)

module Extraction {tx o}
  (o∈  : o  outputs tx)
  (jo  : Is-just originₛₘ)
  where

  source dest : ∃Xˢ  S
  source = proj₁  proj₂  proj₂
  dest   = proj₁  proj₂  proj₂  proj₂

  provenanceˢ :
      (tx∈ : tx ∈′ L)
     (◆∈v : ◆∈ value o)
      ------------------
     ∃Xˢ
  provenanceˢ tx∈ ◆∈v
    with l , l≺∈⇒Suffix tx∈
    with vl≼⇒valid (proj₂ L) l≺
    with n , tr , _ , n>0 , p≡initiality vl o∈ ◆∈v jo
     = _ , _ , X¹→Xˢ (X→X¹ n>0 tr′ p≡′)
    where
      tr′ : Trace L tx n
      tr′ = weakenTrace l≺ tr

      p≡′ : T (policyₛₘ $ mkPendingMPS {L = L} tr′ )
      p≡′ = subst  x  T (policyₛₘ $ toPendingMPS x (origin tr) )) (sym $ mps≡ {L = L} {_ , vl} l≺ tr) p≡
      -- rewrite mps≡ {L = L}{_ , vl} l≺ tr = p≡

  extract :
      (tx∈ : tx ∈′ L)
     (◆∈v : ◆∈ value o)
     let  = provenanceˢ tx∈ ◆∈v in
    ---------------------------------
    source  ↝⋆ dest 
  extract tx∈ ◆∈v = let _ , _ , _ , _ , _ , _ ,  = provenanceˢ tx∈ ◆∈v
                    in  Xˢ→R