Source code on Github
----------------------------------------------
-- ** Basic definition for UTxO-based ledgers.

module UTxOErr.UTxO where

open import Prelude.Init; open SetAsType
open L.Mem
open import Prelude.General
open import Prelude.Maybes
open import Prelude.DecEq
open import Prelude.Decidable
open import Prelude.ToN
open import Prelude.Lists hiding (_⁉_; _‼_; map↦)
open import Prelude.Lists.Dec
open import Prelude.Lists.WithK
open import Prelude.Functor
open import Prelude.Applicative
open import Prelude.FromList
open import Prelude.Membership using (_∈?_; _∉?_)
open import Prelude.Irrelevance
open import Prelude.Apartness
open import Prelude.Allable hiding (All)

-- open import UTxOErr.Maps
open import Prelude.Maps

open import Common public

record TxOutputRef : Type where
  constructor _indexed-at_
  field txId  : HashId
        index : 
open TxOutputRef public
unquoteDecl DecEq-TxOR = DERIVE DecEq [ quote TxOutputRef , DecEq-TxOR ]

open CommonInfo TxOutputRef public

-- The state of a ledger maps output references locked by a validator to a value.

S : Type
S = Map⟨ TxOutputRef  TxOutput 

mkUtxo :  {out} tx  out L.Mem.∈ outputs tx  TxOutputRef × TxOutput
mkUtxo {out} tx out∈ = (tx ) indexed-at toℕ (L.Any.index out∈) , out

postulate Unique-mkUtxo :  t  Unique $ map proj₁ $ mapWith∈ (t .outputs) (mkUtxo t)

utxoTx : Tx  S
utxoTx tx = fromList $ L.Mem.mapWith∈ (tx .outputs) (mkUtxo tx)

-- ** Utilities relating Prelude.Maps and Prelude.Bags.
module _ {K V : Type}  _ : DecEq K   _ : DecEq V  where
  open import Prelude.ToList
  import Prelude.Sets as S
  import Prelude.Bags as B

  _‼_ : {k : K} (m : Map⟨ K  V )  k ∈ᵈ m  V
  m  k∈ = destruct-Is-just (∈ᵈ⇒⁉ m k∈) .proj₁

  _─ᵏˢ_ : Map⟨ K  V   List K  Map⟨ K  V 
  m ─ᵏˢ ks = filterK (_∉? ks) m

  keys : Map⟨ K  V   List K
  keys = map proj₁  toList

  keysˢ : Map⟨ K  V   B.Bag⟨ K 
  keysˢ = fromList  keys

  values⊑ : (m : Map⟨ K  V )   $ All (_∈ᵈ m)  List V
  values⊑ m (ks , ks⊆) = mapWith∈ ks ((m ‼_)  L.All.lookup ks⊆)

  values values′ : Map⟨ K  V   List V
  values = map proj₂  toList
  values′ m = values⊑ m (keys m , L.All.tabulate id)

  values⊑ˢ : (m : Map⟨ K  V )   $ All (_∈ᵈ m)  B.Bag⟨ V 
  values⊑ˢ = fromList ∘₂ values⊑

  valuesˢ valuesˢ′ : Map⟨ K  V   B.Bag⟨ V 
  valuesˢ = fromList  map proj₂  toList
  valuesˢ′ m = values⊑ˢ m (keys m , L.All.tabulate id)

  open ≡-Reasoning
  private variable
    m m′ : Map⟨ K  V 
    ks : List K; kvs : List (K × V)

  open import Prelude.Lists.Dec
  open import Prelude.Apartness

  -- basic list properties
  postulate
    nubBy-from∘to :  {A B : Type}  _ : DecEq B  {f : A  B} {xs : List A} 
      Unique (map f xs)  nubBy f xs  xs
    Unique-proj₁ :  {A B : Type} {xys : List (A × B)} 
      Unique (map proj₁ xys)  Unique xys
    Disjoint-proj₁ :  {A B : Type} {xys xys′ : List (A × B)} 
      map proj₁ xys  map proj₁ xys′  xys  xys′

  nub∘nubBy-from∘to :  {A B : Type}  _ : DecEq A   _ : DecEq B 
    {f : A  B} {xs : List A} 
    Unique (map f xs) 
      nub (nubBy f xs)  xs
  nub∘nubBy-from∘to {f = f}{xs} uniq =
    begin
      nub (nubBy f xs)
    ≡⟨ nub-from∘to $ Unique-nubBy f xs 
      nubBy f xs
    ≡⟨ nubBy-from∘to uniq 
      xs
    

  toList-∪ : keys m  keys m′   toList (m  m′)  toList m ++ toList m′
  toList-∪ {m@(_  ·uniq-m)}{m′@(_  ·uniq-m′)} m♯m′ =
    begin
      toList (m  m′)
    ≡⟨⟩
      nub (nubBy proj₁ (toList m ++ toList m′))
    ≡⟨ cong nub $ nubBy-from∘to uniq-proj₁-++ 
      nub (toList m ++ toList m′)
    ≡⟨ nub-from∘to uniq-++ 
      toList m ++ toList m′
    
    where
      uniq-m  = recompute dec ·uniq-m
      uniq-m′ = recompute dec ·uniq-m′

      uniq-++ : Unique (toList m ++ toList m′)
      uniq-++ = L.Uniq.++⁺ (Unique-proj₁ uniq-m) (Unique-proj₁ uniq-m′)
                           (Disjoint-proj₁ m♯m′)

      uniq-proj₁-++ : Unique $ map proj₁ (toList m ++ toList m′)
      uniq-proj₁-++ rewrite L.map-++-commute proj₁ (toList m) (toList m′)
        = L.Uniq.++⁺ uniq-m uniq-m′ m♯m′

  module _ m m′ (m♯m′ : keys m  keys m′) where
    valuesˢ-∪ : valuesˢ (m  m′)  valuesˢ m B.∪ valuesˢ m′
    valuesˢ-∪ =
      begin
        valuesˢ (m  m′)
      ≡⟨⟩
        fromList (proj₂ <$> toList (m  m′))
      ≡⟨ cong (fromList  map proj₂) $ toList-∪ {m}{m′} m♯m′ 
        fromList (proj₂ <$> (toList m ++ toList m′))
      ≡⟨ cong fromList $ L.map-++-commute proj₂ (toList m) (toList m′) 
        fromList ((proj₂ <$> toList m) ++ (proj₂ <$> toList m′))
      ≡⟨⟩
        fromList (proj₂ <$> toList m) B.∪ fromList (proj₂ <$> toList m′)
      ≡⟨⟩
        valuesˢ m B.∪ valuesˢ m′
      

  valuesˢ∘fromList : Unique (proj₁ <$> kvs) 
    valuesˢ (fromList kvs)  fromList (proj₂ <$> kvs)
  valuesˢ∘fromList = cong (fromList  map proj₂)  nub∘nubBy-from∘to

  valuesˢ-─ :  m (p :  $ All (_∈ᵈ m)) 
    valuesˢ (m ─ᵏˢ p .proj₁)  valuesˢ m B.─ values⊑ˢ m p
  valuesˢ-─ m (ks , ks⊆) =
    begin
      valuesˢ (m ─ᵏˢ ks)
    ≡⟨⟩
      fromList (proj₂ <$> toList (m ─ᵏˢ ks))
    ≡⟨⟩
      fromList (proj₂ <$> filter ((_∉? ks)  proj₁) (toList m))
    ≡⟨ cong fromList H 
      fromList
        (map proj₂ (toList m) ∸[bag] (mapWith∈ ks ((m ‼_)  L.All.lookup ks⊆)))
    ≡⟨⟩
      fromList (proj₂ <$> toList m)
        B.─
      fromList (mapWith∈ ks ((m ‼_)  L.All.lookup ks⊆))
    ≡⟨⟩
      fromList (proj₂ <$> toList m) B.─ fromList (values⊑ m (-, ks⊆))
    ≡⟨⟩
      valuesˢ m B.─ values⊑ˢ m (-, ks⊆)
    
    where
      postulate
        H : map proj₂ (filter ((_∉? ks)  proj₁) (toList m))
           map proj₂ (toList m) ∸[bag] (mapWith∈ ks ((m ‼_)  L.All.lookup ks⊆))


  postulate
    values⊆⇒⊆[bag] :  m (p :  $ All (_∈ᵈ m))  values⊑ m p ⊆[bag] values m

  values⊆⇒⊆ˢ :  m (p :  $ All (_∈ᵈ m))  values⊑ˢ m p B.⊆ˢ valuesˢ m
  values⊆⇒⊆ˢ = values⊆⇒⊆[bag]

record IsValidTx (tx : Tx) (utxos : S) : Type where
  field
    noDoubleSpending :
      ·Unique (outputRefs tx)

    validOutputRefs :
      ∀[ ref  outputRefs tx ]
        (ref ∈ᵈ utxos)

  resolved : Resolved tx
  resolved = (utxos ‼_)  L.All.lookup validOutputRefs

  txInfo = mkTxInfo tx resolved
  resolvedInputs = resolveInputs tx resolved

  field
    preservesValues :
      tx .forge +  resolvedInputs (value  proj₂)   (tx .outputs) value

    allInputsValidate :
      ∀[ i  tx .inputs ]
        T (i .validator txInfo (i .redeemer))

    validateValidHashes :
      ∀[ (i , o)  resolvedInputs ]
        (o .address  i .validator )

open IsValidTx public

instance
  ·Valid :  {tx s}  · IsValidTx tx s
  ·Valid {s = _  ·uniq} .∀≡
    (record { noDoubleSpending = ndp
            ; validOutputRefs = vor
            ; preservesValues = pv
            ; allInputsValidate = aiv
            ; validateValidHashes = vvh
            })
    (record { noDoubleSpending = ndp′
            ; validOutputRefs = vor′
            ; preservesValues = pv′
            ; allInputsValidate = aiv′
            ; validateValidHashes = vvh′
            })
    with uniqrecompute dec ·uniq
    rewrite ∀≡ ndp ndp′
          | L.All.irrelevant (∈-irr uniq) vor vor′
          | ∀≡ pv pv′
          | L.All.irrelevant B.T-irrelevant aiv aiv′
          | ∀≡ vvh vvh′
          = refl

isValidTx? :  tx s  Dec (IsValidTx tx s)
isValidTx? tx s@(_  ·uniq)
  with uniqrecompute dec ·uniq
  with dec
... | no ¬p = no (¬p  noDoubleSpending)
... | yes ndp with dec
... | no ¬p = no (¬p  validOutputRefs)
... | yes vor with dec
... | no ¬p = no absurd
  where absurd : ¬ IsValidTx tx s
        absurd (record {validOutputRefs = vor′; preservesValues = p})
          rewrite L.All.irrelevant (∈-irr uniq) vor vor′ = ¬p p
... | yes pv with dec
... | no ¬p = no absurd
  where absurd : ¬ IsValidTx tx s
        absurd (record {validOutputRefs = vor′; allInputsValidate = p})
          rewrite L.All.irrelevant (∈-irr uniq) vor vor′ = ¬p p
... | yes aiv with dec
... | no ¬p = no absurd
  where absurd : ¬ IsValidTx tx s
        absurd (record {validOutputRefs = vor′; validateValidHashes = p})
          rewrite L.All.irrelevant (∈-irr uniq) vor vor′ = ¬p p
... | yes vvh = yes record
  { noDoubleSpending = ndp
  ; validOutputRefs = vor
  ; preservesValues = pv
  ; allInputsValidate = aiv
  ; validateValidHashes = vvh }

isValidTx : Tx  S  Bool
isValidTx tx s =  isValidTx? tx s 

-- this is a property of the postulated cryptography (hashing in this case)
postulate s♯t :  {t s}  IsValidTx t s  keys (s ─ᵏˢ outputRefs t)  keys (utxoTx t)