Skip to content

Commit 61af8b0

Browse files
committed
[BDAP] Add global account messages for avatars and profile data
1 parent cc27498 commit 61af8b0

File tree

7 files changed

+664
-0
lines changed

7 files changed

+664
-0
lines changed

src/Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ DYNAMIC_CORE_H = \
8282
bdap/domainentrydb.h \
8383
bdap/certificate.h \
8484
bdap/entrycheckpoints.h \
85+
bdap/globaldata.h \
8586
bdap/identity.h \
8687
bdap/linking.h \
8788
bdap/linkingdb.h \
@@ -268,6 +269,7 @@ libdynamic_server_a_SOURCES = \
268269
bdap/domainentrydb.cpp \
269270
bdap/certificate.cpp \
270271
bdap/entrycheckpoints.cpp \
272+
bdap/globaldata.cpp \
271273
bdap/identity.cpp \
272274
bdap/linking.cpp \
273275
bdap/linkingdb.cpp \

src/base58.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ class CBase58Data
100100
int CompareTo(const CBase58Data& b58) const;
101101

102102
bool operator==(const CBase58Data& b58) const { return CompareTo(b58) == 0; }
103+
bool operator!=(const CBase58Data& b58) const { return CompareTo(b58) != 0; }
103104
bool operator<=(const CBase58Data& b58) const { return CompareTo(b58) <= 0; }
104105
bool operator>=(const CBase58Data& b58) const { return CompareTo(b58) >= 0; }
105106
bool operator<(const CBase58Data& b58) const { return CompareTo(b58) < 0; }

src/bdap/globaldata.cpp

Lines changed: 362 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,362 @@
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

Comments
 (0)