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 1307 : void CMNAuth::PushMNAUTH(CNode* pnode, CConnman& connman)
22 : {
23 1307 : const CActiveMasternodeInfo* activeMnInfo{nullptr};
24 1307 : if (!fMasterNode || !activeMasternodeManager ||
25 350 : (activeMnInfo = activeMasternodeManager->GetInfo())->proTxHash.IsNull()) {
26 1151 : return;
27 : }
28 :
29 175 : uint256 signHash;
30 175 : {
31 175 : LOCK(pnode->cs_mnauth);
32 940 : 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 156 : int nOurNodeVersion{PROTOCOL_VERSION};
42 624 : if (Params().NetworkIDString() != CBaseChainParams::MAIN && gArgs.IsArgSet("-pushversion")) {
43 0 : nOurNodeVersion = (int)gArgs.GetArg("-pushversion", PROTOCOL_VERSION);
44 : }
45 156 : 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 156 : signHash = ::SerializeHash(std::make_tuple(activeMnInfo->pubKeyOperator, pnode->receivedMNAuthChallenge, pnode->fInbound, nOurNodeVersion));
49 : }
50 : }
51 :
52 156 : CMNAuth mnauth;
53 156 : mnauth.proRegTxHash = activeMnInfo->proTxHash;
54 156 : mnauth.sig = activeMnInfo->keyOperator.Sign(signHash);
55 :
56 156 : LogPrint(BCLog::NET_MN, "CMNAuth::%s -- Sending MNAUTH, peer=%d\n", __func__, pnode->GetId());
57 156 : connman.PushMessage(pnode, CNetMsgMaker(pnode->GetSendVersion()).Make(NetMsgType::MNAUTH, mnauth));
58 : }
59 :
60 55640 : bool CMNAuth::ProcessMessage(CNode* pnode, const std::string& strCommand, CDataStream& vRecv, CConnman& connman, CValidationState& state)
61 : {
62 55640 : 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 55634 : if (strCommand == NetMsgType::MNAUTH) {
68 156 : CMNAuth mnauth;
69 156 : vRecv >> mnauth;
70 : // only one MNAUTH allowed
71 468 : bool fAlreadyHaveMNAUTH = WITH_LOCK(pnode->cs_mnauth, return !pnode->verifiedProRegTxHash.IsNull(););
72 156 : if (fAlreadyHaveMNAUTH) {
73 0 : return state.DoS(100, false, REJECT_INVALID, "duplicate mnauth");
74 : }
75 :
76 156 : 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 312 : if (mnauth.proRegTxHash.IsNull()) {
82 0 : return state.DoS(100, false, REJECT_INVALID, "empty mnauth proRegTxHash");
83 : }
84 :
85 156 : if (!mnauth.sig.IsValid()) {
86 0 : return state.DoS(100, false, REJECT_INVALID, "invalid mnauth signature");
87 : }
88 :
89 156 : auto mnList = deterministicMNManager->GetListAtChainTip();
90 266 : auto dmn = mnList.GetMN(mnauth.proRegTxHash);
91 156 : 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 156 : uint256 signHash;
99 156 : {
100 156 : LOCK(pnode->cs_mnauth);
101 156 : int nOurNodeVersion{PROTOCOL_VERSION};
102 624 : 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 156 : 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 468 : signHash = ::SerializeHash(std::make_tuple(dmn->pdmnState->pubKeyOperator, pnode->sentMNAuthChallenge, !pnode->fInbound, pnode->nVersion.load()));
110 : }
111 156 : LogPrint(BCLog::NET_MN, "CMNAuth::%s -- constructed signHash for nVersion %d, peer=%d\n", __func__, pnode->nVersion, pnode->GetId());
112 : }
113 :
114 156 : 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 156 : if (!pnode->fInbound) {
121 101 : g_mmetaman.GetMetaInfo(mnauth.proRegTxHash)->SetLastOutboundSuccess(GetAdjustedTime());
122 101 : if (pnode->m_masternode_probe_connection) {
123 92 : LogPrint(BCLog::NET_MN, "%s -- Masternode probe successful for %s, disconnecting. peer=%d\n",
124 : __func__, mnauth.proRegTxHash.ToString(), pnode->GetId());
125 46 : pnode->fDisconnect = true;
126 46 : 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 1077 : if (pnode->fDisconnect) {
139 : // we've already disconnected the new peer
140 : return;
141 : }
142 :
143 1077 : 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 1077 : 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 93 : CNetMsgMaker msgMaker(pnode->GetSendVersion());
189 93 : connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QSENDRECSIGS, true));
190 93 : 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 6971 : 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 6971 : if (diff.updatedMNs.empty() && diff.removedMns.empty()) {
202 : return;
203 : }
204 :
205 6899 : g_connman->ForEachNode([&](CNode* pnode) {
206 77462 : LOCK(pnode->cs_mnauth);
207 2004500 : if (pnode->verifiedProRegTxHash.IsNull()) {
208 60220 : return;
209 : }
210 17242 : auto verifiedDmn = oldMNList.GetMN(pnode->verifiedProRegTxHash);
211 8621 : if (!verifiedDmn) {
212 60220 : return;
213 : }
214 8621 : bool doRemove = false;
215 8621 : if (diff.removedMns.count(verifiedDmn->GetInternalId())) {
216 : doRemove = true;
217 : } else {
218 8618 : auto it = diff.updatedMNs.find(verifiedDmn->GetInternalId());
219 8618 : if (it != diff.updatedMNs.end()) {
220 2138 : if ((it->second.fields & CDeterministicMNStateDiff::Field_pubKeyOperator) && it->second.state.pubKeyOperator.GetHash() != pnode->verifiedPubKeyHash) {
221 4 : doRemove = true;
222 : }
223 : }
224 : }
225 :
226 8621 : if (doRemove) {
227 14 : LogPrint(BCLog::NET_MN, "CMNAuth::NotifyMasternodeListChanged -- Disconnecting MN %s due to key changed/removed, peer=%d\n",
228 : pnode->verifiedProRegTxHash.ToString(), pnode->GetId());
229 8621 : pnode->fDisconnect = true;
230 : }
231 : });
232 : }
|