121bd1e26b1084a0620c76d9b7d0e0184ce5efa1
[novacoin.git] / src / alert.cpp
1 //
2 // Alert system
3 //
4
5 #include <boost/foreach.hpp>
6 #include <map>
7
8 #include "alert.h"
9 #include "key.h"
10 #include "net.h"
11 #include "sync.h"
12 #include "ui_interface.h"
13
14 using namespace std;
15
16 map<uint256, CAlert> mapAlerts;
17 CCriticalSection cs_mapAlerts;
18
19 static const char* pszMainKey = "043fa441fd4203d03f5df2b75ea14e36f20d39f43e7a61aa7552ab9bcd7ecb0e77a3be4585b13fcdaa22ef6e51f1ff6f2929bec2494385b086fb86610e33193195";
20
21 // TestNet alerts pubKey
22 static const char* pszTestKey = "0471dc165db490094d35cde15b1f5d755fa6ad6f2b5ed0f340e3f17f57389c3c2af113a8cbcc885bde73305a553b5640c83021128008ddf882e856336269080496";
23
24 // TestNet alerts private key
25 // "308201130201010420b665cff1884e53da26376fd1b433812c9a5a8a4d5221533b15b9629789bb7e42a081a53081a2020101302c06072a8648ce3d0101022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f300604010004010704410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141020101a1440342000471dc165db490094d35cde15b1f5d755fa6ad6f2b5ed0f340e3f17f57389c3c2af113a8cbcc885bde73305a553b5640c83021128008ddf882e856336269080496"
26
27 void CUnsignedAlert::SetNull()
28 {
29     nVersion = 1;
30     nRelayUntil = 0;
31     nExpiration = 0;
32     nID = 0;
33     nCancel = 0;
34     setCancel.clear();
35     nMinVer = 0;
36     nMaxVer = 0;
37     setSubVer.clear();
38     nPriority = 0;
39
40     strComment.clear();
41     strStatusBar.clear();
42     strReserved.clear();
43 }
44
45 std::string CUnsignedAlert::ToString() const
46 {
47     std::string strSetCancel;
48     BOOST_FOREACH(int n, setCancel)
49         strSetCancel += strprintf("%d ", n);
50     std::string strSetSubVer;
51     BOOST_FOREACH(std::string str, setSubVer)
52         strSetSubVer += "\"" + str + "\" ";
53     return strprintf(
54         "CAlert(\n"
55         "    nVersion     = %d\n"
56         "    nRelayUntil  = %" PRId64 "\n"
57         "    nExpiration  = %" PRId64 "\n"
58         "    nID          = %d\n"
59         "    nCancel      = %d\n"
60         "    setCancel    = %s\n"
61         "    nMinVer      = %d\n"
62         "    nMaxVer      = %d\n"
63         "    setSubVer    = %s\n"
64         "    nPriority    = %d\n"
65         "    strComment   = \"%s\"\n"
66         "    strStatusBar = \"%s\"\n"
67         ")\n",
68         nVersion,
69         nRelayUntil,
70         nExpiration,
71         nID,
72         nCancel,
73         strSetCancel.c_str(),
74         nMinVer,
75         nMaxVer,
76         strSetSubVer.c_str(),
77         nPriority,
78         strComment.c_str(),
79         strStatusBar.c_str());
80 }
81
82 void CUnsignedAlert::print() const
83 {
84     printf("%s", ToString().c_str());
85 }
86
87 void CAlert::SetNull()
88 {
89     CUnsignedAlert::SetNull();
90     vchMsg.clear();
91     vchSig.clear();
92 }
93
94 bool CAlert::IsNull() const
95 {
96     return (nExpiration == 0);
97 }
98
99 uint256 CAlert::GetHash() const
100 {
101     return Hash(this->vchMsg.begin(), this->vchMsg.end());
102 }
103
104 bool CAlert::IsInEffect() const
105 {
106     return (GetAdjustedTime() < nExpiration);
107 }
108
109 bool CAlert::Cancels(const CAlert& alert) const
110 {
111     if (!IsInEffect())
112         return false; // this was a no-op before 31403
113     return (alert.nID <= nCancel || setCancel.count(alert.nID));
114 }
115
116 bool CAlert::AppliesTo(int nVersion, std::string strSubVerIn) const
117 {
118     // TODO: rework for client-version-embedded-in-strSubVer ?
119     return (IsInEffect() &&
120             nMinVer <= nVersion && nVersion <= nMaxVer &&
121             (setSubVer.empty() || setSubVer.count(strSubVerIn)));
122 }
123
124 bool CAlert::AppliesToMe() const
125 {
126     return AppliesTo(PROTOCOL_VERSION, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector<std::string>()));
127 }
128
129 bool CAlert::RelayTo(CNode* pnode) const
130 {
131     if (!IsInEffect())
132         return false;
133     // don't relay to nodes which haven't sent their version message
134     if (pnode->nVersion == 0)
135         return false;
136     // returns true if wasn't already contained in the set
137     if (pnode->setKnown.insert(GetHash()).second)
138     {
139         if (AppliesTo(pnode->nVersion, pnode->strSubVer) ||
140             AppliesToMe() ||
141             GetAdjustedTime() < nRelayUntil)
142         {
143             pnode->PushMessage("alert", *this);
144             return true;
145         }
146     }
147     return false;
148 }
149
150 bool CAlert::CheckSignature() const
151 {
152     CKey key;
153     if (!key.SetPubKey(ParseHex(fTestNet ? pszTestKey : pszMainKey)))
154         return error("CAlert::CheckSignature() : SetPubKey failed");
155     if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig))
156         return error("CAlert::CheckSignature() : verify signature failed");
157
158     // Now unserialize the data
159     CDataStream sMsg(vchMsg, SER_NETWORK, PROTOCOL_VERSION);
160     sMsg >> *(CUnsignedAlert*)this;
161     return true;
162 }
163
164 CAlert CAlert::getAlertByHash(const uint256 &hash)
165 {
166     CAlert retval;
167     {
168         LOCK(cs_mapAlerts);
169         map<uint256, CAlert>::iterator mi = mapAlerts.find(hash);
170         if(mi != mapAlerts.end())
171             retval = mi->second;
172     }
173     return retval;
174 }
175
176 bool CAlert::ProcessAlert()
177 {
178     if (!CheckSignature())
179         return false;
180     if (!IsInEffect())
181         return false;
182
183     // alert.nID=max is reserved for if the alert key is
184     // compromised. It must have a pre-defined message,
185     // must never expire, must apply to all versions,
186     // and must cancel all previous
187     // alerts or it will be ignored (so an attacker can't
188     // send an "everything is OK, don't panic" version that
189     // cannot be overridden):
190     int maxInt = std::numeric_limits<int>::max();
191     if (nID == maxInt)
192     {
193         if (!(
194                 nExpiration == maxInt &&
195                 nCancel == (maxInt-1) &&
196                 nMinVer == 0 &&
197                 nMaxVer == maxInt &&
198                 setSubVer.empty() &&
199                 nPriority == maxInt &&
200                 strStatusBar == "URGENT: Alert key compromised, upgrade required"
201                 ))
202             return false;
203     }
204
205     {
206         LOCK(cs_mapAlerts);
207         // Cancel previous alerts
208         for (map<uint256, CAlert>::iterator mi = mapAlerts.begin(); mi != mapAlerts.end();)
209         {
210             const CAlert& alert = (*mi).second;
211             if (Cancels(alert))
212             {
213                 printf("cancelling alert %d\n", alert.nID);
214                 uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED);
215                 mapAlerts.erase(mi++);
216             }
217             else if (!alert.IsInEffect())
218             {
219                 printf("expiring alert %d\n", alert.nID);
220                 uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED);
221                 mapAlerts.erase(mi++);
222             }
223             else
224                 mi++;
225         }
226
227         // Check if this alert has been cancelled
228         BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts)
229         {
230             const CAlert& alert = item.second;
231             if (alert.Cancels(*this))
232             {
233                 printf("alert already cancelled by %d\n", alert.nID);
234                 return false;
235             }
236         }
237
238         // Add to mapAlerts
239         mapAlerts.insert(make_pair(GetHash(), *this));
240         // Notify UI if it applies to me
241         if(AppliesToMe())
242             uiInterface.NotifyAlertChanged(GetHash(), CT_NEW);
243     }
244
245     printf("accepted alert %d, AppliesToMe()=%d\n", nID, AppliesToMe());
246     return true;
247 }