Skip to content

Commit 1b5f63a

Browse files
committed
Address PR review: simplify calcMinFeeRecursive and remove redundant constraints
- Remove getMultiAssets helper and inline the MaryValue extraction, since EraCommonConstraints guarantees Value ~ MaryValue for all eras including Dijkstra - Remove unnecessary parameters from go's recursive loop — utxo, pparams, poolids, stakeDelegDeposits, drepDelegDeposits, and nExtraWitnesses are never modified and are already in scope from the enclosing function - Remove redundant obtainCommonConstraints calls in test generators where constraints are already in scope from the outer call
1 parent c53efcd commit 1b5f63a

2 files changed

Lines changed: 48 additions & 59 deletions

File tree

  • cardano-api

cardano-api/src/Cardano/Api/Experimental/Tx/Internal/Fee.hs

Lines changed: 27 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -661,17 +661,26 @@ evaluateTransactionFee pp (UnsignedTx tx) keywitcount byronwitcount refScriptsSi
661661
L.estimateMinFeeTx pp tx (fromIntegral keywitcount) (fromIntegral byronwitcount) refScriptsSize
662662

663663
data FeeCalculationError
664-
= NotEnoughAda Coin
664+
= -- | Updating an existing change output resulted in negative ADA.
665+
NotEnoughAdaForChangeOutput Coin
666+
| -- | Creating a new change output would require negative ADA.
667+
NotEnoughAdaForNewOutput Coin
665668
| NonAdaAssetsUnbalanced L.MultiAsset
666669
| -- | @MinUTxONotMet actual required@: an output does not meet the minimum UTxO requirement.
667670
MinUTxONotMet L.Coin L.Coin
668671
| FeeCalculationDidNotConverge
669672
deriving (Show, Eq)
670673

671674
instance Error FeeCalculationError where
672-
prettyError (NotEnoughAda balance) =
675+
prettyError (NotEnoughAdaForChangeOutput balance) =
673676
mconcat
674-
[ "The transaction balance is negative: "
677+
[ "Not enough ADA when updating existing change output. Balance: "
678+
, pretty balance
679+
, "\nThis means that the transaction does not have enough ada to cover the fees. The usual solution is to provide more inputs, or inputs with more ada."
680+
]
681+
prettyError (NotEnoughAdaForNewOutput balance) =
682+
mconcat
683+
[ "Not enough ADA when creating new change output. Balance: "
675684
, pretty balance
676685
, "\nThis means that the transaction does not have enough ada to cover the fees. The usual solution is to provide more inputs, or inputs with more ada."
677686
]
@@ -721,7 +730,7 @@ instance Error FeeCalculationError where
721730
-- balance and appended to the end of the existing outputs; if a change
722731
-- output already exists it is updated in place. If the resulting change
723732
-- output would have negative ADA, the transaction is unrecoverable and
724-
-- 'NotEnoughAda' is returned. Otherwise the function recurses, because
733+
-- 'NotEnoughAdaForChangeOutput' or 'NotEnoughAdaForNewOutput' is returned. Otherwise the function recurses, because
725734
-- the changed output may alter the transaction size and therefore the
726735
-- required fee, and must also satisfy the minimum UTxO
727736
-- (@coinPerUTxOByte@) constraint.
@@ -760,18 +769,13 @@ calcMinFeeRecursive changeAddr unsignedTx utxo pparams poolids stakeDelegDeposit
760769
| multiAssetIsNegative =
761770
Left $ NonAdaAssetsUnbalanced multiAssets
762771
| otherwise =
763-
go
764-
maxIterations
765-
unsignedTx
766-
utxo
767-
pparams
768-
poolids
769-
stakeDelegDeposits
770-
drepDelegDeposits
771-
nExtraWitnesses
772+
go maxIterations unsignedTx
772773
where
773774
initialBalance = evaluateTransactionBalance pparams poolids stakeDelegDeposits drepDelegDeposits utxo unsignedTx
774-
multiAssets = getMultiAssets (useEra @era) initialBalance
775+
multiAssets =
776+
obtainCommonConstraints (useEra @era) $
777+
let L.MaryValue _ ma = initialBalance
778+
in ma
775779
-- Check whether any native token quantity is negative.
776780
-- ADA is zeroed out so it doesn't influence the check.
777781
multiAssetIsNegative =
@@ -783,34 +787,28 @@ calcMinFeeRecursive changeAddr unsignedTx utxo pparams poolids stakeDelegDeposit
783787
go
784788
:: Int
785789
-> UnsignedTx (LedgerEra era)
786-
-> L.UTxO (LedgerEra era)
787-
-> L.PParams (LedgerEra era)
788-
-> Set PoolId
789-
-> Map StakeCredential L.Coin
790-
-> Map (Ledger.Credential Ledger.DRepRole) L.Coin
791-
-> Int
792790
-> Either FeeCalculationError (UnsignedTx (LedgerEra era))
793-
go 0 _ _ _ _ _ _ _ = Left FeeCalculationDidNotConverge
794-
go n unSignTx@(UnsignedTx ledgerTx) utxo' pparams' poolids' stakeDelegDeposits' drepDelegDeposits' nExtraWitnesses'
791+
go 0 _ = Left FeeCalculationDidNotConverge
792+
go n unSignTx@(UnsignedTx ledgerTx)
795793
| minFee == txBodyFee && L.isZero txBalanceValue = do
796794
-- Case 1
797795
let outs = toList $ ledgerTx ^. L.bodyTxL . L.outputsTxBodyL
798-
mapM_ (checkOutputMinUTxO pparams') outs
796+
mapM_ (checkOutputMinUTxO pparams) outs
799797
return unSignTx
800798
| minFee == txBodyFee = do
801799
-- Case 2
802800
balancedOuts <- balanceTxOuts @era changeAddr txBalanceValue unSignTx
803801
let updatedTx = UnsignedTx (ledgerTx & L.bodyTxL . L.outputsTxBodyL .~ balancedOuts)
804-
go (n - 1) updatedTx utxo' pparams' poolids' stakeDelegDeposits' drepDelegDeposits' nExtraWitnesses'
802+
go (n - 1) updatedTx
805803
| otherwise =
806804
-- Case 3
807805
let newTx = UnsignedTx (ledgerTx & L.bodyTxL . L.feeTxBodyL .~ minFee)
808-
in go (n - 1) newTx utxo' pparams' poolids' stakeDelegDeposits' drepDelegDeposits' nExtraWitnesses'
806+
in go (n - 1) newTx
809807
where
810-
minFee = obtainCommonConstraints (useEra @era) $ L.calcMinFeeTx utxo' pparams' ledgerTx nExtraWitnesses'
808+
minFee = obtainCommonConstraints (useEra @era) $ L.calcMinFeeTx utxo pparams ledgerTx nExtraWitnesses
811809
txBodyFee = ledgerTx ^. L.bodyTxL . L.feeTxBodyL
812810
txBalanceValue =
813-
evaluateTransactionBalance pparams' poolids' stakeDelegDeposits' drepDelegDeposits' utxo' unSignTx
811+
evaluateTransactionBalance pparams poolids stakeDelegDeposits drepDelegDeposits utxo unSignTx
814812

815813
checkOutputMinUTxO
816814
:: forall era
@@ -826,13 +824,6 @@ checkOutputMinUTxO pp out =
826824
Left (TxOut offending, minRequired) ->
827825
Left $ MinUTxONotMet (offending ^. L.coinTxOutL) minRequired
828826

829-
getMultiAssets :: Era era -> L.Value (LedgerEra era) -> L.MultiAsset
830-
getMultiAssets era val = case era of
831-
DijkstraEra -> mempty
832-
ConwayEra ->
833-
let L.MaryValue _ ma = val
834-
in ma
835-
836827
balanceTxOuts
837828
:: forall era
838829
. HasCallStack
@@ -854,13 +845,13 @@ balanceTxOuts changeAddr txBalance (UnsignedTx tx) =
854845
let newValue = (lastOut ^. L.valueTxOutL) <> txBalance
855846
changeCoin = L.coin newValue
856847
in if changeCoin < 0
857-
then Left $ NotEnoughAda changeCoin
848+
then Left $ NotEnoughAdaForChangeOutput changeCoin
858849
else Right $ rest Seq.:|> (lastOut & L.valueTxOutL .~ newValue)
859850
_ ->
860851
-- Append a new change output
861852
let changeCoin = L.coin txBalance
862853
in if changeCoin < 0
863-
then Left $ NotEnoughAda changeCoin
854+
then Left $ NotEnoughAdaForNewOutput changeCoin
864855
else Right $ outs Seq.:|> L.mkBasicTxOut changeAddr txBalance
865856

866857
-- Essentially we check for the existence of collateral inputs. If they exist we

cardano-api/test/cardano-api-test/Test/Cardano/Api/Experimental.hs

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ tests =
104104
"Case 2: transaction with no outputs creates change output"
105105
prop_calcMinFeeRecursive_no_tx_outs
106106
, testProperty
107-
"Tiny surplus consumed by fee increase yields NotEnoughAda"
107+
"Tiny surplus consumed by fee increase yields NotEnoughAdaForChangeOutput"
108108
prop_calcMinFeeRecursive_tiny_surplus_not_enough_ada
109109
]
110110
]
@@ -606,8 +606,7 @@ genFundedSimpleTx era = do
606606
sendTxOut =
607607
Exp.obtainCommonConstraints era $
608608
Exp.TxOut $
609-
Exp.obtainCommonConstraints era $
610-
Ledger.mkBasicTxOut addr (L.MaryValue sendCoin mempty)
609+
Ledger.mkBasicTxOut addr (L.MaryValue sendCoin mempty)
611610
txBodyContent =
612611
Exp.defaultTxBodyContent
613612
& Exp.setTxIns [(txIn, Exp.AnyKeyWitnessPlaceholder)]
@@ -644,8 +643,7 @@ genFundedMultiAssetTx era = do
644643
sendTxOut =
645644
Exp.obtainCommonConstraints era $
646645
Exp.TxOut $
647-
Exp.obtainCommonConstraints era $
648-
Ledger.mkBasicTxOut addr (L.MaryValue sendCoin multiAsset)
646+
Ledger.mkBasicTxOut addr (L.MaryValue sendCoin multiAsset)
649647
txBodyContent =
650648
Exp.defaultTxBodyContent
651649
& Exp.setTxIns [(txIn, Exp.AnyKeyWitnessPlaceholder)]
@@ -678,8 +676,7 @@ genUnderfundedTx era = do
678676
sendTxOut =
679677
Exp.obtainCommonConstraints era $
680678
Exp.TxOut $
681-
Exp.obtainCommonConstraints era $
682-
Ledger.mkBasicTxOut addr (L.MaryValue sendCoin mempty)
679+
Ledger.mkBasicTxOut addr (L.MaryValue sendCoin mempty)
683680
txBodyContent =
684681
Exp.defaultTxBodyContent
685682
& Exp.setTxIns [(txIn, Exp.AnyKeyWitnessPlaceholder)]
@@ -744,15 +741,16 @@ prop_calcMinFeeRecursive_fee_fixpoint = H.property $ do
744741
resultTx H.=== secondResult
745742

746743
-- | When the outputs exceed the UTxO value the function returns
747-
-- 'Left (NotEnoughAda _)' with a negative deficit coin.
744+
-- 'Left (NotEnoughAdaForNewOutput _)' with a negative deficit coin.
748745
prop_calcMinFeeRecursive_insufficient_funds :: Property
749746
prop_calcMinFeeRecursive_insufficient_funds = H.property $ do
750747
(unsignedTx, utxo, changeAddr) <- H.forAll $ genUnderfundedTx Exp.ConwayEra
751748
case Exp.calcMinFeeRecursive changeAddr unsignedTx utxo exampleProtocolParams mempty mempty mempty 0 of
752-
Left (Exp.NotEnoughAda deficit) -> H.assert $ deficit < L.Coin 0
749+
Left (Exp.NotEnoughAdaForNewOutput deficit) -> H.assert $ deficit < L.Coin 0
753750
Left Exp.NonAdaAssetsUnbalanced{} -> H.annotate "Unexpected NonAdaAssetsUnbalanced error" >> H.failure
754751
Left Exp.MinUTxONotMet{} -> H.annotate "Unexpected MinUTxONotMet error" >> H.failure
755752
Left Exp.FeeCalculationDidNotConverge -> H.annotate "Unexpected FeeCalculationDidNotConverge error" >> H.failure
753+
Left err -> H.annotateShow err >> H.failure
756754
Right _ -> H.failure
757755

758756
-- | Generates a transaction whose output demands a native token that does
@@ -787,8 +785,7 @@ genNonAdaUnbalancedTx era = do
787785
sendTxOut =
788786
Exp.obtainCommonConstraints era $
789787
Exp.TxOut $
790-
Exp.obtainCommonConstraints era $
791-
Ledger.mkBasicTxOut addr sendValue
788+
Ledger.mkBasicTxOut addr sendValue
792789
txBodyContent =
793790
Exp.defaultTxBodyContent
794791
& Exp.setTxIns [(txIn, Exp.AnyKeyWitnessPlaceholder)]
@@ -827,14 +824,12 @@ genMinUTxOViolatingTx era = do
827824
sendTxOut1 =
828825
Exp.obtainCommonConstraints era $
829826
Exp.TxOut $
830-
Exp.obtainCommonConstraints era $
831-
Ledger.mkBasicTxOut addr (L.MaryValue (L.Coin 1_000_000) mempty)
827+
Ledger.mkBasicTxOut addr (L.MaryValue (L.Coin 1_000_000) mempty)
832828
-- Output 2: tokens with tiny ADA (below min UTxO)
833829
sendTxOut2 =
834830
Exp.obtainCommonConstraints era $
835831
Exp.TxOut $
836-
Exp.obtainCommonConstraints era $
837-
Ledger.mkBasicTxOut addr (L.MaryValue (L.Coin 1_000) multiAsset)
832+
Ledger.mkBasicTxOut addr (L.MaryValue (L.Coin 1_000) multiAsset)
838833
txBodyContent =
839834
Exp.defaultTxBodyContent
840835
& Exp.setTxIns [(txIn, Exp.AnyKeyWitnessPlaceholder)]
@@ -877,7 +872,8 @@ prop_calcMinFeeRecursive_non_ada_unbalanced = H.property $ do
877872
(unsignedTx, utxo, changeAddr) <- H.forAll $ genNonAdaUnbalancedTx Exp.ConwayEra
878873
case Exp.calcMinFeeRecursive changeAddr unsignedTx utxo exampleProtocolParams mempty mempty mempty 0 of
879874
Left (Exp.NonAdaAssetsUnbalanced _) -> H.success
880-
Left Exp.NotEnoughAda{} -> H.annotate "Unexpected NotEnoughAda" >> H.failure
875+
Left Exp.NotEnoughAdaForChangeOutput{} -> H.annotate "Unexpected NotEnoughAdaForChangeOutput" >> H.failure
876+
Left Exp.NotEnoughAdaForNewOutput{} -> H.annotate "Unexpected NotEnoughAdaForNewOutput" >> H.failure
881877
Left Exp.MinUTxONotMet{} -> H.annotate "Unexpected MinUTxONotMet" >> H.failure
882878
Left Exp.FeeCalculationDidNotConverge -> H.annotate "Unexpected FeeCalculationDidNotConverge" >> H.failure
883879
Right _ -> H.annotate "Expected NonAdaAssetsUnbalanced but got Right" >> H.failure
@@ -892,7 +888,8 @@ prop_calcMinFeeRecursive_min_utxo_not_met = H.property $ do
892888
Left (Exp.MinUTxONotMet actual required) -> do
893889
H.annotate $ "Actual: " <> show actual <> ", Required: " <> show required
894890
H.assert $ actual < required
895-
Left Exp.NotEnoughAda{} -> H.annotate "Unexpected NotEnoughAda" >> H.failure
891+
Left Exp.NotEnoughAdaForChangeOutput{} -> H.annotate "Unexpected NotEnoughAdaForChangeOutput" >> H.failure
892+
Left Exp.NotEnoughAdaForNewOutput{} -> H.annotate "Unexpected NotEnoughAdaForNewOutput" >> H.failure
896893
Left Exp.NonAdaAssetsUnbalanced{} -> H.annotate "Unexpected NonAdaAssetsUnbalanced" >> H.failure
897894
Left Exp.FeeCalculationDidNotConverge -> H.annotate "Unexpected FeeCalculationDidNotConverge" >> H.failure
898895
Right _ -> H.annotate "Expected MinUTxONotMet but got Right" >> H.failure
@@ -926,7 +923,7 @@ prop_calcMinFeeRecursive_no_tx_outs = H.property $ do
926923
-- 1. After fee convergence at F1, a positive balance triggers Case 2.
927924
-- 2. Adding the change output raises the fee to F2.
928925
-- 3. The change is updated: (surplus - F1) + (F1 - F2) = surplus - F2 < 0.
929-
-- 4. balanceTxOuts returns NotEnoughAda.
926+
-- 4. balanceTxOuts returns NotEnoughAdaForChangeOutput.
930927
genTinySurplusTx
931928
:: Exp.Era era
932929
-> Gen
@@ -955,8 +952,7 @@ genTinySurplusTx era = do
955952
sendTxOut =
956953
Exp.obtainCommonConstraints era $
957954
Exp.TxOut $
958-
Exp.obtainCommonConstraints era $
959-
Ledger.mkBasicTxOut addr (L.MaryValue sendCoin mempty)
955+
Ledger.mkBasicTxOut addr (L.MaryValue sendCoin mempty)
960956
txBodyContent =
961957
Exp.defaultTxBodyContent
962958
& Exp.setTxIns [(txIn, Exp.AnyKeyWitnessPlaceholder)]
@@ -966,12 +962,12 @@ genTinySurplusTx era = do
966962

967963
-- | When the surplus is just barely enough to cover the initial fee but not
968964
-- the higher fee after adding a change output, the change output balance
969-
-- goes negative and the function returns NotEnoughAda.
965+
-- goes negative and the function returns NotEnoughAdaForChangeOutput.
970966
prop_calcMinFeeRecursive_tiny_surplus_not_enough_ada :: Property
971967
prop_calcMinFeeRecursive_tiny_surplus_not_enough_ada = H.property $ do
972968
(unsignedTx, utxo, changeAddr) <- H.forAll $ genTinySurplusTx Exp.ConwayEra
973969
case Exp.calcMinFeeRecursive changeAddr unsignedTx utxo exampleProtocolParams mempty mempty mempty 0 of
974-
Left (Exp.NotEnoughAda deficit) -> do
970+
Left (Exp.NotEnoughAdaForChangeOutput deficit) -> do
975971
H.annotate $ "Deficit: " <> show deficit
976972
H.assert $ deficit < L.Coin 0
977973
Left (Exp.MinUTxONotMet actual required) -> do
@@ -980,4 +976,6 @@ prop_calcMinFeeRecursive_tiny_surplus_not_enough_ada = H.property $ do
980976
H.annotate $ "Change output ADA: " <> show actual <> ", minUTxO: " <> show required
981977
H.assert $ actual < required
982978
Left err -> H.annotateShow err >> H.failure
983-
Right _ -> H.annotate "Expected NotEnoughAda or MinUTxONotMet but tx balanced successfully" >> H.failure
979+
Right _ ->
980+
H.annotate "Expected NotEnoughAdaForChangeOutput or MinUTxONotMet but tx balanced successfully"
981+
>> H.failure

0 commit comments

Comments
 (0)