forked from dashpay/dash
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmnauth.cpp
More file actions
206 lines (177 loc) · 9.74 KB
/
mnauth.cpp
File metadata and controls
206 lines (177 loc) · 9.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
// Copyright (c) 2019-2025 The Dash Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <evo/mnauth.h>
#include <active/masternode.h>
#include <bls/bls.h>
#include <evo/deterministicmns.h>
#include <llmq/utils.h>
#include <masternode/meta.h>
#include <masternode/sync.h>
#include <chainparams.h>
#include <net.h>
#include <netmessagemaker.h>
#include <util/time.h>
void CMNAuth::PushMNAUTH(CNode& peer, CConnman& connman, const CActiveMasternodeManager& mn_activeman)
{
CMNAuth mnauth;
if (mn_activeman.GetProTxHash().IsNull()) {
return;
}
const auto receivedMNAuthChallenge = peer.GetReceivedMNAuthChallenge();
if (receivedMNAuthChallenge.IsNull()) {
return;
}
// We include fInbound in signHash to forbid interchanging of challenges by a man in the middle (MITM). This way
// we protect ourselves against MITM in this form:
// node1 <- Eve -> node2
// It does not protect against:
// node1 -> Eve -> node2
// This is ok as we only use MNAUTH as a DoS protection and not for sensitive stuff
int nOurNodeVersion{PROTOCOL_VERSION};
if (Params().NetworkIDString() != CBaseChainParams::MAIN && gArgs.IsArgSet("-pushversion")) {
nOurNodeVersion = gArgs.GetIntArg("-pushversion", PROTOCOL_VERSION);
}
const uint256 signHash{::SerializeHash(std::make_tuple(mn_activeman.GetPubKey(), receivedMNAuthChallenge, peer.IsInboundConn(), nOurNodeVersion))};
mnauth.proRegTxHash = mn_activeman.GetProTxHash();
// all clients uses basic BLS
mnauth.sig = mn_activeman.Sign(signHash, false);
LogPrint(BCLog::NET_NETCONN, "CMNAuth::%s -- Sending MNAUTH, peer=%d\n", __func__, peer.GetId());
connman.PushMessage(&peer, CNetMsgMaker(peer.GetCommonVersion()).Make(NetMsgType::MNAUTH, mnauth));
}
MessageProcessingResult CMNAuth::ProcessMessage(CNode& peer, ServiceFlags node_services, CConnman& connman, CMasternodeMetaMan& mn_metaman,
const CActiveMasternodeManager* const mn_activeman, const CMasternodeSync& mn_sync,
const CDeterministicMNList& tip_mn_list, std::string_view msg_type, CDataStream& vRecv)
{
assert(mn_metaman.IsValid());
if (msg_type != NetMsgType::MNAUTH || !mn_sync.IsBlockchainSynced()) {
// we can't verify MNAUTH messages when we don't have the latest MN list
return {};
}
CMNAuth mnauth;
vRecv >> mnauth;
// only one MNAUTH allowed
if (!peer.GetVerifiedProRegTxHash().IsNull()) {
return MisbehavingError{100, "duplicate mnauth"};
}
if ((~node_services) & (NODE_NETWORK | NODE_BLOOM)) {
// either NODE_NETWORK or NODE_BLOOM bit is missing in node's services
return MisbehavingError{100, "mnauth from a node with invalid services"};
}
if (mnauth.proRegTxHash.IsNull()) {
return MisbehavingError{100, "empty mnauth proRegTxHash"};
}
if (!mnauth.sig.IsValid()) {
LogPrint(BCLog::NET_NETCONN, "CMNAuth::ProcessMessage -- invalid mnauth for protx=%s with sig=%s\n",
mnauth.proRegTxHash.ToString(), mnauth.sig.ToString(false));
return MisbehavingError{100, "invalid mnauth signature"};
}
const auto dmn = tip_mn_list.GetMN(mnauth.proRegTxHash);
if (!dmn) {
// in case node was unlucky and not up to date, just let it be connected as a regular node, which gives it
// a chance to get up-to-date and thus realize that it's not a MN anymore. We still give it a
// low DoS score.
return MisbehavingError{10, "missing mnauth masternode"};
}
const CBLSPublicKey pubKey(dmn->pdmnState->pubKeyOperator.Get());
// See comment in PushMNAUTH (fInbound is negated here as we're on the other side of the connection)
const uint256 signHash{::SerializeHash(std::make_tuple(pubKey, peer.GetSentMNAuthChallenge(), !peer.IsInboundConn(), peer.nVersion.load()))};
LogPrint(BCLog::NET_NETCONN, "CMNAuth::%s -- constructed signHash for nVersion %d, peer=%d\n", __func__, peer.nVersion, peer.GetId());
if (!mnauth.sig.VerifyInsecure(dmn->pdmnState->pubKeyOperator.Get(), signHash, false)) {
// Same as above, MN seems to not know its fate yet, so give it a chance to update. If this is a
// malicious node (DoSing us), it'll get banned soon.
return MisbehavingError{10, "mnauth signature verification failed"};
}
if (!peer.IsInboundConn()) {
mn_metaman.SetLastOutboundSuccess(mnauth.proRegTxHash, GetTime<std::chrono::seconds>().count());
if (peer.m_masternode_probe_connection) {
LogPrint(BCLog::NET_NETCONN, "CMNAuth::ProcessMessage -- Masternode probe successful for %s, disconnecting. peer=%d\n",
mnauth.proRegTxHash.ToString(), peer.GetId());
peer.fDisconnect = true;
return {};
}
}
const uint256 myProTxHash = mn_activeman != nullptr ? mn_activeman->GetProTxHash() : uint256();
connman.ForEachNode([&](CNode* pnode2) {
if (peer.fDisconnect) {
// we've already disconnected the new peer
return;
}
if (pnode2->GetVerifiedProRegTxHash() == mnauth.proRegTxHash) {
if (mn_activeman != nullptr && !myProTxHash.IsNull()) {
const auto deterministicOutbound = llmq::utils::DeterministicOutboundConnection(myProTxHash, mnauth.proRegTxHash);
LogPrint(BCLog::NET_NETCONN, "CMNAuth::ProcessMessage -- Masternode %s has already verified as peer %d, deterministicOutbound=%s. peer=%d\n",
mnauth.proRegTxHash.ToString(), pnode2->GetId(), deterministicOutbound.ToString(), peer.GetId());
if (deterministicOutbound == myProTxHash) {
// NOTE: do not drop inbound nodes here, mark them as probes so that
// they would be disconnected later in CMasternodeUtils::DoMaintenance
if (pnode2->IsInboundConn()) {
LogPrint(BCLog::NET_NETCONN, "CMNAuth::ProcessMessage -- marking old inbound for dropping it later, peer=%d\n", pnode2->GetId());
pnode2->m_masternode_probe_connection = true;
} else if (peer.IsInboundConn()) {
LogPrint(BCLog::NET_NETCONN, "CMNAuth::ProcessMessage -- marking new inbound for dropping it later, peer=%d\n", peer.GetId());
peer.m_masternode_probe_connection = true;
}
} else {
if (!pnode2->IsInboundConn()) {
LogPrint(BCLog::NET_NETCONN, "CMNAuth::ProcessMessage -- dropping old outbound, peer=%d\n", pnode2->GetId());
pnode2->fDisconnect = true;
} else if (!peer.IsInboundConn()) {
LogPrint(BCLog::NET_NETCONN, "CMNAuth::ProcessMessage -- dropping new outbound, peer=%d\n", peer.GetId());
peer.fDisconnect = true;
}
}
} else {
LogPrint(BCLog::NET_NETCONN, "CMNAuth::ProcessMessage -- Masternode %s has already verified as peer %d, dropping new connection. peer=%d\n",
mnauth.proRegTxHash.ToString(), pnode2->GetId(), peer.GetId());
peer.fDisconnect = true;
}
}
});
if (peer.fDisconnect) {
return {};
}
peer.SetVerifiedProRegTxHash(mnauth.proRegTxHash);
peer.SetVerifiedPubKeyHash(dmn->pdmnState->pubKeyOperator.GetHash());
if (!peer.m_masternode_iqr_connection && connman.IsMasternodeQuorumRelayMember(peer.GetVerifiedProRegTxHash())) {
// Tell our peer that we're interested in plain LLMQ recovered signatures.
// Otherwise, the peer would only announce/send messages resulting from QRECSIG,
// e.g. InstantSend locks or ChainLocks. SPV and regular full nodes should not send
// this message as they are usually only interested in the higher level messages.
const CNetMsgMaker msgMaker(peer.GetCommonVersion());
connman.PushMessage(&peer, msgMaker.Make(NetMsgType::QSENDRECSIGS, true));
peer.m_masternode_iqr_connection = true;
}
LogPrint(BCLog::NET_NETCONN, "CMNAuth::%s -- Valid MNAUTH for %s, peer=%d\n", __func__, mnauth.proRegTxHash.ToString(), peer.GetId());
return {};
}
void CMNAuth::NotifyMasternodeListChanged(bool undo, const CDeterministicMNList& oldMNList, const CDeterministicMNListDiff& diff, CConnman& connman)
{
// we're only interested in updated/removed MNs. Added MNs are of no interest for us
if (diff.updatedMNs.empty() && diff.removedMns.empty()) {
return;
}
connman.ForEachNode([&oldMNList, &diff](CNode* pnode) {
const auto verifiedProRegTxHash = pnode->GetVerifiedProRegTxHash();
if (verifiedProRegTxHash.IsNull()) {
return;
}
const auto verifiedDmn = oldMNList.GetMN(verifiedProRegTxHash);
if (!verifiedDmn) {
return;
}
bool doRemove = false;
if (diff.removedMns.count(verifiedDmn->GetInternalId())) {
doRemove = true;
} else if (const auto it = diff.updatedMNs.find(verifiedDmn->GetInternalId()); it != diff.updatedMNs.end()) {
if ((it->second.fields & CDeterministicMNStateDiff::Field_pubKeyOperator) && it->second.state.pubKeyOperator.GetHash() != pnode->GetVerifiedPubKeyHash()) {
doRemove = true;
}
}
if (doRemove) {
LogPrint(BCLog::NET_NETCONN, "CMNAuth::NotifyMasternodeListChanged -- Disconnecting MN %s due to key changed/removed, peer=%d\n",
verifiedProRegTxHash.ToString(), pnode->GetId());
pnode->fDisconnect = true;
}
});
}