{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}

module Cardano.Ledger.Binary.Version (
  -- * Versioning
  Version,
  getVersion,
  MinVersion,
  MaxVersion,
  natVersion,
  natVersionProxy,
  succVersion,
  mkVersion,
  mkVersion64,
  getVersion64,
  allVersions,

  -- ** Concrete era versions
  byronProtVer,
  shelleyProtVer,
)
where

import Cardano.Binary (FromCBOR (..), ToCBOR (..))
import Control.DeepSeq (NFData)
import Control.Monad.Trans.Fail.String (errorFail)
import Data.Aeson (FromJSON (..), ToJSON (..))
import Data.Proxy (Proxy (..))
import Data.Word (Word64)
import GHC.TypeLits (KnownNat, natVal, type (<=))
import NoThunks.Class (NoThunks)

--------------------------------------------------------------------------------
-- Version
--------------------------------------------------------------------------------

-- | Protocol version number that is used during encoding and decoding. All supported
-- versions are in the range from `MinVersion` to `MaxVersion`.
newtype Version = Version Word64
  deriving (Version -> Version -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Version -> Version -> Bool
$c/= :: Version -> Version -> Bool
== :: Version -> Version -> Bool
$c== :: Version -> Version -> Bool
Eq, Eq Version
Version -> Version -> Bool
Version -> Version -> Ordering
Version -> Version -> Version
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 :: Version -> Version -> Version
$cmin :: Version -> Version -> Version
max :: Version -> Version -> Version
$cmax :: Version -> Version -> Version
>= :: Version -> Version -> Bool
$c>= :: Version -> Version -> Bool
> :: Version -> Version -> Bool
$c> :: Version -> Version -> Bool
<= :: Version -> Version -> Bool
$c<= :: Version -> Version -> Bool
< :: Version -> Version -> Bool
$c< :: Version -> Version -> Bool
compare :: Version -> Version -> Ordering
$ccompare :: Version -> Version -> Ordering
Ord, Int -> Version -> ShowS
[Version] -> ShowS
Version -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Version] -> ShowS
$cshowList :: [Version] -> ShowS
show :: Version -> String
$cshow :: Version -> String
showsPrec :: Int -> Version -> ShowS
$cshowsPrec :: Int -> Version -> ShowS
Show, Version -> ()
forall a. (a -> ()) -> NFData a
rnf :: Version -> ()
$crnf :: Version -> ()
NFData, Context -> Version -> IO (Maybe ThunkInfo)
Proxy Version -> String
forall a.
(Context -> a -> IO (Maybe ThunkInfo))
-> (Context -> a -> IO (Maybe ThunkInfo))
-> (Proxy a -> String)
-> NoThunks a
showTypeOf :: Proxy Version -> String
$cshowTypeOf :: Proxy Version -> String
wNoThunks :: Context -> Version -> IO (Maybe ThunkInfo)
$cwNoThunks :: Context -> Version -> IO (Maybe ThunkInfo)
noThunks :: Context -> Version -> IO (Maybe ThunkInfo)
$cnoThunks :: Context -> Version -> IO (Maybe ThunkInfo)
NoThunks, Typeable Version
Version -> Encoding
(forall t. ToCBOR t => Proxy t -> Size) -> Proxy [Version] -> Size
(forall t. ToCBOR t => Proxy t -> Size) -> Proxy Version -> Size
forall a.
Typeable a
-> (a -> Encoding)
-> ((forall t. ToCBOR t => Proxy t -> Size) -> Proxy a -> Size)
-> ((forall t. ToCBOR t => Proxy t -> Size) -> Proxy [a] -> Size)
-> ToCBOR a
encodedListSizeExpr :: (forall t. ToCBOR t => Proxy t -> Size) -> Proxy [Version] -> Size
$cencodedListSizeExpr :: (forall t. ToCBOR t => Proxy t -> Size) -> Proxy [Version] -> Size
encodedSizeExpr :: (forall t. ToCBOR t => Proxy t -> Size) -> Proxy Version -> Size
$cencodedSizeExpr :: (forall t. ToCBOR t => Proxy t -> Size) -> Proxy Version -> Size
toCBOR :: Version -> Encoding
$ctoCBOR :: Version -> Encoding
ToCBOR, [Version] -> Encoding
[Version] -> Value
Version -> Bool
Version -> Encoding
Version -> Value
forall a.
(a -> Value)
-> (a -> Encoding)
-> ([a] -> Value)
-> ([a] -> Encoding)
-> (a -> Bool)
-> ToJSON a
omitField :: Version -> Bool
$comitField :: Version -> Bool
toEncodingList :: [Version] -> Encoding
$ctoEncodingList :: [Version] -> Encoding
toJSONList :: [Version] -> Value
$ctoJSONList :: [Version] -> Value
toEncoding :: Version -> Encoding
$ctoEncoding :: Version -> Encoding
toJSON :: Version -> Value
$ctoJSON :: Version -> Value
ToJSON)

-- | Minimum supported version
type MinVersion = 0

-- | Maximum supported version. This is the major protocol version of the latest known
-- protocol version that we want to support, including for development and testing.
type MaxVersion = 11

instance Enum Version where
  toEnum :: Int -> Version
toEnum = forall a. HasCallStack => Fail a -> a
errorFail forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall i (m :: * -> *). (Integral i, MonadFail m) => i -> m Version
mkVersion
  fromEnum :: Version -> Int
fromEnum (Version Word64
v) = forall a. Enum a => a -> Int
fromEnum Word64
v

instance Bounded Version where
  minBound :: Version
minBound = Word64 -> Version
Version (forall a. Num a => Integer -> a
fromInteger (forall (n :: Natural) (proxy :: Natural -> *).
KnownNat n =>
proxy n -> Integer
natVal (forall {k} (t :: k). Proxy t
Proxy @MinVersion)))
  maxBound :: Version
maxBound = Word64 -> Version
Version (forall a. Num a => Integer -> a
fromInteger (forall (n :: Natural) (proxy :: Natural -> *).
KnownNat n =>
proxy n -> Integer
natVal (forall {k} (t :: k). Proxy t
Proxy @MaxVersion)))

instance FromCBOR Version where
  fromCBOR :: forall s. Decoder s Version
fromCBOR = forall a s. FromCBOR a => Decoder s a
fromCBOR forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= forall (m :: * -> *). MonadFail m => Word64 -> m Version
mkVersion64
  {-# INLINE fromCBOR #-}

instance FromJSON Version where
  parseJSON :: Value -> Parser Version
parseJSON Value
v = forall a. FromJSON a => Value -> Parser a
parseJSON Value
v forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= forall (m :: * -> *). MonadFail m => Word64 -> m Version
mkVersion64

-- | Same as `natVersionProxy`, construct a version from a type level `Nat`, except it can be
-- supplied through @TypeApplications@.
natVersion :: forall v. (KnownNat v, MinVersion <= v, v <= MaxVersion) => Version
natVersion :: forall (v :: Natural).
(KnownNat v, MinVersion <= v, v <= MaxVersion) =>
Version
natVersion = forall (v :: Natural).
(KnownNat v, MinVersion <= v, v <= MaxVersion) =>
Proxy v -> Version
natVersionProxy (forall {k} (t :: k). Proxy t
Proxy @v)
{-# INLINE natVersion #-}

-- | Safely construct a `Version` from a type level `Nat`, which is supplied as a `Proxy`
natVersionProxy :: (KnownNat v, MinVersion <= v, v <= MaxVersion) => Proxy v -> Version
natVersionProxy :: forall (v :: Natural).
(KnownNat v, MinVersion <= v, v <= MaxVersion) =>
Proxy v -> Version
natVersionProxy = Word64 -> Version
Version forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Num a => Integer -> a
fromInteger forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (n :: Natural) (proxy :: Natural -> *).
KnownNat n =>
proxy n -> Integer
natVal
{-# INLINE natVersionProxy #-}

-- | Construct a `Version` and fail if the supplied value is not a supported version number.
mkVersion :: (Integral i, MonadFail m) => i -> m Version
mkVersion :: forall i (m :: * -> *). (Integral i, MonadFail m) => i -> m Version
mkVersion i
v
  | Integer
vi forall a. Ord a => a -> a -> Bool
< forall a. Integral a => a -> Integer
toInteger (forall a. Bounded a => a
minBound :: Word64) = forall (m :: * -> *) a. MonadFail m => String -> m a
fail forall a b. (a -> b) -> a -> b
$ String
"Version is too small: " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show Integer
vi
  | Integer
vi forall a. Ord a => a -> a -> Bool
> forall a. Integral a => a -> Integer
toInteger (forall a. Bounded a => a
maxBound :: Word64) = forall (m :: * -> *) a. MonadFail m => String -> m a
fail forall a b. (a -> b) -> a -> b
$ String
"Version is too big: " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show Integer
vi
  | Bool
otherwise = forall (m :: * -> *). MonadFail m => Word64 -> m Version
mkVersion64 (forall a b. (Integral a, Num b) => a -> b
fromIntegral i
v)
  where
    vi :: Integer
vi = forall a. Integral a => a -> Integer
toInteger i
v
{-# INLINE mkVersion #-}

-- | Construct a `Version` and fail if the supplied value is not supported version number.
mkVersion64 :: MonadFail m => Word64 -> m Version
mkVersion64 :: forall (m :: * -> *). MonadFail m => Word64 -> m Version
mkVersion64 Word64
v
  | Word64
minVersion forall a. Ord a => a -> a -> Bool
<= Word64
v Bool -> Bool -> Bool
&& Word64
v forall a. Ord a => a -> a -> Bool
<= Word64
maxVersion =
      forall (f :: * -> *) a. Applicative f => a -> f a
pure (Word64 -> Version
Version (forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
v))
  | Bool
otherwise =
      forall (m :: * -> *) a. MonadFail m => String -> m a
fail forall a b. (a -> b) -> a -> b
$
        String
"Unsupported version value: "
          forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show Word64
v
          forall a. [a] -> [a] -> [a]
++ String
". Expected value in bounds: ["
          forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show Word64
minVersion
          forall a. [a] -> [a] -> [a]
++ String
", "
          forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show Word64
maxVersion
          forall a. [a] -> [a] -> [a]
++ String
"]"
  where
    Version Word64
minVersion = forall a. Bounded a => a
minBound
    Version Word64
maxVersion = forall a. Bounded a => a
maxBound
{-# INLINE mkVersion64 #-}

-- | Convert a `Version` to an `Integral` value.
--
-- /Note/ - Version spans a fairly small range of non-negative numbers, so this should be
-- safe even for smallest integral types.
getVersion :: Integral i => Version -> i
getVersion :: forall i. Integral i => Version -> i
getVersion (Version Word64
w64) = forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
w64
{-# INLINE getVersion #-}

-- | Extract `Word64` representation of the `Version`
getVersion64 :: Version -> Word64
getVersion64 :: Version -> Word64
getVersion64 (Version Word64
w64) = Word64
w64
{-# INLINE getVersion64 #-}

-- | Increment version by 1.
succVersion :: MonadFail m => Version -> m Version
succVersion :: forall (m :: * -> *). MonadFail m => Version -> m Version
succVersion (Version Word64
v64) = forall (m :: * -> *). MonadFail m => Word64 -> m Version
mkVersion64 (Word64
v64 forall a. Num a => a -> a -> a
+ Word64
1)
{-# INLINE succVersion #-}

allVersions :: [Version]
allVersions :: [Version]
allVersions = [forall a. Bounded a => a
minBound .. forall a. Bounded a => a
maxBound]

byronProtVer :: Version
byronProtVer :: Version
byronProtVer = forall (v :: Natural).
(KnownNat v, MinVersion <= v, v <= MaxVersion) =>
Version
natVersion @1
{-# INLINE byronProtVer #-}

shelleyProtVer :: Version
shelleyProtVer :: Version
shelleyProtVer = forall (v :: Natural).
(KnownNat v, MinVersion <= v, v <= MaxVersion) =>
Version
natVersion @2
{-# INLINE shelleyProtVer #-}