PIVX Core  5.6.99
P2P Digital Currency
mnauth.cpp
Go to the documentation of this file.
1 // Copyright (c) 2019-2021 The Dash Core developers
2 // Copyright (c) 2021-2022 The PIVX Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or https://www.opensource.org/licenses/mit-license.php.
5 
6 #include "evo/mnauth.h"
7 
8 #include "activemasternode.h"
9 #include "chainparams.h"
10 #include "consensus/validation.h"
11 #include "net.h" // for CSerializedNetMsg
12 #include "netmessagemaker.h"
17 #include "util/system.h" // for fMasternode and gArgs access
18 
19 #include "version.h" // for MNAUTH_NODE_VER_VERSION
20 
21 void CMNAuth::PushMNAUTH(CNode* pnode, CConnman& connman)
22 {
23  const CActiveMasternodeInfo* activeMnInfo{nullptr};
25  (activeMnInfo = activeMasternodeManager->GetInfo())->proTxHash.IsNull()) {
26  return;
27  }
28 
29  uint256 signHash;
30  {
31  LOCK(pnode->cs_mnauth);
32  if (pnode->receivedMNAuthChallenge.IsNull()) {
33  return;
34  }
35  // We include fInbound in signHash to forbid interchanging of challenges by a man in the middle (MITM). This way
36  // we protect ourselves against MITM in this form:
37  // node1 <- Eve -> node2
38  // It does not protect against:
39  // node1 -> Eve -> node2
40  // This is ok as we only use MNAUTH as a DoS protection and not for sensitive stuff
41  int nOurNodeVersion{PROTOCOL_VERSION};
42  if (Params().NetworkIDString() != CBaseChainParams::MAIN && gArgs.IsArgSet("-pushversion")) {
43  nOurNodeVersion = (int)gArgs.GetArg("-pushversion", PROTOCOL_VERSION);
44  }
45  if (pnode->nVersion < MNAUTH_NODE_VER_VERSION || nOurNodeVersion < MNAUTH_NODE_VER_VERSION) {
46  signHash = ::SerializeHash(std::make_tuple(activeMnInfo->pubKeyOperator, pnode->receivedMNAuthChallenge, pnode->fInbound));
47  } else {
48  signHash = ::SerializeHash(std::make_tuple(activeMnInfo->pubKeyOperator, pnode->receivedMNAuthChallenge, pnode->fInbound, nOurNodeVersion));
49  }
50  }
51 
52  CMNAuth mnauth;
53  mnauth.proRegTxHash = activeMnInfo->proTxHash;
54  mnauth.sig = activeMnInfo->keyOperator.Sign(signHash);
55 
56  LogPrint(BCLog::NET_MN, "CMNAuth::%s -- Sending MNAUTH, peer=%d\n", __func__, pnode->GetId());
57  connman.PushMessage(pnode, CNetMsgMaker(pnode->GetSendVersion()).Make(NetMsgType::MNAUTH, mnauth));
58 }
59 
60 bool CMNAuth::ProcessMessage(CNode* pnode, const std::string& strCommand, CDataStream& vRecv, CConnman& connman, CValidationState& state)
61 {
63  // we can't verify MNAUTH messages when we don't have the latest MN list
64  return true;
65  }
66 
67  if (strCommand == NetMsgType::MNAUTH) {
68  CMNAuth mnauth;
69  vRecv >> mnauth;
70  // only one MNAUTH allowed
71  bool fAlreadyHaveMNAUTH = WITH_LOCK(pnode->cs_mnauth, return !pnode->verifiedProRegTxHash.IsNull(););
72  if (fAlreadyHaveMNAUTH) {
73  return state.DoS(100, false, REJECT_INVALID, "duplicate mnauth");
74  }
75 
76  if ((~pnode->nServices) & (NODE_NETWORK | NODE_BLOOM)) {
77  // either NODE_NETWORK or NODE_BLOOM bit is missing in node's services
78  return state.DoS(100, false, REJECT_INVALID, "mnauth from a node with invalid services");
79  }
80 
81  if (mnauth.proRegTxHash.IsNull()) {
82  return state.DoS(100, false, REJECT_INVALID, "empty mnauth proRegTxHash");
83  }
84 
85  if (!mnauth.sig.IsValid()) {
86  return state.DoS(100, false, REJECT_INVALID, "invalid mnauth signature");
87  }
88 
89  auto mnList = deterministicMNManager->GetListAtChainTip();
90  auto dmn = mnList.GetMN(mnauth.proRegTxHash);
91  if (!dmn) {
92  // in case node was unlucky and not up to date, just let it be connected as a regular node, which gives it
93  // a chance to get up-to-date and thus realize that it's not a MN anymore. We still give it a
94  // low DoS score.
95  return state.DoS(10, false, REJECT_INVALID, "missing mnauth masternode");
96  }
97 
98  uint256 signHash;
99  {
100  LOCK(pnode->cs_mnauth);
101  int nOurNodeVersion{PROTOCOL_VERSION};
102  if (Params().NetworkIDString() != CBaseChainParams::MAIN && gArgs.IsArgSet("-pushversion")) {
103  nOurNodeVersion = gArgs.GetArg("-pushversion", PROTOCOL_VERSION);
104  }
105  // See comment in PushMNAUTH (fInbound is negated here as we're on the other side of the connection)
106  if (pnode->nVersion < MNAUTH_NODE_VER_VERSION || nOurNodeVersion < MNAUTH_NODE_VER_VERSION) {
107  signHash = ::SerializeHash(std::make_tuple(dmn->pdmnState->pubKeyOperator, pnode->sentMNAuthChallenge, !pnode->fInbound));
108  } else {
109  signHash = ::SerializeHash(std::make_tuple(dmn->pdmnState->pubKeyOperator, pnode->sentMNAuthChallenge, !pnode->fInbound, pnode->nVersion.load()));
110  }
111  LogPrint(BCLog::NET_MN, "CMNAuth::%s -- constructed signHash for nVersion %d, peer=%d\n", __func__, pnode->nVersion, pnode->GetId());
112  }
113 
114  if (!mnauth.sig.VerifyInsecure(dmn->pdmnState->pubKeyOperator.Get(), signHash)) {
115  // Same as above, MN seems to not know its fate yet, so give it a chance to update. If this is a
116  // malicious node (DoSing us), it'll get banned soon.
117  return state.DoS(10, false, REJECT_INVALID, "mnauth signature verification failed");
118  }
119 
120  if (!pnode->fInbound) {
121  g_mmetaman.GetMetaInfo(mnauth.proRegTxHash)->SetLastOutboundSuccess(GetAdjustedTime());
122  if (pnode->m_masternode_probe_connection) {
123  LogPrint(BCLog::NET_MN, "%s -- Masternode probe successful for %s, disconnecting. peer=%d\n",
124  __func__, mnauth.proRegTxHash.ToString(), pnode->GetId());
125  pnode->fDisconnect = true;
126  return true;
127  }
128  }
129 
130  // future: Move this to the first line of this function..
131  const CActiveMasternodeInfo* activeMnInfo{nullptr};
133  (activeMnInfo = activeMasternodeManager->GetInfo())->proTxHash.IsNull()) {
134  return true;
135  }
136 
137  connman.ForEachNode([&](CNode* pnode2) {
138  if (pnode->fDisconnect) {
139  // we've already disconnected the new peer
140  return;
141  }
142 
143  if (pnode2->verifiedProRegTxHash == mnauth.proRegTxHash) {
144  if (fMasterNode) {
145  auto deterministicOutbound = llmq::DeterministicOutboundConnection(activeMnInfo->proTxHash, mnauth.proRegTxHash);
146  LogPrint(BCLog::NET_MN, "CMNAuth::ProcessMessage -- Masternode %s has already verified as peer %d, deterministicOutbound=%s. peer=%d\n",
147  mnauth.proRegTxHash.ToString(), pnode2->GetId(), deterministicOutbound.ToString(), pnode->GetId());
148  if (deterministicOutbound == activeMnInfo->proTxHash) {
149  if (pnode2->fInbound) {
150  LogPrint(BCLog::NET_MN, "CMNAuth::ProcessMessage -- dropping old inbound, peer=%d\n", pnode2->GetId());
151  pnode2->fDisconnect = true;
152  } else if (pnode->fInbound) {
153  LogPrint(BCLog::NET_MN, "CMNAuth::ProcessMessage -- dropping new inbound, peer=%d\n", pnode->GetId());
154  pnode->fDisconnect = true;
155  }
156  } else {
157  if (!pnode2->fInbound) {
158  LogPrint(BCLog::NET_MN, "CMNAuth::ProcessMessage -- dropping old outbound, peer=%d\n", pnode2->GetId());
159  pnode2->fDisconnect = true;
160  } else if (!pnode->fInbound) {
161  LogPrint(BCLog::NET_MN, "CMNAuth::ProcessMessage -- dropping new outbound, peer=%d\n", pnode->GetId());
162  pnode->fDisconnect = true;
163  }
164  }
165  } else {
166  LogPrint(BCLog::NET_MN, "CMNAuth::ProcessMessage -- Masternode %s has already verified as peer %d, dropping new connection. peer=%d\n",
167  mnauth.proRegTxHash.ToString(), pnode2->GetId(), pnode->GetId());
168  pnode->fDisconnect = true;
169  }
170  }
171  });
172 
173  if (pnode->fDisconnect) {
174  return true;
175  }
176 
177  {
178  LOCK(pnode->cs_mnauth);
179  pnode->verifiedProRegTxHash = mnauth.proRegTxHash;
180  pnode->verifiedPubKeyHash = dmn->pdmnState->pubKeyOperator.GetHash();
181  }
182 
184  // Tell our peer that we're interested in plain LLMQ recovered signatures.
185  // Otherwise, the peer would only announce/send messages resulting from QRECSIG,
186  // future e.g. tx locks or chainlocks. SPV and regular full nodes should not send
187  // this message as they are usually only interested in the higher level messages.
188  CNetMsgMaker msgMaker(pnode->GetSendVersion());
189  connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QSENDRECSIGS, true));
190  pnode->m_masternode_iqr_connection = true;
191  }
192 
193  LogPrint(BCLog::NET_MN, "CMNAuth::%s -- Valid MNAUTH for %s, peer=%d\n", __func__, mnauth.proRegTxHash.ToString(), pnode->GetId());
194  }
195  return true;
196 }
197 
199 {
200  // we're only interested in updated/removed MNs. Added MNs are of no interest for us
201  if (diff.updatedMNs.empty() && diff.removedMns.empty()) {
202  return;
203  }
204 
205  g_connman->ForEachNode([&](CNode* pnode) {
206  LOCK(pnode->cs_mnauth);
207  if (pnode->verifiedProRegTxHash.IsNull()) {
208  return;
209  }
210  auto verifiedDmn = oldMNList.GetMN(pnode->verifiedProRegTxHash);
211  if (!verifiedDmn) {
212  return;
213  }
214  bool doRemove = false;
215  if (diff.removedMns.count(verifiedDmn->GetInternalId())) {
216  doRemove = true;
217  } else {
218  auto it = diff.updatedMNs.find(verifiedDmn->GetInternalId());
219  if (it != diff.updatedMNs.end()) {
220  if ((it->second.fields & CDeterministicMNStateDiff::Field_pubKeyOperator) && it->second.state.pubKeyOperator.GetHash() != pnode->verifiedPubKeyHash) {
221  doRemove = true;
222  }
223  }
224  }
225 
226  if (doRemove) {
227  LogPrint(BCLog::NET_MN, "CMNAuth::NotifyMasternodeListChanged -- Disconnecting MN %s due to key changed/removed, peer=%d\n",
228  pnode->verifiedProRegTxHash.ToString(), pnode->GetId());
229  pnode->fDisconnect = true;
230  }
231  });
232 }
CActiveDeterministicMasternodeManager * activeMasternodeManager
const CChainParams & Params()
Return the currently selected parameters.
bool IsArgSet(const std::string &strArg) const
Return true if the given argument has been manually set.
Definition: system.cpp:425
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
Definition: system.cpp:449
const CActiveMasternodeInfo * GetInfo() const
bool VerifyInsecure(const CBLSPublicKey &pubKey, const uint256 &hash) const
bool IsValid() const
Definition: bls_wrapper.h:87
static const std::string MAIN
Chain name strings.
Definition: net.h:145
void PushMessage(CNode *pnode, CSerializedNetMsg &&msg, bool allowOptimisticSend=DEFAULT_ALLOW_OPTIMISTIC_SEND)
Definition: net.cpp:2758
TierTwoConnMan * GetTierTwoConnMan()
Unique tier two connections manager.
Definition: net.h:375
void ForEachNode(Callable &&func)
Definition: net.h:257
std::set< uint64_t > removedMns
std::map< uint64_t, CDeterministicMNStateDiff > updatedMNs
CDeterministicMNCPtr GetMN(const uint256 &proTxHash) const
This class handles the p2p message MNAUTH.
Definition: mnauth.h:37
uint256 proRegTxHash
Definition: mnauth.h:39
static void PushMNAUTH(CNode *pnode, CConnman &connman)
Definition: mnauth.cpp:21
static void NotifyMasternodeListChanged(bool undo, const CDeterministicMNList &oldMNList, const CDeterministicMNListDiff &diff)
Definition: mnauth.cpp:198
static bool ProcessMessage(CNode *pnode, const std::string &strCommand, CDataStream &vRecv, CConnman &connman, CValidationState &state)
Definition: mnauth.cpp:60
CBLSSignature sig
Definition: mnauth.h:40
CMasternodeMetaInfoPtr GetMetaInfo(const uint256 &proTxHash, bool fCreate=true)
CSerializedNetMsg Make(int nFlags, std::string sCommand, Args &&... args)
Information about a peer.
Definition: net.h:669
std::atomic< int > nVersion
Definition: net.h:699
NodeId GetId() const
Definition: net.h:825
int GetSendVersion() const
Definition: net.cpp:789
std::atomic< ServiceFlags > nServices
Definition: net.h:673
uint256 verifiedProRegTxHash
Definition: net.h:802
Mutex cs_mnauth
Definition: net.h:799
uint256 verifiedPubKeyHash
Definition: net.h:803
uint256 sentMNAuthChallenge
Definition: net.h:800
uint256 receivedMNAuthChallenge
Definition: net.h:801
std::atomic< bool > m_masternode_probe_connection
Definition: net.h:711
std::atomic< bool > m_masternode_iqr_connection
Definition: net.h:712
const bool fInbound
Definition: net.h:715
std::atomic_bool fDisconnect
Definition: net.h:722
Capture information about block/transaction validation.
Definition: validation.h:24
bool DoS(int level, bool ret=false, unsigned int chRejectCodeIn=0, std::string strRejectReasonIn="", bool corruptionIn=false, const std::string &strDebugMessageIn="")
Definition: validation.h:39
bool isMasternodeQuorumRelayMember(const uint256 &protxHash)
bool IsBlockchainSynced() const
std::string ToString() const
Definition: uint256.cpp:65
bool IsNull() const
Definition: uint256.h:36
256-bit opaque blob.
Definition: uint256.h:138
std::unique_ptr< CDeterministicMNManager > deterministicMNManager
if(!read_stdin(buffer))
Definition: fuzz.cpp:72
uint256 SerializeHash(const T &obj, int nType=SER_GETHASH, int nVersion=PROTOCOL_VERSION)
Compute the 256-bit hash of an object's serialization.
Definition: hash.h:289
std::unique_ptr< CConnman > g_connman
Definition: init.cpp:90
@ LOCK
Definition: lockunlock.h:16
#define LogPrint(category,...)
Definition: logging.h:163
CMasternodeMetaMan g_mmetaman
@ NET_MN
Definition: logging.h:67
const char * QSENDRECSIGS
The qsendrecsigs message is used to propagate LLMQ intra-quorum partial recovered signatures.
Definition: protocol.cpp:57
const char * MNAUTH
The mnauth message is used authenticate MN connections.
Definition: protocol.cpp:58
@ NODE_BLOOM
Definition: protocol.h:321
@ NODE_NETWORK
Definition: protocol.h:318
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:247
ArgsManager gArgs
Definition: system.cpp:89
std::atomic< bool > fMasterNode
Definition: system.cpp:87
TierTwoSyncState g_tiertwo_sync_state
int64_t GetAdjustedTime()
Definition: timedata.cpp:36