Skip to content

Commit cddef43

Browse files
committed
fixup: all advertise should be notified
Not only idle peers but all peers should be notified when something changes for one of their TX.s
1 parent 86f75fc commit cddef43

4 files changed

Lines changed: 108 additions & 38 deletions

File tree

ouroboros-network/lib/Ouroboros/Network/TxSubmission/Inbound/V2/Registry.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ withPeer policy TxSubmissionMempoolReader { mempoolGetSnapshot } sharedStateVar
203203

204204
unregisterPeer :: Time -> SharedTxState peeraddr txid -> SharedTxState peeraddr txid
205205
unregisterPeer now st@SharedTxState { sharedPeers, sharedTxTable, sharedRetainedTxs, sharedTxIdToKey, sharedKeyToTxId, sharedGeneration } =
206-
bumpIdlePeerGenerations peersToWake $ st {
206+
bumpPeerGenerations peersToWake $ st {
207207
sharedPeers = sharedPeers',
208208
sharedTxTable = sharedTxTable',
209209
sharedRetainedTxs = sharedRetainedTxs,
@@ -544,7 +544,7 @@ updatePeerPhase peeraddr peerPhaseNew
544544
let sharedPeerState' = sharedPeerState { sharedPeerPhase = peerPhaseNew }
545545
st' = st { sharedPeers = Map.insert peeraddr sharedPeerState' sharedPeers
546546
, sharedGeneration = sharedGeneration + 1 }
547-
in bumpIdlePeerGenerations (phaseWakePeers peerPhaseOld) st'
547+
in bumpPeerGenerations (phaseWakePeers peerPhaseOld) st'
548548
else st
549549
_ -> st -- TODO error?
550550
where

ouroboros-network/lib/Ouroboros/Network/TxSubmission/Inbound/V2/State.hs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ applyRequestTxsChoice ctx txsToRequest txsToRequestSize txTable =
220220
peerRequestedTxsSize = peerRequestedTxsSize (pacPeerState ctx) + txsToRequestSize
221221
}
222222
sharedState'' =
223-
bumpIdlePeerGenerations
223+
bumpPeerGenerations
224224
(advertisingPeersForTxKeysExcept (pacPeerAddr ctx) requestedKeys (pacSharedState ctx))
225225
((pacSharedState ctx) {
226226
sharedTxTable = txTable,
@@ -952,7 +952,7 @@ handleReceivedTxs mempoolHasTx now policy peeraddr txs peerState sharedState =
952952

953953
-- Flag peers that may now have work available after processing txs.
954954
sharedState' =
955-
bumpIdlePeerGenerations
955+
bumpPeerGenerations
956956
(Set.union receivedWakePeers omittedWakePeers)
957957
sharedState''
958958

@@ -1120,7 +1120,7 @@ handleSubmittedTxs now policy peeraddr acceptedTxs rejectedTxs peerState sharedS
11201120
IntSet.foldl' updateRejected (sharedStateAfterRejectedPeer, Set.empty) rejectedKeys
11211121

11221122
sharedState' =
1123-
bumpIdlePeerGenerations
1123+
bumpPeerGenerations
11241124
(Set.union acceptedAdvertisers rejectedWakePeers)
11251125
sharedState''
11261126

@@ -1274,7 +1274,7 @@ handleReceivedTxIds mempoolHasTx now policy peeraddr requestedTxIds txidsAndSize
12741274

12751275
sharedState''
12761276
| sharedChanged || peerAdvertisedKeys' /= peerAdvertisedKeys0 =
1277-
bumpIdlePeerGenerations peersToWake $
1277+
bumpPeerGenerations peersToWake $
12781278
sharedStateHandled {
12791279
sharedPeers =
12801280
if peerAdvertisedKeys' == peerAdvertisedKeys0

ouroboros-network/lib/Ouroboros/Network/TxSubmission/Inbound/V2/Types.hs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ module Ouroboros.Network.TxSubmission.Inbound.V2.Types
5959
, PeerTxLocalState (..)
6060
, SharedPeerState (..)
6161
, peerGenerationOf
62-
, bumpIdlePeerGenerations
62+
, bumpPeerGenerations
6363
-- TxKey with helper functions
6464
, TxKey (..)
6565
, lookupTxKey
@@ -565,22 +565,22 @@ peerGenerationOf peeraddr SharedTxState { sharedPeers } =
565565
Just SharedPeerState { sharedPeerGeneration } -> sharedPeerGeneration
566566
Nothing -> error "TxSubmission.V2.peerGenerationOf: missing peer"
567567

568-
bumpIdlePeerGenerations :: Ord peeraddr
569-
=> Set.Set peeraddr
570-
-> SharedTxState peeraddr txid
571-
-> SharedTxState peeraddr txid
572-
bumpIdlePeerGenerations peersToWake st@SharedTxState { sharedPeers } =
568+
bumpPeerGenerations :: Ord peeraddr
569+
=> Set.Set peeraddr
570+
-> SharedTxState peeraddr txid
571+
-> SharedTxState peeraddr txid
572+
bumpPeerGenerations peersToWake st@SharedTxState { sharedPeers } =
573573
st {
574574
sharedPeers = foldl' bumpOne sharedPeers (Set.toList peersToWake)
575575
}
576576
where
577577
bumpOne peersMap peeraddr =
578-
Map.adjust bumpIdlePeer peeraddr peersMap
579-
where
580-
bumpIdlePeer sharedPeerState@SharedPeerState { sharedPeerPhase, sharedPeerGeneration }
581-
| sharedPeerPhase == PeerIdle =
582-
sharedPeerState { sharedPeerGeneration = sharedPeerGeneration + 1 }
583-
| otherwise = sharedPeerState
578+
Map.adjust
579+
(\sharedPeerState ->
580+
sharedPeerState
581+
{ sharedPeerGeneration = sharedPeerGeneration sharedPeerState + 1 })
582+
peeraddr
583+
peersMap
584584

585585
lookupTxKey :: HasRawTxId txid
586586
=> txid

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

Lines changed: 90 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ tests =
6868
, testProperty "handleSubmittedTxs retains accepted and drops rejected" prop_handleSubmittedTxs_retainsAcceptedAndDropsRejected
6969
, testProperty "nextPeerAction prioritises submitting buffered owned txs" prop_nextPeerAction_prioritisesSubmit
7070
, testProperty "nextPeerAction claims claimable tx for best idle advertiser" prop_nextPeerAction_claimsClaimableTx
71+
, testProperty "nextPeerAction claims a released tx from another advertiser" prop_nextPeerAction_claimsRejectedTxFromOtherAdvertiser
7172
, testCaseSteps "nextPeerAction claims a tx once the score delay threshold has elapsed" unit_nextPeerAction_claimsAtScoreDelayThreshold
7273
, testProperty "nextPeerAction steals expired lease for best idle advertiser" prop_nextPeerAction_claimsExpiredLease
7374
, testProperty "nextPeerAction requests an oversized first tx within the soft budget" prop_nextPeerAction_requestsOversizedFirstTx
@@ -85,7 +86,7 @@ tests =
8586
, testProperty "nextPeerAction keeps retained txs before expiry" prop_nextPeerAction_keepsRetained
8687
, testProperty "PeerDoNothing waits for the earliest shared expiry" prop_nextPeerAction_earliestWakeDelay
8788
, testProperty "PeerDoNothing uses the current peer generation" prop_nextPeerAction_returnsPeerGeneration
88-
, testProperty "handleSubmittedTxs bumps idle advertiser generations" prop_handleSubmittedTxs_bumpsIdleAdvertisers
89+
, testProperty "handleSubmittedTxs bumps advertiser generations" prop_handleSubmittedTxs_bumpsAdvertisers
8990
, testCaseSteps "advertisingPeersForTxExcept scans the authoritative peer key sets" unit_advertisingPeersForTxExcept_scansAuthoritativePeerSets
9091
, testCaseSteps "removeAdvertisingPeersForResolvedTx clears all advertising peers for a resolved key" unit_removeAdvertisingPeersForResolvedTx_clearsAllAdvertisingPeers
9192
, testCaseSteps "updatePeerPhase only wakes the peer becoming idle" unit_updatePeerPhase_wakesOnlyBecomingIdlePeer
@@ -746,19 +747,16 @@ prop_handleReceivedTxIds
746747
case otherPeerOpt of
747748
Just op | not (null mempoolResolveActiveGroup) ->
748749
let original = lookupPeerOrFail op sharedStateWithRetained
749-
post = lookupPeerOrFail op sharedState'
750-
bumpIfIdle g
751-
| sharedPeerPhase original == PeerIdle = g + 1
752-
| otherwise = g
753-
in conjoin
750+
post = lookupPeerOrFail op sharedState' in
751+
conjoin
754752
[ counterexample "other peer's advertised keys not restored"
755753
(sharedPeerAdvertisedTxKeys post
756754
=== sharedPeerAdvertisedTxKeys original)
757755
, counterexample "other peer's phase changed unexpectedly"
758756
(sharedPeerPhase post === sharedPeerPhase original)
759757
, counterexample "other peer's generation bump mismatch"
760758
(sharedPeerGeneration post
761-
=== bumpIfIdle (sharedPeerGeneration original))
759+
=== sharedPeerGeneration original + 1)
762760
]
763761
_ -> property True
764762

@@ -1214,25 +1212,22 @@ prop_handleReceivedTxs
12141212
]
12151213

12161214
-- otherPeer is added to wakePeers when any surviving omitted entry
1217-
-- exists; its generation is bumped iff it was PeerIdle. LateMempool
1218-
-- is set up single-advertiser so it does not wake otherPeer here.
1215+
-- exists; its generation is bumped unconditionally. LateMempool is
1216+
-- set up single-advertiser so it does not wake otherPeer here.
12191217
checkOtherPeerState =
12201218
case otherPeerOpt of
12211219
Just op | not (IntSet.null omittedSurvivingKeys) ->
12221220
let original = lookupPeerOrFail op sharedStateBase
1223-
post = lookupPeerOrFail op sharedState'
1224-
bumpIfIdle g
1225-
| sharedPeerPhase original == PeerIdle = g + 1
1226-
| otherwise = g
1227-
in conjoin
1221+
post = lookupPeerOrFail op sharedState' in
1222+
conjoin
12281223
[ counterexample "other peer's advertised keys changed"
12291224
(sharedPeerAdvertisedTxKeys post
12301225
=== sharedPeerAdvertisedTxKeys original)
12311226
, counterexample "other peer's phase changed"
12321227
(sharedPeerPhase post === sharedPeerPhase original)
12331228
, counterexample "other peer's generation bump mismatch"
12341229
(sharedPeerGeneration post
1235-
=== bumpIfIdle (sharedPeerGeneration original))
1230+
=== sharedPeerGeneration original + 1)
12361231
]
12371232
_ -> property True
12381233

@@ -1520,6 +1515,80 @@ unit_nextPeerAction_claimsAtScoreDelayThreshold step = do
15201515
(peerAction, peerState', sharedState') =
15211516
nextPeerAction now defaultTxDecisionPolicy peeraddr peerState0 sharedState0
15221517

1518+
-- | A peer whose submission attempt has been cleared (e.g. after a
1519+
-- mempool rejection) must not prevent another advertiser from claiming
1520+
-- the same tx. After one peer's attempt is cleared and its lease
1521+
-- released back to 'TxClaimable', any other peer that still advertises
1522+
-- the tx should be able to claim it on its next 'nextPeerAction' pass.
1523+
--
1524+
-- Exercises the cross-peer retry invariant in the 'txSelectable' /
1525+
-- 'nextPeerAction' path: once no peer has an outstanding attempt on a
1526+
-- tx and its lease is claimable, a still-advertising peer is eligible
1527+
-- to re-claim.
1528+
prop_nextPeerAction_claimsRejectedTxFromOtherAdvertiser :: Property
1529+
prop_nextPeerAction_claimsRejectedTxFromOtherAdvertiser =
1530+
counterexample "peerB should be able to claim the released tx"
1531+
$ case action of
1532+
PeerRequestTxs keys ->
1533+
counterexample ("keys requested: " ++ show keys)
1534+
$ TxKey txKeyInt `elem` keys
1535+
_ ->
1536+
counterexample ("peerB action: " ++ show action) False
1537+
where
1538+
peerA, peerB :: PeerAddr
1539+
peerA = 1
1540+
peerB = 2
1541+
1542+
txid :: TxId
1543+
txid = 4
1544+
1545+
txKeyInt :: Int
1546+
txKeyInt = 0
1547+
1548+
txSize :: SizeInBytes
1549+
txSize = 100
1550+
1551+
txBody :: Tx TxId
1552+
txBody = mkTx txid txSize
1553+
1554+
sharedState0 :: SharedTxState PeerAddr TxId
1555+
sharedState0 =
1556+
ensurePeerAdvertisesTxKeys peerA [TxKey txKeyInt]
1557+
$ ensurePeerAdvertisesTxKeys peerB [TxKey txKeyInt]
1558+
$ emptySharedTxState
1559+
{ sharedTxIdToKey = Map.singleton (getRawTxId txid) (TxKey txKeyInt)
1560+
, sharedKeyToTxId = IntMap.singleton txKeyInt txid
1561+
, sharedNextTxKey = txKeyInt + 1
1562+
, sharedTxTable = IntMap.singleton txKeyInt TxEntry
1563+
{ txLease = TxLeased peerA (addTime 1 now)
1564+
, txAdvertiserCount = 2
1565+
, txAttempts = Map.singleton peerA TxBuffered
1566+
}
1567+
}
1568+
1569+
peerAState :: PeerTxLocalState (Tx TxId)
1570+
peerAState = emptyPeerTxLocalState
1571+
{ peerUnacknowledgedTxIds = StrictSeq.singleton (TxKey txKeyInt)
1572+
, peerDownloadedTxs = IntMap.singleton txKeyInt txBody
1573+
}
1574+
1575+
peerBState :: PeerTxLocalState (Tx TxId)
1576+
peerBState = emptyPeerTxLocalState
1577+
{ peerUnacknowledgedTxIds = StrictSeq.singleton (TxKey txKeyInt)
1578+
, peerAvailableTxIds = IntMap.singleton txKeyInt txSize
1579+
}
1580+
1581+
(_peerAStateAfter, sharedStateAfterRejection) =
1582+
handleSubmittedTxs now defaultTxDecisionPolicy peerA
1583+
[]
1584+
[TxKey txKeyInt]
1585+
peerAState
1586+
sharedState0
1587+
1588+
(action, _peerBStateAfter, _sharedStateFinal) =
1589+
nextPeerAction now defaultTxDecisionPolicy peerB peerBState
1590+
sharedStateAfterRejection
1591+
15231592
-- Verifies that nextPeerAction can steal an expired lease for the best idle
15241593
-- advertiser and request that tx.
15251594
prop_nextPeerAction_claimsExpiredLease
@@ -2324,20 +2393,21 @@ prop_nextPeerAction_returnsPeerGeneration (Positive peeraddr) =
23242393
peerState0 = emptyPeerTxLocalState { peerRequestedTxIds = maxNumTxIdsToRequest defaultTxDecisionPolicy }
23252394
(peerAction, _, _) = nextPeerAction now defaultTxDecisionPolicy peeraddr peerState0 sharedState0
23262395

2327-
-- Verifies that handleSubmittedTxs bumps idle advertisers while leaving
2328-
-- submitting and waiting advertisers unchanged.
2329-
prop_handleSubmittedTxs_bumpsIdleAdvertisers
2396+
-- Verifies that handleSubmittedTxs bumps the generation of every other
2397+
-- advertiser of the resolved tx, regardless of phase, while leaving the
2398+
-- submitting peer's own generation unchanged.
2399+
prop_handleSubmittedTxs_bumpsAdvertisers
23302400
:: Positive Int
23312401
-> Positive Int
23322402
-> Positive Int
23332403
-> TxId
23342404
-> Positive Int
23352405
-> Property
2336-
prop_handleSubmittedTxs_bumpsIdleAdvertisers (Positive owner0) (Positive peerA0) (Positive peerB0) txid0 txSize0 =
2406+
prop_handleSubmittedTxs_bumpsAdvertisers (Positive owner0) (Positive peerA0) (Positive peerB0) txid0 txSize0 =
23372407
owner /= peerA && owner /= peerB && peerA /= peerB ==>
23382408
conjoin
23392409
[ sharedPeerGeneration (lookupPeerOrFail peerA sharedState') === 1
2340-
, sharedPeerGeneration (lookupPeerOrFail peerB sharedState') === 0
2410+
, sharedPeerGeneration (lookupPeerOrFail peerB sharedState') === 1
23412411
, sharedPeerGeneration (lookupPeerOrFail owner sharedState') === 0
23422412
]
23432413
where

0 commit comments

Comments
 (0)