{-# LANGUAGE NamedFieldPuns     #-}
{-# LANGUAGE NumericUnderscores #-}
{-# LANGUAGE TypeApplications   #-}

module Ouroboros.Network.TxSubmission.Inbound.V2.Policy
  ( TxDecisionPolicy (..)
  , mkTxDecisionPolicy
  , saneTxDecisionPolicy
  , TxConfigurationError (..)
  , TxSubmissionConfig (..)
  , defaultTxSubmissionConfigV2
  , max_TX_SIZE
  , max_TXID_SIZE
    -- * Re-exports
  , NumTxIdsToReq (..)
  ) where

import Control.DeepSeq
import Control.Exception (Exception)
import Control.Monad (unless)
import Control.Monad.Class.MonadTime.SI
import Ouroboros.Network.Protocol.Limits (sizeLimitForState)
import Ouroboros.Network.Protocol.TxSubmission2.Codec (byteLimitsTxSubmission2)
import Ouroboros.Network.Protocol.TxSubmission2.Type (NumTxIdsToAck (..),
           NumTxIdsToReq (..), SingTxSubmission (SingIdle, SingTxs))
import Ouroboros.Network.SizeInBytes (SizeInBytes (..))
import Ouroboros.Network.TxSubmission.Inbound.V2.Types
           (const_MAX_TX_SIZE_DISCREPANCY)


-- | Maximal tx size.
--
-- It bounds the advertised tx size on every node-to-node version: the V2
-- inbound disconnects from a peer which advertises a larger tx
-- (`ProtocolErrorTxSizeTooLarge`).  The bound is far above the ledger's
-- maximal tx size, and every version was already limited by it through the
-- ingress queue of the mini-protocol (see below).  Increasing it requires
-- a new node-to-node version, since nodes running older versions disconnect
-- from peers which advertise larger txs.
--
-- Affects:
--
-- * `TxDecisionPolicy`
-- * `maximumIngressQueue` for `tx-submission` mini-protocol, see
--   `Ouroboros.Network.NodeToNode.txSubmissionProtocolLimits`
--
max_TX_SIZE :: SizeInBytes
max_TX_SIZE :: SizeInBytes
max_TX_SIZE = SizeInBytes
65_540


-- | Upper bound on the size of an encoded txid, the same as used by
-- `Cardano.Network.NodeToNode.txSubmissionProtocolLimits`.
--
-- It bounds `maxNumUnacknowledgedTxIds`: a `MsgRequestTxs` can carry up to
-- `maxNumUnacknowledgedTxIds` txids and must fit within the `StIdle` size
-- limit, see `saneTxDecisionPolicy`.
--
max_TXID_SIZE :: SizeInBytes
max_TXID_SIZE :: SizeInBytes
max_TXID_SIZE = SizeInBytes
44


-- | Policy for making decisions
--
data TxDecisionPolicy = TxDecisionPolicy {
      --
      -- Configuration of tx decision logic.
      --

      TxDecisionPolicy -> SizeInBytes
txsSizeInflightPerPeer         :: !SizeInBytes,
      -- ^ a limit of tx size in-flight from a single peer.
      -- It can be exceed by max tx size.

      TxDecisionPolicy -> SizeInBytes
txsSizePerBatch                :: !SizeInBytes,
      -- ^ Hard limit on the total advertised transaction size in one body
      -- request batch, except for the first tx in a batch, which is
      -- requested even if it is larger.  Hence a batch is at most
      -- `max txsSizePerBatch max_TX_SIZE`.  The default reserves space for
      -- reply framing and for the `const_MAX_TX_SIZE_DISCREPANCY` tolerance
      -- of each tx.  It MUST be positive, and the reply to the largest batch
      -- MUST fit within the `StTxs` size limit (see `saneTxDecisionPolicy`).
      -- It can be smaller than `max_TX_SIZE`: a larger tx is requested in
      -- a batch on its own.

      TxDecisionPolicy -> Int
maxOutstandingTxBatchesPerPeer :: !Int,
      -- ^ a limit of outstanding tx-body request batches from a single peer.

      TxDecisionPolicy -> Int
txInflightMultiplicity         :: !Int,
      -- ^ from how many peers download the `txid` simultaneously

      TxDecisionPolicy -> DiffTime
bufferedTxsMinLifetime         :: !DiffTime,
      -- ^ how long TXs that have been added to the mempool will be
      -- kept in the `bufferedTxs` cache.

      TxDecisionPolicy -> Double
scoreRate                      :: !Double,
      -- ^ rate at which "rejected" TXs drain. Unit: TX/seconds.

      TxDecisionPolicy -> Double
scoreMax                       :: !Double,
      -- ^ Maximum number of "rejections". Unit: seconds

      TxDecisionPolicy -> Double
scoreAcceptDecrement           :: !Double,
      -- ^ amount subtracted from the peer's score for each tx body the peer delivered that the mempool accepted.

      TxDecisionPolicy -> DiffTime
interTxSpace                   :: !DiffTime,
      -- ^ space between actual requests for the same TX. This the time a peer has soul ownership
      -- of their TX lease. When it expires other peers may issue additional requests.

      TxDecisionPolicy -> DiffTime
inflightTimeout                :: !DiffTime,
      -- ^ Maximum time a peer's attempt may sit between claim and
      -- entering submission before the per-entry inflight-multiplicity
      -- cap is bumped, allowing another peer to attempt in parallel.
      TxDecisionPolicy -> DiffTime
maxPeerClaimDelay              :: !DiffTime,
      -- ^ Maximum delay penalty for poor performing peers.

      TxDecisionPolicy -> Bool
disablePipelinedTxIdRequests   :: !Bool
      -- ^ When 'True', the txid picker never issues pipelined
      -- @MsgRequestTxIds@ messages; only blocking requests fire (and
      -- only when the unack window has been fully drained). Used
      -- by some benchmarks.
    }
  deriving (TxDecisionPolicy -> TxDecisionPolicy -> Bool
(TxDecisionPolicy -> TxDecisionPolicy -> Bool)
-> (TxDecisionPolicy -> TxDecisionPolicy -> Bool)
-> Eq TxDecisionPolicy
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: TxDecisionPolicy -> TxDecisionPolicy -> Bool
== :: TxDecisionPolicy -> TxDecisionPolicy -> Bool
$c/= :: TxDecisionPolicy -> TxDecisionPolicy -> Bool
/= :: TxDecisionPolicy -> TxDecisionPolicy -> Bool
Eq, Int -> TxDecisionPolicy -> ShowS
[TxDecisionPolicy] -> ShowS
TxDecisionPolicy -> String
(Int -> TxDecisionPolicy -> ShowS)
-> (TxDecisionPolicy -> String)
-> ([TxDecisionPolicy] -> ShowS)
-> Show TxDecisionPolicy
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> TxDecisionPolicy -> ShowS
showsPrec :: Int -> TxDecisionPolicy -> ShowS
$cshow :: TxDecisionPolicy -> String
show :: TxDecisionPolicy -> String
$cshowList :: [TxDecisionPolicy] -> ShowS
showList :: [TxDecisionPolicy] -> ShowS
Show)

instance NFData TxDecisionPolicy where
  rnf :: TxDecisionPolicy -> ()
rnf TxDecisionPolicy{} = ()

-- | The default policy for the given config.  Returns a `TxConfigurationError`
-- if it isn't sane (see `saneTxDecisionPolicy`); the node should then report
-- it and halt.
--
mkTxDecisionPolicy
  :: TxSubmissionConfig
  -> Either TxConfigurationError TxDecisionPolicy
mkTxDecisionPolicy :: TxSubmissionConfig -> Either TxConfigurationError TxDecisionPolicy
mkTxDecisionPolicy TxSubmissionConfig
config =
    TxDecisionPolicy
policy TxDecisionPolicy
-> Either TxConfigurationError ()
-> Either TxConfigurationError TxDecisionPolicy
forall a b.
a -> Either TxConfigurationError b -> Either TxConfigurationError a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ TxDecisionPolicy
-> TxSubmissionConfig -> Either TxConfigurationError ()
saneTxDecisionPolicy TxDecisionPolicy
policy TxSubmissionConfig
config
  where
    policy :: TxDecisionPolicy
policy = TxDecisionPolicy {
      -- Saturates rather than wraps around if `maxNumTxIdsToRequest` is too
      -- large (such a config does not pass `saneTxDecisionPolicy`).
      txsSizeInflightPerPeer :: SizeInBytes
txsSizeInflightPerPeer =
        forall a b. (Integral a, Num b) => a -> b
fromIntegral @Integer @SizeInBytes
          (Integer -> SizeInBytes) -> Integer -> SizeInBytes
forall a b. (a -> b) -> a -> b
$ Integer -> Integer -> Integer
forall a. Ord a => a -> a -> a
min (SizeInBytes -> Integer
forall a. Integral a => a -> Integer
toInteger (SizeInBytes
forall a. Bounded a => a
maxBound :: SizeInBytes))
                (SizeInBytes -> Integer
forall a. Integral a => a -> Integer
toInteger SizeInBytes
max_TX_SIZE Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* NumTxIdsToReq -> Integer
forall a. Integral a => a -> Integer
toInteger (TxSubmissionConfig -> NumTxIdsToReq
maxNumTxIdsToRequest TxSubmissionConfig
config)),
      txsSizePerBatch :: SizeInBytes
txsSizePerBatch =
          SizeInBytes -> SizeInBytes
safeLimit
        (SizeInBytes -> SizeInBytes) -> SizeInBytes -> SizeInBytes
forall a b. (a -> b) -> a -> b
$ Word -> SizeInBytes
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word
stTxsSizeLimit
        SizeInBytes -> SizeInBytes -> SizeInBytes
forall a. Num a => a -> a -> a
- SizeInBytes
msgReplyTxsFraming,
      maxOutstandingTxBatchesPerPeer :: Int
maxOutstandingTxBatchesPerPeer = Int
4,
      txInflightMultiplicity :: Int
txInflightMultiplicity = Int
2,
      bufferedTxsMinLifetime :: DiffTime
bufferedTxsMinLifetime = DiffTime
2,
      scoreRate :: Double
scoreRate              = Double
0.001,
      scoreMax :: Double
scoreMax               = Double
15 Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
60,
      scoreAcceptDecrement :: Double
scoreAcceptDecrement   = Double
3,
      interTxSpace :: DiffTime
interTxSpace           = DiffTime
0.250,
      inflightTimeout :: DiffTime
inflightTimeout        = DiffTime
0.600,
      maxPeerClaimDelay :: DiffTime
maxPeerClaimDelay      = DiffTime
0.250,
      disablePipelinedTxIdRequests :: Bool
disablePipelinedTxIdRequests = Bool
False
    }
    -- for `~2.5MB`, leave 50KB room
    safeLimit :: SizeInBytes -> SizeInBytes
    safeLimit :: SizeInBytes -> SizeInBytes
safeLimit SizeInBytes
limit = SizeInBytes
limit SizeInBytes -> SizeInBytes -> SizeInBytes
forall a. Num a => a -> a -> a
- SizeInBytes
limit SizeInBytes -> SizeInBytes -> SizeInBytes
forall a. Integral a => a -> a -> a
`div` SizeInBytes
50

-- | `MsgReplyTxs` has four bytes of CBOR framing: the outer list, message
-- tag, indefinite transaction list and break marker.
--
msgReplyTxsFraming :: SizeInBytes
msgReplyTxsFraming :: SizeInBytes
msgReplyTxsFraming = SizeInBytes
4

-- | `MsgRequestTxs` has four bytes of CBOR framing: the outer list, message
-- tag, indefinite txid list and break marker.
--
msgRequestTxsFraming :: SizeInBytes
msgRequestTxsFraming :: SizeInBytes
msgRequestTxsFraming = SizeInBytes
4

-- | Size limit of the `StTxs` state, i.e. of a `MsgReplyTxs` message.
--
stTxsSizeLimit :: Word
stTxsSizeLimit :: Word
stTxsSizeLimit =
    ProtocolSizeLimits
  (TxSubmission2 (ZonkAny 2) (ZonkAny 3)) (ZonkAny 5)
-> forall (st :: TxSubmission2 (ZonkAny 2) (ZonkAny 3)).
   ActiveState st =>
   StateToken st -> Word
forall ps bytes.
ProtocolSizeLimits ps bytes
-> forall (st :: ps). ActiveState st => StateToken st -> Word
sizeLimitForState
      ((ZonkAny 5 -> Word)
-> ProtocolSizeLimits
     (TxSubmission2 (ZonkAny 2) (ZonkAny 3)) (ZonkAny 5)
forall bytes txid tx.
(bytes -> Word) -> ProtocolSizeLimits (TxSubmission2 txid tx) bytes
byteLimitsTxSubmission2 (String -> ZonkAny 5 -> Word
forall a. HasCallStack => String -> a
error String
"invariant violation!"))
      StateToken 'StTxs
SingTxSubmission 'StTxs
forall {txid} {tx}. SingTxSubmission 'StTxs
SingTxs

-- | Size limit of the `StIdle` state, i.e. of a `MsgRequestTxs` message.
--
stIdleSizeLimit :: Word
stIdleSizeLimit :: Word
stIdleSizeLimit =
    ProtocolSizeLimits
  (TxSubmission2 (ZonkAny 0) (ZonkAny 1)) (ZonkAny 4)
-> forall (st :: TxSubmission2 (ZonkAny 0) (ZonkAny 1)).
   ActiveState st =>
   StateToken st -> Word
forall ps bytes.
ProtocolSizeLimits ps bytes
-> forall (st :: ps). ActiveState st => StateToken st -> Word
sizeLimitForState
      ((ZonkAny 4 -> Word)
-> ProtocolSizeLimits
     (TxSubmission2 (ZonkAny 0) (ZonkAny 1)) (ZonkAny 4)
forall bytes txid tx.
(bytes -> Word) -> ProtocolSizeLimits (TxSubmission2 txid tx) bytes
byteLimitsTxSubmission2 (String -> ZonkAny 4 -> Word
forall a. HasCallStack => String -> a
error String
"invariant violation!"))
      StateToken 'StIdle
SingTxSubmission 'StIdle
forall {txid} {tx}. SingTxSubmission 'StIdle
SingIdle

-- | Shared options between the inbound and outbound side.
--
data TxSubmissionConfig =
    TxSubmissionConfig {
      TxSubmissionConfig -> NumTxIdsToAck
maxNumUnacknowledgedTxIds :: !NumTxIdsToAck,
      -- ^ maximum number of unacknowledged txids; it MUST be positive, and
      -- small enough for a `MsgRequestTxs` with that many txids to fit within
      -- the `StIdle` size limit (see `max_TXID_SIZE`).  Before
      -- `NodeToNodeV_16` it is a protocol parameter and must not be changed;
      -- since `NodeToNodeV_16` it is a local setting.
      TxSubmissionConfig -> NumTxIdsToReq
maxNumTxIdsToRequest      :: !NumTxIdsToReq
      -- ^ maximum number of txids an inbound client can request at a time.
    }
  deriving (TxSubmissionConfig -> TxSubmissionConfig -> Bool
(TxSubmissionConfig -> TxSubmissionConfig -> Bool)
-> (TxSubmissionConfig -> TxSubmissionConfig -> Bool)
-> Eq TxSubmissionConfig
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: TxSubmissionConfig -> TxSubmissionConfig -> Bool
== :: TxSubmissionConfig -> TxSubmissionConfig -> Bool
$c/= :: TxSubmissionConfig -> TxSubmissionConfig -> Bool
/= :: TxSubmissionConfig -> TxSubmissionConfig -> Bool
Eq, Int -> TxSubmissionConfig -> ShowS
[TxSubmissionConfig] -> ShowS
TxSubmissionConfig -> String
(Int -> TxSubmissionConfig -> ShowS)
-> (TxSubmissionConfig -> String)
-> ([TxSubmissionConfig] -> ShowS)
-> Show TxSubmissionConfig
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> TxSubmissionConfig -> ShowS
showsPrec :: Int -> TxSubmissionConfig -> ShowS
$cshow :: TxSubmissionConfig -> String
show :: TxSubmissionConfig -> String
$cshowList :: [TxSubmissionConfig] -> ShowS
showList :: [TxSubmissionConfig] -> ShowS
Show)

instance NFData TxSubmissionConfig where
  rnf :: TxSubmissionConfig -> ()
rnf TxSubmissionConfig{} = ()

-- | `TxSubmissionConfig` for `TxSubmissionLogicV2`.
--
defaultTxSubmissionConfigV2 :: TxSubmissionConfig
defaultTxSubmissionConfigV2 :: TxSubmissionConfig
defaultTxSubmissionConfigV2 = TxSubmissionConfig {
    maxNumUnacknowledgedTxIds :: NumTxIdsToAck
maxNumUnacknowledgedTxIds = Word16 -> NumTxIdsToAck
NumTxIdsToAck Word16
10,
    maxNumTxIdsToRequest :: NumTxIdsToReq
maxNumTxIdsToRequest      = Word16 -> NumTxIdsToReq
NumTxIdsToReq Word16
6
  }

-- | Why a `TxDecisionPolicy` with a `TxSubmissionConfig` isn't sane, see
-- `saneTxDecisionPolicy`.  Each constructor carries the offending values.
--
data TxConfigurationError
  -- | `maxNumUnacknowledgedTxIds` must be positive
  = NumUnacknowledgedTxIdsTooSmall NumTxIdsToAck
  -- | a `MsgRequestTxs` with `maxNumUnacknowledgedTxIds` txids of size
  -- `max_TXID_SIZE` doesn't fit within the `StIdle` size limit
  | NumUnacknowledgedTxIdsTooLarge NumTxIdsToAck
  -- | `maxNumTxIdsToRequest` must be positive
  | MaxNumTxIdsToRequestTooSmall NumTxIdsToReq
  -- | @max_TX_SIZE * maxNumTxIdsToRequest@ doesn't fit in `SizeInBytes`
  | MaxNumTxIdsToRequestTooLarge NumTxIdsToReq
  -- | `txsSizeInflightPerPeer` must be positive
  | TxsSizeInflightPerPeerTooSmall SizeInBytes
  -- | `txsSizePerBatch` must be positive
  | TxsSizePerBatchTooSmall SizeInBytes
  -- | the reply to the largest batch, with `maxNumUnacknowledgedTxIds` txs
  -- each up to `const_MAX_TX_SIZE_DISCREPANCY` larger than advertised,
  -- doesn't fit within the `StTxs` size limit
  | TxsSizePerBatchTooLarge SizeInBytes NumTxIdsToAck
  -- | `maxOutstandingTxBatchesPerPeer` must be positive
  | MaxOutstandingTxBatchesPerPeerTooSmall Int
  -- | `txInflightMultiplicity` must be positive
  | TxInflightMultiplicityTooSmall Int
  -- | `bufferedTxsMinLifetime` must be non-negative
  | BufferedTxsMinLifetimeTooSmall DiffTime
  -- | `scoreRate` must be non-negative
  | ScoreRateTooSmall Double
  -- | `scoreMax` must be non-negative
  | ScoreMaxTooSmall Double
  -- | `scoreAcceptDecrement` must be non-negative
  | ScoreAcceptDecrementTooSmall Double
  -- | `interTxSpace` must be non-negative
  | InterTxSpaceTooSmall DiffTime
  -- | `inflightTimeout` (the first field) must be at least `interTxSpace`
  -- (the second field)
  | InflightTimeoutTooSmall DiffTime DiffTime
  -- | `maxPeerClaimDelay` must be non-negative
  | MaxPeerClaimDelayTooSmall DiffTime
  deriving (TxConfigurationError -> TxConfigurationError -> Bool
(TxConfigurationError -> TxConfigurationError -> Bool)
-> (TxConfigurationError -> TxConfigurationError -> Bool)
-> Eq TxConfigurationError
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: TxConfigurationError -> TxConfigurationError -> Bool
== :: TxConfigurationError -> TxConfigurationError -> Bool
$c/= :: TxConfigurationError -> TxConfigurationError -> Bool
/= :: TxConfigurationError -> TxConfigurationError -> Bool
Eq, Int -> TxConfigurationError -> ShowS
[TxConfigurationError] -> ShowS
TxConfigurationError -> String
(Int -> TxConfigurationError -> ShowS)
-> (TxConfigurationError -> String)
-> ([TxConfigurationError] -> ShowS)
-> Show TxConfigurationError
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> TxConfigurationError -> ShowS
showsPrec :: Int -> TxConfigurationError -> ShowS
$cshow :: TxConfigurationError -> String
show :: TxConfigurationError -> String
$cshowList :: [TxConfigurationError] -> ShowS
showList :: [TxConfigurationError] -> ShowS
Show)

instance Exception TxConfigurationError

-- | Sanity check for a 'TxDecisionPolicy': 'Right' when every field lies
-- in the range the decision logic assumes, otherwise the first violated
-- bound as a 'TxConfigurationError'.
--
-- All window and size limits must be positive (otherwise the peer can make
-- no progress), the rate, score and delay parameters must
-- be non-negative, and 'inflightTimeout' must be at least 'interTxSpace'
-- (their difference is used as a non-negative delay when bumping a stuck
-- entry's inflight-multiplicity cap).  'mkTxDecisionPolicy' satisfies
-- it.  The reply to the largest batch MUST fit within the `StTxs` size
-- limit.  A batch can exceed `txsSizePerBatch` by being a single tx (of at
-- most `max_TX_SIZE`), so its advertised size is at most `max txsSizePerBatch
-- max_TX_SIZE`.  It holds at most `maxNumUnacknowledgedTxIds` txs, and each
-- received tx can be larger than advertised by up to
-- `const_MAX_TX_SIZE_DISCREPANCY`. A `MsgRequestTxs` with
-- `maxNumUnacknowledgedTxIds` txids of size `max_TXID_SIZE` MUST fit within
-- the `StIdle` size limit. `max_TX_SIZE * maxNumTxIdsToRequest` MUST fit in
-- `SizeInBytes`.
saneTxDecisionPolicy :: TxDecisionPolicy
                     -> TxSubmissionConfig
                     -> Either TxConfigurationError ()
saneTxDecisionPolicy :: TxDecisionPolicy
-> TxSubmissionConfig -> Either TxConfigurationError ()
saneTxDecisionPolicy TxDecisionPolicy {
                       SizeInBytes
txsSizeInflightPerPeer :: TxDecisionPolicy -> SizeInBytes
txsSizeInflightPerPeer :: SizeInBytes
txsSizeInflightPerPeer,
                       SizeInBytes
txsSizePerBatch :: TxDecisionPolicy -> SizeInBytes
txsSizePerBatch :: SizeInBytes
txsSizePerBatch,
                       Int
maxOutstandingTxBatchesPerPeer :: TxDecisionPolicy -> Int
maxOutstandingTxBatchesPerPeer :: Int
maxOutstandingTxBatchesPerPeer,
                       Int
txInflightMultiplicity :: TxDecisionPolicy -> Int
txInflightMultiplicity :: Int
txInflightMultiplicity,
                       DiffTime
bufferedTxsMinLifetime :: TxDecisionPolicy -> DiffTime
bufferedTxsMinLifetime :: DiffTime
bufferedTxsMinLifetime,
                       Double
scoreRate :: TxDecisionPolicy -> Double
scoreRate :: Double
scoreRate,
                       Double
scoreMax :: TxDecisionPolicy -> Double
scoreMax :: Double
scoreMax,
                       Double
scoreAcceptDecrement :: TxDecisionPolicy -> Double
scoreAcceptDecrement :: Double
scoreAcceptDecrement,
                       DiffTime
interTxSpace :: TxDecisionPolicy -> DiffTime
interTxSpace :: DiffTime
interTxSpace,
                       DiffTime
inflightTimeout :: TxDecisionPolicy -> DiffTime
inflightTimeout :: DiffTime
inflightTimeout,
                       DiffTime
maxPeerClaimDelay :: TxDecisionPolicy -> DiffTime
maxPeerClaimDelay :: DiffTime
maxPeerClaimDelay
                     }
                     TxSubmissionConfig {
                       NumTxIdsToAck
maxNumUnacknowledgedTxIds :: TxSubmissionConfig -> NumTxIdsToAck
maxNumUnacknowledgedTxIds :: NumTxIdsToAck
maxNumUnacknowledgedTxIds,
                       NumTxIdsToReq
maxNumTxIdsToRequest :: TxSubmissionConfig -> NumTxIdsToReq
maxNumTxIdsToRequest :: NumTxIdsToReq
maxNumTxIdsToRequest
                     } = do
    Bool
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (NumTxIdsToAck
maxNumUnacknowledgedTxIds NumTxIdsToAck -> NumTxIdsToAck -> Bool
forall a. Ord a => a -> a -> Bool
>= NumTxIdsToAck
1)
      (Either TxConfigurationError () -> Either TxConfigurationError ())
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall a b. (a -> b) -> a -> b
$ TxConfigurationError -> Either TxConfigurationError ()
forall a b. a -> Either a b
Left (NumTxIdsToAck -> TxConfigurationError
NumUnacknowledgedTxIdsTooSmall NumTxIdsToAck
maxNumUnacknowledgedTxIds)
    Bool
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (   SizeInBytes -> Integer
forall a. Integral a => a -> Integer
toInteger SizeInBytes
msgRequestTxsFraming
             Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ SizeInBytes -> Integer
forall a. Integral a => a -> Integer
toInteger SizeInBytes
max_TXID_SIZE Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* NumTxIdsToAck -> Integer
forall a. Integral a => a -> Integer
toInteger NumTxIdsToAck
maxNumUnacknowledgedTxIds
            Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<= Word -> Integer
forall a. Integral a => a -> Integer
toInteger Word
stIdleSizeLimit)
      (Either TxConfigurationError () -> Either TxConfigurationError ())
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall a b. (a -> b) -> a -> b
$ TxConfigurationError -> Either TxConfigurationError ()
forall a b. a -> Either a b
Left (NumTxIdsToAck -> TxConfigurationError
NumUnacknowledgedTxIdsTooLarge NumTxIdsToAck
maxNumUnacknowledgedTxIds)
    Bool
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (NumTxIdsToReq
maxNumTxIdsToRequest NumTxIdsToReq -> NumTxIdsToReq -> Bool
forall a. Ord a => a -> a -> Bool
>= NumTxIdsToReq
1)
      (Either TxConfigurationError () -> Either TxConfigurationError ())
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall a b. (a -> b) -> a -> b
$ TxConfigurationError -> Either TxConfigurationError ()
forall a b. a -> Either a b
Left (NumTxIdsToReq -> TxConfigurationError
MaxNumTxIdsToRequestTooSmall NumTxIdsToReq
maxNumTxIdsToRequest)
    Bool
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (    SizeInBytes -> Integer
forall a. Integral a => a -> Integer
toInteger SizeInBytes
max_TX_SIZE Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* NumTxIdsToReq -> Integer
forall a. Integral a => a -> Integer
toInteger NumTxIdsToReq
maxNumTxIdsToRequest
             Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<= SizeInBytes -> Integer
forall a. Integral a => a -> Integer
toInteger (SizeInBytes
forall a. Bounded a => a
maxBound :: SizeInBytes))
      (Either TxConfigurationError () -> Either TxConfigurationError ())
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall a b. (a -> b) -> a -> b
$ TxConfigurationError -> Either TxConfigurationError ()
forall a b. a -> Either a b
Left (NumTxIdsToReq -> TxConfigurationError
MaxNumTxIdsToRequestTooLarge NumTxIdsToReq
maxNumTxIdsToRequest)
    Bool
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (SizeInBytes
txsSizeInflightPerPeer SizeInBytes -> SizeInBytes -> Bool
forall a. Ord a => a -> a -> Bool
> SizeInBytes
0)
      (Either TxConfigurationError () -> Either TxConfigurationError ())
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall a b. (a -> b) -> a -> b
$ TxConfigurationError -> Either TxConfigurationError ()
forall a b. a -> Either a b
Left (SizeInBytes -> TxConfigurationError
TxsSizeInflightPerPeerTooSmall SizeInBytes
txsSizeInflightPerPeer)
    Bool
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (SizeInBytes
txsSizePerBatch SizeInBytes -> SizeInBytes -> Bool
forall a. Ord a => a -> a -> Bool
> SizeInBytes
0)
      (Either TxConfigurationError () -> Either TxConfigurationError ())
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall a b. (a -> b) -> a -> b
$ TxConfigurationError -> Either TxConfigurationError ()
forall a b. a -> Either a b
Left (SizeInBytes -> TxConfigurationError
TxsSizePerBatchTooSmall SizeInBytes
txsSizePerBatch)
    Bool
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (   SizeInBytes -> Integer
forall a. Integral a => a -> Integer
toInteger SizeInBytes
msgReplyTxsFraming
             Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ SizeInBytes -> Integer
forall a. Integral a => a -> Integer
toInteger (SizeInBytes -> SizeInBytes -> SizeInBytes
forall a. Ord a => a -> a -> a
max SizeInBytes
txsSizePerBatch SizeInBytes
max_TX_SIZE)
             Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ SizeInBytes -> Integer
forall a. Integral a => a -> Integer
toInteger SizeInBytes
const_MAX_TX_SIZE_DISCREPANCY
               Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* NumTxIdsToAck -> Integer
forall a. Integral a => a -> Integer
toInteger NumTxIdsToAck
maxNumUnacknowledgedTxIds
            Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<= Word -> Integer
forall a. Integral a => a -> Integer
toInteger Word
stTxsSizeLimit)
      (Either TxConfigurationError () -> Either TxConfigurationError ())
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall a b. (a -> b) -> a -> b
$ TxConfigurationError -> Either TxConfigurationError ()
forall a b. a -> Either a b
Left (SizeInBytes -> NumTxIdsToAck -> TxConfigurationError
TxsSizePerBatchTooLarge SizeInBytes
txsSizePerBatch NumTxIdsToAck
maxNumUnacknowledgedTxIds)
    Bool
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Int
maxOutstandingTxBatchesPerPeer Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
1)
      (Either TxConfigurationError () -> Either TxConfigurationError ())
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall a b. (a -> b) -> a -> b
$ TxConfigurationError -> Either TxConfigurationError ()
forall a b. a -> Either a b
Left (Int -> TxConfigurationError
MaxOutstandingTxBatchesPerPeerTooSmall Int
maxOutstandingTxBatchesPerPeer)
    Bool
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Int
txInflightMultiplicity Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
1)
      (Either TxConfigurationError () -> Either TxConfigurationError ())
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall a b. (a -> b) -> a -> b
$ TxConfigurationError -> Either TxConfigurationError ()
forall a b. a -> Either a b
Left (Int -> TxConfigurationError
TxInflightMultiplicityTooSmall Int
txInflightMultiplicity)
    Bool
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (DiffTime
bufferedTxsMinLifetime DiffTime -> DiffTime -> Bool
forall a. Ord a => a -> a -> Bool
>= DiffTime
0)
      (Either TxConfigurationError () -> Either TxConfigurationError ())
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall a b. (a -> b) -> a -> b
$ TxConfigurationError -> Either TxConfigurationError ()
forall a b. a -> Either a b
Left (DiffTime -> TxConfigurationError
BufferedTxsMinLifetimeTooSmall DiffTime
bufferedTxsMinLifetime)
    Bool
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Double
scoreRate Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
>= Double
0)
      (Either TxConfigurationError () -> Either TxConfigurationError ())
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall a b. (a -> b) -> a -> b
$ TxConfigurationError -> Either TxConfigurationError ()
forall a b. a -> Either a b
Left (Double -> TxConfigurationError
ScoreRateTooSmall Double
scoreRate)
    Bool
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Double
scoreMax Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
>= Double
0)
      (Either TxConfigurationError () -> Either TxConfigurationError ())
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall a b. (a -> b) -> a -> b
$ TxConfigurationError -> Either TxConfigurationError ()
forall a b. a -> Either a b
Left (Double -> TxConfigurationError
ScoreMaxTooSmall Double
scoreMax)
    Bool
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Double
scoreAcceptDecrement Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
>= Double
0)
      (Either TxConfigurationError () -> Either TxConfigurationError ())
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall a b. (a -> b) -> a -> b
$ TxConfigurationError -> Either TxConfigurationError ()
forall a b. a -> Either a b
Left (Double -> TxConfigurationError
ScoreAcceptDecrementTooSmall Double
scoreAcceptDecrement)
    Bool
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (DiffTime
interTxSpace DiffTime -> DiffTime -> Bool
forall a. Ord a => a -> a -> Bool
>= DiffTime
0)
      (Either TxConfigurationError () -> Either TxConfigurationError ())
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall a b. (a -> b) -> a -> b
$ TxConfigurationError -> Either TxConfigurationError ()
forall a b. a -> Either a b
Left (DiffTime -> TxConfigurationError
InterTxSpaceTooSmall DiffTime
interTxSpace)
    Bool
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (DiffTime
inflightTimeout DiffTime -> DiffTime -> Bool
forall a. Ord a => a -> a -> Bool
>= DiffTime
interTxSpace)
      (Either TxConfigurationError () -> Either TxConfigurationError ())
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall a b. (a -> b) -> a -> b
$ TxConfigurationError -> Either TxConfigurationError ()
forall a b. a -> Either a b
Left (DiffTime -> DiffTime -> TxConfigurationError
InflightTimeoutTooSmall DiffTime
inflightTimeout DiffTime
interTxSpace)
    Bool
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (DiffTime
maxPeerClaimDelay DiffTime -> DiffTime -> Bool
forall a. Ord a => a -> a -> Bool
>= DiffTime
0)
      (Either TxConfigurationError () -> Either TxConfigurationError ())
-> Either TxConfigurationError () -> Either TxConfigurationError ()
forall a b. (a -> b) -> a -> b
$ TxConfigurationError -> Either TxConfigurationError ()
forall a b. a -> Either a b
Left (DiffTime -> TxConfigurationError
MaxPeerClaimDelayTooSmall DiffTime
maxPeerClaimDelay)