Skip to content

Commit 47f2ed7

Browse files
committed
tx-test: fix test case counting of valid and invalid txs
In case of duplicate txids a second invalid txids could be counted as valid because it was tracked with txid along
1 parent e92d89a commit 47f2ed7

4 files changed

Lines changed: 121 additions & 12 deletions

File tree

ouroboros-network/ouroboros-network.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,7 @@ library ouroboros-network-tests-lib
970970
Test.Ouroboros.Network.TxSubmission.AppV1
971971
Test.Ouroboros.Network.TxSubmission.AppV2
972972
Test.Ouroboros.Network.TxSubmission.Mempool.Simple
973+
Test.Ouroboros.Network.TxSubmission.MempoolWriter
973974
Test.Ouroboros.Network.TxSubmission.TxLogic
974975
Test.Ouroboros.Network.TxSubmission.Types
975976

ouroboros-network/tests/lib/Test/Ouroboros/Network/TxSubmission.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ module Test.Ouroboros.Network.TxSubmission (tests) where
22

33
import Test.Ouroboros.Network.TxSubmission.AppV1 qualified as AppV1
44
import Test.Ouroboros.Network.TxSubmission.AppV2 qualified as AppV2
5+
import Test.Ouroboros.Network.TxSubmission.MempoolWriter qualified as MempoolWriter
56
import Test.Ouroboros.Network.TxSubmission.TxLogic qualified as TxLogic
67

78
import Test.Tasty (TestTree, testGroup)
89

910
tests :: TestTree
1011
tests = testGroup "Ouroboros.Network.TxSubmission"
1112
[ AppV1.tests
13+
, MempoolWriter.tests
1214
, TxLogic.tests
1315
, AppV2.tests
1416
]
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
module Test.Ouroboros.Network.TxSubmission.MempoolWriter (tests) where
2+
3+
import Control.Concurrent.Class.MonadSTM (newTVarIO, readTVarIO)
4+
import Control.Monad.IOSim (runSimOrThrow)
5+
6+
import Ouroboros.Network.Protocol.TxSubmission2.Type (SizeInBytes (..))
7+
import Ouroboros.Network.TxSubmission.Inbound.V1 (TxSubmissionMempoolWriter (..))
8+
9+
import Test.Ouroboros.Network.TxSubmission.Types
10+
11+
import Test.Tasty (TestTree, testGroup)
12+
import Test.Tasty.HUnit (testCaseSteps, (@?=))
13+
14+
15+
tests :: TestTree
16+
tests = testGroup "MempoolWriter"
17+
[ testCaseSteps "getMempoolWriter records only valid duplicates" unit_getMempoolWriter_recordsOnlyValidDuplicates
18+
]
19+
20+
21+
unit_getMempoolWriter_recordsOnlyValidDuplicates :: (String -> IO ()) -> IO ()
22+
unit_getMempoolWriter_recordsOnlyValidDuplicates step = do
23+
step "Populate the inbound mempool with one valid tx and submit one invalid duplicate plus one valid duplicate"
24+
let (accepted, rejected, duplicateTxIds) =
25+
runSimOrThrow $ do
26+
duplicateVar <- newTVarIO []
27+
mempool <- newMempool [mkTx 17 True]
28+
let writer = getMempoolWriter duplicateVar mempool
29+
result <- mempoolAddTxs writer [mkTx 17 False, mkTx 17 True]
30+
duplicates <- readTVarIO duplicateVar
31+
pure (fst result, snd result, duplicates)
32+
33+
step "Assert both submissions are rejected as duplicates but only the valid duplicate is recorded for result accounting"
34+
accepted @?= []
35+
rejected @?= [(17, DuplicateTx), (17, DuplicateTx)]
36+
duplicateTxIds @?= [17]
37+
where
38+
mkTx :: TxId -> Bool -> Tx TxId
39+
mkTx txid isValid =
40+
Tx {
41+
getTxId = txid,
42+
getTxSize = SizeInBytes 1,
43+
getTxAdvSize = SizeInBytes 1,
44+
getTxValid = isValid
45+
}

ouroboros-network/tests/lib/Test/Ouroboros/Network/TxSubmission/Types.hs

Lines changed: 73 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ module Test.Ouroboros.Network.TxSubmission.Types
1717
, readMempool
1818
, getMempoolReader
1919
, getMempoolWriter
20+
, InvalidTx (..)
2021
, maxTxSize
2122
, LargeNonEmptyList (..)
2223
, SimResults (..)
@@ -31,6 +32,7 @@ import Prelude hiding (seq)
3132
import NoThunks.Class
3233

3334
import Control.Concurrent.Class.MonadSTM
35+
import Control.Concurrent.Class.MonadSTM.Strict qualified as StrictSTM
3436
import Control.DeepSeq
3537
import Control.Exception (SomeException (..))
3638
import Control.Monad.Class.MonadAsync
@@ -48,6 +50,10 @@ import Codec.CBOR.Encoding qualified as CBOR
4850
import Codec.CBOR.Read qualified as CBOR
4951

5052
import Data.ByteString.Lazy (ByteString)
53+
import Data.Either (partitionEithers)
54+
import Data.List qualified as List
55+
import Data.Sequence qualified as Seq
56+
import Data.Set qualified as Set
5157
import Data.Typeable (Typeable)
5258
import GHC.Generics (Generic)
5359

@@ -137,18 +143,73 @@ getMempoolWriter :: forall txid m.
137143
=> TVar m [txid]
138144
-> Mempool m txid (Tx txid)
139145
-> TxSubmissionMempoolWriter txid (Tx txid) Integer m InvalidTx
140-
getMempoolWriter duplicateVar =
141-
Mempool.getWriter DuplicateTx
142-
getTxId
143-
(\_ txs -> return
144-
[ if getTxValid tx
145-
then Right tx
146-
else Left (getTxId tx, InvalidTx)
147-
| tx <- txs
148-
]
149-
)
150-
(\t -> atomically $ modifyTVar' duplicateVar
151-
(map fst (filter ((== DuplicateTx) . snd) t) <>))
146+
getMempoolWriter duplicateVar (Mempool.Mempool mempoolVar) =
147+
TxSubmissionMempoolWriter {
148+
txId = getTxId,
149+
mempoolAddTxs = \txs -> do
150+
(acceptedTxs, rejectedTxs, duplicateValidTxIds) <- atomically $ do
151+
Mempool.MempoolSeq { Mempool.mempoolSet, Mempool.mempoolSeq, Mempool.nextIdx } <-
152+
StrictSTM.readTVar mempoolVar
153+
154+
let (duplicateTxs, txsToValidate) =
155+
List.partition (\tx -> getTxId tx `Set.member` mempoolSet) txs
156+
duplicateRejectedTxs =
157+
[ (getTxId tx, DuplicateTx)
158+
| tx <- duplicateTxs
159+
]
160+
duplicateValidTxIds =
161+
[ getTxId tx
162+
| tx <- duplicateTxs
163+
, getTxValid tx
164+
]
165+
(invalidRejectedTxs, validTxs) =
166+
partitionEithers
167+
[ if getTxValid tx
168+
then Right tx
169+
else Left (getTxId tx, InvalidTx)
170+
| tx <- txsToValidate
171+
]
172+
173+
(delta, mempoolSeq', nextIdx', acceptedTxs, duplicateValidTxIds') =
174+
List.foldl'
175+
(\(set, seq, idx, accepted, duplicates) tx ->
176+
let txid = getTxId tx in
177+
if txid `Set.member` set
178+
then ( set
179+
, seq
180+
, idx
181+
, accepted
182+
, txid : duplicates
183+
)
184+
else ( Set.insert txid set
185+
, seq Seq.|> Mempool.WithIndex idx tx
186+
, succ idx
187+
, txid : accepted
188+
, duplicates
189+
)
190+
)
191+
(Set.empty, mempoolSeq, nextIdx, [], [])
192+
validTxs
193+
194+
StrictSTM.writeTVar
195+
mempoolVar
196+
Mempool.MempoolSeq {
197+
Mempool.mempoolSet = mempoolSet `Set.union` delta,
198+
Mempool.mempoolSeq = mempoolSeq',
199+
Mempool.nextIdx = nextIdx'
200+
}
201+
202+
pure
203+
( acceptedTxs
204+
, invalidRejectedTxs
205+
++ duplicateRejectedTxs
206+
++ [ (txid, DuplicateTx) | txid <- duplicateValidTxIds' ]
207+
, duplicateValidTxIds ++ duplicateValidTxIds'
208+
)
209+
210+
atomically $ modifyTVar' duplicateVar (duplicateValidTxIds <>)
211+
pure (acceptedTxs, rejectedTxs)
212+
}
152213

153214

154215
txSubmissionCodec2 :: MonadST m

0 commit comments

Comments
 (0)