-- editorconfig-checker-disable-file
{-# LANGUAGE LambdaCase #-}
module PlutusLedgerApi.Common.Versions
    ( module PlutusLedgerApi.Common.ProtocolVersions
    , LedgerPlutusVersion (..)
    , languageIntroducedIn
    , languagesAvailableIn
    , builtinsIntroducedIn
    , builtinsAvailableIn
    ) where

import PlutusCore
import PlutusLedgerApi.Common.ProtocolVersions
import PlutusPrelude

import Data.Map qualified as Map
import Data.Set qualified as Set
import Prettyprinter

{- Note [New builtins and protocol versions]
When we add a new builtin to the language, that is a *backwards-compatible* change.
Old scripts will still work (since they don't use the new builtins), we just make some more
scripts possible.

It would be nice, therefore, to get away with just having one definition of the set of builtin
functions. Then the new builtins will just "work". However, this neglects the fact that
the new builtins will be added to the builtin universe in the *software update* that
brings a new version of Plutus, but they should only be usable after the corresponding
*hard fork*. So there is a period of time in which they must be present in the software but not
usable, so we need to decide this conditionally based on the protocol version.

To do this we need to:
- Know which protocol version a builtin was introduced in.
- Given the protocol version, check a program for builtins that should not be usable yet.

Note that this doesn't currently handle removals of builtins, although it fairly straighforwardly
could do, just by tracking when they were removed.
-}

-- | The plutus language version as seen from the ledger's side.
-- Note: the ordering of constructors matters for deriving Ord
data LedgerPlutusVersion =
      PlutusV1
    | PlutusV2
    | PlutusV3
   deriving stock (LedgerPlutusVersion -> LedgerPlutusVersion -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: LedgerPlutusVersion -> LedgerPlutusVersion -> Bool
$c/= :: LedgerPlutusVersion -> LedgerPlutusVersion -> Bool
== :: LedgerPlutusVersion -> LedgerPlutusVersion -> Bool
$c== :: LedgerPlutusVersion -> LedgerPlutusVersion -> Bool
Eq, Eq LedgerPlutusVersion
LedgerPlutusVersion -> LedgerPlutusVersion -> Bool
LedgerPlutusVersion -> LedgerPlutusVersion -> Ordering
LedgerPlutusVersion -> LedgerPlutusVersion -> LedgerPlutusVersion
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: LedgerPlutusVersion -> LedgerPlutusVersion -> LedgerPlutusVersion
$cmin :: LedgerPlutusVersion -> LedgerPlutusVersion -> LedgerPlutusVersion
max :: LedgerPlutusVersion -> LedgerPlutusVersion -> LedgerPlutusVersion
$cmax :: LedgerPlutusVersion -> LedgerPlutusVersion -> LedgerPlutusVersion
>= :: LedgerPlutusVersion -> LedgerPlutusVersion -> Bool
$c>= :: LedgerPlutusVersion -> LedgerPlutusVersion -> Bool
> :: LedgerPlutusVersion -> LedgerPlutusVersion -> Bool
$c> :: LedgerPlutusVersion -> LedgerPlutusVersion -> Bool
<= :: LedgerPlutusVersion -> LedgerPlutusVersion -> Bool
$c<= :: LedgerPlutusVersion -> LedgerPlutusVersion -> Bool
< :: LedgerPlutusVersion -> LedgerPlutusVersion -> Bool
$c< :: LedgerPlutusVersion -> LedgerPlutusVersion -> Bool
compare :: LedgerPlutusVersion -> LedgerPlutusVersion -> Ordering
$ccompare :: LedgerPlutusVersion -> LedgerPlutusVersion -> Ordering
Ord, Int -> LedgerPlutusVersion -> ShowS
[LedgerPlutusVersion] -> ShowS
LedgerPlutusVersion -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [LedgerPlutusVersion] -> ShowS
$cshowList :: [LedgerPlutusVersion] -> ShowS
show :: LedgerPlutusVersion -> String
$cshow :: LedgerPlutusVersion -> String
showsPrec :: Int -> LedgerPlutusVersion -> ShowS
$cshowsPrec :: Int -> LedgerPlutusVersion -> ShowS
Show, forall x. Rep LedgerPlutusVersion x -> LedgerPlutusVersion
forall x. LedgerPlutusVersion -> Rep LedgerPlutusVersion x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep LedgerPlutusVersion x -> LedgerPlutusVersion
$cfrom :: forall x. LedgerPlutusVersion -> Rep LedgerPlutusVersion x
Generic, Int -> LedgerPlutusVersion
LedgerPlutusVersion -> Int
LedgerPlutusVersion -> [LedgerPlutusVersion]
LedgerPlutusVersion -> LedgerPlutusVersion
LedgerPlutusVersion -> LedgerPlutusVersion -> [LedgerPlutusVersion]
LedgerPlutusVersion
-> LedgerPlutusVersion
-> LedgerPlutusVersion
-> [LedgerPlutusVersion]
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
enumFromThenTo :: LedgerPlutusVersion
-> LedgerPlutusVersion
-> LedgerPlutusVersion
-> [LedgerPlutusVersion]
$cenumFromThenTo :: LedgerPlutusVersion
-> LedgerPlutusVersion
-> LedgerPlutusVersion
-> [LedgerPlutusVersion]
enumFromTo :: LedgerPlutusVersion -> LedgerPlutusVersion -> [LedgerPlutusVersion]
$cenumFromTo :: LedgerPlutusVersion -> LedgerPlutusVersion -> [LedgerPlutusVersion]
enumFromThen :: LedgerPlutusVersion -> LedgerPlutusVersion -> [LedgerPlutusVersion]
$cenumFromThen :: LedgerPlutusVersion -> LedgerPlutusVersion -> [LedgerPlutusVersion]
enumFrom :: LedgerPlutusVersion -> [LedgerPlutusVersion]
$cenumFrom :: LedgerPlutusVersion -> [LedgerPlutusVersion]
fromEnum :: LedgerPlutusVersion -> Int
$cfromEnum :: LedgerPlutusVersion -> Int
toEnum :: Int -> LedgerPlutusVersion
$ctoEnum :: Int -> LedgerPlutusVersion
pred :: LedgerPlutusVersion -> LedgerPlutusVersion
$cpred :: LedgerPlutusVersion -> LedgerPlutusVersion
succ :: LedgerPlutusVersion -> LedgerPlutusVersion
$csucc :: LedgerPlutusVersion -> LedgerPlutusVersion
Enum, LedgerPlutusVersion
forall a. a -> a -> Bounded a
maxBound :: LedgerPlutusVersion
$cmaxBound :: LedgerPlutusVersion
minBound :: LedgerPlutusVersion
$cminBound :: LedgerPlutusVersion
Bounded)

instance Pretty LedgerPlutusVersion where
    pretty :: forall ann. LedgerPlutusVersion -> Doc ann
pretty = forall a ann. Show a => a -> Doc ann
viaShow

{-| A map indicating which builtin functions were introduced in which 'ProtocolVersion'. Each builtin function should appear at most once.

This *must* be updated when new builtins are added.
See Note [New builtins and protocol versions]
-}
builtinsIntroducedIn :: Map.Map (LedgerPlutusVersion, ProtocolVersion) (Set.Set DefaultFun)
builtinsIntroducedIn :: Map (LedgerPlutusVersion, ProtocolVersion) (Set DefaultFun)
builtinsIntroducedIn = forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList [
  -- Alonzo is protocolversion=5.0
  ((LedgerPlutusVersion
PlutusV1, ProtocolVersion
alonzoPV), forall a. Ord a => [a] -> Set a
Set.fromList [
          DefaultFun
AddInteger, DefaultFun
SubtractInteger, DefaultFun
MultiplyInteger, DefaultFun
DivideInteger, DefaultFun
QuotientInteger, DefaultFun
RemainderInteger, DefaultFun
ModInteger, DefaultFun
EqualsInteger, DefaultFun
LessThanInteger, DefaultFun
LessThanEqualsInteger,
          DefaultFun
AppendByteString, DefaultFun
ConsByteString, DefaultFun
SliceByteString, DefaultFun
LengthOfByteString, DefaultFun
IndexByteString, DefaultFun
EqualsByteString, DefaultFun
LessThanByteString, DefaultFun
LessThanEqualsByteString,
          DefaultFun
Sha2_256, DefaultFun
Sha3_256, DefaultFun
Blake2b_256, DefaultFun
VerifyEd25519Signature,
          DefaultFun
AppendString, DefaultFun
EqualsString, DefaultFun
EncodeUtf8, DefaultFun
DecodeUtf8,
          DefaultFun
IfThenElse,
          DefaultFun
ChooseUnit,
          DefaultFun
Trace,
          DefaultFun
FstPair, DefaultFun
SndPair,
          DefaultFun
ChooseList, DefaultFun
MkCons, DefaultFun
HeadList, DefaultFun
TailList, DefaultFun
NullList,
          DefaultFun
ChooseData, DefaultFun
ConstrData, DefaultFun
MapData, DefaultFun
ListData, DefaultFun
IData, DefaultFun
BData, DefaultFun
UnConstrData, DefaultFun
UnMapData, DefaultFun
UnListData, DefaultFun
UnIData, DefaultFun
UnBData, DefaultFun
EqualsData,
          DefaultFun
MkPairData, DefaultFun
MkNilData, DefaultFun
MkNilPairData
          ]),
  -- Vasil is protocolversion=7.0
  ((LedgerPlutusVersion
PlutusV2, ProtocolVersion
vasilPV), forall a. Ord a => [a] -> Set a
Set.fromList [
          DefaultFun
SerialiseData
          ]),
  -- Chang is protocolversion=8.0
  ((LedgerPlutusVersion
PlutusV2, ProtocolVersion
changPV), forall a. Ord a => [a] -> Set a
Set.fromList [
          DefaultFun
VerifyEcdsaSecp256k1Signature, DefaultFun
VerifySchnorrSecp256k1Signature
          ])
  ]

-- | Query the protocol version that a specific ledger plutus version was first introduced in.
-- 'Introduction' in this context means the enablement/allowance of scripts of that language version to be executed on-chain.
languageIntroducedIn :: LedgerPlutusVersion -> ProtocolVersion
languageIntroducedIn :: LedgerPlutusVersion -> ProtocolVersion
languageIntroducedIn = \case
    LedgerPlutusVersion
PlutusV1 -> ProtocolVersion
alonzoPV
    LedgerPlutusVersion
PlutusV2 -> ProtocolVersion
vasilPV
    LedgerPlutusVersion
PlutusV3 -> ProtocolVersion
changPV

-- | Given a protocol version return a set of all available plutus languages that are enabled/allowed to run.
-- Assumes that languages once introduced/enabled, will never be disabled in the future.
languagesAvailableIn :: ProtocolVersion -> Set.Set LedgerPlutusVersion
languagesAvailableIn :: ProtocolVersion -> Set LedgerPlutusVersion
languagesAvailableIn ProtocolVersion
searchPv =
    forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap LedgerPlutusVersion -> Set LedgerPlutusVersion
ledgerVersionToSet forall a. (Enum a, Bounded a) => [a]
enumerate
  where
    -- OPTIMIZE: could be done faster using takeWhile
    ledgerVersionToSet :: LedgerPlutusVersion -> Set.Set LedgerPlutusVersion
    ledgerVersionToSet :: LedgerPlutusVersion -> Set LedgerPlutusVersion
ledgerVersionToSet LedgerPlutusVersion
lv
        | LedgerPlutusVersion -> ProtocolVersion
languageIntroducedIn LedgerPlutusVersion
lv forall a. Ord a => a -> a -> Bool
<= ProtocolVersion
searchPv = forall a. a -> Set a
Set.singleton LedgerPlutusVersion
lv
        | Bool
otherwise = forall a. Monoid a => a
mempty

{-| Which builtin functions are available in the given 'ProtocolVersion'?

See Note [New builtins and protocol versions]
-}
builtinsAvailableIn :: LedgerPlutusVersion -> ProtocolVersion -> Set.Set DefaultFun
builtinsAvailableIn :: LedgerPlutusVersion -> ProtocolVersion -> Set DefaultFun
builtinsAvailableIn LedgerPlutusVersion
thisLv ProtocolVersion
thisPv = forall (t :: * -> *) m. (Foldable t, Monoid m) => t m -> m
fold forall a b. (a -> b) -> a -> b
$ forall k a. Map k a -> [a]
Map.elems forall a b. (a -> b) -> a -> b
$
    forall k a. (k -> Bool) -> Map k a -> Map k a
Map.takeWhileAntitone (LedgerPlutusVersion, ProtocolVersion) -> Bool
builtinAvailableIn Map (LedgerPlutusVersion, ProtocolVersion) (Set DefaultFun)
builtinsIntroducedIn
    where
      builtinAvailableIn :: (LedgerPlutusVersion, ProtocolVersion) -> Bool
      builtinAvailableIn :: (LedgerPlutusVersion, ProtocolVersion) -> Bool
builtinAvailableIn (LedgerPlutusVersion
introducedInLv,ProtocolVersion
introducedInPv) =
          -- both should be satisfied
          LedgerPlutusVersion
introducedInLv forall a. Ord a => a -> a -> Bool
<= LedgerPlutusVersion
thisLv Bool -> Bool -> Bool
&& ProtocolVersion
introducedInPv forall a. Ord a => a -> a -> Bool
<= ProtocolVersion
thisPv