Skip to content

Commit 5b56aaf

Browse files
Add budget scale clamping
1 parent eb63363 commit 5b56aaf

File tree

3 files changed

+43
-14
lines changed

3 files changed

+43
-14
lines changed

src/BotPlutusInterface/ExBudget.hs

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module BotPlutusInterface.ExBudget (
33
estimateBudget,
44
) where
55

6-
import BotPlutusInterface.QueryNode (NodeInfo (NodeInfo))
6+
import BotPlutusInterface.QueryNode (NodeInfo (NodeInfo), NodeQueryError)
77
import BotPlutusInterface.QueryNode qualified as QueryNode
88
import BotPlutusInterface.Types (
99
BudgetEstimationError (..),
@@ -14,11 +14,15 @@ import BotPlutusInterface.Types (
1414
TxFile (..),
1515
)
1616
import Cardano.Api qualified as CAPI
17+
import Cardano.Api.Shelley (ProtocolParameters (protocolParamMaxTxExUnits))
18+
import Cardano.Prelude (maybeToEither)
1719
import Control.Arrow (left)
20+
import Data.Either (rights)
1821
import Data.List (sort)
1922
import Data.Map (Map)
2023
import Data.Map qualified as Map
2124
import Data.Text qualified as Text
25+
import GHC.Natural (Natural)
2226
import Ledger (ExBudget (ExBudget), ExCPU (ExCPU), ExMemory (ExMemory), MintingPolicyHash, TxOutRef)
2327
import Ledger.Tx.CardanoAPI (fromCardanoPolicyId, fromCardanoTxIn)
2428
import System.Directory.Internal.Prelude (getEnv)
@@ -37,26 +41,49 @@ estimateBudget pabConf txFile = do
3741
Raw rp -> deserialiseRaw rp
3842
Signed sp -> fmap CAPI.getTxBody <$> deserialiseSigned sp
3943

44+
pparamsRes <- QueryNode.queryProtocolParams debugNodeInf
45+
4046
budgetRes <-
4147
either
4248
(pure . Left)
43-
(getExUnits debugNodeInf)
49+
(getExUnits pparamsRes debugNodeInf)
4450
txBody
4551

46-
let txBudget = do
47-
body <- txBody
48-
budget <- budgetRes
49-
let scaledBudget = fmap (fmap $ scaleBudget pabConf.pcBudgetMultiplier) budget
50-
(spendingBudgets, policyBudgets) <- mkBudgetMaps scaledBudget body
51-
Right $ TxBudget spendingBudgets policyBudgets
52+
return $
53+
do
54+
body <- txBody
55+
budget <- budgetRes
56+
pparams <- left toBudgetError pparamsRes
57+
maxUnits <- maybeToEither (BudgetEstimationError "Missing max units in parameters") $ protocolParamMaxTxExUnits pparams
58+
59+
let scaledBudget = getScaledBudget maxUnits pabConf.pcBudgetMultiplier budget
60+
61+
(spendingBudgets, policyBudgets) <- mkBudgetMaps scaledBudget body
5262

53-
return txBudget
63+
Right $ TxBudget spendingBudgets policyBudgets
64+
65+
-- | Scale the budget clamping the total to the parameter limits
66+
getScaledBudget :: CAPI.ExecutionUnits -> Rational -> ExUnitsMap -> ExUnitsMap
67+
getScaledBudget maxUnits scaler budget = fmap (fmap $ scaleBudget scalers) budget
68+
where
69+
budgetSum = foldr addBudgets (CAPI.ExecutionUnits 0 0) $ rights $ Map.elems budget
70+
scalers =
71+
( clampedScaler (CAPI.executionSteps budgetSum) (CAPI.executionSteps maxUnits) scaler
72+
, clampedScaler (CAPI.executionMemory budgetSum) (CAPI.executionMemory maxUnits) scaler
73+
)
74+
75+
clampedScaler :: Natural -> Natural -> Rational -> Rational
76+
clampedScaler 0 _ scaler = scaler
77+
clampedScaler val maxVal scaler = min scaler (toRational maxVal / toRational val)
5478

5579
-- | Scale the budget by the multipliers in config
56-
scaleBudget :: Rational -> CAPI.ExecutionUnits -> CAPI.ExecutionUnits
57-
scaleBudget scaler (CAPI.ExecutionUnits steps mem) = CAPI.ExecutionUnits (scale steps) (scale mem)
80+
scaleBudget :: (Rational, Rational) -> CAPI.ExecutionUnits -> CAPI.ExecutionUnits
81+
scaleBudget (stepsScaler, memScaler) (CAPI.ExecutionUnits steps mem) = CAPI.ExecutionUnits (scale steps stepsScaler) (scale mem memScaler)
5882
where
59-
scale x = round $ toRational x * scaler
83+
scale x scaler = round $ toRational x * scaler
84+
85+
addBudgets :: CAPI.ExecutionUnits -> CAPI.ExecutionUnits -> CAPI.ExecutionUnits
86+
addBudgets (CAPI.ExecutionUnits steps mem) (CAPI.ExecutionUnits steps' mem') = CAPI.ExecutionUnits (steps + steps') (mem + mem')
6087

6188
-- | Deserialize transaction body from ".signed" file
6289
deserialiseSigned :: FilePath -> IO (Either BudgetEstimationError (CAPI.Tx CAPI.AlonzoEra))
@@ -92,13 +119,13 @@ type ExUnitsMap =
92119

93120
-- | Calculate execution units using `Cardano.Api``
94121
getExUnits ::
122+
Either NodeQueryError ProtocolParameters ->
95123
NodeInfo ->
96124
CAPI.TxBody CAPI.AlonzoEra ->
97125
IO (Either BudgetEstimationError ExUnitsMap)
98-
getExUnits nodeInf txBody = do
126+
getExUnits pparams nodeInf txBody = do
99127
sysStart <- QueryNode.querySystemStart nodeInf
100128
eraHist <- QueryNode.queryEraHistory nodeInf
101-
pparams <- QueryNode.queryProtocolParams nodeInf
102129
utxo <- QueryNode.queryOutsByInputs nodeInf capiIns
103130
return $
104131
flattenEvalResult $

src/BotPlutusInterface/QueryNode.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
-- | Several query functions to query local node
44
module BotPlutusInterface.QueryNode (
55
NodeInfo (..),
6+
NodeQueryError (..),
67
queryProtocolParams,
78
querySystemStart,
89
queryEraHistory,

test/Spec/BotPlutusInterface/Config.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,4 +113,5 @@ pabConfigExample =
113113
, pcPort = 1021
114114
, pcEnableTxEndpoint = True
115115
, pcCollectStats = False
116+
, pcBudgetMultiplier = 1
116117
}

0 commit comments

Comments
 (0)