]> Git Repo - VerusCoin.git/blob - src/alert.cpp
Merge pull request #2644 from sipa/constfindblock
[VerusCoin.git] / src / alert.cpp
1 //
2 // Alert system
3 //
4
5 #include <algorithm>
6 #include <boost/algorithm/string/classification.hpp>
7 #include <boost/algorithm/string/replace.hpp>
8 #include <boost/foreach.hpp>
9 #include <map>
10
11 #include "alert.h"
12 #include "key.h"
13 #include "net.h"
14 #include "sync.h"
15 #include "ui_interface.h"
16
17 using namespace std;
18
19 map<uint256, CAlert> mapAlerts;
20 CCriticalSection cs_mapAlerts;
21
22 static const char* pszMainKey = "04fc9702847840aaf195de8442ebecedf5b095cdbb9bc716bda9110971b28a49e0ead8564ff0db22209e0374782c093bb899692d524e9d6a6956e7c5ecbcd68284";
23 static const char* pszTestKey = "04302390343f91cc401d56d68b123028bf52e5fca1939df127f63c6467cdf9c8e2c14b61104cf817d0b780da337893ecc4aaff1309e536162dabbdb45200ca2b0a";
24
25 void CUnsignedAlert::SetNull()
26 {
27     nVersion = 1;
28     nRelayUntil = 0;
29     nExpiration = 0;
30     nID = 0;
31     nCancel = 0;
32     setCancel.clear();
33     nMinVer = 0;
34     nMaxVer = 0;
35     setSubVer.clear();
36     nPriority = 0;
37
38     strComment.clear();
39     strStatusBar.clear();
40     strReserved.clear();
41 }
42
43 std::string CUnsignedAlert::ToString() const
44 {
45     std::string strSetCancel;
46     BOOST_FOREACH(int n, setCancel)
47         strSetCancel += strprintf("%d ", n);
48     std::string strSetSubVer;
49     BOOST_FOREACH(std::string str, setSubVer)
50         strSetSubVer += "\"" + str + "\" ";
51     return strprintf(
52         "CAlert(\n"
53         "    nVersion     = %d\n"
54         "    nRelayUntil  = %"PRI64d"\n"
55         "    nExpiration  = %"PRI64d"\n"
56         "    nID          = %d\n"
57         "    nCancel      = %d\n"
58         "    setCancel    = %s\n"
59         "    nMinVer      = %d\n"
60         "    nMaxVer      = %d\n"
61         "    setSubVer    = %s\n"
62         "    nPriority    = %d\n"
63         "    strComment   = \"%s\"\n"
64         "    strStatusBar = \"%s\"\n"
65         ")\n",
66         nVersion,
67         nRelayUntil,
68         nExpiration,
69         nID,
70         nCancel,
71         strSetCancel.c_str(),
72         nMinVer,
73         nMaxVer,
74         strSetSubVer.c_str(),
75         nPriority,
76         strComment.c_str(),
77         strStatusBar.c_str());
78 }
79
80 void CUnsignedAlert::print() const
81 {
82     printf("%s", ToString().c_str());
83 }
84
85 void CAlert::SetNull()
86 {
87     CUnsignedAlert::SetNull();
88     vchMsg.clear();
89     vchSig.clear();
90 }
91
92 bool CAlert::IsNull() const
93 {
94     return (nExpiration == 0);
95 }
96
97 uint256 CAlert::GetHash() const
98 {
99     return Hash(this->vchMsg.begin(), this->vchMsg.end());
100 }
101
102 bool CAlert::IsInEffect() const
103 {
104     return (GetAdjustedTime() < nExpiration);
105 }
106
107 bool CAlert::Cancels(const CAlert& alert) const
108 {
109     if (!IsInEffect())
110         return false; // this was a no-op before 31403
111     return (alert.nID <= nCancel || setCancel.count(alert.nID));
112 }
113
114 bool CAlert::AppliesTo(int nVersion, std::string strSubVerIn) const
115 {
116     // TODO: rework for client-version-embedded-in-strSubVer ?
117     return (IsInEffect() &&
118             nMinVer <= nVersion && nVersion <= nMaxVer &&
119             (setSubVer.empty() || setSubVer.count(strSubVerIn)));
120 }
121
122 bool CAlert::AppliesToMe() const
123 {
124     return AppliesTo(PROTOCOL_VERSION, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector<std::string>()));
125 }
126
127 bool CAlert::RelayTo(CNode* pnode) const
128 {
129     if (!IsInEffect())
130         return false;
131     // returns true if wasn't already contained in the set
132     if (pnode->setKnown.insert(GetHash()).second)
133     {
134         if (AppliesTo(pnode->nVersion, pnode->strSubVer) ||
135             AppliesToMe() ||
136             GetAdjustedTime() < nRelayUntil)
137         {
138             pnode->PushMessage("alert", *this);
139             return true;
140         }
141     }
142     return false;
143 }
144
145 bool CAlert::CheckSignature() const
146 {
147     CPubKey key(ParseHex(fTestNet ? pszTestKey : pszMainKey));
148     if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig))
149         return error("CAlert::CheckSignature() : verify signature failed");
150
151     // Now unserialize the data
152     CDataStream sMsg(vchMsg, SER_NETWORK, PROTOCOL_VERSION);
153     sMsg >> *(CUnsignedAlert*)this;
154     return true;
155 }
156
157 CAlert CAlert::getAlertByHash(const uint256 &hash)
158 {
159     CAlert retval;
160     {
161         LOCK(cs_mapAlerts);
162         map<uint256, CAlert>::iterator mi = mapAlerts.find(hash);
163         if(mi != mapAlerts.end())
164             retval = mi->second;
165     }
166     return retval;
167 }
168
169 bool CAlert::ProcessAlert(bool fThread)
170 {
171     if (!CheckSignature())
172         return false;
173     if (!IsInEffect())
174         return false;
175
176     // alert.nID=max is reserved for if the alert key is
177     // compromised. It must have a pre-defined message,
178     // must never expire, must apply to all versions,
179     // and must cancel all previous
180     // alerts or it will be ignored (so an attacker can't
181     // send an "everything is OK, don't panic" version that
182     // cannot be overridden):
183     int maxInt = std::numeric_limits<int>::max();
184     if (nID == maxInt)
185     {
186         if (!(
187                 nExpiration == maxInt &&
188                 nCancel == (maxInt-1) &&
189                 nMinVer == 0 &&
190                 nMaxVer == maxInt &&
191                 setSubVer.empty() &&
192                 nPriority == maxInt &&
193                 strStatusBar == "URGENT: Alert key compromised, upgrade required"
194                 ))
195             return false;
196     }
197
198     {
199         LOCK(cs_mapAlerts);
200         // Cancel previous alerts
201         for (map<uint256, CAlert>::iterator mi = mapAlerts.begin(); mi != mapAlerts.end();)
202         {
203             const CAlert& alert = (*mi).second;
204             if (Cancels(alert))
205             {
206                 printf("cancelling alert %d\n", alert.nID);
207                 uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED);
208                 mapAlerts.erase(mi++);
209             }
210             else if (!alert.IsInEffect())
211             {
212                 printf("expiring alert %d\n", alert.nID);
213                 uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED);
214                 mapAlerts.erase(mi++);
215             }
216             else
217                 mi++;
218         }
219
220         // Check if this alert has been cancelled
221         BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts)
222         {
223             const CAlert& alert = item.second;
224             if (alert.Cancels(*this))
225             {
226                 printf("alert already cancelled by %d\n", alert.nID);
227                 return false;
228             }
229         }
230
231         // Add to mapAlerts
232         mapAlerts.insert(make_pair(GetHash(), *this));
233         // Notify UI and -alertnotify if it applies to me
234         if(AppliesToMe())
235         {
236             uiInterface.NotifyAlertChanged(GetHash(), CT_NEW);
237             std::string strCmd = GetArg("-alertnotify", "");
238             if (!strCmd.empty())
239             {
240                 // Alert text should be plain ascii coming from a trusted source, but to
241                 // be safe we first strip anything not in safeChars, then add single quotes around
242                 // the whole string before passing it to the shell:
243                 std::string singleQuote("'");
244                 // safeChars chosen to allow simple messages/URLs/email addresses, but avoid anything
245                 // even possibly remotely dangerous like & or >
246                 std::string safeChars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890 .,;_/:?@");
247                 std::string safeStatus;
248                 for (std::string::size_type i = 0; i < strStatusBar.size(); i++)
249                 {
250                     if (safeChars.find(strStatusBar[i]) != std::string::npos)
251                         safeStatus.push_back(strStatusBar[i]);
252                 }
253                 safeStatus = singleQuote+safeStatus+singleQuote;
254                 boost::replace_all(strCmd, "%s", safeStatus);
255
256                 if (fThread)
257                     boost::thread t(runCommand, strCmd); // thread runs free
258                 else
259                     runCommand(strCmd);
260             }
261         }
262     }
263
264     printf("accepted alert %d, AppliesToMe()=%d\n", nID, AppliesToMe());
265     return true;
266 }
This page took 0.037722 seconds and 4 git commands to generate.