@@ -1011,6 +1011,7 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
10111011 switch (inv.type )
10121012 {
10131013 case MSG_TX:
1014+ case MSG_DSTX:
10141015 case MSG_LEGACY_TXLOCK_REQUEST: // we treat legacy IX messages as TX messages
10151016 {
10161017 assert (recentRejects);
@@ -1034,7 +1035,17 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
10341035 // and re-request the locked transaction (which did not make it into the mempool
10351036 // previously due to txn-mempool-conflict rule). This means that we must ignore
10361037 // recentRejects filter for such locked txes here.
1037- return (recentRejects->contains (inv.hash ) && !llmq::quorumInstantSendManager->IsLocked (inv.hash )) ||
1038+ // We also ignore recentRejects filter for DSTX-es because a malicious peer might
1039+ // relay a valid DSTX as a regular TX first which would skip all the specific checks
1040+ // but would cause such tx to be rejected by ATMP due to 0 fee. Ignoring it here
1041+ // should let DSTX to be propagated by honest peer later. Note, that a malicious
1042+ // masternode would not be able to exploit this to spam the network with specially
1043+ // crafted invalid DSTX-es and potentially cause high load cheaply, because
1044+ // corresponding checks in ProcessMessage won't let it to send DSTX-es too often.
1045+ bool fIgnoreRecentRejects = llmq::quorumInstantSendManager->IsLocked (inv.hash ) || inv.type == MSG_DSTX;
1046+
1047+ return (!fIgnoreRecentRejects && recentRejects->contains (inv.hash )) ||
1048+ (inv.type == MSG_DSTX && static_cast <bool >(CPrivateSend::GetDSTX (inv.hash ))) ||
10381049 mempool.exists (inv.hash ) ||
10391050 pcoinsTip->HaveCoinInCache (COutPoint (inv.hash , 0 )) || // Best effort: only try output 0 and 1
10401051 pcoinsTip->HaveCoinInCache (COutPoint (inv.hash , 1 )) ||
@@ -1060,10 +1071,6 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
10601071 return sporkManager.GetSporkByHash (inv.hash , spork);
10611072 }
10621073
1063- case MSG_DSTX: {
1064- return static_cast <bool >(CPrivateSend::GetDSTX (inv.hash ));
1065- }
1066-
10671074 case MSG_GOVERNANCE_OBJECT:
10681075 case MSG_GOVERNANCE_OBJECT_VOTE:
10691076 return ! governance.ConfirmInventoryRequest (inv);
@@ -1274,17 +1281,29 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
12741281
12751282 // Send stream from relay memory
12761283 bool push = false ;
1277- if (inv.type == MSG_TX) {
1284+ if (inv.type == MSG_TX || inv.type == MSG_DSTX) {
1285+ CPrivateSendBroadcastTx dstx;
1286+ if (inv.type == MSG_DSTX) {
1287+ dstx = CPrivateSend::GetDSTX (inv.hash );
1288+ }
12781289 auto mi = mapRelay.find (inv.hash );
12791290 if (mi != mapRelay.end ()) {
1280- connman->PushMessage (pfrom, msgMaker.Make (NetMsgType::TX, *mi->second ));
1291+ if (dstx) {
1292+ connman->PushMessage (pfrom, msgMaker.Make (NetMsgType::DSTX, dstx));
1293+ } else {
1294+ connman->PushMessage (pfrom, msgMaker.Make (NetMsgType::TX, *mi->second ));
1295+ }
12811296 push = true ;
12821297 } else if (pfrom->timeLastMempoolReq ) {
12831298 auto txinfo = mempool.info (inv.hash );
12841299 // To protect privacy, do not answer getdata using the mempool when
12851300 // that TX couldn't have been INVed in reply to a MEMPOOL request.
12861301 if (txinfo.tx && txinfo.nTime <= pfrom->timeLastMempoolReq ) {
1287- connman->PushMessage (pfrom, msgMaker.Make (NetMsgType::TX, *txinfo.tx ));
1302+ if (dstx) {
1303+ connman->PushMessage (pfrom, msgMaker.Make (NetMsgType::DSTX, dstx));
1304+ } else {
1305+ connman->PushMessage (pfrom, msgMaker.Make (NetMsgType::TX, *txinfo.tx ));
1306+ }
12881307 push = true ;
12891308 }
12901309 }
@@ -1298,14 +1317,6 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
12981317 }
12991318 }
13001319
1301- if (!push && inv.type == MSG_DSTX) {
1302- CPrivateSendBroadcastTx dstx = CPrivateSend::GetDSTX (inv.hash );
1303- if (dstx) {
1304- connman->PushMessage (pfrom, msgMaker.Make (NetMsgType::DSTX, dstx));
1305- push = true ;
1306- }
1307- }
1308-
13091320 if (!push && inv.type == MSG_GOVERNANCE_OBJECT) {
13101321 LogPrint (BCLog::NET, " ProcessGetData -- MSG_GOVERNANCE_OBJECT: inv = %s\n " , inv.ToString ());
13111322 CDataStream ss (SER_NETWORK, pfrom->GetSendVersion ());
@@ -2452,7 +2463,19 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
24522463 return true ; // not an error
24532464 }
24542465
2455- auto dmn = deterministicMNManager->GetListAtChainTip ().GetMNByCollateral (dstx.masternodeOutpoint );
2466+ const CBlockIndex* pindex{nullptr };
2467+ CDeterministicMNCPtr dmn{nullptr };
2468+ {
2469+ LOCK (cs_main);
2470+ pindex = chainActive.Tip ();
2471+ }
2472+ // It could be that a MN is no longer in the list but its DSTX is not yet mined.
2473+ // Try to find a MN up to 24 blocks deep to make sure such dstx-es are relayed and processed correctly.
2474+ for (int i = 0 ; i < 24 && pindex; ++i) {
2475+ dmn = deterministicMNManager->GetListForBlock (pindex).GetMNByCollateral (dstx.masternodeOutpoint );
2476+ if (dmn) break ;
2477+ pindex = pindex->pprev ;
2478+ }
24562479 if (!dmn) {
24572480 LogPrint (BCLog::PRIVATESEND, " DSTX -- Can't find masternode %s to verify %s\n " , dstx.masternodeOutpoint .ToStringShort (), hashTx.ToString ());
24582481 return false ;
@@ -2523,6 +2546,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
25232546 CInv _inv (MSG_TX, txin.prevout .hash );
25242547 pfrom->AddInventoryKnown (_inv);
25252548 if (!AlreadyHave (_inv)) pfrom->AskFor (_inv);
2549+ // We don't know if the previous tx was a regular or a mixing one, try both
2550+ CInv _inv2 (MSG_DSTX, txin.prevout .hash );
2551+ pfrom->AddInventoryKnown (_inv2);
2552+ if (!AlreadyHave (_inv2)) pfrom->AskFor (_inv2);
25262553 }
25272554 AddOrphanTx (ptx, pfrom->GetId ());
25282555
@@ -3785,7 +3812,11 @@ bool PeerLogicValidation::SendMessages(CNode* pto, std::atomic<bool>& interruptM
37853812
37863813 for (const auto & txinfo : vtxinfo) {
37873814 const uint256& hash = txinfo.tx ->GetHash ();
3788- CInv inv (MSG_TX, hash);
3815+ int nInvType = MSG_TX;
3816+ if (CPrivateSend::GetDSTX (hash)) {
3817+ nInvType = MSG_DSTX;
3818+ }
3819+ CInv inv (nInvType, hash);
37893820 pto->setInventoryTxToSend .erase (hash);
37903821 if (pto->pfilter ) {
37913822 if (!pto->pfilter ->IsRelevantAndUpdate (*txinfo.tx )) continue ;
@@ -3851,7 +3882,11 @@ bool PeerLogicValidation::SendMessages(CNode* pto, std::atomic<bool>& interruptM
38513882 }
38523883 if (pto->pfilter && !pto->pfilter ->IsRelevantAndUpdate (*txinfo.tx )) continue ;
38533884 // Send
3854- vInv.push_back (CInv (MSG_TX, hash));
3885+ int nInvType = MSG_TX;
3886+ if (CPrivateSend::GetDSTX (hash)) {
3887+ nInvType = MSG_DSTX;
3888+ }
3889+ vInv.push_back (CInv (nInvType, hash));
38553890 nRelayedTransactions++;
38563891 {
38573892 // Expire old relay messages
0 commit comments