{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE DerivingVia        #-}
{-# LANGUAGE RecordWildCards    #-}

-- | Genesis config for the spec
--
-- Intended for qualified import
--
-- > import           Ouroboros.Consensus.ByronSpec.Ledger.Genesis (ByronSpecGenesis)
-- > import qualified Ouroboros.Consensus.ByronSpec.Ledger.Genesis as Genesis
module Ouroboros.Consensus.ByronSpec.Ledger.Genesis (
    ByronSpecGenesis (..)
  , modFeeParams
  , modPBftThreshold
  , modPParams
  , modUtxo
  , modUtxoValues
    -- * Conversions
  , fromChainEnv
  , toChainEnv
  ) where

import           Data.Coerce (coerce)
import           Data.Set (Set)
import           NoThunks.Class (AllowThunk (..), NoThunks)
import           Numeric.Natural (Natural)

import qualified Byron.Spec.Chain.STS.Rule.Chain as Spec
import qualified Byron.Spec.Ledger.Core as Spec
import qualified Byron.Spec.Ledger.UTxO as Spec
import qualified Byron.Spec.Ledger.Update as Spec
import qualified Control.State.Transition as Spec

import           Ouroboros.Consensus.ByronSpec.Ledger.Orphans ()

{-------------------------------------------------------------------------------
  Genesis config
-------------------------------------------------------------------------------}

-- | The equivalent of the genesis config for the abstract ledger
data ByronSpecGenesis = ByronSpecGenesis {
      ByronSpecGenesis -> Set VKeyGenesis
byronSpecGenesisDelegators    :: Set Spec.VKeyGenesis
    , ByronSpecGenesis -> UTxO
byronSpecGenesisInitUtxo      :: Spec.UTxO
    , ByronSpecGenesis -> PParams
byronSpecGenesisInitPParams   :: Spec.PParams
    , ByronSpecGenesis -> BlockCount
byronSpecGenesisSecurityParam :: Spec.BlockCount

      -- | Slot length
      --
      -- The Byron spec itself does not talk about slot length at all. Here we
      -- record it primarily to support the relation between the spec and the
      -- real implementation. For this reason we choose the same representation
      -- as the real PBFT does ('ppSlotDuration' in 'ProtocolParameters').
    , ByronSpecGenesis -> Natural
byronSpecGenesisSlotLength    :: Natural
    }
  deriving stock (Int -> ByronSpecGenesis -> ShowS
[ByronSpecGenesis] -> ShowS
ByronSpecGenesis -> String
(Int -> ByronSpecGenesis -> ShowS)
-> (ByronSpecGenesis -> String)
-> ([ByronSpecGenesis] -> ShowS)
-> Show ByronSpecGenesis
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [ByronSpecGenesis] -> ShowS
$cshowList :: [ByronSpecGenesis] -> ShowS
show :: ByronSpecGenesis -> String
$cshow :: ByronSpecGenesis -> String
showsPrec :: Int -> ByronSpecGenesis -> ShowS
$cshowsPrec :: Int -> ByronSpecGenesis -> ShowS
Show)
  deriving Context -> ByronSpecGenesis -> IO (Maybe ThunkInfo)
Proxy ByronSpecGenesis -> String
(Context -> ByronSpecGenesis -> IO (Maybe ThunkInfo))
-> (Context -> ByronSpecGenesis -> IO (Maybe ThunkInfo))
-> (Proxy ByronSpecGenesis -> String)
-> NoThunks ByronSpecGenesis
forall a.
(Context -> a -> IO (Maybe ThunkInfo))
-> (Context -> a -> IO (Maybe ThunkInfo))
-> (Proxy a -> String)
-> NoThunks a
showTypeOf :: Proxy ByronSpecGenesis -> String
$cshowTypeOf :: Proxy ByronSpecGenesis -> String
wNoThunks :: Context -> ByronSpecGenesis -> IO (Maybe ThunkInfo)
$cwNoThunks :: Context -> ByronSpecGenesis -> IO (Maybe ThunkInfo)
noThunks :: Context -> ByronSpecGenesis -> IO (Maybe ThunkInfo)
$cnoThunks :: Context -> ByronSpecGenesis -> IO (Maybe ThunkInfo)
NoThunks via AllowThunk ByronSpecGenesis

modPBftThreshold :: (Double -> Double)
                 -> ByronSpecGenesis -> ByronSpecGenesis
modPBftThreshold :: (Double -> Double) -> ByronSpecGenesis -> ByronSpecGenesis
modPBftThreshold = (PParams -> PParams) -> ByronSpecGenesis -> ByronSpecGenesis
modPParams ((PParams -> PParams) -> ByronSpecGenesis -> ByronSpecGenesis)
-> ((Double -> Double) -> PParams -> PParams)
-> (Double -> Double)
-> ByronSpecGenesis
-> ByronSpecGenesis
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Double -> Double) -> PParams -> PParams
modPParamsPBftThreshold

-- | Modify the @a@ and @b@ fee parameters
modFeeParams :: ((Int, Int) -> (Int, Int))
             -> ByronSpecGenesis -> ByronSpecGenesis
modFeeParams :: ((Int, Int) -> (Int, Int)) -> ByronSpecGenesis -> ByronSpecGenesis
modFeeParams = (PParams -> PParams) -> ByronSpecGenesis -> ByronSpecGenesis
modPParams ((PParams -> PParams) -> ByronSpecGenesis -> ByronSpecGenesis)
-> (((Int, Int) -> (Int, Int)) -> PParams -> PParams)
-> ((Int, Int) -> (Int, Int))
-> ByronSpecGenesis
-> ByronSpecGenesis
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Int, Int) -> (Int, Int)) -> PParams -> PParams
modPParamsFeeParams

-- | Adjust all values in the initial UTxO equally
modUtxoValues :: (Integer -> Integer) -> ByronSpecGenesis -> ByronSpecGenesis
modUtxoValues :: (Integer -> Integer) -> ByronSpecGenesis -> ByronSpecGenesis
modUtxoValues = (UTxO -> UTxO) -> ByronSpecGenesis -> ByronSpecGenesis
modUtxo ((UTxO -> UTxO) -> ByronSpecGenesis -> ByronSpecGenesis)
-> ((Integer -> Integer) -> UTxO -> UTxO)
-> (Integer -> Integer)
-> ByronSpecGenesis
-> ByronSpecGenesis
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Lovelace -> Lovelace) -> UTxO -> UTxO
Spec.mapUTxOValues ((Lovelace -> Lovelace) -> UTxO -> UTxO)
-> ((Integer -> Integer) -> Lovelace -> Lovelace)
-> (Integer -> Integer)
-> UTxO
-> UTxO
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Integer -> Integer) -> Lovelace -> Lovelace
coerce

modUtxo :: (Spec.UTxO -> Spec.UTxO) -> ByronSpecGenesis -> ByronSpecGenesis
modUtxo :: (UTxO -> UTxO) -> ByronSpecGenesis -> ByronSpecGenesis
modUtxo UTxO -> UTxO
f ByronSpecGenesis
genesis = ByronSpecGenesis
genesis {
      byronSpecGenesisInitUtxo :: UTxO
byronSpecGenesisInitUtxo = UTxO -> UTxO
f (ByronSpecGenesis -> UTxO
byronSpecGenesisInitUtxo ByronSpecGenesis
genesis)
    }

modPParams :: (Spec.PParams -> Spec.PParams)
           -> ByronSpecGenesis -> ByronSpecGenesis
modPParams :: (PParams -> PParams) -> ByronSpecGenesis -> ByronSpecGenesis
modPParams PParams -> PParams
f ByronSpecGenesis
genesis = ByronSpecGenesis
genesis {
      byronSpecGenesisInitPParams :: PParams
byronSpecGenesisInitPParams = PParams -> PParams
f (ByronSpecGenesis -> PParams
byronSpecGenesisInitPParams ByronSpecGenesis
genesis)
    }

{-------------------------------------------------------------------------------
  Internal: accessors for the protocol parameters
-------------------------------------------------------------------------------}

modPParamsPBftThreshold :: (Double -> Double)
                        -> Spec.PParams -> Spec.PParams
modPParamsPBftThreshold :: (Double -> Double) -> PParams -> PParams
modPParamsPBftThreshold Double -> Double
f PParams
pparams = PParams
pparams {
      _bkSgnCntT :: BkSgnCntT
Spec._bkSgnCntT = Double -> BkSgnCntT
Spec.BkSgnCntT (Double -> Double
f Double
threshold)
    }
  where
    Spec.BkSgnCntT Double
threshold = PParams -> BkSgnCntT
Spec._bkSgnCntT PParams
pparams

modPParamsFeeParams :: ((Int, Int) -> (Int, Int))
                    -> Spec.PParams -> Spec.PParams
modPParamsFeeParams :: ((Int, Int) -> (Int, Int)) -> PParams -> PParams
modPParamsFeeParams (Int, Int) -> (Int, Int)
f PParams
pparams = PParams
pparams {
      _factorA :: FactorA
Spec._factorA = Int -> FactorA
Spec.FactorA (Int -> FactorA) -> Int -> FactorA
forall a b. (a -> b) -> a -> b
$ (Int, Int) -> Int
forall a b. (a, b) -> a
fst ((Int, Int) -> (Int, Int)
f (Int
a, Int
b))
    , _factorB :: FactorB
Spec._factorB = Int -> FactorB
Spec.FactorB (Int -> FactorB) -> Int -> FactorB
forall a b. (a -> b) -> a -> b
$ (Int, Int) -> Int
forall a b. (a, b) -> b
snd ((Int, Int) -> (Int, Int)
f (Int
a, Int
b))
    }
  where
    Spec.FactorA Int
a = PParams -> FactorA
Spec._factorA PParams
pparams
    Spec.FactorB Int
b = PParams -> FactorB
Spec._factorB PParams
pparams

{-------------------------------------------------------------------------------
  Conversions
-------------------------------------------------------------------------------}

-- | Derive CHAIN rule environment
toChainEnv :: ByronSpecGenesis -> Spec.Environment Spec.CHAIN
toChainEnv :: ByronSpecGenesis -> Environment CHAIN
toChainEnv ByronSpecGenesis{Natural
Set VKeyGenesis
PParams
UTxO
BlockCount
byronSpecGenesisSlotLength :: Natural
byronSpecGenesisSecurityParam :: BlockCount
byronSpecGenesisInitPParams :: PParams
byronSpecGenesisInitUtxo :: UTxO
byronSpecGenesisDelegators :: Set VKeyGenesis
byronSpecGenesisSlotLength :: ByronSpecGenesis -> Natural
byronSpecGenesisSecurityParam :: ByronSpecGenesis -> BlockCount
byronSpecGenesisInitPParams :: ByronSpecGenesis -> PParams
byronSpecGenesisInitUtxo :: ByronSpecGenesis -> UTxO
byronSpecGenesisDelegators :: ByronSpecGenesis -> Set VKeyGenesis
..} = Environment CHAIN -> Environment CHAIN
disableConsensusChecks (
      Word64 -> Slot
Spec.Slot Word64
0 -- current slot
    , UTxO
byronSpecGenesisInitUtxo
    , Set VKeyGenesis
byronSpecGenesisDelegators
    , PParams
byronSpecGenesisInitPParams
    , BlockCount
byronSpecGenesisSecurityParam
    )
  where
    -- We are only interested in updating the /ledger state/, not the /consensus
    -- chain state/. Unfortunately, the Byron spec does not make that
    -- distinction, and so when we call the CHAIN rule, we might get some errors
    -- here that the implementation does not report (because it would only find
    -- them when we update the chain state). There are at least two possible
    -- proper solutions for this:
    --
    -- 1. Modify the spec so that we /do/ have the separation. Note that if we
    --    did, we would not use the chain state part of the spec, since the
    --    chain state part of the dual ledger is determined entirely by the
    --    concrete Byron block.
    -- 2. Turn 'applyExtLedger' and related types into a type class of their
    --    own, so that we can override it specifically for the dual ledger.
    --
    -- Either way, we are only testing the /ledger/ part of the two blocks here,
    -- not the consensus part. For now we just override some parameters in the
    -- environment to work around the problem and make sure that none of the
    -- consensus checks in the spec can fail.
    disableConsensusChecks :: Spec.Environment Spec.CHAIN
                           -> Spec.Environment Spec.CHAIN
    disableConsensusChecks :: Environment CHAIN -> Environment CHAIN
disableConsensusChecks ( _currentSlot
                           , utx0
                           , delegators
                           , pparams
                           , k
                           ) = (
          -- Disable 'SlotInTheFuture' failure
          Word64 -> Slot
Spec.Slot Word64
forall a. Bounded a => a
maxBound
        , UTxO
utx0
        , Set VKeyGenesis
delegators
          -- Disable 'TooManyIssuedBlocks' failure
        , PParams
pparams { _bkSgnCntT :: BkSgnCntT
Spec._bkSgnCntT = Double -> BkSgnCntT
Spec.BkSgnCntT Double
1 }
        , BlockCount
k
        )

-- | Construct genesis config from CHAIN environment
--
-- This doesn't make an awful lot of sense, but the abstract spec doesn't /have/
-- a concept of a genesis config, and instead the CHAIN environment fulfills
-- that role. In order to be able to reuse the test generators, we therefore
-- also define a translation in the opposite direction.
fromChainEnv :: Natural -> Spec.Environment Spec.CHAIN -> ByronSpecGenesis
fromChainEnv :: Natural -> Environment CHAIN -> ByronSpecGenesis
fromChainEnv Natural
byronSpecGenesisSlotLength
             ( _currentSlot
             , byronSpecGenesisInitUtxo
             , byronSpecGenesisDelegators
             , byronSpecGenesisInitPParams
             , byronSpecGenesisSecurityParam
             ) = ByronSpecGenesis :: Set VKeyGenesis
-> UTxO -> PParams -> BlockCount -> Natural -> ByronSpecGenesis
ByronSpecGenesis{Natural
Set VKeyGenesis
PParams
UTxO
BlockCount
byronSpecGenesisSecurityParam :: BlockCount
byronSpecGenesisInitPParams :: PParams
byronSpecGenesisDelegators :: Set VKeyGenesis
byronSpecGenesisInitUtxo :: UTxO
byronSpecGenesisSlotLength :: Natural
byronSpecGenesisSlotLength :: Natural
byronSpecGenesisSecurityParam :: BlockCount
byronSpecGenesisInitPParams :: PParams
byronSpecGenesisInitUtxo :: UTxO
byronSpecGenesisDelegators :: Set VKeyGenesis
..}