Remove p2p alert handling

This commit is contained in:
BtcDrak 2016-03-07 19:44:09 +00:00
parent 73b7eb501e
commit bbb9d1d123
8 changed files with 52 additions and 673 deletions

View file

@ -87,7 +87,6 @@ endif
# bitcoin core #
BITCOIN_CORE_H = \
addrman.h \
alert.h \
base58.h \
bloom.h \
chain.h \
@ -176,7 +175,6 @@ libbitcoin_server_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CP
libbitcoin_server_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_server_a_SOURCES = \
addrman.cpp \
alert.cpp \
bloom.cpp \
chain.cpp \
checkpoints.cpp \

View file

@ -29,7 +29,7 @@ JSON_TEST_FILES = \
test/data/tx_valid.json \
test/data/sighash.json
RAW_TEST_FILES = test/data/alertTests.raw
RAW_TEST_FILES =
GENERATED_TEST_FILES = $(JSON_TEST_FILES:.json=.json.h) $(RAW_TEST_FILES:.raw=.raw.h)

View file

@ -1,266 +0,0 @@
// 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 <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::RelayTo(CNode* pnode) const
{
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)
{
pnode->PushMessage(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, 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);
}

View file

@ -1,113 +0,0 @@
// 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, int nType, int nVersion) {
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, int nType, int nVersion) {
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, bool fThread = true); // fThread means run -alertnotify in a free-running thread
static void Notify(const std::string& strMessage, bool fThread);
/*
* 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

View file

@ -6,7 +6,6 @@
#include "main.h"
#include "addrman.h"
#include "alert.h"
#include "arith_uint256.h"
#include "chainparams.h"
#include "checkpoints.h"
@ -4213,14 +4212,8 @@ void static CheckBlockIndex(const Consensus::Params& consensusParams)
assert(nNodes == forward.size());
}
//////////////////////////////////////////////////////////////////////////////
//
// CAlert
//
std::string GetWarnings(const std::string& strFor)
{
int nPriority = 0;
string strStatusBar;
string strRPC;
string strGUI;
@ -4236,37 +4229,20 @@ std::string GetWarnings(const std::string& strFor)
// Misc warnings like out of disk space and clock is wrong
if (strMiscWarning != "")
{
nPriority = 1000;
strStatusBar = strGUI = strMiscWarning;
}
if (fLargeWorkForkFound)
{
nPriority = 2000;
strStatusBar = strRPC = "Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.";
strGUI = _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.");
}
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.";
strGUI = _("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")
return strGUI;
else if (strFor == "statusbar")
@ -4588,13 +4564,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
}
// Relay alerts
{
LOCK(cs_mapAlerts);
BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts)
item.second.RelayTo(pfrom);
}
pfrom->fSuccessfullyConnected = true;
string remoteAddr;
@ -5302,37 +5271,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
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);
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
alert.RelayTo(pnode);
}
}
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)
{
CBloomFilter filter;

View file

@ -8,7 +8,6 @@
#include "guiconstants.h"
#include "peertablemodel.h"
#include "alert.h"
#include "chainparams.h"
#include "checkpoints.h"
#include "clientversion.h"

View file

@ -4,198 +4,21 @@
// Unit tests for alert system
#include "alert.h"
#include "chain.h"
#include "chainparams.h"
#include "clientversion.h"
#include "data/alertTests.raw.h"
#include "main.h" // For PartitionCheck
#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>
#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";
BOOST_FIXTURE_TEST_SUITE(Alert_tests, TestingSetup)
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(alert_tests::alertTests, alert_tests::alertTests + sizeof(alert_tests::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_CASE(PartitionAlert)
{
{
// Test PartitionCheck
CCriticalSection csDummy;
CBlockIndex indexDummy[100];
@ -251,6 +74,6 @@ BOOST_AUTO_TEST_CASE(PartitionAlert)
strMiscWarning = "";
SetMockTime(0);
}
}
BOOST_AUTO_TEST_SUITE_END()

Binary file not shown.