@@ -47,6 +47,7 @@ import Cardano.Ledger.Huddle.Gen qualified as Gen
4747import Codec.CBOR.Cuddle.CDDL.CBORGenerator (CBORGen , liftAntiGen , withAntiGen )
4848import Codec.CBOR.Term (Term (.. ))
4949import Control.Monad (zipWithM )
50+ import Data.Maybe (mapMaybe )
5051import Data.Proxy (Proxy (.. ))
5152import Data.Text ()
5253import Data.Text qualified as T
@@ -101,10 +102,30 @@ subTransactionsRule pname p =
101102 withCBORGen generate $
102103 pname =.= huddleRule1 @ " nonempty_oset" p (huddleRule @ " sub_transaction" p)
103104 where
105+ -- The Haskell representation is @OMap TxId (Tx SubTx era)@: dedup is by
106+ -- body hash, so generated sub_transactions must have distinct bodies, not
107+ -- just distinct full @[body, witness, aux]@ tuples.
104108 generate = do
105- -- Limit the number of subtransactions generated to max 3, since they are quite large
106109 nElems <- Gen. sized $ \ sz -> choose (1 , max 1 (min sz 3 ))
107- S <$> generateMaybeTaggedSet nElems (scale (`div` 2 ) $ genRule @ " sub_transaction" @ era )
110+ let subTxGen = scale (`div` 2 ) $ genRule @ " sub_transaction" @ era
111+ txs <- uniqueByBody nElems subTxGen
112+ elemsArr <- genArrayTerm txs
113+ tagged <- Gen. arbitrary
114+ pure $ S $ if tagged then TTagged 258 elemsArr else elemsArr
115+ uniqueByBody :: Int -> CBORGen Term -> CBORGen [Term ]
116+ uniqueByBody n gen = loop [] n
117+ where
118+ triesPerElement = 20 :: Int
119+ loop acc 0 = pure acc
120+ loop acc k = attempt triesPerElement acc k
121+ attempt 0 acc _ = pure acc
122+ attempt tries acc k = do
123+ tx <- gen
124+ case bodyOf tx of
125+ Just b | b `notElem` mapMaybe bodyOf acc -> loop (tx : acc) (k - 1 )
126+ _ -> attempt (tries - 1 ) acc k
127+ bodyOf (TList (b : _)) = Just b
128+ bodyOf _ = Nothing
108129
109130subTransactionRule ::
110131 forall era .
0 commit comments