Skip to content

Commit

Permalink
CAlert
Browse files Browse the repository at this point in the history
  • Loading branch information
Bushstar committed Mar 11, 2019
1 parent c77e670 commit c4bf142
Show file tree
Hide file tree
Showing 19 changed files with 553 additions and 35 deletions.
2 changes: 2 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ endif
BITCOIN_CORE_H = \
addrdb.h \
addrman.h \
alert.h \
base58.h \
bech32.h \
bloom.h \
Expand Down Expand Up @@ -218,6 +219,7 @@ libbitcoin_server_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_server_a_SOURCES = \
addrdb.cpp \
addrman.cpp \
alert.cpp \
bloom.cpp \
blockencodings.cpp \
chain.cpp \
Expand Down
256 changes: 256 additions & 0 deletions src/alert.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2015 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <alert.h>

#include <clientversion.h>
#include <net.h>
#include <pubkey.h>
#include <timedata.h>
#include <ui_interface.h>
#include <util.h>
#include <utilstrencodings.h>
#include <netmessagemaker.h>

#include <stdint.h>
#include <algorithm>
#include <map>

#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/thread.hpp>

using namespace std;

map<uint256, CAlert> mapAlerts;
CCriticalSection cs_mapAlerts;

void CUnsignedAlert::SetNull()
{
nVersion = 1;
nRelayUntil = 0;
nExpiration = 0;
nID = 0;
nCancel = 0;
setCancel.clear();
nMinVer = 0;
nMaxVer = 0;
setSubVer.clear();
nPriority = 0;

strComment.clear();
strStatusBar.clear();
strReserved.clear();
}

std::string CUnsignedAlert::ToString() const
{
std::string strSetCancel;
for(int n : setCancel)
strSetCancel += strprintf("%d ", n);
std::string strSetSubVer;
for(const std::string& str : setSubVer)
strSetSubVer += "\"" + str + "\" ";
return strprintf(
"CAlert(\n"
" nVersion = %d\n"
" nRelayUntil = %d\n"
" nExpiration = %d\n"
" nID = %d\n"
" nCancel = %d\n"
" setCancel = %s\n"
" nMinVer = %d\n"
" nMaxVer = %d\n"
" setSubVer = %s\n"
" nPriority = %d\n"
" strComment = \"%s\"\n"
" strStatusBar = \"%s\"\n"
")\n",
nVersion,
nRelayUntil,
nExpiration,
nID,
nCancel,
strSetCancel,
nMinVer,
nMaxVer,
strSetSubVer,
nPriority,
strComment,
strStatusBar);
}

void CAlert::SetNull()
{
CUnsignedAlert::SetNull();
vchMsg.clear();
vchSig.clear();
}

bool CAlert::IsNull() const
{
return (nExpiration == 0);
}

uint256 CAlert::GetHash() const
{
return Hash(this->vchMsg.begin(), this->vchMsg.end());
}

bool CAlert::IsInEffect() const
{
return (GetAdjustedTime() < nExpiration);
}

bool CAlert::Cancels(const CAlert& alert) const
{
if (!IsInEffect())
return false; // this was a no-op before 31403
return (alert.nID <= nCancel || setCancel.count(alert.nID));
}

bool CAlert::AppliesTo(int nVersion, const std::string& strSubVerIn) const
{
// TODO: rework for client-version-embedded-in-strSubVer ?
return (IsInEffect() &&
nMinVer <= nVersion && nVersion <= nMaxVer &&
(setSubVer.empty() || setSubVer.count(strSubVerIn)));
}

bool CAlert::AppliesToMe() const
{
return AppliesTo(PROTOCOL_VERSION, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector<std::string>()));
}

bool CAlert::RelayTo(CNode* pnode) const
{
const CNetMsgMaker msgMaker(pnode->GetSendVersion());

if (!IsInEffect())
return false;
// don't relay to nodes which haven't sent their version message
if (pnode->nVersion == 0)
return false;
// returns true if wasn't already contained in the set
if (pnode->setKnown.insert(GetHash()).second)
{
if (AppliesTo(pnode->nVersion, pnode->strSubVer) ||
AppliesToMe() ||
GetAdjustedTime() < nRelayUntil)
{
CConnman& connman = *g_connman;
connman.PushMessage(pnode, msgMaker.Make(NetMsgType::ALERT, *this));
return true;
}
}
return false;
}

bool CAlert::CheckSignature(const std::vector<unsigned char>& alertKey) const
{
CPubKey key(alertKey);
if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig))
return error("CAlert::CheckSignature(): verify signature failed");

// Now unserialize the data
CDataStream sMsg(vchMsg, SER_NETWORK, PROTOCOL_VERSION);
sMsg >> *(CUnsignedAlert*)this;
return true;
}

CAlert CAlert::getAlertByHash(const uint256 &hash)
{
CAlert retval;
{
LOCK(cs_mapAlerts);
map<uint256, CAlert>::iterator mi = mapAlerts.find(hash);
if(mi != mapAlerts.end())
retval = mi->second;
}
return retval;
}

bool CAlert::ProcessAlert(const std::vector<unsigned char>& alertKey)
{
if (!CheckSignature(alertKey))
return error("CAlert::ProcessAlert: verify signature failed");
if (!IsInEffect())
return error("CAlert::ProcessAlert: Expired");

// alert.nID=max is reserved for if the alert key is
// compromised. It must have a pre-defined message,
// must never expire, must apply to all versions,
// and must cancel all previous
// alerts or it will be ignored (so an attacker can't
// send an "everything is OK, don't panic" version that
// cannot be overridden):
int maxInt = std::numeric_limits<int>::max();
if (nID == maxInt)
{
if (!(
nExpiration == maxInt &&
nCancel == (maxInt-1) &&
nMinVer == 0 &&
nMaxVer == maxInt &&
setSubVer.empty() &&
nPriority == maxInt &&
strStatusBar == "URGENT: Alert key compromised, upgrade required"
))
return false;
}

{
LOCK(cs_mapAlerts);
// Cancel previous alerts
for (map<uint256, CAlert>::iterator mi = mapAlerts.begin(); mi != mapAlerts.end();)
{
const CAlert& alert = (*mi).second;
if (Cancels(alert))
{
uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED);
mapAlerts.erase(mi++);
}
else if (!alert.IsInEffect())
{
uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED);
mapAlerts.erase(mi++);
}
else
mi++;
}

// Check if this alert has been cancelled
for (auto& item : mapAlerts)
{
const CAlert& alert = item.second;
if (alert.Cancels(*this))
return error("CAlert::ProcessAlert: Cancelled");
}

// Add to mapAlerts
mapAlerts.insert(make_pair(GetHash(), *this));

if(AppliesToMe())
{
uiInterface.NotifyAlertChanged(GetHash(), CT_NEW);
Notify(strStatusBar);
}
}

return true;
}

void CAlert::Notify(const std::string& strMessage)
{
// Alert text should be plain ascii coming from a trusted source, but to
// be safe we first strip anything not in safeChars, then add single quotes around
// the whole string before passing it to the shell:
std::string singleQuote("'");
std::string safeStatus = SanitizeString(strMessage);
safeStatus = singleQuote+safeStatus+singleQuote;

std::thread t(runCommand, safeStatus);
t.detach(); // thread runs free
}
113 changes: 113 additions & 0 deletions src/alert.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2015 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_ALERT_H
#define BITCOIN_ALERT_H

#include <serialize.h>
#include <sync.h>

#include <map>
#include <set>
#include <stdint.h>
#include <string>

class CAlert;
class CNode;
class uint256;

extern std::map<uint256, CAlert> mapAlerts;
extern CCriticalSection cs_mapAlerts;

/** Alerts are for notifying old versions if they become too obsolete and
* need to upgrade. The message is displayed in the status bar.
* Alert messages are broadcast as a vector of signed data. Unserializing may
* not read the entire buffer if the alert is for a newer version, but older
* versions can still relay the original data.
*/
class CUnsignedAlert
{
public:
int nVersion;
int64_t nRelayUntil; // when newer nodes stop relaying to newer nodes
int64_t nExpiration;
int nID;
int nCancel;
std::set<int> setCancel;
int nMinVer; // lowest version inclusive
int nMaxVer; // highest version inclusive
std::set<std::string> setSubVer; // empty matches all
int nPriority;

// Actions
std::string strComment;
std::string strStatusBar;
std::string strReserved;

ADD_SERIALIZE_METHODS;

template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(this->nVersion);
nVersion = this->nVersion;
READWRITE(nRelayUntil);
READWRITE(nExpiration);
READWRITE(nID);
READWRITE(nCancel);
READWRITE(setCancel);
READWRITE(nMinVer);
READWRITE(nMaxVer);
READWRITE(setSubVer);
READWRITE(nPriority);

READWRITE(LIMITED_STRING(strComment, 65536));
READWRITE(LIMITED_STRING(strStatusBar, 256));
READWRITE(LIMITED_STRING(strReserved, 256));
}

void SetNull();

std::string ToString() const;
};

/** An alert is a combination of a serialized CUnsignedAlert and a signature. */
class CAlert : public CUnsignedAlert
{
public:
std::vector<unsigned char> vchMsg;
std::vector<unsigned char> vchSig;

CAlert()
{
SetNull();
}

ADD_SERIALIZE_METHODS;

template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(vchMsg);
READWRITE(vchSig);
}

void SetNull();
bool IsNull() const;
uint256 GetHash() const;
bool IsInEffect() const;
bool Cancels(const CAlert& alert) const;
bool AppliesTo(int nVersion, const std::string& strSubVerIn) const;
bool AppliesToMe() const;
bool RelayTo(CNode* pnode) const;
bool CheckSignature(const std::vector<unsigned char>& alertKey) const;
bool ProcessAlert(const std::vector<unsigned char>& alertKey);
static void Notify(const std::string& strMessage);

/*
* Get copy of (active) alert object by hash. Returns a null alert if it is not found.
*/
static CAlert getAlertByHash(const uint256 &hash);
};

#endif // BITCOIN_ALERT_H

0 comments on commit c4bf142

Please sign in to comment.