{-# OPTIONS --safe #-}

open import Ledger.Conway.Transaction
open import Ledger.Conway.Abstract

module Ledger.Conway.Script.Validation
  (txs : _) (open TransactionStructure txs)
  (abs : AbstractFunctions txs) (open AbstractFunctions abs) (open indexOf indexOfImp)
  where

open import Ledger.Prelude
open import Ledger.Conway.Certs govStructure

data ScriptPurpose : Type where
  Cert     : DCert         ScriptPurpose
  Rwrd     : RwdAddr       ScriptPurpose
  Mint     : ScriptHash    ScriptPurpose
  Spend    : TxIn          ScriptPurpose
  Vote     : Voter         ScriptPurpose
  Propose  : GovProposal   ScriptPurpose

rdptr : TxBody  ScriptPurpose  Maybe RdmrPtr
rdptr txb = λ where
  (Cert h)      map (Cert    ,_) $ indexOfDCert    h txcerts
  (Rwrd h)      map (Rewrd   ,_) $ indexOfRwdAddr  h txwdrls
  (Mint h)      map (Mint    ,_) $ indexOfPolicyId h (policies mint)
  (Spend h)     map (Spend   ,_) $ indexOfTxIn     h txins
  (Vote h)      map (Vote    ,_) $ indexOfVote     h (map GovVote.voter txvote)
  (Propose h)   map (Propose ,_) $ indexOfProposal h txprop
 where open TxBody txb

indexedRdmrs : Tx  ScriptPurpose  Maybe (Redeemer × ExUnits)
indexedRdmrs tx sp = maybe  x  lookupᵐ? txrdmrs x) nothing (rdptr body sp)
  where open Tx tx; open TxWitnesses wits

getDatum : Tx  UTxO  ScriptPurpose  Maybe Datum
getDatum tx utxo (Spend txin) =
  do (_ , _ , just d , _)  lookupᵐ? utxo txin
                          where
                            (_ , _ , nothing , _)  nothing
     case d of λ where
       (inj₁ d)  just d
       (inj₂ h)  lookupᵐ? m h
     where
       m = setToMap (mapˢ < hash , id > (TxWitnesses.txdats (Tx.wits tx)))
getDatum tx utxo _ = nothing

record TxInfo : Type where
  field realizedInputs : UTxO
        txouts  : Ix  TxOut
        fee     : Value
        mint    : Value
        txcerts : List DCert
        txwdrls : Wdrl
        txvldt  : Maybe Slot × Maybe Slot
        vkKey   :  KeyHash
        txdats  :  Datum
        txid    : TxId

txInfo : Language  PParams
                   UTxO
                   Tx
                   TxInfo
txInfo l pp utxo tx = record
  { TxBody body
  ; TxWitnesses wits
  ; realizedInputs = utxo  txins
  ; fee = inject txfee
  ; mint = mint
  ; vkKey = reqSigHash
  } where open Tx tx; open TxBody body

credsNeeded : UTxO  TxBody   (ScriptPurpose × Credential)
credsNeeded utxo txb
  =  mapˢ  (i , o)   (Spend  i , payCred (proj₁ o))) ((utxo  (txins  collateral)) ˢ)
    mapˢ  a         (Rwrd   a , stake a)) (dom  txwdrls )
    mapPartial  c   (Cert   c ,_) <$> cwitness c) (fromList txcerts)
    mapˢ  x         (Mint   x , ScriptObj x)) (policies mint)
    mapˢ  v         (Vote   v , proj₂ v)) (fromList (map voter txvote))
    mapPartial  p  if p .policy then  {sh}  just (Propose  p , ScriptObj sh)) else nothing)
                (fromList txprop)
  where
    open TxBody txb
    open GovVote
    open RwdAddr
    open GovProposal

valContext : TxInfo  ScriptPurpose  Data
valContext txinfo sp = toData (txinfo , sp)

txOutToDataHash : TxOut  Maybe DataHash
txOutToDataHash (_ , _ , d , _) = d >>= isInj₂

txOutToP2Script
  : UTxO  Tx
   TxOut  Maybe P2Script
txOutToP2Script utxo tx (a , _) =
  do sh  isScriptObj (payCred a)
     s   lookupScriptHash sh tx utxo
     toP2Script s

opaque
  collectP2ScriptsWithContext
    : PParams  Tx  UTxO
     List (P2Script × List Data × ExUnits × CostModel)
  collectP2ScriptsWithContext pp tx utxo
    = setToList
    $ mapPartial  (sp , c)  if isScriptObj c
                                then  {sh}  toScriptInput sp sh)
                                else nothing)
    $ credsNeeded utxo (tx .Tx.body)
    where
      toScriptInput
        : ScriptPurpose  ScriptHash
         Maybe (P2Script × List Data × ExUnits × CostModel)
      toScriptInput sp sh =
        do s  lookupScriptHash sh tx utxo
           p2s  toP2Script s
           (rdmr , exunits)  indexedRdmrs tx sp
           let data'     = maybe [_] [] (getDatum tx utxo sp) ++ rdmr  [ valContext (txInfo (language p2s) pp utxo tx) sp ]
               costModel = PParams.costmdls pp
           just (p2s , data' , exunits , costModel)

evalP2Scripts : List (P2Script × List Data × ExUnits × CostModel)  Bool
evalP2Scripts = all  (s , d , eu , cm)  runPLCScript cm s eu d)