Re-introduce alert functionality (#1470)
Re-introduce alert functionality removed from Bitcoin upstream
This commit is contained in:
parent
dee5e404b3
commit
41c868f47e
|
@ -78,6 +78,7 @@ endif
|
||||||
BITCOIN_CORE_H = \
|
BITCOIN_CORE_H = \
|
||||||
addrdb.h \
|
addrdb.h \
|
||||||
addrman.h \
|
addrman.h \
|
||||||
|
alert.h \
|
||||||
auxpow.h \
|
auxpow.h \
|
||||||
base58.h \
|
base58.h \
|
||||||
bloom.h \
|
bloom.h \
|
||||||
|
@ -182,6 +183,7 @@ libbitcoin_server_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||||
libbitcoin_server_a_SOURCES = \
|
libbitcoin_server_a_SOURCES = \
|
||||||
addrman.cpp \
|
addrman.cpp \
|
||||||
addrdb.cpp \
|
addrdb.cpp \
|
||||||
|
alert.cpp \
|
||||||
bloom.cpp \
|
bloom.cpp \
|
||||||
blockencodings.cpp \
|
blockencodings.cpp \
|
||||||
chain.cpp \
|
chain.cpp \
|
||||||
|
@ -304,6 +306,7 @@ libbitcoin_consensus_a_SOURCES = \
|
||||||
libbitcoin_common_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
|
libbitcoin_common_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
|
||||||
libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||||
libbitcoin_common_a_SOURCES = \
|
libbitcoin_common_a_SOURCES = \
|
||||||
|
alert.cpp \
|
||||||
amount.cpp \
|
amount.cpp \
|
||||||
arith_uint256.cpp \
|
arith_uint256.cpp \
|
||||||
auxpow.cpp \
|
auxpow.cpp \
|
||||||
|
|
|
@ -71,7 +71,7 @@ JSON_TEST_FILES = \
|
||||||
test/data/tx_valid.json \
|
test/data/tx_valid.json \
|
||||||
test/data/sighash.json
|
test/data/sighash.json
|
||||||
|
|
||||||
RAW_TEST_FILES =
|
RAW_TEST_FILES = test/data/alertTests.raw
|
||||||
|
|
||||||
GENERATED_TEST_FILES = $(JSON_TEST_FILES:.json=.json.h) $(RAW_TEST_FILES:.raw=.raw.h)
|
GENERATED_TEST_FILES = $(JSON_TEST_FILES:.json=.json.h) $(RAW_TEST_FILES:.raw=.raw.h)
|
||||||
|
|
||||||
|
@ -80,6 +80,7 @@ BITCOIN_TESTS =\
|
||||||
test/arith_uint256_tests.cpp \
|
test/arith_uint256_tests.cpp \
|
||||||
test/scriptnum10.h \
|
test/scriptnum10.h \
|
||||||
test/addrman_tests.cpp \
|
test/addrman_tests.cpp \
|
||||||
|
test/alert_tests.cpp \
|
||||||
test/amount_tests.cpp \
|
test/amount_tests.cpp \
|
||||||
test/allocator_tests.cpp \
|
test/allocator_tests.cpp \
|
||||||
test/auxpow_tests.cpp \
|
test/auxpow_tests.cpp \
|
||||||
|
|
246
src/alert.cpp
Normal file
246
src/alert.cpp
Normal file
|
@ -0,0 +1,246 @@
|
||||||
|
// 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 "netmessagemaker.h"
|
||||||
|
#include "pubkey.h"
|
||||||
|
#include "timedata.h"
|
||||||
|
#include "ui_interface.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "utilstrencodings.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include <boost/algorithm/string/classification.hpp>
|
||||||
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
|
#include <boost/foreach.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;
|
||||||
|
BOOST_FOREACH(int n, setCancel)
|
||||||
|
strSetCancel += strprintf("%d ", n);
|
||||||
|
std::string strSetSubVer;
|
||||||
|
BOOST_FOREACH(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::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, bool fThread)
|
||||||
|
{
|
||||||
|
if (!CheckSignature(alertKey))
|
||||||
|
return false;
|
||||||
|
if (!IsInEffect())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// 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))
|
||||||
|
{
|
||||||
|
LogPrint("alert", "cancelling alert %d\n", alert.nID);
|
||||||
|
uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED);
|
||||||
|
mapAlerts.erase(mi++);
|
||||||
|
}
|
||||||
|
else if (!alert.IsInEffect())
|
||||||
|
{
|
||||||
|
LogPrint("alert", "expiring alert %d\n", alert.nID);
|
||||||
|
uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED);
|
||||||
|
mapAlerts.erase(mi++);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
mi++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this alert has been cancelled
|
||||||
|
BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts)
|
||||||
|
{
|
||||||
|
const CAlert& alert = item.second;
|
||||||
|
if (alert.Cancels(*this))
|
||||||
|
{
|
||||||
|
LogPrint("alert", "alert already cancelled by %d\n", alert.nID);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to mapAlerts
|
||||||
|
mapAlerts.insert(make_pair(GetHash(), *this));
|
||||||
|
// Notify UI and -alertnotify if it applies to me
|
||||||
|
if(AppliesToMe())
|
||||||
|
{
|
||||||
|
uiInterface.NotifyAlertChanged(GetHash(), CT_NEW);
|
||||||
|
Notify(strStatusBar, fThread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LogPrint("alert", "accepted alert %d, AppliesToMe()=%d\n", nID, AppliesToMe());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CAlert::Notify(const std::string& strMessage, bool fThread)
|
||||||
|
{
|
||||||
|
std::string strCmd = GetArg("-alertnotify", "");
|
||||||
|
if (strCmd.empty()) return;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
boost::replace_all(strCmd, "%s", safeStatus);
|
||||||
|
|
||||||
|
if (fThread)
|
||||||
|
boost::thread t(runCommand, strCmd); // thread runs free
|
||||||
|
else
|
||||||
|
runCommand(strCmd);
|
||||||
|
}
|
112
src/alert.h
Normal file
112
src/alert.h
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
// 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 CheckSignature(const std::vector<unsigned char>& alertKey) const;
|
||||||
|
bool ProcessAlert(const std::vector<unsigned char>& alertKey, bool fThread = true);
|
||||||
|
static void Notify(const std::string& strMessage, bool fThread = true);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
|
@ -148,6 +148,7 @@ public:
|
||||||
pchMessageStart[1] = 0xc0;
|
pchMessageStart[1] = 0xc0;
|
||||||
pchMessageStart[2] = 0xc0;
|
pchMessageStart[2] = 0xc0;
|
||||||
pchMessageStart[3] = 0xc0;
|
pchMessageStart[3] = 0xc0;
|
||||||
|
vAlertPubKey = ParseHex("04d4da7a5dae4db797d9b0644d57a5cd50e05a70f36091cd62e2fc41c98ded06340be5a43a35e185690cd9cde5d72da8f6d065b499b06f51dcfba14aad859f443a");
|
||||||
nDefaultPort = 22556;
|
nDefaultPort = 22556;
|
||||||
nPruneAfterHeight = 100000;
|
nPruneAfterHeight = 100000;
|
||||||
|
|
||||||
|
@ -297,6 +298,7 @@ public:
|
||||||
pchMessageStart[1] = 0xc1;
|
pchMessageStart[1] = 0xc1;
|
||||||
pchMessageStart[2] = 0xb7;
|
pchMessageStart[2] = 0xb7;
|
||||||
pchMessageStart[3] = 0xdc;
|
pchMessageStart[3] = 0xdc;
|
||||||
|
vAlertPubKey = ParseHex("042756726da3c7ef515d89212ee1705023d14be389e25fe15611585661b9a20021908b2b80a3c7200a0139dd2b26946606aab0eef9aa7689a6dc2c7eee237fa834");
|
||||||
nDefaultPort = 44556;
|
nDefaultPort = 44556;
|
||||||
nPruneAfterHeight = 1000;
|
nPruneAfterHeight = 1000;
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
const CMessageHeader::MessageStartChars& MessageStart() const { return pchMessageStart; }
|
const CMessageHeader::MessageStartChars& MessageStart() const { return pchMessageStart; }
|
||||||
|
const std::vector<unsigned char>& AlertKey() const { return vAlertPubKey; }
|
||||||
int GetDefaultPort() const { return nDefaultPort; }
|
int GetDefaultPort() const { return nDefaultPort; }
|
||||||
|
|
||||||
const CBlock& GenesisBlock() const { return genesis; }
|
const CBlock& GenesisBlock() const { return genesis; }
|
||||||
|
@ -87,6 +88,8 @@ protected:
|
||||||
Consensus::Params consensus;
|
Consensus::Params consensus;
|
||||||
Consensus::Params *pConsensusRoot; // Binary search tree root
|
Consensus::Params *pConsensusRoot; // Binary search tree root
|
||||||
CMessageHeader::MessageStartChars pchMessageStart;
|
CMessageHeader::MessageStartChars pchMessageStart;
|
||||||
|
//! Raw pub key bytes for the broadcast alert signing key.
|
||||||
|
std::vector<unsigned char> vAlertPubKey;
|
||||||
int nDefaultPort;
|
int nDefaultPort;
|
||||||
uint64_t nPruneAfterHeight;
|
uint64_t nPruneAfterHeight;
|
||||||
std::vector<CDNSSeedData> vSeeds;
|
std::vector<CDNSSeedData> vSeeds;
|
||||||
|
|
|
@ -325,6 +325,7 @@ std::string HelpMessage(HelpMessageMode mode)
|
||||||
std::string strUsage = HelpMessageGroup(_("Options:"));
|
std::string strUsage = HelpMessageGroup(_("Options:"));
|
||||||
strUsage += HelpMessageOpt("-?", _("Print this help message and exit"));
|
strUsage += HelpMessageOpt("-?", _("Print this help message and exit"));
|
||||||
strUsage += HelpMessageOpt("-version", _("Print version and exit"));
|
strUsage += HelpMessageOpt("-version", _("Print version and exit"));
|
||||||
|
strUsage += HelpMessageOpt("-alerts", strprintf(_("Receive and display P2P network alerts (default: %u)"), DEFAULT_ALERTS));
|
||||||
strUsage += HelpMessageOpt("-alertnotify=<cmd>", _("Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)"));
|
strUsage += HelpMessageOpt("-alertnotify=<cmd>", _("Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)"));
|
||||||
strUsage += HelpMessageOpt("-blocknotify=<cmd>", _("Execute command when the best block changes (%s in cmd is replaced by block hash, %i is replaced by block number)"));
|
strUsage += HelpMessageOpt("-blocknotify=<cmd>", _("Execute command when the best block changes (%s in cmd is replaced by block hash, %i is replaced by block number)"));
|
||||||
if (showDebug)
|
if (showDebug)
|
||||||
|
@ -1051,6 +1052,8 @@ bool AppInitParameterInteraction()
|
||||||
fAcceptDatacarrier = GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER);
|
fAcceptDatacarrier = GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER);
|
||||||
nMaxDatacarrierBytes = GetArg("-datacarriersize", nMaxDatacarrierBytes);
|
nMaxDatacarrierBytes = GetArg("-datacarriersize", nMaxDatacarrierBytes);
|
||||||
|
|
||||||
|
fAlerts = GetBoolArg("-alerts", DEFAULT_ALERTS);
|
||||||
|
|
||||||
// Option to startup with mocktime set (used for regression testing):
|
// Option to startup with mocktime set (used for regression testing):
|
||||||
SetMockTime(GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op
|
SetMockTime(GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op
|
||||||
|
|
||||||
|
|
18
src/net.h
18
src/net.h
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include "addrdb.h"
|
#include "addrdb.h"
|
||||||
#include "addrman.h"
|
#include "addrman.h"
|
||||||
|
#include "alert.h"
|
||||||
#include "amount.h"
|
#include "amount.h"
|
||||||
#include "bloom.h"
|
#include "bloom.h"
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
|
@ -680,6 +681,9 @@ public:
|
||||||
CAmount lastSentFeeFilter;
|
CAmount lastSentFeeFilter;
|
||||||
int64_t nextSendTimeFeeFilter;
|
int64_t nextSendTimeFeeFilter;
|
||||||
|
|
||||||
|
// Alert relay
|
||||||
|
std::vector<CAlert> vAlertToSend;
|
||||||
|
|
||||||
CNode(NodeId id, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const std::string &addrNameIn = "", bool fInboundIn = false);
|
CNode(NodeId id, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const std::string &addrNameIn = "", bool fInboundIn = false);
|
||||||
~CNode();
|
~CNode();
|
||||||
|
|
||||||
|
@ -769,6 +773,14 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PushAlert(const CAlert& _alert)
|
||||||
|
{
|
||||||
|
// don't relay to nodes which haven't sent their version message
|
||||||
|
if (_alert.IsInEffect() && nVersion != 0) {
|
||||||
|
vAlertToSend.push_back(_alert);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void AddInventoryKnown(const CInv& inv)
|
void AddInventoryKnown(const CInv& inv)
|
||||||
{
|
{
|
||||||
|
@ -790,6 +802,12 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PushAlertHash(const uint256 &hash)
|
||||||
|
{
|
||||||
|
LOCK(cs_inventory);
|
||||||
|
vBlockHashesToAnnounce.push_back(hash);
|
||||||
|
}
|
||||||
|
|
||||||
void PushBlockHash(const uint256 &hash)
|
void PushBlockHash(const uint256 &hash)
|
||||||
{
|
{
|
||||||
LOCK(cs_inventory);
|
LOCK(cs_inventory);
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "net_processing.h"
|
#include "net_processing.h"
|
||||||
|
|
||||||
#include "addrman.h"
|
#include "addrman.h"
|
||||||
|
#include "alert.h"
|
||||||
#include "arith_uint256.h"
|
#include "arith_uint256.h"
|
||||||
#include "blockencodings.h"
|
#include "blockencodings.h"
|
||||||
#include "chainparams.h"
|
#include "chainparams.h"
|
||||||
|
@ -1366,13 +1367,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
||||||
pfrom->nTimeOffset = nTimeOffset;
|
pfrom->nTimeOffset = nTimeOffset;
|
||||||
AddTimeData(pfrom->addr, nTimeOffset);
|
AddTimeData(pfrom->addr, nTimeOffset);
|
||||||
|
|
||||||
// If the peer is old enough to have the old alert system, send it the final alert.
|
// Relay alerts
|
||||||
/* if (pfrom->nVersion <= 70012) {
|
{
|
||||||
// TODO: Replace this with a valid Dogecoin alert
|
LOCK(cs_mapAlerts);
|
||||||
// Disabled meantime as the remote client considers the nonsense alert a hack and drops the connection
|
BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts)
|
||||||
CDataStream finalAlert(ParseHex("60010000000000000000000000ffffff7f00000000ffffff7ffeffff7f01ffffff7f00000000ffffff7f00ffffff7f002f555247454e543a20416c657274206b657920636f6d70726f6d697365642c2075706772616465207265717569726564004630440220653febd6410f470f6bae11cad19c48413becb1ac2c17f908fd0fd53bdc3abd5202206d0e9c96fe88d4a0f01ed9dedae2b6f9e00da94cad0fecaae66ecf689bf71b50"), SER_NETWORK, PROTOCOL_VERSION);
|
pfrom->PushAlert(item.second);
|
||||||
connman.PushMessage(pfrom, CNetMsgMaker(nSendVersion).Make("alert", finalAlert));
|
}
|
||||||
} */
|
|
||||||
|
|
||||||
// Feeler connections exist only to verify if address is online.
|
// Feeler connections exist only to verify if address is online.
|
||||||
if (pfrom->fFeeler) {
|
if (pfrom->fFeeler) {
|
||||||
|
@ -2523,6 +2523,38 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else if (fAlerts && strCommand == NetMsgType::ALERT)
|
||||||
|
{
|
||||||
|
CAlert alert;
|
||||||
|
vRecv >> alert;
|
||||||
|
|
||||||
|
uint256 alertHash = alert.GetHash();
|
||||||
|
if (pfrom->setKnown.count(alertHash) == 0)
|
||||||
|
{
|
||||||
|
if (alert.ProcessAlert(chainparams.AlertKey()))
|
||||||
|
{
|
||||||
|
// Relay
|
||||||
|
pfrom->setKnown.insert(alertHash);
|
||||||
|
{
|
||||||
|
|
||||||
|
connman.ForEachNode([&alert](CNode* pnode)
|
||||||
|
{
|
||||||
|
pnode->PushAlert(alert);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Small DoS penalty so peers that send us lots of
|
||||||
|
// duplicate/expired/invalid-signature/whatever alerts
|
||||||
|
// eventually get banned.
|
||||||
|
// This isn't a Misbehaving(100) (immediate ban) because the
|
||||||
|
// peer might be an older or different implementation with
|
||||||
|
// a different signature key, etc.
|
||||||
|
Misbehaving(pfrom->GetId(), 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
else if (strCommand == NetMsgType::FILTERLOAD)
|
else if (strCommand == NetMsgType::FILTERLOAD)
|
||||||
{
|
{
|
||||||
|
@ -3267,6 +3299,25 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
|
||||||
pto->nextSendTimeFeeFilter = timeNow + GetRandInt(MAX_FEEFILTER_CHANGE_DELAY) * 1000000;
|
pto->nextSendTimeFeeFilter = timeNow + GetRandInt(MAX_FEEFILTER_CHANGE_DELAY) * 1000000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Message: alert
|
||||||
|
//
|
||||||
|
BOOST_FOREACH(const CAlert &alert, pto->vAlertToSend) {
|
||||||
|
// returns true if wasn't already contained in the set
|
||||||
|
if (pto->setKnown.insert(alert.GetHash()).second)
|
||||||
|
{
|
||||||
|
if (alert.AppliesTo(pto->nVersion, pto->strSubVer) ||
|
||||||
|
alert.AppliesToMe() ||
|
||||||
|
GetAdjustedTime() < alert.nRelayUntil)
|
||||||
|
{
|
||||||
|
connman.PushMessage(pto, msgMaker.Make(NetMsgType::ALERT, alert));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pto->vAlertToSend.clear();
|
||||||
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ const char *GETADDR="getaddr";
|
||||||
const char *MEMPOOL="mempool";
|
const char *MEMPOOL="mempool";
|
||||||
const char *PING="ping";
|
const char *PING="ping";
|
||||||
const char *PONG="pong";
|
const char *PONG="pong";
|
||||||
|
const char *ALERT="alert";
|
||||||
const char *NOTFOUND="notfound";
|
const char *NOTFOUND="notfound";
|
||||||
const char *FILTERLOAD="filterload";
|
const char *FILTERLOAD="filterload";
|
||||||
const char *FILTERADD="filteradd";
|
const char *FILTERADD="filteradd";
|
||||||
|
@ -60,6 +61,7 @@ const static std::string allNetMessageTypes[] = {
|
||||||
NetMsgType::MEMPOOL,
|
NetMsgType::MEMPOOL,
|
||||||
NetMsgType::PING,
|
NetMsgType::PING,
|
||||||
NetMsgType::PONG,
|
NetMsgType::PONG,
|
||||||
|
NetMsgType::ALERT,
|
||||||
NetMsgType::NOTFOUND,
|
NetMsgType::NOTFOUND,
|
||||||
NetMsgType::FILTERLOAD,
|
NetMsgType::FILTERLOAD,
|
||||||
NetMsgType::FILTERADD,
|
NetMsgType::FILTERADD,
|
||||||
|
|
|
@ -160,6 +160,13 @@ extern const char *PING;
|
||||||
* @see https://bitcoin.org/en/developer-reference#pong
|
* @see https://bitcoin.org/en/developer-reference#pong
|
||||||
*/
|
*/
|
||||||
extern const char *PONG;
|
extern const char *PONG;
|
||||||
|
/**
|
||||||
|
* The alert message warns nodes of problems that may affect them or the rest
|
||||||
|
* of the network.
|
||||||
|
* @since protocol version 311.
|
||||||
|
* @see https://bitcoin.org/en/developer-reference#alert
|
||||||
|
*/
|
||||||
|
extern const char *ALERT;
|
||||||
/**
|
/**
|
||||||
* The notfound message is a reply to a getdata message which requested an
|
* The notfound message is a reply to a getdata message which requested an
|
||||||
* object the receiving node does not have available for relay.
|
* object the receiving node does not have available for relay.
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "guiutil.h"
|
#include "guiutil.h"
|
||||||
#include "peertablemodel.h"
|
#include "peertablemodel.h"
|
||||||
|
|
||||||
|
#include "alert.h"
|
||||||
#include "chainparams.h"
|
#include "chainparams.h"
|
||||||
#include "checkpoints.h"
|
#include "checkpoints.h"
|
||||||
#include "clientversion.h"
|
#include "clientversion.h"
|
||||||
|
@ -163,8 +164,20 @@ void ClientModel::updateNetworkActive(bool networkActive)
|
||||||
Q_EMIT networkActiveChanged(networkActive);
|
Q_EMIT networkActiveChanged(networkActive);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientModel::updateAlert()
|
void ClientModel::updateAlert(const QString &hash, int status)
|
||||||
{
|
{
|
||||||
|
// Show error message notification for new alert
|
||||||
|
if(status == CT_NEW)
|
||||||
|
{
|
||||||
|
uint256 hash_256;
|
||||||
|
hash_256.SetHex(hash.toStdString());
|
||||||
|
CAlert alert = CAlert::getAlertByHash(hash_256);
|
||||||
|
if(!alert.IsNull())
|
||||||
|
{
|
||||||
|
Q_EMIT message(tr("Network Alert"), QString::fromStdString(alert.strStatusBar), CClientUIInterface::ICON_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Q_EMIT alertsChanged(getStatusBarWarnings());
|
Q_EMIT alertsChanged(getStatusBarWarnings());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,10 +285,12 @@ static void NotifyNetworkActiveChanged(ClientModel *clientmodel, bool networkAct
|
||||||
Q_ARG(bool, networkActive));
|
Q_ARG(bool, networkActive));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void NotifyAlertChanged(ClientModel *clientmodel)
|
static void NotifyAlertChanged(ClientModel *clientmodel, const uint256 &hash, ChangeType status)
|
||||||
{
|
{
|
||||||
qDebug() << "NotifyAlertChanged";
|
qDebug() << "NotifyAlertChanged: " + QString::fromStdString(hash.GetHex()) + " status=" + QString::number(status);
|
||||||
QMetaObject::invokeMethod(clientmodel, "updateAlert", Qt::QueuedConnection);
|
QMetaObject::invokeMethod(clientmodel, "updateAlert", Qt::QueuedConnection,
|
||||||
|
Q_ARG(QString, QString::fromStdString(hash.GetHex())),
|
||||||
|
Q_ARG(int, status));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void BannedListChanged(ClientModel *clientmodel)
|
static void BannedListChanged(ClientModel *clientmodel)
|
||||||
|
@ -318,7 +333,7 @@ void ClientModel::subscribeToCoreSignals()
|
||||||
uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2));
|
uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2));
|
||||||
uiInterface.NotifyNumConnectionsChanged.connect(boost::bind(NotifyNumConnectionsChanged, this, _1));
|
uiInterface.NotifyNumConnectionsChanged.connect(boost::bind(NotifyNumConnectionsChanged, this, _1));
|
||||||
uiInterface.NotifyNetworkActiveChanged.connect(boost::bind(NotifyNetworkActiveChanged, this, _1));
|
uiInterface.NotifyNetworkActiveChanged.connect(boost::bind(NotifyNetworkActiveChanged, this, _1));
|
||||||
uiInterface.NotifyAlertChanged.connect(boost::bind(NotifyAlertChanged, this));
|
uiInterface.NotifyAlertChanged.connect(boost::bind(NotifyAlertChanged, this, _1, _2));
|
||||||
uiInterface.BannedListChanged.connect(boost::bind(BannedListChanged, this));
|
uiInterface.BannedListChanged.connect(boost::bind(BannedListChanged, this));
|
||||||
uiInterface.NotifyBlockTip.connect(boost::bind(BlockTipChanged, this, _1, _2, false));
|
uiInterface.NotifyBlockTip.connect(boost::bind(BlockTipChanged, this, _1, _2, false));
|
||||||
uiInterface.NotifyHeaderTip.connect(boost::bind(BlockTipChanged, this, _1, _2, true));
|
uiInterface.NotifyHeaderTip.connect(boost::bind(BlockTipChanged, this, _1, _2, true));
|
||||||
|
@ -330,7 +345,7 @@ void ClientModel::unsubscribeFromCoreSignals()
|
||||||
uiInterface.ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2));
|
uiInterface.ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2));
|
||||||
uiInterface.NotifyNumConnectionsChanged.disconnect(boost::bind(NotifyNumConnectionsChanged, this, _1));
|
uiInterface.NotifyNumConnectionsChanged.disconnect(boost::bind(NotifyNumConnectionsChanged, this, _1));
|
||||||
uiInterface.NotifyNetworkActiveChanged.disconnect(boost::bind(NotifyNetworkActiveChanged, this, _1));
|
uiInterface.NotifyNetworkActiveChanged.disconnect(boost::bind(NotifyNetworkActiveChanged, this, _1));
|
||||||
uiInterface.NotifyAlertChanged.disconnect(boost::bind(NotifyAlertChanged, this));
|
uiInterface.NotifyAlertChanged.disconnect(boost::bind(NotifyAlertChanged, this, _1, _2));
|
||||||
uiInterface.BannedListChanged.disconnect(boost::bind(BannedListChanged, this));
|
uiInterface.BannedListChanged.disconnect(boost::bind(BannedListChanged, this));
|
||||||
uiInterface.NotifyBlockTip.disconnect(boost::bind(BlockTipChanged, this, _1, _2, false));
|
uiInterface.NotifyBlockTip.disconnect(boost::bind(BlockTipChanged, this, _1, _2, false));
|
||||||
uiInterface.NotifyHeaderTip.disconnect(boost::bind(BlockTipChanged, this, _1, _2, true));
|
uiInterface.NotifyHeaderTip.disconnect(boost::bind(BlockTipChanged, this, _1, _2, true));
|
||||||
|
|
|
@ -115,7 +115,7 @@ public Q_SLOTS:
|
||||||
void updateTimer();
|
void updateTimer();
|
||||||
void updateNumConnections(int numConnections);
|
void updateNumConnections(int numConnections);
|
||||||
void updateNetworkActive(bool networkActive);
|
void updateNetworkActive(bool networkActive);
|
||||||
void updateAlert();
|
void updateAlert(const QString &hash, int status);
|
||||||
void updateBanlist();
|
void updateBanlist();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
198
src/test/alert_tests.cpp
Normal file
198
src/test/alert_tests.cpp
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
// Copyright (c) 2013-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.
|
||||||
|
|
||||||
|
// Unit tests for alert system
|
||||||
|
|
||||||
|
#include "alert.h"
|
||||||
|
#include "chain.h"
|
||||||
|
#include "chainparams.h"
|
||||||
|
#include "clientversion.h"
|
||||||
|
#include "data/alertTests.raw.h"
|
||||||
|
#include "serialize.h"
|
||||||
|
#include "streams.h"
|
||||||
|
#include "utilstrencodings.h"
|
||||||
|
|
||||||
|
#include "test/testutil.h"
|
||||||
|
#include "test/test_bitcoin.h"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
#include <boost/filesystem/operations.hpp>
|
||||||
|
#include <boost/foreach.hpp>
|
||||||
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
|
extern std::map<std::string, std::string> mapArgs;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
//
|
||||||
|
// alertTests contains 7 alerts, generated with this code:
|
||||||
|
// (SignAndSave code not shown, alert signing key is secret)
|
||||||
|
//
|
||||||
|
{
|
||||||
|
CAlert alert;
|
||||||
|
alert.nRelayUntil = 60;
|
||||||
|
alert.nExpiration = 24 * 60 * 60;
|
||||||
|
alert.nID = 1;
|
||||||
|
alert.nCancel = 0; // cancels previous messages up to this ID number
|
||||||
|
alert.nMinVer = 0; // These versions are protocol versions
|
||||||
|
alert.nMaxVer = 999001;
|
||||||
|
alert.nPriority = 1;
|
||||||
|
alert.strComment = "Alert comment";
|
||||||
|
alert.strStatusBar = "Alert 1";
|
||||||
|
|
||||||
|
SignAndSave(alert, "test/alertTests");
|
||||||
|
|
||||||
|
alert.setSubVer.insert(std::string("/Satoshi:0.1.0/"));
|
||||||
|
alert.strStatusBar = "Alert 1 for Satoshi 0.1.0";
|
||||||
|
SignAndSave(alert, "test/alertTests");
|
||||||
|
|
||||||
|
alert.setSubVer.insert(std::string("/Satoshi:0.2.0/"));
|
||||||
|
alert.strStatusBar = "Alert 1 for Satoshi 0.1.0, 0.2.0";
|
||||||
|
SignAndSave(alert, "test/alertTests");
|
||||||
|
|
||||||
|
alert.setSubVer.clear();
|
||||||
|
++alert.nID;
|
||||||
|
alert.nCancel = 1;
|
||||||
|
alert.nPriority = 100;
|
||||||
|
alert.strStatusBar = "Alert 2, cancels 1";
|
||||||
|
SignAndSave(alert, "test/alertTests");
|
||||||
|
|
||||||
|
alert.nExpiration += 60;
|
||||||
|
++alert.nID;
|
||||||
|
SignAndSave(alert, "test/alertTests");
|
||||||
|
|
||||||
|
++alert.nID;
|
||||||
|
alert.nMinVer = 11;
|
||||||
|
alert.nMaxVer = 22;
|
||||||
|
SignAndSave(alert, "test/alertTests");
|
||||||
|
|
||||||
|
++alert.nID;
|
||||||
|
alert.strStatusBar = "Alert 2 for Satoshi 0.1.0";
|
||||||
|
alert.setSubVer.insert(std::string("/Satoshi:0.1.0/"));
|
||||||
|
SignAndSave(alert, "test/alertTests");
|
||||||
|
|
||||||
|
++alert.nID;
|
||||||
|
alert.nMinVer = 0;
|
||||||
|
alert.nMaxVer = 999999;
|
||||||
|
alert.strStatusBar = "Evil Alert'; /bin/ls; echo '";
|
||||||
|
alert.setSubVer.clear();
|
||||||
|
SignAndSave(alert, "test/alertTests");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct ReadAlerts : public TestingSetup
|
||||||
|
{
|
||||||
|
ReadAlerts()
|
||||||
|
{
|
||||||
|
std::vector<unsigned char> vch(alertTests, alertTests + sizeof(alertTests));
|
||||||
|
CDataStream stream(vch, SER_DISK, CLIENT_VERSION);
|
||||||
|
try {
|
||||||
|
while (!stream.eof())
|
||||||
|
{
|
||||||
|
CAlert alert;
|
||||||
|
stream >> alert;
|
||||||
|
alerts.push_back(alert);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception&) { }
|
||||||
|
}
|
||||||
|
~ReadAlerts() { }
|
||||||
|
|
||||||
|
static std::vector<std::string> read_lines(boost::filesystem::path filepath)
|
||||||
|
{
|
||||||
|
std::vector<std::string> result;
|
||||||
|
|
||||||
|
std::ifstream f(filepath.string().c_str());
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(f,line))
|
||||||
|
result.push_back(line);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<CAlert> alerts;
|
||||||
|
};
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_SUITE(Alert_tests, ReadAlerts)
|
||||||
|
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(AlertApplies)
|
||||||
|
{
|
||||||
|
SetMockTime(11);
|
||||||
|
const std::vector<unsigned char>& alertKey = Params(CBaseChainParams::MAIN).AlertKey();
|
||||||
|
|
||||||
|
BOOST_FOREACH(const CAlert& alert, alerts)
|
||||||
|
{
|
||||||
|
BOOST_CHECK(alert.CheckSignature(alertKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_CHECK(alerts.size() >= 3);
|
||||||
|
|
||||||
|
// Matches:
|
||||||
|
BOOST_CHECK(alerts[0].AppliesTo(1, ""));
|
||||||
|
BOOST_CHECK(alerts[0].AppliesTo(999001, ""));
|
||||||
|
BOOST_CHECK(alerts[0].AppliesTo(1, "/Satoshi:11.11.11/"));
|
||||||
|
|
||||||
|
BOOST_CHECK(alerts[1].AppliesTo(1, "/Satoshi:0.1.0/"));
|
||||||
|
BOOST_CHECK(alerts[1].AppliesTo(999001, "/Satoshi:0.1.0/"));
|
||||||
|
|
||||||
|
BOOST_CHECK(alerts[2].AppliesTo(1, "/Satoshi:0.1.0/"));
|
||||||
|
BOOST_CHECK(alerts[2].AppliesTo(1, "/Satoshi:0.2.0/"));
|
||||||
|
|
||||||
|
// Don't match:
|
||||||
|
BOOST_CHECK(!alerts[0].AppliesTo(-1, ""));
|
||||||
|
BOOST_CHECK(!alerts[0].AppliesTo(999002, ""));
|
||||||
|
|
||||||
|
BOOST_CHECK(!alerts[1].AppliesTo(1, ""));
|
||||||
|
BOOST_CHECK(!alerts[1].AppliesTo(1, "Satoshi:0.1.0"));
|
||||||
|
BOOST_CHECK(!alerts[1].AppliesTo(1, "/Satoshi:0.1.0"));
|
||||||
|
BOOST_CHECK(!alerts[1].AppliesTo(1, "Satoshi:0.1.0/"));
|
||||||
|
BOOST_CHECK(!alerts[1].AppliesTo(-1, "/Satoshi:0.1.0/"));
|
||||||
|
BOOST_CHECK(!alerts[1].AppliesTo(999002, "/Satoshi:0.1.0/"));
|
||||||
|
BOOST_CHECK(!alerts[1].AppliesTo(1, "/Satoshi:0.2.0/"));
|
||||||
|
|
||||||
|
BOOST_CHECK(!alerts[2].AppliesTo(1, "/Satoshi:0.3.0/"));
|
||||||
|
|
||||||
|
SetMockTime(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(AlertNotify)
|
||||||
|
{
|
||||||
|
SetMockTime(11);
|
||||||
|
const std::vector<unsigned char>& alertKey = Params(CBaseChainParams::MAIN).AlertKey();
|
||||||
|
|
||||||
|
boost::filesystem::path temp = GetTempPath() /
|
||||||
|
boost::filesystem::unique_path("alertnotify-%%%%.txt");
|
||||||
|
|
||||||
|
mapArgs["-alertnotify"] = std::string("echo %s >> ") + temp.string();
|
||||||
|
|
||||||
|
BOOST_FOREACH(CAlert alert, alerts)
|
||||||
|
alert.ProcessAlert(alertKey, false);
|
||||||
|
|
||||||
|
std::vector<std::string> r = read_lines(temp);
|
||||||
|
BOOST_CHECK_EQUAL(r.size(), 4u);
|
||||||
|
|
||||||
|
// Windows built-in echo semantics are different than posixy shells. Quotes and
|
||||||
|
// whitespace are printed literally.
|
||||||
|
|
||||||
|
#ifndef WIN32
|
||||||
|
BOOST_CHECK_EQUAL(r[0], "Alert 1");
|
||||||
|
BOOST_CHECK_EQUAL(r[1], "Alert 2, cancels 1");
|
||||||
|
BOOST_CHECK_EQUAL(r[2], "Alert 2, cancels 1");
|
||||||
|
BOOST_CHECK_EQUAL(r[3], "Evil Alert; /bin/ls; echo "); // single-quotes should be removed
|
||||||
|
#else
|
||||||
|
BOOST_CHECK_EQUAL(r[0], "'Alert 1' ");
|
||||||
|
BOOST_CHECK_EQUAL(r[1], "'Alert 2, cancels 1' ");
|
||||||
|
BOOST_CHECK_EQUAL(r[2], "'Alert 2, cancels 1' ");
|
||||||
|
BOOST_CHECK_EQUAL(r[3], "'Evil Alert; /bin/ls; echo ' ");
|
||||||
|
#endif
|
||||||
|
boost::filesystem::remove(temp);
|
||||||
|
|
||||||
|
SetMockTime(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool falseFunc() { return false; }
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
BIN
src/test/data/alertTests.raw
Normal file
BIN
src/test/data/alertTests.raw
Normal file
Binary file not shown.
|
@ -89,9 +89,10 @@ public:
|
||||||
boost::signals2::signal<void (bool networkActive)> NotifyNetworkActiveChanged;
|
boost::signals2::signal<void (bool networkActive)> NotifyNetworkActiveChanged;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Status bar alerts changed.
|
* New, updated or cancelled alert.
|
||||||
|
* @note called with lock cs_mapAlerts held.
|
||||||
*/
|
*/
|
||||||
boost::signals2::signal<void ()> NotifyAlertChanged;
|
boost::signals2::signal<void (const uint256 &hash, ChangeType status)> NotifyAlertChanged;
|
||||||
|
|
||||||
/** A wallet has been loaded. */
|
/** A wallet has been loaded. */
|
||||||
boost::signals2::signal<void (CWallet* wallet)> LoadWallet;
|
boost::signals2::signal<void (CWallet* wallet)> LoadWallet;
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#include "validation.h"
|
#include "validation.h"
|
||||||
|
|
||||||
|
#include "alert.h"
|
||||||
#include "arith_uint256.h"
|
#include "arith_uint256.h"
|
||||||
#include "chainparams.h"
|
#include "chainparams.h"
|
||||||
#include "checkpoints.h"
|
#include "checkpoints.h"
|
||||||
|
@ -75,6 +76,7 @@ bool fCheckBlockIndex = false;
|
||||||
bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED;
|
bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED;
|
||||||
size_t nCoinCacheUsage = 5000 * 300;
|
size_t nCoinCacheUsage = 5000 * 300;
|
||||||
uint64_t nPruneTarget = 0;
|
uint64_t nPruneTarget = 0;
|
||||||
|
bool fAlerts = DEFAULT_ALERTS;
|
||||||
int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE;
|
int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE;
|
||||||
bool fEnableReplacement = DEFAULT_ENABLE_REPLACEMENT;
|
bool fEnableReplacement = DEFAULT_ENABLE_REPLACEMENT;
|
||||||
|
|
||||||
|
@ -1232,19 +1234,7 @@ CBlockIndex *pindexBestForkTip = NULL, *pindexBestForkBase = NULL;
|
||||||
|
|
||||||
static void AlertNotify(const std::string& strMessage)
|
static void AlertNotify(const std::string& strMessage)
|
||||||
{
|
{
|
||||||
uiInterface.NotifyAlertChanged();
|
CAlert::Notify(strMessage);
|
||||||
std::string strCmd = GetArg("-alertnotify", "");
|
|
||||||
if (strCmd.empty()) return;
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
boost::replace_all(strCmd, "%s", safeStatus);
|
|
||||||
|
|
||||||
boost::thread t(runCommand, strCmd); // thread runs free
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckForkWarningConditions()
|
void CheckForkWarningConditions()
|
||||||
|
|
|
@ -47,6 +47,8 @@ struct ChainTxData;
|
||||||
struct PrecomputedTransactionData;
|
struct PrecomputedTransactionData;
|
||||||
struct LockPoints;
|
struct LockPoints;
|
||||||
|
|
||||||
|
/** Default for accepting alerts from the P2P network. */
|
||||||
|
static const bool DEFAULT_ALERTS = true;
|
||||||
/** Default for DEFAULT_WHITELISTRELAY. */
|
/** Default for DEFAULT_WHITELISTRELAY. */
|
||||||
static const bool DEFAULT_WHITELISTRELAY = true;
|
static const bool DEFAULT_WHITELISTRELAY = true;
|
||||||
/** Default for DEFAULT_WHITELISTFORCERELAY. */
|
/** Default for DEFAULT_WHITELISTFORCERELAY. */
|
||||||
|
@ -176,6 +178,7 @@ extern size_t nCoinCacheUsage;
|
||||||
extern CFeeRate minRelayTxFee;
|
extern CFeeRate minRelayTxFee;
|
||||||
/** Absolute maximum transaction fee (in satoshis) used by wallet and mempool (rejects high fee in sendrawtransaction) */
|
/** Absolute maximum transaction fee (in satoshis) used by wallet and mempool (rejects high fee in sendrawtransaction) */
|
||||||
extern CAmount maxTxFee;
|
extern CAmount maxTxFee;
|
||||||
|
extern bool fAlerts;
|
||||||
/** If the tip is older than this (in seconds), the node is considered to be in initial block download. */
|
/** If the tip is older than this (in seconds), the node is considered to be in initial block download. */
|
||||||
extern int64_t nMaxTipAge;
|
extern int64_t nMaxTipAge;
|
||||||
extern bool fEnableReplacement;
|
extern bool fEnableReplacement;
|
||||||
|
|
|
@ -3,11 +3,16 @@
|
||||||
// Distributed under the MIT software license, see the accompanying
|
// Distributed under the MIT software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include "alert.h"
|
||||||
#include "sync.h"
|
#include "sync.h"
|
||||||
#include "clientversion.h"
|
#include "clientversion.h"
|
||||||
|
#include "uint256.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "utilstrencodings.h"
|
||||||
#include "warnings.h"
|
#include "warnings.h"
|
||||||
|
|
||||||
|
#include <boost/foreach.hpp>
|
||||||
|
|
||||||
CCriticalSection cs_warnings;
|
CCriticalSection cs_warnings;
|
||||||
std::string strMiscWarning;
|
std::string strMiscWarning;
|
||||||
bool fLargeWorkForkFound = false;
|
bool fLargeWorkForkFound = false;
|
||||||
|
@ -45,6 +50,7 @@ bool GetfLargeWorkInvalidChainFound()
|
||||||
|
|
||||||
std::string GetWarnings(const std::string& strFor)
|
std::string GetWarnings(const std::string& strFor)
|
||||||
{
|
{
|
||||||
|
int nPriority = 0;
|
||||||
std::string strStatusBar;
|
std::string strStatusBar;
|
||||||
std::string strRPC;
|
std::string strRPC;
|
||||||
std::string strGUI;
|
std::string strGUI;
|
||||||
|
@ -63,21 +69,38 @@ std::string GetWarnings(const std::string& strFor)
|
||||||
// Misc warnings like out of disk space and clock is wrong
|
// Misc warnings like out of disk space and clock is wrong
|
||||||
if (strMiscWarning != "")
|
if (strMiscWarning != "")
|
||||||
{
|
{
|
||||||
|
nPriority = 1000;
|
||||||
strStatusBar = strMiscWarning;
|
strStatusBar = strMiscWarning;
|
||||||
strGUI += (strGUI.empty() ? "" : uiAlertSeperator) + strMiscWarning;
|
strGUI += (strGUI.empty() ? "" : uiAlertSeperator) + strMiscWarning;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fLargeWorkForkFound)
|
if (fLargeWorkForkFound)
|
||||||
{
|
{
|
||||||
|
nPriority = 2000;
|
||||||
strStatusBar = strRPC = "Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.";
|
strStatusBar = strRPC = "Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.";
|
||||||
strGUI += (strGUI.empty() ? "" : uiAlertSeperator) + _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.");
|
strGUI += (strGUI.empty() ? "" : uiAlertSeperator) + _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.");
|
||||||
}
|
}
|
||||||
else if (fLargeWorkInvalidChainFound)
|
else if (fLargeWorkInvalidChainFound)
|
||||||
{
|
{
|
||||||
|
nPriority = 2000;
|
||||||
strStatusBar = strRPC = "Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.";
|
strStatusBar = strRPC = "Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.";
|
||||||
strGUI += (strGUI.empty() ? "" : uiAlertSeperator) + _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.");
|
strGUI += (strGUI.empty() ? "" : uiAlertSeperator) + _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Alerts
|
||||||
|
{
|
||||||
|
LOCK(cs_mapAlerts);
|
||||||
|
BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts)
|
||||||
|
{
|
||||||
|
const CAlert& alert = item.second;
|
||||||
|
if (alert.AppliesToMe() && alert.nPriority > nPriority)
|
||||||
|
{
|
||||||
|
nPriority = alert.nPriority;
|
||||||
|
strStatusBar = strGUI = alert.strStatusBar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (strFor == "gui")
|
if (strFor == "gui")
|
||||||
return strGUI;
|
return strGUI;
|
||||||
else if (strFor == "statusbar")
|
else if (strFor == "statusbar")
|
||||||
|
|
Loading…
Reference in a new issue