@@ -35,7 +35,9 @@ import Cardano.Slotting.EpochInfo qualified as CS
35
35
import Cardano.Slotting.Slot qualified as CS
36
36
import Cardano.Slotting.Time qualified as CS
37
37
38
+ import Control.Monad
38
39
import Data.Aeson (eitherDecodeStrict )
40
+ import Data.Bifunctor (first )
39
41
import Data.ByteString qualified as B
40
42
import Data.Default (def )
41
43
import Data.Function
@@ -55,9 +57,185 @@ import Hedgehog (MonadTest, Property, forAll, (===))
55
57
import Hedgehog qualified as H
56
58
import Hedgehog.Extras qualified as H
57
59
import Hedgehog.Gen qualified as Gen
60
+ import Hedgehog.Range qualified as Range
58
61
import Test.Tasty (TestTree , testGroup )
59
62
import Test.Tasty.Hedgehog (testProperty )
60
63
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
+
61
239
-- | Test that the fee is the same when spending minted asset manually or when autobalancing it
62
240
prop_make_transaction_body_autobalance_return_correct_fee_for_multi_asset :: Property
63
241
prop_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
396
574
, L. pProcReturnAddr =
397
575
L. RewardAccount
398
576
{ L. raNetwork = L. Testnet
399
- , L. raCredential =
400
- L. KeyHashObj (L. KeyHash {L. unKeyHash = " 0b1b872f7953bccfc4245f3282b3363f3d19e9e001a5c41e307363d7" })
577
+ , L. raCredential = L. KeyHashObj " 0b1b872f7953bccfc4245f3282b3363f3d19e9e001a5c41e307363d7"
401
578
}
402
579
, L. pProcGovAction = L. InfoAction
403
580
, L. pProcAnchor = anchor
@@ -452,9 +629,7 @@ mkSimpleUTxOs sbe =
452
629
(ShelleyAddressInEra sbe)
453
630
( ShelleyAddress
454
631
L. Testnet
455
- ( L. KeyHashObj $
456
- L. KeyHash " ebe9de78a37f84cc819c0669791aa0474d4f0a764e54b9f90cfe2137"
457
- )
632
+ (L. KeyHashObj " ebe9de78a37f84cc819c0669791aa0474d4f0a764e54b9f90cfe2137" )
458
633
L. StakeRefNull
459
634
)
460
635
)
@@ -518,9 +693,7 @@ mkUtxos beo mScriptHash = babbageEraOnwardsConstraints beo $ do
518
693
(ShelleyAddressInEra sbe)
519
694
( ShelleyAddress
520
695
L. Testnet
521
- ( L. KeyHashObj $
522
- L. KeyHash " ebe9de78a37f84cc819c0669791aa0474d4f0a764e54b9f90cfe2137"
523
- )
696
+ (L. KeyHashObj " ebe9de78a37f84cc819c0669791aa0474d4f0a764e54b9f90cfe2137" )
524
697
L. StakeRefNull
525
698
)
526
699
)
@@ -530,7 +703,7 @@ mkUtxos beo mScriptHash = babbageEraOnwardsConstraints beo $ do
530
703
(L. Coin 4_000_000 )
531
704
( L. MultiAsset $
532
705
fromList
533
- [(L. PolicyID scriptHash, [(L. AssetName " eeee" , 1 )]) | scriptHash <- maybeToList mScriptHash]
706
+ [(L. PolicyID scriptHash, [(" eeee" , 1 )]) | scriptHash <- maybeToList mScriptHash]
534
707
)
535
708
)
536
709
)
@@ -569,7 +742,7 @@ mkTxOutput beo address coin mScriptHash = babbageEraOnwardsConstraints beo $ do
569
742
coin
570
743
( L. MultiAsset $
571
744
fromList
572
- [(L. PolicyID scriptHash, [(L. AssetName " eeee" , 2 )]) | scriptHash <- maybeToList mScriptHash]
745
+ [(L. PolicyID scriptHash, [(" eeee" , 2 )]) | scriptHash <- maybeToList mScriptHash]
573
746
)
574
747
)
575
748
)
@@ -597,6 +770,12 @@ tests =
597
770
testGroup
598
771
" Test.Cardano.Api.Typed.TxBody"
599
772
[ 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
600
779
" makeTransactionBodyAutoBalance test correct fees when mutli-asset tx"
601
780
prop_make_transaction_body_autobalance_return_correct_fee_for_multi_asset
602
781
, testProperty
0 commit comments