{-# OPTIONS --safe #-}

open import Ledger.Prelude
open import Ledger.Core.Specification.Crypto
open import Ledger.Conway.Specification.Abstract
open import Ledger.Conway.Specification.Transaction

module Ledger.Conway.Specification.Utxow
  (txs : _) (open TransactionStructure txs)
  (abs : AbstractFunctions txs) (open AbstractFunctions abs)
  where
open import Ledger.Conway.Specification.Utxo txs abs
open import Ledger.Conway.Specification.Script.Validation txs abs
open import Ledger.Conway.Specification.Certs govStructure


module _ (o : TxOut) where
  d = proj₁ (proj₂ (proj₂ o))
  data HasInlineDatum : Set where
    InlineDatum  :  {d'}  d  just (inj₁ d')  HasInlineDatum

instance
  Dec-HasInlineDatum :  {o}  HasInlineDatum o 
  Dec-HasInlineDatum {_ , _ , just (inj₁ x) , _} =  yes (InlineDatum refl)
  Dec-HasInlineDatum {_ , _ , just (inj₂ x) , _} =  no λ where
    (InlineDatum x)  case x of λ ()
  Dec-HasInlineDatum {_ , _ , nothing , _} =  no λ where
    (InlineDatum x)  case x of λ ()

module _ (txb : TxBody) (let open TxBody txb) where
  data UsesV3Features : Set where
    HasVotes : txGovVotes  []  UsesV3Features
    HasProps : txGovProposals  []  UsesV3Features
    HasDonation : txDonation  0  UsesV3Features
    HasTreasure : currentTreasury  nothing  UsesV3Features

instance
  Dec-UsesV3Features :  {txb}  UsesV3Features txb 
  Dec-UsesV3Features {record { txGovVotes = [] ; txGovProposals = [] ; txDonation = zero ; currentTreasury = nothing }}
    =  no λ where (HasVotes x)     x refl
                   (HasProps x)     x refl
                   (HasDonation x)  x refl
                   (HasTreasure x)  x refl
  Dec-UsesV3Features {record { txGovVotes = [] ; txGovProposals = [] ; txDonation = zero ; currentTreasury = just x }}
    =  yes (HasTreasure  ()))
  Dec-UsesV3Features {record { txGovVotes = [] ; txGovProposals = [] ; txDonation = suc txDonation }}
    =  yes (HasDonation  ()))
  Dec-UsesV3Features {record { txGovVotes = [] ; txGovProposals = x  txGovProposals }} =  yes (HasProps  ()))
  Dec-UsesV3Features {record { txGovVotes = x  txGovVotes }} =  yes (HasVotes  ()))

-- See note "languages" below
languages : Tx  UTxO   ScriptHash   Language
languages tx utxo shs =
  mapPartial getLanguage $ filterˢ  s  hash s  shs) $ txscripts tx utxo


allowedLanguages : Tx  UTxO   Language
allowedLanguages tx utxo =
  if (∃[ o  os ] isBootstrapAddr (proj₁ o))
    then 
  else if UsesV3Features txb
    then fromList (PlutusV3  [])
  else if ∃[ o  os ] HasInlineDatum o
    then fromList (PlutusV2  PlutusV3  [])
  else
    fromList (PlutusV1  PlutusV2  PlutusV3  [])
  where
    txb = tx .Tx.body; open TxBody txb
    os = range (outs txb)  range (utxo  (txIns  refInputs))


hashScriptIntegrity
  : PParams
    Language
   RdmrPtr  (Redeemer × ExUnits)
    Datum
   Maybe ScriptHash
hashScriptIntegrity pp langs rdrms dats
  with rdrms ˢ  ∅ˢ | langs  ∅ˢ | dats  ∅ˢ
...  | yes _        | yes _      | yes _ = nothing
...  | _            | _          | _     =
    just $ hash (dats , rdrms , mapˢ (getLanguageView pp) langs)



private variable
  Γ     : UTxOEnv
  s s'  : UTxOState
  tx    : Tx

open UTxOState


data _⊢_⇀⦇_,UTXOW⦈_ : UTxOEnv  UTxOState  Tx  UTxOState  Type where

  UTXOW-inductive :
    let  open Tx tx renaming (body to txb); open TxBody txb; open TxWitnesses wits
         utxo                = s .utxo
         witsKeyHashes       = mapˢ hash (dom vkSigs)
         witsScriptHashes    = mapˢ hash scripts
         refScriptHashes     = mapˢ hash (refScripts tx utxo)
         neededScriptHashes  = mapPartial (isScriptObj   proj₂) (credsNeeded utxo txb)
         neededVKeyHashes    = mapPartial (isKeyHashObj  proj₂) (credsNeeded utxo txb)
                                reqSignerHashes
         txdatsHashes        = mapˢ hash txdats
         inputsDataHashes    = mapPartial  txout   if txOutToP2Script utxo tx txout
                                                      then txOutToDataHash txout
                                                      else nothing) (range (utxo  txIns))
         refInputsDataHashes = mapPartial txOutToDataHash (range (utxo  refInputs))
         outputsDataHashes   = mapPartial txOutToDataHash (range txOuts)
         nativeScripts       = mapPartial toP1Script (txscripts tx utxo)
         scriptRdrptrs       =
           mapPartial
              (sp , c)  if credentialToP2Script utxo tx c
                           then rdptr txb sp
                           else nothing)
             (credsNeeded utxo txb)
    in
      ∀[ (vk , σ)  vkSigs ] isSigned vk (txidBytes txId) σ
      ∀[ s  nativeScripts ] (hash s  neededScriptHashes  validP1Script witsKeyHashes txVldt s)
      neededVKeyHashes  witsKeyHashes
      neededScriptHashes - refScriptHashes ≡ᵉ witsScriptHashes
      inputsDataHashes  txdatsHashes
      txdatsHashes  inputsDataHashes  outputsDataHashes  refInputsDataHashes
      dom txrdmrs ≡ᵉ scriptRdrptrs
      languages tx utxo neededScriptHashes 
         dom (PParams.costmdls (PParamsOf Γ))  allowedLanguages tx utxo
      txADhash  map hash txAD
      scriptIntegrityHash 
          hashScriptIntegrity
            (PParamsOf Γ)
            (languages tx utxo neededScriptHashes)
            txrdmrs
            txdats
      Γ  s ⇀⦇ tx ,UTXO⦈ s'
       ────────────────────────────────
       Γ  s ⇀⦇ tx ,UTXOW⦈ s'


pattern UTXOW-inductive⋯ p₁ p₂ p₃ p₄ p₅ p₆ p₇ p₈ p₉ p₁₀ h
      = UTXOW-inductive (p₁ , p₂ , p₃ , p₄ , p₅ , p₆ , p₇ , p₈ , p₉ , p₁₀ , h)
pattern UTXOW⇒UTXO x = UTXOW-inductive⋯ _ _ _ _ _ _ _ _ _ _ x

unquoteDecl UTXOW-inductive-premises =
  genPremises UTXOW-inductive-premises (quote UTXOW-inductive)