Skip to content

Commit a60b646

Browse files
committed
Mempool test: generate txs larger than the entire mempool
1 parent 9b97e3d commit a60b646

File tree

2 files changed

+60
-3
lines changed

2 files changed

+60
-3
lines changed

ouroboros-consensus/ouroboros-consensus.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,7 @@ test-suite consensus-test
544544
base-deriving-via,
545545
cardano-binary,
546546
cardano-crypto-class,
547+
cardano-crypto-tests,
547548
cardano-slotting:{cardano-slotting, testlib},
548549
cborg,
549550
containers,

ouroboros-consensus/test/consensus-test/Test/Consensus/Mempool.hs

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ module Test.Consensus.Mempool (tests) where
3535
import Cardano.Binary (Encoding, toCBOR)
3636
import Cardano.Crypto.Hash
3737
import Control.Exception (assert)
38-
import Control.Monad (foldM, forM, forM_, void)
38+
import Control.Monad (foldM, forM, forM_, guard, void)
3939
import Control.Monad.Except (Except, runExcept)
4040
import Control.Monad.IOSim (runSimOrThrow)
4141
import Control.Monad.State (State, evalState, get, modify)
@@ -47,6 +47,7 @@ import Data.Map.Strict (Map)
4747
import qualified Data.Map.Strict as Map
4848
import Data.Maybe (mapMaybe)
4949
import Data.Semigroup (stimes)
50+
import Data.Set (Set)
5051
import qualified Data.Set as Set
5152
import Data.Word (Word32)
5253
import GHC.Stack (HasCallStack)
@@ -65,6 +66,7 @@ import Ouroboros.Consensus.Util (repeatedly, repeatedlyM,
6566
safeMaximumOn, (.:))
6667
import Ouroboros.Consensus.Util.Condense (condense)
6768
import Ouroboros.Consensus.Util.IOLike
69+
import Test.Crypto.Hash ()
6870
import Test.QuickCheck hiding (elements)
6971
import Test.Tasty (TestTree, testGroup)
7072
import Test.Tasty.QuickCheck (testProperty)
@@ -506,7 +508,8 @@ genValidTxs = go []
506508
(tx, ledger') <- genValidTx ledger
507509
go (tx:txs) (n - 1) ledger'
508510

509-
-- | Generate a valid transaction (but ignoring any per-tx size limits).
511+
-- | Generate a valid transaction (but ignoring any per-tx size limits, see Note
512+
-- [Transaction size limit]).
510513
genValidTx :: LedgerState TestBlock -> Gen (TestTx, LedgerState TestBlock)
511514
genValidTx ledgerState@(SimpleLedgerState MockState { mockUtxo = utxo }) = do
512515
-- Never let someone go broke, otherwise we risk concentrating all the
@@ -557,6 +560,23 @@ genInvalidTx ledgerState@(SimpleLedgerState MockState { mockUtxo = utxo }) = do
557560
tx = mkSimpleGenTx $ Tx DoNotExpire ins outs
558561
return $ assert (not (txIsValid testLedgerConfigNoSizeLimits ledgerState tx)) tx
559562

563+
-- | Generate an invalid tx that is larger than the given measure.
564+
genLargeInvalidTx :: TheMeasure -> Gen TestTx
565+
genLargeInvalidTx (IgnoringOverflow sz) = go Set.empty
566+
where
567+
go ins = case isLargeTx ins of
568+
Just tx -> pure tx
569+
Nothing -> do
570+
newTxIn <- arbitrary
571+
go (Set.insert newTxIn ins)
572+
573+
isLargeTx :: Set TxIn -> Maybe TestTx
574+
isLargeTx ins = do
575+
let outs = []
576+
tx = mkSimpleGenTx $ Tx DoNotExpire ins outs
577+
guard $ genTxSize tx > sz
578+
pure tx
579+
560580
-- | Apply a transaction to the ledger
561581
--
562582
-- We don't have blocks in this test, but transactions only. In this function
@@ -619,6 +639,21 @@ validTxs = map fst . filter snd . txs
619639
invalidTxs :: TestSetupWithTxs -> [GenTx TestBlock]
620640
invalidTxs = map fst . filter (not . snd) . txs
621641

642+
{-
643+
Note [Transaction size limit]
644+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
645+
646+
An important property of the mempool is that adding a transaction that can never
647+
fit into the mempool must not block, also see
648+
https://github.com/IntersectMBO/ouroboros-consensus/issues/1226. We test this
649+
while generating a TestSetupWithTxs by always including a transaction that is
650+
larger than the entire mempool, and setting the per-tx size limit such that just
651+
this transaction is invalid due to its size, but not impacting the validity of
652+
any other transactions. Therefore, we disable the size limit in e.g.
653+
'genValidTx' to only capture UTxO-related validity for them by using an
654+
appropriate ledger config ('testLedgerConfigNoSizeLimits').
655+
-}
656+
622657
instance Arbitrary TestSetupWithTxs where
623658
arbitrary = sized $ \n -> do
624659
nbTxs <- choose (0, n)
@@ -639,7 +674,28 @@ instance Arbitrary TestSetupWithTxs where
639674
then NoMempoolCapacityBytesOverride
640675
else MempoolCapacityBytesOverride $ mpCap <> newSize
641676
}
642-
return TestSetupWithTxs { testSetup = testSetup', txs }
677+
let mempoolCap :: TheMeasure
678+
mempoolCap = computeMempoolCapacity
679+
testLedgerConfigNoSizeLimits
680+
(TickedSimpleLedgerState ledger)
681+
(testMempoolCapOverride testSetup)
682+
683+
684+
largeInvalidTx <- genLargeInvalidTx mempoolCap
685+
let txs' = (largeInvalidTx, False) : txs
686+
-- Set the maximum tx size to the mempool capacity. This won't
687+
-- invalidate any valid tx in @txs@ as the capacity was chosen such that
688+
-- all @txs@ fit into the mempool. Also see Note [Transaction size
689+
-- limit].
690+
testSetup'' = testSetup' { testLedgerCfg =
691+
(testLedgerCfg testSetup') { simpleLedgerMockConfig =
692+
MockConfig {
693+
mockCfgMaxTxSize = Just (unIgnoringOverflow mempoolCap)
694+
}
695+
}
696+
}
697+
698+
return TestSetupWithTxs { testSetup = testSetup'', txs = txs' }
643699

644700
shrink TestSetupWithTxs { testSetup, txs } =
645701
[ TestSetupWithTxs { testSetup = testSetup', txs }

0 commit comments

Comments
 (0)