Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various fixes for DSTX-es #3295

Merged
merged 6 commits into from
Jan 22, 2020
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/dsnotificationinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,5 @@ void CDSNotificationInterface::NotifyMasternodeListChanged(bool undo, const CDet
void CDSNotificationInterface::NotifyChainLock(const CBlockIndex* pindex, const llmq::CChainLockSig& clsig)
{
llmq::quorumInstantSendManager->NotifyChainLock(pindex);
CPrivateSend::NotifyChainLock(pindex);
}
2 changes: 1 addition & 1 deletion src/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -1001,7 +1001,7 @@ class CNode
void PushInventory(const CInv& inv)
{
LOCK(cs_inventory);
if (inv.type == MSG_TX) {
if (inv.type == MSG_TX || inv.type == MSG_DSTX) {
if (!filterInventoryKnown.contains(inv.hash)) {
LogPrint(BCLog::NET, "PushInventory -- inv: %s peer=%d\n", inv.ToString(), id);
setInventoryTxToSend.insert(inv.hash);
Expand Down
85 changes: 61 additions & 24 deletions src/net_processing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1012,6 +1012,7 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
switch (inv.type)
{
case MSG_TX:
case MSG_DSTX:
case MSG_LEGACY_TXLOCK_REQUEST: // we treat legacy IX messages as TX messages
{
assert(recentRejects);
Expand All @@ -1030,12 +1031,24 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
if (mapOrphanTransactions.count(inv.hash)) return true;
}

// When we receive an islock for a previously rejected transaction, we have to
// drop the first-seen tx (which such a locked transaction was conflicting with)
// and re-request the locked transaction (which did not make it into the mempool
// previously due to txn-mempool-conflict rule). This means that we must ignore
// recentRejects filter for such locked txes here.
return (recentRejects->contains(inv.hash) && !llmq::quorumInstantSendManager->IsLocked(inv.hash)) ||
bool fHaveRejected{false};
if (recentRejects->contains(inv.hash)) {
// When we receive an islock for a previously rejected transaction, we have to
// drop the first-seen tx (which such a locked transaction was conflicting with)
// and re-request the locked transaction (which did not make it into the mempool
// previously due to txn-mempool-conflict rule). This means that we must ignore
// recentRejects filter for such locked txes here.
// We also ignore recentRejects filter for DSTX-es because a malicious peer might
// relay a valid DSTX as a regular TX first which would skip all the specific checks
// but would cause such tx to be rejected by ATMP due to 0 fee. Ignoring it here
// should let DSTX to be propagated by honest peer later. Note, that a malicious
// masternode would not be able to exploit this to spam the network with specially
// crafted invalid DSTX-es and potentially cause high load cheaply, because
// corresponding checks in ProcessMessage won't let it to send DSTX-es too often.
fHaveRejected = !(llmq::quorumInstantSendManager->IsLocked(inv.hash) || inv.type == MSG_DSTX);
}
return fHaveRejected ||
(inv.type == MSG_DSTX && static_cast<bool>(CPrivateSend::GetDSTX(inv.hash))) ||
mempool.exists(inv.hash) ||
pcoinsTip->HaveCoinInCache(COutPoint(inv.hash, 0)) || // Best effort: only try output 0 and 1
pcoinsTip->HaveCoinInCache(COutPoint(inv.hash, 1)) ||
Expand All @@ -1061,10 +1074,6 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
return sporkManager.GetSporkByHash(inv.hash, spork);
}

case MSG_DSTX: {
return static_cast<bool>(CPrivateSend::GetDSTX(inv.hash));
}

case MSG_GOVERNANCE_OBJECT:
case MSG_GOVERNANCE_OBJECT_VOTE:
return ! governance.ConfirmInventoryRequest(inv);
Expand Down Expand Up @@ -1275,17 +1284,29 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam

// Send stream from relay memory
bool push = false;
if (inv.type == MSG_TX) {
if (inv.type == MSG_TX || inv.type == MSG_DSTX) {
CPrivateSendBroadcastTx dstx;
if (inv.type == MSG_DSTX) {
dstx = CPrivateSend::GetDSTX(inv.hash);
}
auto mi = mapRelay.find(inv.hash);
if (mi != mapRelay.end()) {
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::TX, *mi->second));
if (dstx) {
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::DSTX, dstx));
} else {
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::TX, *mi->second));
}
push = true;
} else if (pfrom->timeLastMempoolReq) {
auto txinfo = mempool.info(inv.hash);
// To protect privacy, do not answer getdata using the mempool when
// that TX couldn't have been INVed in reply to a MEMPOOL request.
if (txinfo.tx && txinfo.nTime <= pfrom->timeLastMempoolReq) {
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::TX, *txinfo.tx));
if (dstx) {
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::DSTX, dstx));
} else {
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::TX, *txinfo.tx));
}
push = true;
}
}
Expand All @@ -1299,14 +1320,6 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
}
}

if (!push && inv.type == MSG_DSTX) {
CPrivateSendBroadcastTx dstx = CPrivateSend::GetDSTX(inv.hash);
if(dstx) {
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::DSTX, dstx));
push = true;
}
}

if (!push && inv.type == MSG_GOVERNANCE_OBJECT) {
LogPrint(BCLog::NET, "ProcessGetData -- MSG_GOVERNANCE_OBJECT: inv = %s\n", inv.ToString());
CDataStream ss(SER_NETWORK, pfrom->GetSendVersion());
Expand Down Expand Up @@ -2453,7 +2466,19 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
return true; // not an error
}

auto dmn = deterministicMNManager->GetListAtChainTip().GetMNByCollateral(dstx.masternodeOutpoint);
const CBlockIndex* pindex{nullptr};
CDeterministicMNCPtr dmn{nullptr};
{
LOCK(cs_main);
pindex = chainActive.Tip();
}
// It could be that a MN is no longer in the list but its DSTX is not yet mined.
// Try to find a MN up to 24 blocks deep to make sure such dstx-es are relayed and processed correctly.
for (int i = 0; i < 24 && pindex; ++i) {
dmn = deterministicMNManager->GetListForBlock(pindex).GetMNByCollateral(dstx.masternodeOutpoint);
if (dmn) break;
pindex = pindex->pprev;
}
if(!dmn) {
LogPrint(BCLog::PRIVATESEND, "DSTX -- Can't find masternode %s to verify %s\n", dstx.masternodeOutpoint.ToStringShort(), hashTx.ToString());
return false;
Expand Down Expand Up @@ -2524,6 +2549,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
CInv _inv(MSG_TX, txin.prevout.hash);
pfrom->AddInventoryKnown(_inv);
if (!AlreadyHave(_inv)) pfrom->AskFor(_inv);
// We don't know if the previous tx was a regular or a mixing one, try both
CInv _inv2(MSG_DSTX, txin.prevout.hash);
pfrom->AddInventoryKnown(_inv2);
if (!AlreadyHave(_inv2)) pfrom->AskFor(_inv2);
}
AddOrphanTx(ptx, pfrom->GetId());

Expand Down Expand Up @@ -3788,7 +3817,11 @@ bool PeerLogicValidation::SendMessages(CNode* pto, std::atomic<bool>& interruptM

for (const auto& txinfo : vtxinfo) {
const uint256& hash = txinfo.tx->GetHash();
CInv inv(MSG_TX, hash);
int nInvType = MSG_TX;
if (CPrivateSend::GetDSTX(hash)) {
nInvType = MSG_DSTX;
}
CInv inv(nInvType, hash);
pto->setInventoryTxToSend.erase(hash);
if (pto->pfilter) {
if (!pto->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
Expand Down Expand Up @@ -3854,7 +3887,11 @@ bool PeerLogicValidation::SendMessages(CNode* pto, std::atomic<bool>& interruptM
}
if (pto->pfilter && !pto->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
// Send
vInv.push_back(CInv(MSG_TX, hash));
int nInvType = MSG_TX;
if (CPrivateSend::GetDSTX(hash)) {
nInvType = MSG_DSTX;
}
vInv.push_back(CInv(nInvType, hash));
nRelayedTransactions++;
{
// Expire old relay messages
Expand Down
7 changes: 7 additions & 0 deletions src/privatesend/privatesend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,13 @@ void CPrivateSend::UpdatedBlockTip(const CBlockIndex* pindex)
}
}

void CPrivateSend::NotifyChainLock(const CBlockIndex* pindex)
{
if (pindex && masternodeSync.IsBlockchainSynced()) {
CheckDSTXes(pindex);
}
}

void CPrivateSend::UpdateDSTXConfirmedHeight(const CTransactionRef& tx, int nHeight)
{
AssertLockHeld(cs_mapdstx);
Expand Down
1 change: 1 addition & 0 deletions src/privatesend/privatesend.h
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,7 @@ class CPrivateSend
static CPrivateSendBroadcastTx GetDSTX(const uint256& hash);

static void UpdatedBlockTip(const CBlockIndex* pindex);
static void NotifyChainLock(const CBlockIndex* pindex);

static void UpdateDSTXConfirmedHeight(const CTransactionRef& tx, int nHeight);
static void TransactionAddedToMempool(const CTransactionRef& tx);
Expand Down