@@ -35,7 +35,9 @@ import Cardano.Slotting.EpochInfo qualified as CS
3535import Cardano.Slotting.Slot qualified as CS
3636import Cardano.Slotting.Time qualified as CS
3737
38+ import Control.Monad
3839import Data.Aeson (eitherDecodeStrict )
40+ import Data.Bifunctor (first )
3941import Data.ByteString qualified as B
4042import Data.Default (def )
4143import Data.Function
@@ -55,9 +57,185 @@ import Hedgehog (MonadTest, Property, forAll, (===))
5557import Hedgehog qualified as H
5658import Hedgehog.Extras qualified as H
5759import Hedgehog.Gen qualified as Gen
60+ import Hedgehog.Range qualified as Range
5861import Test.Tasty (TestTree , testGroup )
5962import Test.Tasty.Hedgehog (testProperty )
6063
64+ prop_make_transaction_body_autobalance_invariants :: Property
65+ prop_make_transaction_body_autobalance_invariants = H. property $ do
66+ let ceo = ConwayEraOnwardsConway
67+ sbe = convert ceo
68+
69+ systemStart <- parseSystemStart " 2021-09-01T00:00:00Z"
70+ let epochInfo = LedgerEpochInfo $ CS. fixedEpochInfo (CS. EpochSize 100 ) (CS. mkSlotLength 1000 )
71+
72+ pparams <-
73+ LedgerProtocolParameters
74+ <$> H. readJsonFileOk " test/cardano-api-test/files/input/protocol-parameters/conway.json"
75+
76+ -- assume a value larger the one from protocol params to account for min utxo scaling with minted assets
77+ let minUtxo = 2_000_000
78+
79+ -- generate utxos with random values
80+ utxos <- fmap (UTxO . fromList) . forAll $ do
81+ Gen. list (Range. constant 1 10 ) $ do
82+ txIn <- genTxIn
83+ addr <- genAddressInEra sbe
84+ utxoValue <- L. Coin <$> Gen. integral (Range. linear minUtxo 20_000_000 )
85+ let mintValue = mempty -- TODO generate and check in invariants
86+ txOut =
87+ TxOut
88+ addr
89+ (TxOutValueShelleyBased sbe $ L. MaryValue utxoValue mintValue)
90+ TxOutDatumNone
91+ ReferenceScriptNone
92+ pure (txIn, txOut)
93+
94+ let utxoSum =
95+ mconcat
96+ [ maryValue
97+ | (_, TxOut _ (TxOutValueShelleyBased _ maryValue) _ _) <- toList utxos
98+ ]
99+ H. noteShowPretty_ utxoSum
100+
101+ -- split inputs into min utxo txouts
102+ let nTxOuts = L. unCoin (L. coin utxoSum) `div` minUtxo - 1 -- leave one out for change
103+ H. noteShow_ nTxOuts
104+ txOut <- forAll $ forM ([1 .. nTxOuts] :: [Integer ]) $ \ _ -> do
105+ addr <- genAddressInEra sbe
106+ let mintValue = mempty -- TODO generate and check in invariants
107+ pure $
108+ TxOut
109+ addr
110+ (TxOutValueShelleyBased sbe $ L. MaryValue (L. Coin minUtxo) mintValue)
111+ TxOutDatumNone
112+ ReferenceScriptNone
113+
114+ changeAddress <- forAll $ genAddressInEra sbe
115+
116+ -- use all UTXOs as inputs
117+ let txInputs = map (,BuildTxWith (KeyWitness KeyWitnessForSpending )) . toList . M. keys . unUTxO $ utxos
118+
119+ let content =
120+ defaultTxBodyContent sbe
121+ & setTxIns txInputs
122+ & setTxOuts txOut
123+ & setTxProtocolParams (pure $ pure pparams)
124+
125+ (BalancedTxBody balancedContent _ change fee) <-
126+ H. leftFail . first prettyError $
127+ makeTransactionBodyAutoBalance
128+ sbe
129+ systemStart
130+ epochInfo
131+ pparams
132+ mempty
133+ mempty
134+ mempty
135+ utxos
136+ content
137+ changeAddress
138+ Nothing
139+
140+ H. note_ " Check that fee is greater than 0"
141+ H. assertWith (L. unCoin fee) $ (<) 0
142+
143+ H. noteShow_ fee
144+ H. noteShowPretty_ change
145+ H. noteShowPretty_ $ txOuts balancedContent
146+
147+ let txOutSum =
148+ mconcat
149+ [ maryValue
150+ | TxOut _ (TxOutValueShelleyBased _ maryValue) _ _ <- txOuts balancedContent
151+ ]
152+
153+ H. note_ " Check that all inputs are spent"
154+ utxoSum === (txOutSum <> inject fee)
155+
156+ prop_make_transaction_body_autobalance_no_change :: Property
157+ prop_make_transaction_body_autobalance_no_change = H. propertyOnce $ do
158+ let ceo = ConwayEraOnwardsConway
159+ sbe = convert ceo
160+
161+ systemStart <- parseSystemStart " 2021-09-01T00:00:00Z"
162+ let epochInfo = LedgerEpochInfo $ CS. fixedEpochInfo (CS. EpochSize 100 ) (CS. mkSlotLength 1000 )
163+
164+ pparams <-
165+ LedgerProtocolParameters
166+ <$> H. readJsonFileOk " test/cardano-api-test/files/input/protocol-parameters/conway.json"
167+
168+ let expectedFee = 170_077
169+ utxoValue = 5_000_000
170+
171+ let address =
172+ AddressInEra
173+ (ShelleyAddressInEra sbe)
174+ ( ShelleyAddress
175+ L. Testnet
176+ (L. KeyHashObj " ebe9de78a37f84cc819c0669791aa0474d4f0a764e54b9f90cfe2137" )
177+ L. StakeRefNull
178+ )
179+ let utxos =
180+ UTxO
181+ [
182+ ( TxIn
183+ " 01f4b788593d4f70de2a45c2e1e87088bfbdfa29577ae1b62aba60e095e3ab53"
184+ (TxIx 0 )
185+ , TxOut
186+ address
187+ ( TxOutValueShelleyBased
188+ sbe
189+ (L. MaryValue utxoValue mempty )
190+ )
191+ TxOutDatumNone
192+ ReferenceScriptNone
193+ )
194+ ]
195+
196+ txInputs = map (,BuildTxWith (KeyWitness KeyWitnessForSpending )) . toList . M. keys . unUTxO $ utxos
197+
198+ -- tx out fully spending the txin minus the fee
199+ txOut =
200+ [ TxOut
201+ address
202+ ( TxOutValueShelleyBased
203+ sbe
204+ (L. MaryValue (utxoValue - expectedFee) mempty )
205+ )
206+ TxOutDatumNone
207+ ReferenceScriptNone
208+ ]
209+
210+ let content =
211+ defaultTxBodyContent sbe
212+ & setTxIns txInputs
213+ & setTxOuts txOut
214+ & setTxProtocolParams (pure $ pure pparams)
215+
216+ (BalancedTxBody balancedContent _ (TxOut _ (TxOutValueShelleyBased _ change) _ _) fee) <-
217+ H. leftFail . first prettyError $
218+ makeTransactionBodyAutoBalance
219+ sbe
220+ systemStart
221+ epochInfo
222+ pparams
223+ mempty
224+ mempty
225+ mempty
226+ utxos
227+ content
228+ address
229+ Nothing
230+
231+ H. noteShowPretty_ change
232+ H. noteShowPretty_ $ txOuts balancedContent
233+
234+ expectedFee === fee
235+
236+ -- check that the txins were fully spent before autobalancing
237+ H. assertWith change L. isZero
238+
61239-- | Test that the fee is the same when spending minted asset manually or when autobalancing it
62240prop_make_transaction_body_autobalance_return_correct_fee_for_multi_asset :: Property
63241prop_make_transaction_body_autobalance_return_correct_fee_for_multi_asset = H. propertyOnce $ do
@@ -396,8 +574,7 @@ prop_ensure_gov_actions_are_preserved_by_autobalance = H.propertyOnce $ do
396574 , L. pProcReturnAddr =
397575 L. RewardAccount
398576 { L. raNetwork = L. Testnet
399- , L. raCredential =
400- L. KeyHashObj (L. KeyHash {L. unKeyHash = " 0b1b872f7953bccfc4245f3282b3363f3d19e9e001a5c41e307363d7" })
577+ , L. raCredential = L. KeyHashObj " 0b1b872f7953bccfc4245f3282b3363f3d19e9e001a5c41e307363d7"
401578 }
402579 , L. pProcGovAction = L. InfoAction
403580 , L. pProcAnchor = anchor
@@ -452,9 +629,7 @@ mkSimpleUTxOs sbe =
452629 (ShelleyAddressInEra sbe)
453630 ( ShelleyAddress
454631 L. Testnet
455- ( L. KeyHashObj $
456- L. KeyHash " ebe9de78a37f84cc819c0669791aa0474d4f0a764e54b9f90cfe2137"
457- )
632+ (L. KeyHashObj " ebe9de78a37f84cc819c0669791aa0474d4f0a764e54b9f90cfe2137" )
458633 L. StakeRefNull
459634 )
460635 )
@@ -518,9 +693,7 @@ mkUtxos beo mScriptHash = babbageEraOnwardsConstraints beo $ do
518693 (ShelleyAddressInEra sbe)
519694 ( ShelleyAddress
520695 L. Testnet
521- ( L. KeyHashObj $
522- L. KeyHash " ebe9de78a37f84cc819c0669791aa0474d4f0a764e54b9f90cfe2137"
523- )
696+ (L. KeyHashObj " ebe9de78a37f84cc819c0669791aa0474d4f0a764e54b9f90cfe2137" )
524697 L. StakeRefNull
525698 )
526699 )
@@ -530,7 +703,7 @@ mkUtxos beo mScriptHash = babbageEraOnwardsConstraints beo $ do
530703 (L. Coin 4_000_000 )
531704 ( L. MultiAsset $
532705 fromList
533- [(L. PolicyID scriptHash, [(L. AssetName " eeee" , 1 )]) | scriptHash <- maybeToList mScriptHash]
706+ [(L. PolicyID scriptHash, [(" eeee" , 1 )]) | scriptHash <- maybeToList mScriptHash]
534707 )
535708 )
536709 )
@@ -569,7 +742,7 @@ mkTxOutput beo address coin mScriptHash = babbageEraOnwardsConstraints beo $ do
569742 coin
570743 ( L. MultiAsset $
571744 fromList
572- [(L. PolicyID scriptHash, [(L. AssetName " eeee" , 2 )]) | scriptHash <- maybeToList mScriptHash]
745+ [(L. PolicyID scriptHash, [(" eeee" , 2 )]) | scriptHash <- maybeToList mScriptHash]
573746 )
574747 )
575748 )
@@ -597,6 +770,12 @@ tests =
597770 testGroup
598771 " Test.Cardano.Api.Typed.TxBody"
599772 [ testProperty
773+ " makeTransactionBodyAutoBalance invariants"
774+ prop_make_transaction_body_autobalance_invariants
775+ , testProperty
776+ " makeTransactionBodyAutoBalance no change"
777+ prop_make_transaction_body_autobalance_no_change
778+ , testProperty
600779 " makeTransactionBodyAutoBalance test correct fees when mutli-asset tx"
601780 prop_make_transaction_body_autobalance_return_correct_fee_for_multi_asset
602781 , testProperty
0 commit comments