@@ -1011,6 +1011,7 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
1011
1011
switch (inv.type )
1012
1012
{
1013
1013
case MSG_TX:
1014
+ case MSG_DSTX:
1014
1015
case MSG_LEGACY_TXLOCK_REQUEST: // we treat legacy IX messages as TX messages
1015
1016
{
1016
1017
assert (recentRejects);
@@ -1034,7 +1035,17 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
1034
1035
// and re-request the locked transaction (which did not make it into the mempool
1035
1036
// previously due to txn-mempool-conflict rule). This means that we must ignore
1036
1037
// 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 ))) ||
1038
1049
mempool.exists (inv.hash ) ||
1039
1050
pcoinsTip->HaveCoinInCache (COutPoint (inv.hash , 0 )) || // Best effort: only try output 0 and 1
1040
1051
pcoinsTip->HaveCoinInCache (COutPoint (inv.hash , 1 )) ||
@@ -1060,10 +1071,6 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
1060
1071
return sporkManager.GetSporkByHash (inv.hash , spork);
1061
1072
}
1062
1073
1063
- case MSG_DSTX: {
1064
- return static_cast <bool >(CPrivateSend::GetDSTX (inv.hash ));
1065
- }
1066
-
1067
1074
case MSG_GOVERNANCE_OBJECT:
1068
1075
case MSG_GOVERNANCE_OBJECT_VOTE:
1069
1076
return ! governance.ConfirmInventoryRequest (inv);
@@ -1274,17 +1281,29 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
1274
1281
1275
1282
// Send stream from relay memory
1276
1283
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
+ }
1278
1289
auto mi = mapRelay.find (inv.hash );
1279
1290
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
+ }
1281
1296
push = true ;
1282
1297
} else if (pfrom->timeLastMempoolReq ) {
1283
1298
auto txinfo = mempool.info (inv.hash );
1284
1299
// To protect privacy, do not answer getdata using the mempool when
1285
1300
// that TX couldn't have been INVed in reply to a MEMPOOL request.
1286
1301
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
+ }
1288
1307
push = true ;
1289
1308
}
1290
1309
}
@@ -1298,14 +1317,6 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
1298
1317
}
1299
1318
}
1300
1319
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
-
1309
1320
if (!push && inv.type == MSG_GOVERNANCE_OBJECT) {
1310
1321
LogPrint (BCLog::NET, " ProcessGetData -- MSG_GOVERNANCE_OBJECT: inv = %s\n " , inv.ToString ());
1311
1322
CDataStream ss (SER_NETWORK, pfrom->GetSendVersion ());
@@ -2461,7 +2472,19 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
2461
2472
return true ; // not an error
2462
2473
}
2463
2474
2464
- auto dmn = deterministicMNManager->GetListAtChainTip ().GetMNByCollateral (dstx.masternodeOutpoint );
2475
+ const CBlockIndex* pindex{nullptr };
2476
+ CDeterministicMNCPtr dmn{nullptr };
2477
+ {
2478
+ LOCK (cs_main);
2479
+ pindex = chainActive.Tip ();
2480
+ }
2481
+ // It could be that a MN is no longer in the list but its DSTX is not yet mined.
2482
+ // Try to find a MN up to 24 blocks deep to make sure such dstx-es are relayed and processed correctly.
2483
+ for (int i = 0 ; i < 24 && pindex; ++i) {
2484
+ dmn = deterministicMNManager->GetListForBlock (pindex).GetMNByCollateral (dstx.masternodeOutpoint );
2485
+ if (dmn) break ;
2486
+ pindex = pindex->pprev ;
2487
+ }
2465
2488
if (!dmn) {
2466
2489
LogPrint (BCLog::PRIVATESEND, " DSTX -- Can't find masternode %s to verify %s\n " , dstx.masternodeOutpoint .ToStringShort (), hashTx.ToString ());
2467
2490
return false ;
@@ -2532,6 +2555,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
2532
2555
CInv _inv (MSG_TX, txin.prevout .hash );
2533
2556
pfrom->AddInventoryKnown (_inv);
2534
2557
if (!AlreadyHave (_inv)) pfrom->AskFor (_inv);
2558
+ // We don't know if the previous tx was a regular or a mixing one, try both
2559
+ CInv _inv2 (MSG_DSTX, txin.prevout .hash );
2560
+ pfrom->AddInventoryKnown (_inv2);
2561
+ if (!AlreadyHave (_inv2)) pfrom->AskFor (_inv2);
2535
2562
}
2536
2563
AddOrphanTx (ptx, pfrom->GetId ());
2537
2564
@@ -3807,7 +3834,11 @@ bool PeerLogicValidation::SendMessages(CNode* pto, std::atomic<bool>& interruptM
3807
3834
3808
3835
for (const auto & txinfo : vtxinfo) {
3809
3836
const uint256& hash = txinfo.tx ->GetHash ();
3810
- CInv inv (MSG_TX, hash);
3837
+ int nInvType = MSG_TX;
3838
+ if (CPrivateSend::GetDSTX (hash)) {
3839
+ nInvType = MSG_DSTX;
3840
+ }
3841
+ CInv inv (nInvType, hash);
3811
3842
pto->setInventoryTxToSend .erase (hash);
3812
3843
if (pto->pfilter ) {
3813
3844
if (!pto->pfilter ->IsRelevantAndUpdate (*txinfo.tx )) continue ;
@@ -3873,7 +3904,11 @@ bool PeerLogicValidation::SendMessages(CNode* pto, std::atomic<bool>& interruptM
3873
3904
}
3874
3905
if (pto->pfilter && !pto->pfilter ->IsRelevantAndUpdate (*txinfo.tx )) continue ;
3875
3906
// Send
3876
- vInv.push_back (CInv (MSG_TX, hash));
3907
+ int nInvType = MSG_TX;
3908
+ if (CPrivateSend::GetDSTX (hash)) {
3909
+ nInvType = MSG_DSTX;
3910
+ }
3911
+ vInv.push_back (CInv (nInvType, hash));
3877
3912
nRelayedTransactions++;
3878
3913
{
3879
3914
// Expire old relay messages
0 commit comments