Skip to content

Commit dbcdfe2

Browse files
committed
Property test for pickTokensToDeposit
Signed-off-by: Sasha Bogicevic <[email protected]>
1 parent e038977 commit dbcdfe2

File tree

2 files changed

+110
-38
lines changed

2 files changed

+110
-38
lines changed

hydra-tx/test/Hydra/Tx/DepositSpec.hs

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import Data.Set qualified as Set
99
import Hydra.Cardano.Api (AssetId (..), AssetName, Coin (..), PolicyAssets (..), PolicyId, Quantity (..), UTxO, selectLovelace, txOutValue, valueToPolicyAssets)
1010
import Hydra.Tx.Deposit (capUTxO, pickTokensToDeposit, splitTokens)
1111
import Test.Hydra.Tx.Fixture (testPolicyId)
12-
import Test.Hydra.Tx.Gen (genUTxOSized)
13-
import Test.QuickCheck (Property, chooseInteger, counterexample, (===), (==>))
12+
import Test.Hydra.Tx.Gen (genUTxOSized, genUTxOWithAssetsSized)
13+
import Test.QuickCheck (Property, chooseInteger, counterexample, cover, elements, forAll, frequency, listOf, oneof, property, (===), (==>))
1414

1515
spec :: Spec
1616
spec =
@@ -171,6 +171,10 @@ spec =
171171
let (valid, invalid) = splitTokens testUTxO tokens
172172
valid `shouldBe` mempty -- All assets in policy must be valid for policy to be valid
173173
invalid `shouldBe` tokens
174+
it "splits multiassets correctly" $
175+
forAll (genUTxOWithAssetsSized 5) $ \utxo ->
176+
forAll (prepareAssetMap utxo) $ \assets ->
177+
property $ propSplitMultiAssetCorrectly utxo assets
174178

175179
describe "property tests" $ do
176180
prop "preserves all input tokens (completeness)" propPreservesAllTokens
@@ -412,3 +416,58 @@ propMonotonicUTxOAdditions utxo1 utxo2 specifiedTokens =
412416
in valid1Policies `Set.isSubsetOf` validCombinedPolicies
413417
& counterexample ("Valid policies in UTxO1: " <> show valid1Policies)
414418
& counterexample ("Valid policies in combined UTxO: " <> show validCombinedPolicies)
419+
420+
propSplitMultiAssetCorrectly :: UTxO -> Map PolicyId PolicyAssets -> Property
421+
propSplitMultiAssetCorrectly utxo specifiedTokens =
422+
let toDeposit = pickTokensToDeposit utxo specifiedTokens
423+
utxoValue = UTxO.totalValue utxo
424+
utxoPolicyAssets = valueToPolicyAssets utxoValue
425+
depositAssets = valueToPolicyAssets $ UTxO.totalValue toDeposit
426+
in all (checkValidTokenInUTxO utxoPolicyAssets) (Map.toList depositAssets)
427+
& cover 10 (containsPolicies utxoPolicyAssets specifiedTokens) "PolicyId's are completely present in the UTxO"
428+
& cover 10 (containsAssets utxoPolicyAssets specifiedTokens) "Assets are completely present in the UTxO"
429+
& cover 1 (Map.null specifiedTokens) "Empty Assets"
430+
& cover 1 (Map.size specifiedTokens > 5) "Assets size > 5"
431+
& counterexample ("Valid tokens: " <> show toDeposit)
432+
& counterexample ("UTxO policy assets: " <> show utxoPolicyAssets)
433+
where
434+
containsPolicies :: Map PolicyId PolicyAssets -> Map PolicyId PolicyAssets -> Bool
435+
containsPolicies utxoAssets depositAssets = sort (Map.keys utxoAssets) == sort (Map.keys depositAssets)
436+
437+
containsAssets :: Map PolicyId PolicyAssets -> Map PolicyId PolicyAssets -> Bool
438+
containsAssets utxoAssets depositAssets =
439+
let deposits = Map.elems depositAssets
440+
in all (`elem` deposits) (Map.elems utxoAssets)
441+
442+
checkValidTokenInUTxO :: Map PolicyId PolicyAssets -> (PolicyId, PolicyAssets) -> Bool
443+
checkValidTokenInUTxO utxoAssets (policyId, PolicyAssets requiredAssets) =
444+
case Map.lookup policyId utxoAssets of
445+
Nothing -> False
446+
Just (PolicyAssets availableAssets) ->
447+
all
448+
( \(assetName, requiredQty) ->
449+
case Map.lookup assetName availableAssets of
450+
Nothing -> False
451+
Just availableQty -> availableQty >= requiredQty
452+
)
453+
(Map.toList requiredAssets)
454+
455+
prepareAssetMap :: UTxO -> Gen (Map PolicyId PolicyAssets)
456+
prepareAssetMap utxo = do
457+
let utxoAssets = valueToPolicyAssets $ UTxO.totalValue utxo
458+
n <- elements [1 .. Map.size utxoAssets]
459+
frequency [(1, randomAssets n utxoAssets), (8, addRandomAssets), (1, pure utxoAssets)]
460+
where
461+
addRandomAssets :: Gen (Map PolicyId PolicyAssets)
462+
addRandomAssets = oneof [addRandomAsset, foldr Map.union mempty <$> listOf addRandomAsset]
463+
464+
addRandomAsset :: Gen (Map PolicyId PolicyAssets)
465+
addRandomAsset = do
466+
policy <- arbitrary
467+
Map.singleton policy <$> arbitrary
468+
469+
randomAssets :: Int -> Map PolicyId PolicyAssets -> Gen (Map PolicyId PolicyAssets)
470+
randomAssets n assets =
471+
let assets' = Map.toList assets
472+
(x, y) = splitAt n assets'
473+
in oneof [pure $ Map.fromList x, pure $ Map.fromList y, pure $ Map.fromList $ drop n assets', pure $ Map.fromList $ take n assets']

hydra-tx/testlib/Test/Hydra/Tx/Gen.hs

Lines changed: 49 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -57,42 +57,48 @@ genTxOut =
5757
, notMultiAsset . fromLedgerTxOut <$> arbitrary
5858
]
5959

60-
notMultiAsset :: TxOut ctx -> TxOut ctx
61-
notMultiAsset =
62-
modifyTxOutValue (lovelaceToValue . selectLovelace)
63-
64-
notByronAddress :: TxOut ctx -> Bool
65-
notByronAddress (TxOut addr _ _ _) = case addr of
66-
ByronAddressInEra{} -> False
67-
_ -> True
68-
69-
realisticAda :: TxOut ctx -> Gen (TxOut ctx)
70-
realisticAda o = sized $ \n -> do
71-
let maxSupply = 45_000_000_000_000_000
72-
realistic = Coin $ maxSupply `div` fromIntegral (max n 1)
73-
makeRealistic v =
74-
let MaryValue c ma = toLedgerValue v
75-
in fromLedgerValue (MaryValue (min c realistic) ma)
76-
pure $
77-
modifyTxOutValue makeRealistic o
78-
79-
ensureSomeAda :: TxOut CtxUTxO -> TxOut ctx
80-
ensureSomeAda =
81-
fromLedgerTxOut . ensureMinCoinTxOut pparams . toLedgerTxOut
82-
83-
noStakeRefPtr :: TxOut ctx -> TxOut ctx
84-
noStakeRefPtr out@(TxOut addr val dat refScript) = case addr of
85-
ShelleyAddressInEra (ShelleyAddress _ cre sr) ->
86-
case sr of
87-
Ledger.StakeRefPtr _ ->
88-
TxOut (ShelleyAddressInEra (ShelleyAddress Ledger.Testnet cre Ledger.StakeRefNull)) val dat refScript
89-
_ ->
90-
TxOut (ShelleyAddressInEra (ShelleyAddress Ledger.Testnet cre sr)) val dat refScript
91-
_ -> out
92-
93-
noRefScripts :: TxOut ctx -> TxOut ctx
94-
noRefScripts out =
95-
out{txOutReferenceScript = ReferenceScriptNone}
60+
notMultiAsset :: TxOut ctx -> TxOut ctx
61+
notMultiAsset =
62+
modifyTxOutValue (lovelaceToValue . selectLovelace)
63+
64+
notByronAddress :: TxOut ctx -> Bool
65+
notByronAddress (TxOut addr _ _ _) = case addr of
66+
ByronAddressInEra{} -> False
67+
_ -> True
68+
69+
realisticAda :: TxOut ctx -> Gen (TxOut ctx)
70+
realisticAda o = sized $ \n -> do
71+
let maxSupply = 45_000_000_000_000_000
72+
realistic = Coin $ maxSupply `div` fromIntegral (max n 1)
73+
makeRealistic v =
74+
let MaryValue c ma = toLedgerValue v
75+
in fromLedgerValue (MaryValue (min c realistic) ma)
76+
pure $
77+
modifyTxOutValue makeRealistic o
78+
79+
ensureSomeAda :: TxOut CtxUTxO -> TxOut ctx
80+
ensureSomeAda =
81+
fromLedgerTxOut . ensureMinCoinTxOut pparams . toLedgerTxOut
82+
83+
noStakeRefPtr :: TxOut ctx -> TxOut ctx
84+
noStakeRefPtr out@(TxOut addr val dat refScript) = case addr of
85+
ShelleyAddressInEra (ShelleyAddress _ cre sr) ->
86+
case sr of
87+
Ledger.StakeRefPtr _ ->
88+
TxOut (ShelleyAddressInEra (ShelleyAddress Ledger.Testnet cre Ledger.StakeRefNull)) val dat refScript
89+
_ ->
90+
TxOut (ShelleyAddressInEra (ShelleyAddress Ledger.Testnet cre sr)) val dat refScript
91+
_ -> out
92+
93+
noRefScripts :: TxOut ctx -> TxOut ctx
94+
noRefScripts out =
95+
out{txOutReferenceScript = ReferenceScriptNone}
96+
97+
genTxOutWithAssets :: Gen (TxOut ctx)
98+
genTxOutWithAssets =
99+
((fromLedgerTxOut <$> arbitrary) `suchThat` notByronAddress)
100+
>>= realisticAda
101+
<&> ensureSomeAda . noRefScripts . noStakeRefPtr
96102

97103
-- | Generate a 'TxOut' with a byron address. This is usually not supported by
98104
-- Hydra or Plutus.
@@ -138,6 +144,13 @@ shrinkUTxO = shrinkMapBy (UTxO . fromList) UTxO.toList (shrinkList shrinkOne)
138144
genUTxO :: Gen UTxO
139145
genUTxO = sized genUTxOSized
140146

147+
genUTxOWithAssetsSized :: Int -> Gen UTxO
148+
genUTxOWithAssetsSized numUTxO =
149+
fold <$> vectorOf numUTxO gen
150+
where
151+
gen :: Gen UTxO
152+
gen = UTxO.singleton <$> arbitrary <*> genTxOutWithAssets
153+
141154
-- | Generate a 'Conway' era 'UTxO' with given number of outputs. See also
142155
-- 'genTxOut'.
143156
genUTxOSized :: Int -> Gen UTxO

0 commit comments

Comments
 (0)