Skip to content

Commit 6423293

Browse files
UdjinM6codablock
authored and
cevap
committed
Various fixes for DSTX-es (dashpay#3295)
* Check MNs up to 24 blocks deep when verifying `dstx` * Handle DSTX-es more like regular txes and not like "other" invs * Try asking for a DSTX too when trying to find missing tx parents * Check DSTX-es when chainlock arrives `HasChainLock` was always `false` in `IsExpired` because tip is updated before the corresponding chainlock is received * Apply `Handle DSTX-es more like regular txes` idea to `AlreadyHave()` * Alternative handling of DSTX+recentRejects Co-authored-by: Alexander Block <[email protected]> Signed-off-by: cevap <[email protected]>
1 parent d994ee0 commit 6423293

File tree

5 files changed

+64
-20
lines changed

5 files changed

+64
-20
lines changed

src/dsnotificationinterface.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,5 @@ void CDSNotificationInterface::NotifyMasternodeListChanged(bool undo, const CDet
109109
void CDSNotificationInterface::NotifyChainLock(const CBlockIndex* pindex, const llmq::CChainLockSig& clsig)
110110
{
111111
llmq::quorumInstantSendManager->NotifyChainLock(pindex);
112+
CPrivateSend::NotifyChainLock(pindex);
112113
}

src/net.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -1004,7 +1004,7 @@ class CNode
10041004
void PushInventory(const CInv& inv)
10051005
{
10061006
LOCK(cs_inventory);
1007-
if (inv.type == MSG_TX) {
1007+
if (inv.type == MSG_TX || inv.type == MSG_DSTX) {
10081008
if (!filterInventoryKnown.contains(inv.hash)) {
10091009
LogPrint(BCLog::NET, "PushInventory -- inv: %s peer=%d\n", inv.ToString(), id);
10101010
setInventoryTxToSend.insert(inv.hash);

src/net_processing.cpp

+54-19
Original file line numberDiff line numberDiff line change
@@ -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());
@@ -2461,7 +2472,19 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
24612472
return true; // not an error
24622473
}
24632474

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+
}
24652488
if(!dmn) {
24662489
LogPrint(BCLog::PRIVATESEND, "DSTX -- Can't find masternode %s to verify %s\n", dstx.masternodeOutpoint.ToStringShort(), hashTx.ToString());
24672490
return false;
@@ -2532,6 +2555,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
25322555
CInv _inv(MSG_TX, txin.prevout.hash);
25332556
pfrom->AddInventoryKnown(_inv);
25342557
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);
25352562
}
25362563
AddOrphanTx(ptx, pfrom->GetId());
25372564

@@ -3807,7 +3834,11 @@ bool PeerLogicValidation::SendMessages(CNode* pto, std::atomic<bool>& interruptM
38073834

38083835
for (const auto& txinfo : vtxinfo) {
38093836
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);
38113842
pto->setInventoryTxToSend.erase(hash);
38123843
if (pto->pfilter) {
38133844
if (!pto->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
@@ -3873,7 +3904,11 @@ bool PeerLogicValidation::SendMessages(CNode* pto, std::atomic<bool>& interruptM
38733904
}
38743905
if (pto->pfilter && !pto->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
38753906
// 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));
38773912
nRelayedTransactions++;
38783913
{
38793914
// Expire old relay messages

src/privatesend/privatesend.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,13 @@ void CPrivateSend::UpdatedBlockTip(const CBlockIndex* pindex)
603603
}
604604
}
605605

606+
void CPrivateSend::NotifyChainLock(const CBlockIndex* pindex)
607+
{
608+
if (pindex && masternodeSync.IsBlockchainSynced()) {
609+
CheckDSTXes(pindex);
610+
}
611+
}
612+
606613
void CPrivateSend::UpdateDSTXConfirmedHeight(const CTransactionRef& tx, int nHeight)
607614
{
608615
AssertLockHeld(cs_mapdstx);

src/privatesend/privatesend.h

+1
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@ class CPrivateSend
465465
static CPrivateSendBroadcastTx GetDSTX(const uint256& hash);
466466

467467
static void UpdatedBlockTip(const CBlockIndex* pindex);
468+
static void NotifyChainLock(const CBlockIndex* pindex);
468469

469470
static void UpdateDSTXConfirmedHeight(const CTransactionRef& tx, int nHeight);
470471
static void TransactionAddedToMempool(const CTransactionRef& tx);

0 commit comments

Comments
 (0)