Line data Source code
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"
13 : #include "llmq/quorums_connections.h"
14 : #include "tiertwo/masternode_meta_manager.h"
15 : #include "tiertwo/net_masternodes.h"
16 : #include "tiertwo/tiertwo_sync_state.h"
17 : #include "util/system.h" // for fMasternode and gArgs access
18 :
19 : #include "version.h" // for MNAUTH_NODE_VER_VERSION
20 :
21 1309 : void CMNAuth::PushMNAUTH(CNode* pnode, CConnman& connman)
22 : {
23 1309 : const CActiveMasternodeInfo* activeMnInfo{nullptr};
24 1309 : if (!fMasterNode || !activeMasternodeManager ||
25 356 : (activeMnInfo = activeMasternodeManager->GetInfo())->proTxHash.IsNull()) {
26 1150 : return;
27 : }
28 :
29 178 : uint256 signHash;
30 178 : {
31 178 : LOCK(pnode->cs_mnauth);
32 946 : if (pnode->receivedMNAuthChallenge.IsNull()) {
33 38 : 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 159 : int nOurNodeVersion{PROTOCOL_VERSION};
42 636 : if (Params().NetworkIDString() != CBaseChainParams::MAIN && gArgs.IsArgSet("-pushversion")) {
43 0 : nOurNodeVersion = (int)gArgs.GetArg("-pushversion", PROTOCOL_VERSION);
44 : }
45 159 : if (pnode->nVersion < MNAUTH_NODE_VER_VERSION || nOurNodeVersion < MNAUTH_NODE_VER_VERSION) {
46 0 : signHash = ::SerializeHash(std::make_tuple(activeMnInfo->pubKeyOperator, pnode->receivedMNAuthChallenge, pnode->fInbound));
47 : } else {
48 159 : signHash = ::SerializeHash(std::make_tuple(activeMnInfo->pubKeyOperator, pnode->receivedMNAuthChallenge, pnode->fInbound, nOurNodeVersion));
49 : }
50 : }
51 :
52 159 : CMNAuth mnauth;
53 159 : mnauth.proRegTxHash = activeMnInfo->proTxHash;
54 159 : mnauth.sig = activeMnInfo->keyOperator.Sign(signHash);
55 :
56 159 : LogPrint(BCLog::NET_MN, "CMNAuth::%s -- Sending MNAUTH, peer=%d\n", __func__, pnode->GetId());
57 159 : connman.PushMessage(pnode, CNetMsgMaker(pnode->GetSendVersion()).Make(NetMsgType::MNAUTH, mnauth));
58 : }
59 :
60 55734 : bool CMNAuth::ProcessMessage(CNode* pnode, const std::string& strCommand, CDataStream& vRecv, CConnman& connman, CValidationState& state)
61 : {
62 55734 : if (!g_tiertwo_sync_state.IsBlockchainSynced()) {
63 : // we can't verify MNAUTH messages when we don't have the latest MN list
64 : return true;
65 : }
66 :
67 55724 : if (strCommand == NetMsgType::MNAUTH) {
68 159 : CMNAuth mnauth;
69 159 : vRecv >> mnauth;
70 : // only one MNAUTH allowed
71 477 : bool fAlreadyHaveMNAUTH = WITH_LOCK(pnode->cs_mnauth, return !pnode->verifiedProRegTxHash.IsNull(););
72 159 : if (fAlreadyHaveMNAUTH) {
73 0 : return state.DoS(100, false, REJECT_INVALID, "duplicate mnauth");
74 : }
75 :
76 159 : if ((~pnode->nServices) & (NODE_NETWORK | NODE_BLOOM)) {
77 : // either NODE_NETWORK or NODE_BLOOM bit is missing in node's services
78 0 : return state.DoS(100, false, REJECT_INVALID, "mnauth from a node with invalid services");
79 : }
80 :
81 320 : if (mnauth.proRegTxHash.IsNull()) {
82 0 : return state.DoS(100, false, REJECT_INVALID, "empty mnauth proRegTxHash");
83 : }
84 :
85 159 : if (!mnauth.sig.IsValid()) {
86 0 : return state.DoS(100, false, REJECT_INVALID, "invalid mnauth signature");
87 : }
88 :
89 159 : auto mnList = deterministicMNManager->GetListAtChainTip();
90 269 : auto dmn = mnList.GetMN(mnauth.proRegTxHash);
91 159 : 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 0 : return state.DoS(10, false, REJECT_INVALID, "missing mnauth masternode");
96 : }
97 :
98 159 : uint256 signHash;
99 159 : {
100 159 : LOCK(pnode->cs_mnauth);
101 159 : int nOurNodeVersion{PROTOCOL_VERSION};
102 636 : if (Params().NetworkIDString() != CBaseChainParams::MAIN && gArgs.IsArgSet("-pushversion")) {
103 0 : 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 159 : if (pnode->nVersion < MNAUTH_NODE_VER_VERSION || nOurNodeVersion < MNAUTH_NODE_VER_VERSION) {
107 0 : signHash = ::SerializeHash(std::make_tuple(dmn->pdmnState->pubKeyOperator, pnode->sentMNAuthChallenge, !pnode->fInbound));
108 : } else {
109 477 : signHash = ::SerializeHash(std::make_tuple(dmn->pdmnState->pubKeyOperator, pnode->sentMNAuthChallenge, !pnode->fInbound, pnode->nVersion.load()));
110 : }
111 159 : LogPrint(BCLog::NET_MN, "CMNAuth::%s -- constructed signHash for nVersion %d, peer=%d\n", __func__, pnode->nVersion, pnode->GetId());
112 : }
113 :
114 159 : 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 0 : return state.DoS(10, false, REJECT_INVALID, "mnauth signature verification failed");
118 : }
119 :
120 159 : if (!pnode->fInbound) {
121 104 : g_mmetaman.GetMetaInfo(mnauth.proRegTxHash)->SetLastOutboundSuccess(GetAdjustedTime());
122 104 : if (pnode->m_masternode_probe_connection) {
123 98 : LogPrint(BCLog::NET_MN, "%s -- Masternode probe successful for %s, disconnecting. peer=%d\n",
124 : __func__, mnauth.proRegTxHash.ToString(), pnode->GetId());
125 49 : pnode->fDisconnect = true;
126 49 : return true;
127 : }
128 : }
129 :
130 : // future: Move this to the first line of this function..
131 110 : const CActiveMasternodeInfo* activeMnInfo{nullptr};
132 110 : if (!fMasterNode || !activeMasternodeManager ||
133 220 : (activeMnInfo = activeMasternodeManager->GetInfo())->proTxHash.IsNull()) {
134 0 : return true;
135 : }
136 :
137 110 : connman.ForEachNode([&](CNode* pnode2) {
138 1088 : if (pnode->fDisconnect) {
139 : // we've already disconnected the new peer
140 : return;
141 : }
142 :
143 1088 : if (pnode2->verifiedProRegTxHash == mnauth.proRegTxHash) {
144 0 : if (fMasterNode) {
145 0 : auto deterministicOutbound = llmq::DeterministicOutboundConnection(activeMnInfo->proTxHash, mnauth.proRegTxHash);
146 0 : 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 0 : if (deterministicOutbound == activeMnInfo->proTxHash) {
149 0 : if (pnode2->fInbound) {
150 0 : LogPrint(BCLog::NET_MN, "CMNAuth::ProcessMessage -- dropping old inbound, peer=%d\n", pnode2->GetId());
151 0 : pnode2->fDisconnect = true;
152 0 : } else if (pnode->fInbound) {
153 0 : LogPrint(BCLog::NET_MN, "CMNAuth::ProcessMessage -- dropping new inbound, peer=%d\n", pnode->GetId());
154 0 : pnode->fDisconnect = true;
155 : }
156 : } else {
157 0 : if (!pnode2->fInbound) {
158 0 : LogPrint(BCLog::NET_MN, "CMNAuth::ProcessMessage -- dropping old outbound, peer=%d\n", pnode2->GetId());
159 0 : pnode2->fDisconnect = true;
160 0 : } else if (!pnode->fInbound) {
161 0 : LogPrint(BCLog::NET_MN, "CMNAuth::ProcessMessage -- dropping new outbound, peer=%d\n", pnode->GetId());
162 0 : pnode->fDisconnect = true;
163 : }
164 : }
165 : } else {
166 0 : 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 1088 : pnode->fDisconnect = true;
169 : }
170 : }
171 : });
172 :
173 110 : if (pnode->fDisconnect) {
174 : return true;
175 : }
176 :
177 110 : {
178 110 : LOCK(pnode->cs_mnauth);
179 110 : pnode->verifiedProRegTxHash = mnauth.proRegTxHash;
180 110 : pnode->verifiedPubKeyHash = dmn->pdmnState->pubKeyOperator.GetHash();
181 : }
182 :
183 110 : if (!pnode->m_masternode_iqr_connection && connman.GetTierTwoConnMan()->isMasternodeQuorumRelayMember(pnode->verifiedProRegTxHash)) {
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 100 : CNetMsgMaker msgMaker(pnode->GetSendVersion());
189 100 : connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QSENDRECSIGS, true));
190 100 : pnode->m_masternode_iqr_connection = true;
191 : }
192 :
193 220 : 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 :
198 7251 : void CMNAuth::NotifyMasternodeListChanged(bool undo, const CDeterministicMNList& oldMNList, const CDeterministicMNListDiff& diff)
199 : {
200 : // we're only interested in updated/removed MNs. Added MNs are of no interest for us
201 7251 : if (diff.updatedMNs.empty() && diff.removedMns.empty()) {
202 : return;
203 : }
204 :
205 7179 : g_connman->ForEachNode([&](CNode* pnode) {
206 80155 : LOCK(pnode->cs_mnauth);
207 2030050 : if (pnode->verifiedProRegTxHash.IsNull()) {
208 60933 : return;
209 : }
210 19222 : auto verifiedDmn = oldMNList.GetMN(pnode->verifiedProRegTxHash);
211 9611 : if (!verifiedDmn) {
212 60933 : return;
213 : }
214 9611 : bool doRemove = false;
215 9611 : if (diff.removedMns.count(verifiedDmn->GetInternalId())) {
216 : doRemove = true;
217 : } else {
218 9611 : auto it = diff.updatedMNs.find(verifiedDmn->GetInternalId());
219 9611 : if (it != diff.updatedMNs.end()) {
220 2190 : if ((it->second.fields & CDeterministicMNStateDiff::Field_pubKeyOperator) && it->second.state.pubKeyOperator.GetHash() != pnode->verifiedPubKeyHash) {
221 4 : doRemove = true;
222 : }
223 : }
224 : }
225 :
226 9611 : if (doRemove) {
227 8 : LogPrint(BCLog::NET_MN, "CMNAuth::NotifyMasternodeListChanged -- Disconnecting MN %s due to key changed/removed, peer=%d\n",
228 : pnode->verifiedProRegTxHash.ToString(), pnode->GetId());
229 9611 : pnode->fDisconnect = true;
230 : }
231 : });
232 : }
|