1
+ // Copyright (c) 2019 Duality Blockchain Solutions Developers
2
+ // Distributed under the MIT software license, see the accompanying
3
+ // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
+
5
+ #include " bdap/globaldata.h"
6
+
7
+ #include " base58.h"
8
+ #include " bdap/domainentry.h"
9
+ #include " bdap/domainentrydb.h"
10
+ #include " bdap/utils.h"
11
+ #include " hash.h"
12
+ #include " net.h" // for g_connman
13
+ #include " netmessagemaker.h"
14
+ #include " streams.h"
15
+ #include " timedata.h"
16
+ #include " util.h"
17
+
18
+
19
+ static std::map<uint256, int64_t > mapRecentMessageLog;
20
+ static CCriticalSection cs_mapRecentMessageLog;
21
+ static int nMessageCounter = 0 ;
22
+
23
+ void CGlobalData::Serialize (std::vector<unsigned char >& vchData)
24
+ {
25
+ CDataStream dsGlobalData (SER_NETWORK, PROTOCOL_VERSION);
26
+ dsGlobalData << *this ;
27
+ vchData = std::vector<unsigned char >(dsGlobalData.begin (), dsGlobalData.end ());
28
+ }
29
+
30
+ bool CGlobalData::UnserializeFromData (const std::vector<unsigned char >& vchData)
31
+ {
32
+ try {
33
+ CDataStream dsGlobalData (vchData, SER_NETWORK, PROTOCOL_VERSION);
34
+ dsGlobalData >> *this ;
35
+ } catch (std::exception &e) {
36
+ SetNull ();
37
+ return false ;
38
+ }
39
+ return true ;
40
+ }
41
+
42
+ std::string CGlobalData::ToString () const
43
+ {
44
+ return strprintf (
45
+ " CGlobalData(\n "
46
+ " nVersion = %d\n "
47
+ " nTimeStamp = %d\n "
48
+ " nType = %d\n "
49
+ " vchFQDN = %s\n "
50
+ " vchData = %s\n "
51
+ " )\n " ,
52
+ nVersion,
53
+ nTimeStamp,
54
+ nType,
55
+ stringFromVch (vchFQDN),
56
+ stringFromVch (vchData));
57
+ }
58
+
59
+ CPubKey CGlobalData::GetPubKey () const
60
+ {
61
+ CPubKey pubKey;
62
+ pubKey.Set (vchWalletPubKey.begin (), vchWalletPubKey.end ());
63
+ return pubKey;
64
+ }
65
+
66
+ CDynamicAddress CGlobalData::WalletAddress () const
67
+ {
68
+ return CDynamicAddress (GetPubKey ().GetID ());
69
+ }
70
+
71
+ uint256 CGlobalAvatar::GetHash () const
72
+ {
73
+ return Hash (this ->vchGlobalData .begin (), this ->vchGlobalData .end ());
74
+ }
75
+
76
+ void CGlobalAvatar::Serialize (std::vector<unsigned char >& vchData)
77
+ {
78
+ CDataStream dsAvatarData (SER_NETWORK, PROTOCOL_VERSION);
79
+ dsAvatarData << *this ;
80
+ vchData = std::vector<unsigned char >(dsAvatarData.begin (), dsAvatarData.end ());
81
+ }
82
+
83
+ bool CGlobalAvatar::UnserializeFromData (const std::vector<unsigned char >& vchData)
84
+ {
85
+ try {
86
+ CDataStream dsAvatarData (vchData, SER_NETWORK, PROTOCOL_VERSION);
87
+ dsAvatarData >> *this ;
88
+ } catch (std::exception &e) {
89
+ SetNull ();
90
+ return false ;
91
+ }
92
+ return true ;
93
+ }
94
+
95
+ bool CGlobalAvatar::Sign (const CKey& key)
96
+ {
97
+ if (!key.Sign (Hash (vchGlobalData.begin (), vchGlobalData.end ()), vchSignature)) {
98
+ LogPrintf (" CGlobalAvatar::%s -- Failed to sign global avatar message.\n " , __func__);
99
+ return false ;
100
+ }
101
+
102
+ return true ;
103
+ }
104
+
105
+ bool CGlobalAvatar::CheckSignature (const std::vector<unsigned char >& vchPubKey) const
106
+ {
107
+ CPubKey key (vchPubKey);
108
+ if (!key.Verify (Hash (vchGlobalData.begin (), vchGlobalData.end ()), vchSignature))
109
+ return error (" CGlobalAvatar::%s(): verify signature failed" , __func__);
110
+
111
+ return true ;
112
+ }
113
+
114
+ bool CGlobalAvatar::RelayMessage (CConnman& connman) const
115
+ {
116
+ CGlobalData data (vchGlobalData);
117
+ if (RELAY_KEEP_ALIVE_SECONDS > std::abs (GetAdjustedTime () - data.nTimeStamp ))
118
+ return false ;
119
+
120
+ connman.ForEachNode ([&connman, this ](CNode* pnode) {
121
+ if (pnode->nVersion != 0 && pnode->nVersion >= MIN_GLOBAL_DATA_PEER_PROTO_VERSION)
122
+ {
123
+ CNetMsgMaker msgMaker (pnode->GetSendVersion ());
124
+ // returns true if wasn't already contained in the set
125
+ if (pnode->setKnown .insert (GetHash ()).second ) {
126
+ connman.PushMessage (pnode, msgMaker.Make (NetMsgType::AVATAR, (*this )));
127
+ }
128
+ }
129
+ });
130
+ return true ;
131
+ }
132
+
133
+ int CGlobalAvatar::ProcessMessage (std::string& strErrorMessage) const
134
+ {
135
+ int64_t nCurrentTimeStamp = GetAdjustedTime ();
136
+ CGlobalData data (vchGlobalData);
137
+ if (ReceivedGlobalDataMessage (GetHash ()))
138
+ {
139
+ strErrorMessage = " Message already received." ;
140
+ return -1 ; // do not relay message again
141
+ }
142
+ if (std::abs (nCurrentTimeStamp - data.nTimeStamp ) > MAX_GLOBAL_MESSAGE_DRIFT_SECONDS)
143
+ {
144
+ strErrorMessage = " Message exceeds maximum time drift." ;
145
+ return -2 ; // message too old or into the future (time drift exceeds maximum allowed)
146
+ }
147
+ if (data.vchFQDN .size () > MAX_OBJECT_FULL_PATH_LENGTH)
148
+ {
149
+ strErrorMessage = " Message length exceeds limit. Adding 10 to ban score." ;
150
+ return 10 ; // this will add 100 to the peer's ban score
151
+ }
152
+ if (vchGlobalData.size () > MAX_GLOBAL_MESSAGE_SIGNATURE_SIZE)
153
+ {
154
+ strErrorMessage = " Signature size is too large. Adding 100 to ban score." ;
155
+ return 10 ; // this will add 100 to the peer's ban score
156
+ }
157
+ if (vchSignature.size () > MAX_GLOBAL_MESSAGE_SIGNATURE_SIZE)
158
+ {
159
+ strErrorMessage = " Signature size is too large. Adding 100 to ban score." ;
160
+ return 100 ; // this will add 100 to the peer's ban score
161
+ }
162
+ // Get the BDAP account entry
163
+ CDomainEntry entry;
164
+ // TODO (BDAP): Make sure local node has a synced blockchain
165
+ if (!GetDomainEntry (data.vchFQDN , entry)) {
166
+ strErrorMessage = " BDAP account in the global avatar message not found. Adding 100 to ban score." ;
167
+ return 100 ; // this will add 100 to the peer's ban score
168
+ }
169
+ // Verify message wallet address pubkey matches BDAP account wallet address
170
+ if (data.WalletAddress () != entry.GetWalletAddress ()) {
171
+ strErrorMessage = " Global avatar message wallet address does not match BDAP entry. Adding 100 to ban score." ;
172
+ return 100 ; // this will add 100 to the peer's ban score
173
+ }
174
+ // Verify signature is correct for the given wallet address pubkey.
175
+ if (!CheckSignature (data.vchWalletPubKey ))
176
+ {
177
+ strErrorMessage = " Global avatar message has an invalid signature. Adding 100 to ban score." ;
178
+ return 100 ; // this will add 100 to the peer's ban score
179
+ }
180
+ // TODO (BDAP): Write to BDAP account database
181
+
182
+ return 0 ; // All checks okay, relay message to peers.
183
+ }
184
+
185
+ bool CGlobalAvatar::RelayTo (CNode* pnode, CConnman& connman) const
186
+ {
187
+ if (pnode->nVersion != 0 && pnode->nVersion >= MIN_GLOBAL_DATA_PEER_PROTO_VERSION)
188
+ {
189
+ CNetMsgMaker msgMaker (pnode->GetSendVersion ());
190
+ CGlobalData data (vchGlobalData);
191
+ if (pnode->setKnown .insert (GetHash ()).second ) {
192
+ if (GetAdjustedTime () < data.nTimeStamp ) {
193
+ connman.PushMessage (pnode, msgMaker.Make (NetMsgType::AVATAR, (*this )));
194
+ }
195
+ else {
196
+ return false ;
197
+ }
198
+ }
199
+ else {
200
+ return false ;
201
+ }
202
+ }
203
+ else {
204
+ return false ;
205
+ }
206
+ return true ;
207
+ }
208
+
209
+ uint256 CGlobalProfile::GetHash () const
210
+ {
211
+ return Hash (this ->vchGlobalData .begin (), this ->vchGlobalData .end ());
212
+ }
213
+
214
+ void CGlobalProfile::Serialize (std::vector<unsigned char >& vchData)
215
+ {
216
+ CDataStream dsProfileData (SER_NETWORK, PROTOCOL_VERSION);
217
+ dsProfileData << *this ;
218
+ vchData = std::vector<unsigned char >(dsProfileData.begin (), dsProfileData.end ());
219
+ }
220
+
221
+ bool CGlobalProfile::UnserializeFromData (const std::vector<unsigned char >& vchData)
222
+ {
223
+ try {
224
+ CDataStream dsProfileData (vchData, SER_NETWORK, PROTOCOL_VERSION);
225
+ dsProfileData >> *this ;
226
+ } catch (std::exception &e) {
227
+ SetNull ();
228
+ return false ;
229
+ }
230
+ return true ;
231
+ }
232
+
233
+ bool CGlobalProfile::Sign (const CKey& key)
234
+ {
235
+ if (!key.Sign (Hash (vchGlobalData.begin (), vchGlobalData.end ()), vchSignature)) {
236
+ LogPrintf (" CGlobalProfile::%s -- Failed to sign global profile message.\n " , __func__);
237
+ return false ;
238
+ }
239
+
240
+ return true ;
241
+ }
242
+
243
+ bool CGlobalProfile::CheckSignature (const std::vector<unsigned char >& vchPubKey) const
244
+ {
245
+ CPubKey key (vchPubKey);
246
+ if (!key.Verify (Hash (vchGlobalData.begin (), vchGlobalData.end ()), vchSignature))
247
+ return error (" CGlobalProfile::%s(): verify signature failed" , __func__);
248
+
249
+ return true ;
250
+ }
251
+
252
+ bool CGlobalProfile::RelayMessage (CConnman& connman) const
253
+ {
254
+ CGlobalData data (vchGlobalData);
255
+ if (RELAY_KEEP_ALIVE_SECONDS > std::abs (GetAdjustedTime () - data.nTimeStamp ))
256
+ return false ;
257
+
258
+ connman.ForEachNode ([&connman, this ](CNode* pnode) {
259
+ if (pnode->nVersion != 0 && pnode->nVersion >= MIN_GLOBAL_DATA_PEER_PROTO_VERSION)
260
+ {
261
+ CNetMsgMaker msgMaker (pnode->GetSendVersion ());
262
+ // returns true if wasn't already contained in the set
263
+ if (pnode->setKnown .insert (GetHash ()).second ) {
264
+ connman.PushMessage (pnode, msgMaker.Make (NetMsgType::PROFILE, (*this )));
265
+ }
266
+ }
267
+ });
268
+ return true ;
269
+ }
270
+
271
+ int CGlobalProfile::ProcessMessage (std::string& strErrorMessage) const
272
+ {
273
+ int64_t nCurrentTimeStamp = GetAdjustedTime ();
274
+ CGlobalData data (vchGlobalData);
275
+ if (ReceivedGlobalDataMessage (GetHash ()))
276
+ {
277
+ strErrorMessage = " Message already received." ;
278
+ return -1 ; // do not relay message again
279
+ }
280
+ if (std::abs (nCurrentTimeStamp - data.nTimeStamp ) > MAX_GLOBAL_MESSAGE_DRIFT_SECONDS)
281
+ {
282
+ strErrorMessage = " Message exceeds maximum time drift." ;
283
+ return -2 ; // message too old or into the future (time drift exceeds maximum allowed)
284
+ }
285
+ if (data.vchFQDN .size () > MAX_OBJECT_FULL_PATH_LENGTH)
286
+ {
287
+ strErrorMessage = " Message length exceeds limit. Adding 10 to ban score." ;
288
+ return 10 ; // this will add 100 to the peer's ban score
289
+ }
290
+ if (vchGlobalData.size () > MAX_GLOBAL_MESSAGE_DATA_LENGTH)
291
+ {
292
+ strErrorMessage = " Signature size is too large. Adding 100 to ban score." ;
293
+ return 10 ; // this will add 100 to the peer's ban score
294
+ }
295
+ if (vchSignature.size () > MAX_GLOBAL_MESSAGE_SIGNATURE_SIZE)
296
+ {
297
+ strErrorMessage = " Signature size is too large. Adding 100 to ban score." ;
298
+ return 100 ; // this will add 100 to the peer's ban score
299
+ }
300
+ // Get the BDAP account entry
301
+ CDomainEntry entry;
302
+ // TODO (BDAP): Make sure local node has a synced blockchain
303
+ if (!GetDomainEntry (data.vchFQDN , entry)) {
304
+ strErrorMessage = " BDAP account in the global profile message not found. Adding 100 to ban score." ;
305
+ return 100 ; // this will add 100 to the peer's ban score
306
+ }
307
+ // Verify message wallet address pubkey matches BDAP account wallet address
308
+ if (data.WalletAddress () != entry.GetWalletAddress ()) {
309
+ strErrorMessage = " Global profile message wallet address does not match BDAP entry. Adding 100 to ban score." ;
310
+ return 100 ; // this will add 100 to the peer's ban score
311
+ }
312
+ // Verify signature is correct for the given wallet address pubkey.
313
+ if (!CheckSignature (data.vchWalletPubKey ))
314
+ {
315
+ strErrorMessage = " Global profile message has an invalid signature. Adding 100 to ban score." ;
316
+ return 100 ; // this will add 100 to the peer's ban score
317
+ }
318
+ // TODO (BDAP): Write to BDAP account database
319
+
320
+ return 0 ; // All checks okay, relay message to peers.
321
+ }
322
+
323
+ bool CGlobalProfile::RelayTo (CNode* pnode, CConnman& connman) const
324
+ {
325
+ if (pnode->nVersion != 0 && pnode->nVersion >= MIN_GLOBAL_DATA_PEER_PROTO_VERSION)
326
+ {
327
+ CNetMsgMaker msgMaker (pnode->GetSendVersion ());
328
+ CGlobalData data (vchGlobalData);
329
+ if (pnode->setKnown .insert (GetHash ()).second ) {
330
+ if (GetAdjustedTime () < data.nTimeStamp ) {
331
+ connman.PushMessage (pnode, msgMaker.Make (NetMsgType::PROFILE, (*this )));
332
+ }
333
+ else {
334
+ return false ;
335
+ }
336
+ }
337
+ else {
338
+ return false ;
339
+ }
340
+ }
341
+ else {
342
+ return false ;
343
+ }
344
+ return true ;
345
+ }
346
+
347
+ bool ReceivedGlobalDataMessage (const uint256& messageHash)
348
+ {
349
+ LOCK (cs_mapRecentMessageLog);
350
+ std::map<uint256, int64_t >::iterator iMessage = mapRecentMessageLog.find (messageHash);
351
+ if (iMessage != mapRecentMessageLog.end ())
352
+ {
353
+ return true ;
354
+ }
355
+ else
356
+ {
357
+ int64_t nTimeStamp = GetAdjustedTime ();
358
+ mapRecentMessageLog[messageHash] = nTimeStamp;
359
+ nMessageCounter++;
360
+ }
361
+ return false ;
362
+ }
0 commit comments