@@ -14,11 +14,15 @@ import BotPlutusInterface.Types (
1414 TxFile (.. ),
1515 )
1616import Cardano.Api qualified as CAPI
17+ import Cardano.Api.Shelley (ProtocolParameters (protocolParamMaxTxExUnits ))
18+ import Cardano.Prelude (maybeToEither )
1719import Control.Arrow (left )
20+ import Data.Either (rights )
1821import Data.List (sort )
1922import Data.Map (Map )
2023import Data.Map qualified as Map
2124import Data.Text qualified as Text
25+ import GHC.Natural (Natural )
2226import Ledger (ExBudget (ExBudget ), ExCPU (ExCPU ), ExMemory (ExMemory ), MintingPolicyHash , TxOutRef )
2327import Ledger.Tx.CardanoAPI (fromCardanoPolicyId , fromCardanoTxIn )
2428import System.Directory.Internal.Prelude (getEnv )
@@ -43,13 +47,47 @@ estimateBudget pabConf txFile = do
4347 (getExUnits debugNodeInf)
4448 txBody
4549
46- let txBudget = do
47- body <- txBody
48- budget <- budgetRes
49- (spendingBudgets, policyBudgets) <- mkBudgetMaps budget body
50- Right $ TxBudget spendingBudgets policyBudgets
50+ return $
51+ do
52+ body <- txBody
53+ budget <- budgetRes
54+ maxUnits <- maybeToEither (BudgetEstimationError " Missing max units in parameters" ) $ protocolParamMaxTxExUnits pabConf. pcProtocolParams
55+
56+ scaledBudget <- getScaledBudget maxUnits pabConf. pcBudgetMultiplier budget
57+
58+ (spendingBudgets, policyBudgets) <- mkBudgetMaps scaledBudget body
59+
60+ Right $ TxBudget spendingBudgets policyBudgets
61+
62+ -- | Scale the budget clamping the total to the parameter limits
63+ getScaledBudget :: CAPI. ExecutionUnits -> Rational -> ExUnitsMap -> Either BudgetEstimationError ExUnitsMap
64+ getScaledBudget maxUnits scaler budget =
65+ if fst scalers >= 1 && snd scalers >= 1
66+ then Right $ fmap (fmap $ scaleBudget scalers) budget
67+ else
68+ Left $
69+ BudgetEstimationError $
70+ Text. pack $
71+ " Exceeded global transaction budget\n Calculated: " ++ show budgetSum ++ " \n Limit: " ++ show maxUnits
72+ where
73+ budgetSum = foldr addBudgets (CAPI. ExecutionUnits 0 0 ) $ rights $ Map. elems budget
74+ scalers =
75+ ( clampedScaler (CAPI. executionSteps budgetSum) (CAPI. executionSteps maxUnits) scaler
76+ , clampedScaler (CAPI. executionMemory budgetSum) (CAPI. executionMemory maxUnits) scaler
77+ )
78+
79+ clampedScaler :: Natural -> Natural -> Rational -> Rational
80+ clampedScaler 0 _ scaler = scaler
81+ clampedScaler val maxVal scaler = min scaler (toRational maxVal / toRational val)
82+
83+ -- | Scale the budget by the multipliers in config
84+ scaleBudget :: (Rational , Rational ) -> CAPI. ExecutionUnits -> CAPI. ExecutionUnits
85+ scaleBudget (stepsScaler, memScaler) (CAPI. ExecutionUnits steps mem) = CAPI. ExecutionUnits (scale steps stepsScaler) (scale mem memScaler)
86+ where
87+ scale x scaler = round $ toRational x * scaler
5188
52- return txBudget
89+ addBudgets :: CAPI. ExecutionUnits -> CAPI. ExecutionUnits -> CAPI. ExecutionUnits
90+ addBudgets (CAPI. ExecutionUnits steps mem) (CAPI. ExecutionUnits steps' mem') = CAPI. ExecutionUnits (steps + steps') (mem + mem')
5391
5492-- | Deserialize transaction body from ".signed" file
5593deserialiseSigned :: FilePath -> IO (Either BudgetEstimationError (CAPI. Tx CAPI. AlonzoEra ))
0 commit comments