Duke Leto
3 years ago
12 changed files with 47 additions and 841 deletions
@ -1,440 +0,0 @@ |
|||
// Copyright (c) 2013 The Bitcoin Core developers
|
|||
// Copyright (c) 2016-2020 The Hush developers
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
// 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" |
|||
#include "rpc/protocol.h" |
|||
#include "rpc/server.h" |
|||
#include "serialize.h" |
|||
#include "streams.h" |
|||
#include "util.h" |
|||
#include "utilstrencodings.h" |
|||
#include "test/test_bitcoin.h" |
|||
#include <fstream> |
|||
#include <boost/filesystem/operations.hpp> |
|||
#include <boost/foreach.hpp> |
|||
#include <boost/test/unit_test.hpp> |
|||
#include "key.h" |
|||
#include "alertkeys.h" |
|||
#include <iostream> |
|||
|
|||
/*
|
|||
* If the alert key pairs have changed, the test suite will fail as the |
|||
* test data is now invalid. To create valid test data, signed with a |
|||
* new alert private key, follow these steps: |
|||
* |
|||
* 1. Copy your private key into alertkeys.h. Don't commit this file! |
|||
* See sendalert.cpp for more info. |
|||
* |
|||
* 2. Set the GENERATE_ALERTS_FLAG to true. |
|||
* |
|||
* 3. Build and run: |
|||
* test_bitcoin -t Generate_Alert_Test_Data |
|||
* |
|||
* 4. Test data is saved in your current directory as alertTests.raw.NEW |
|||
* Copy this file to: src/test/data/alertTests.raw |
|||
* |
|||
* For debugging purposes, terminal output can be copied into: |
|||
* src/test/data/alertTests.raw.h |
|||
* |
|||
* 5. Clean up... |
|||
* - Set GENERATE_ALERTS_FLAG back to false. |
|||
* - Remove your private key from alertkeys.h |
|||
* |
|||
* 6. Build and verify the new test data: |
|||
* test_bitcoin -t Alert_tests |
|||
* |
|||
*/ |
|||
#define GENERATE_ALERTS_FLAG false |
|||
|
|||
#if GENERATE_ALERTS_FLAG |
|||
|
|||
// NOTE:
|
|||
// A function SignAndSave() was used by Bitcoin Core to create alert test data
|
|||
// but it has not been made publicly available. So instead, we have adapted
|
|||
// some publicly available code which achieves the intended result:
|
|||
// https://gist.github.com/lukem512/9b272bd35e2cdefbf386
|
|||
|
|||
|
|||
// Code to output a C-style array of values
|
|||
template<typename T> |
|||
std::string HexStrArray(const T itbegin, const T itend, int lineLength) |
|||
{ |
|||
std::string rv; |
|||
static const char hexmap[16] = { '0', '1', '2', '3', '4', '5', '6', '7', |
|||
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; |
|||
rv.reserve((itend-itbegin)*3); |
|||
int i = 0; |
|||
for(T it = itbegin; it < itend; ++it) |
|||
{ |
|||
unsigned char val = (unsigned char)(*it); |
|||
if(it != itbegin) |
|||
{ |
|||
if (i % lineLength == 0) |
|||
rv.push_back('\n'); |
|||
else |
|||
rv.push_back(' '); |
|||
} |
|||
rv.push_back('0'); |
|||
rv.push_back('x'); |
|||
rv.push_back(hexmap[val>>4]); |
|||
rv.push_back(hexmap[val&15]); |
|||
rv.push_back(','); |
|||
i++; |
|||
} |
|||
|
|||
return rv; |
|||
} |
|||
|
|||
template<typename T> |
|||
inline std::string HexStrArray(const T& vch, int lineLength) |
|||
{ |
|||
return HexStrArray(vch.begin(), vch.end(), lineLength); |
|||
} |
|||
|
|||
|
|||
// Sign CAlert with alert private key
|
|||
bool SignAlert(CAlert &alert) |
|||
{ |
|||
// serialize alert data
|
|||
CDataStream sMsg(SER_NETWORK, PROTOCOL_VERSION); |
|||
sMsg << *(CUnsignedAlert*)&alert; |
|||
alert.vchMsg = std::vector<unsigned char>(sMsg.begin(), sMsg.end()); |
|||
|
|||
// sign alert
|
|||
std::vector<unsigned char> vchTmp(ParseHex(pszPrivKey)); |
|||
CPrivKey vchPrivKey(vchTmp.begin(), vchTmp.end()); |
|||
CKey key; |
|||
if (!key.SetPrivKey(vchPrivKey, false)) |
|||
{ |
|||
printf("key.SetPrivKey failed\n"); |
|||
return false; |
|||
} |
|||
if (!key.Sign(Hash(alert.vchMsg.begin(), alert.vchMsg.end()), alert.vchSig)) |
|||
{ |
|||
printf("SignAlert() : key.Sign failed\n"); |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
// Sign a CAlert and serialize it
|
|||
bool SignAndSerialize(CAlert &alert, CDataStream &buffer) |
|||
{ |
|||
// Sign
|
|||
if(!SignAlert(alert)) |
|||
{ |
|||
printf("SignAndSerialize() : could not sign alert\n"); |
|||
return false; |
|||
} |
|||
// ...and save!
|
|||
buffer << alert; |
|||
return true; |
|||
} |
|||
|
|||
void GenerateAlertTests() |
|||
{ |
|||
CDataStream sBuffer(SER_DISK, CLIENT_VERSION); |
|||
|
|||
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"; |
|||
|
|||
// Replace SignAndSave with SignAndSerialize
|
|||
SignAndSerialize(alert, sBuffer); |
|||
|
|||
// More tests go here ...
|
|||
alert.setSubVer.insert(std::string("/MagicBean:0.1.0/")); |
|||
alert.strStatusBar = "Alert 1 for MagicBean 0.1.0"; |
|||
SignAndSerialize(alert, sBuffer); |
|||
|
|||
alert.setSubVer.insert(std::string("/MagicBean:0.2.0/")); |
|||
alert.strStatusBar = "Alert 1 for MagicBean 0.1.0, 0.2.0"; |
|||
SignAndSerialize(alert, sBuffer); |
|||
|
|||
alert.setSubVer.clear(); |
|||
++alert.nID; |
|||
alert.nCancel = 1; |
|||
alert.nPriority = 100; |
|||
alert.strStatusBar = "Alert 2, cancels 1"; |
|||
SignAndSerialize(alert, sBuffer); |
|||
|
|||
alert.nExpiration += 60; |
|||
++alert.nID; |
|||
SignAndSerialize(alert, sBuffer); |
|||
|
|||
++alert.nID; |
|||
alert.nPriority = 5000; |
|||
alert.strStatusBar = "Alert 3, disables RPC"; |
|||
alert.strRPCError = "RPC disabled"; |
|||
SignAndSerialize(alert, sBuffer); |
|||
|
|||
++alert.nID; |
|||
alert.nPriority = 5000; |
|||
alert.strStatusBar = "Alert 4, re-enables RPC"; |
|||
alert.strRPCError = ""; |
|||
SignAndSerialize(alert, sBuffer); |
|||
|
|||
++alert.nID; |
|||
alert.nMinVer = 11; |
|||
alert.nMaxVer = 22; |
|||
alert.nPriority = 100; |
|||
SignAndSerialize(alert, sBuffer); |
|||
|
|||
++alert.nID; |
|||
alert.strStatusBar = "Alert 2 for MagicBean 0.1.0"; |
|||
alert.setSubVer.insert(std::string("/MagicBean:0.1.0/")); |
|||
SignAndSerialize(alert, sBuffer); |
|||
|
|||
++alert.nID; |
|||
alert.nMinVer = 0; |
|||
alert.nMaxVer = 999999; |
|||
alert.strStatusBar = "Evil Alert'; /bin/ls; echo '"; |
|||
alert.setSubVer.clear(); |
|||
bool b = SignAndSerialize(alert, sBuffer); |
|||
|
|||
if (b) { |
|||
// Print the hex array, which will become the contents of alertTest.raw.h
|
|||
std::vector<unsigned char> vch = std::vector<unsigned char>(sBuffer.begin(), sBuffer.end()); |
|||
printf("%s\n", HexStrArray(vch, 8).c_str()); |
|||
|
|||
// Write the data to alertTests.raw.NEW, to be copied to src/test/data/alertTests.raw
|
|||
std::ofstream outfile("alertTests.raw.NEW", std::ios::out | std::ios::binary); |
|||
outfile.write((const char*)&vch[0], vch.size()); |
|||
outfile.close(); |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
struct GenerateAlertTestsFixture : public TestingSetup { |
|||
GenerateAlertTestsFixture() {} |
|||
~GenerateAlertTestsFixture() {} |
|||
}; |
|||
|
|||
BOOST_FIXTURE_TEST_SUITE(Generate_Alert_Test_Data, GenerateAlertTestsFixture); |
|||
BOOST_AUTO_TEST_CASE(GenerateTheAlertTests) |
|||
{ |
|||
GenerateAlertTests(); |
|||
} |
|||
BOOST_AUTO_TEST_SUITE_END() |
|||
|
|||
|
|||
#else |
|||
|
|||
|
|||
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, "/MagicBean:11.11.11/")); |
|||
|
|||
BOOST_CHECK(alerts[1].AppliesTo(1, "/MagicBean:0.1.0/")); |
|||
BOOST_CHECK(alerts[1].AppliesTo(999001, "/MagicBean:0.1.0/")); |
|||
|
|||
BOOST_CHECK(alerts[2].AppliesTo(1, "/MagicBean:0.1.0/")); |
|||
BOOST_CHECK(alerts[2].AppliesTo(1, "/MagicBean: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, "MagicBean:0.1.0")); |
|||
BOOST_CHECK(!alerts[1].AppliesTo(1, "/MagicBean:0.1.0")); |
|||
BOOST_CHECK(!alerts[1].AppliesTo(1, "MagicBean:0.1.0/")); |
|||
BOOST_CHECK(!alerts[1].AppliesTo(-1, "/MagicBean:0.1.0/")); |
|||
BOOST_CHECK(!alerts[1].AppliesTo(999002, "/MagicBean:0.1.0/")); |
|||
BOOST_CHECK(!alerts[1].AppliesTo(1, "/MagicBean:0.2.0/")); |
|||
|
|||
BOOST_CHECK(!alerts[2].AppliesTo(1, "/MagicBean: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(), 6u); |
|||
|
|||
// 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], "Alert 3, disables RPC"); |
|||
BOOST_CHECK_EQUAL(r[4], "Alert 4, reenables RPC"); // dashes should be removed
|
|||
BOOST_CHECK_EQUAL(r[5], "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], "'Alert 3, disables RPC' "); |
|||
BOOST_CHECK_EQUAL(r[4], "'Alert 4, reenables RPC' "); // dashes should be removed
|
|||
BOOST_CHECK_EQUAL(r[5], "'Evil Alert; /bin/ls; echo ' "); |
|||
#endif |
|||
boost::filesystem::remove(temp); |
|||
|
|||
SetMockTime(0); |
|||
mapAlerts.clear(); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(AlertDisablesRPC) |
|||
{ |
|||
SetMockTime(11); |
|||
const std::vector<unsigned char>& alertKey = Params(CBaseChainParams::MAIN).AlertKey(); |
|||
|
|||
// Command should work before alerts
|
|||
BOOST_CHECK_EQUAL(GetWarnings("rpc"), ""); |
|||
|
|||
// First alert should disable RPC
|
|||
alerts[5].ProcessAlert(alertKey, false); |
|||
BOOST_CHECK_EQUAL(alerts[5].strRPCError, "RPC disabled"); |
|||
BOOST_CHECK_EQUAL(GetWarnings("rpc"), "RPC disabled"); |
|||
|
|||
// Second alert should re-enable RPC
|
|||
alerts[6].ProcessAlert(alertKey, false); |
|||
BOOST_CHECK_EQUAL(alerts[6].strRPCError, ""); |
|||
BOOST_CHECK_EQUAL(GetWarnings("rpc"), ""); |
|||
|
|||
SetMockTime(0); |
|||
mapAlerts.clear(); |
|||
} |
|||
|
|||
static bool falseFunc() { return false; } |
|||
|
|||
BOOST_AUTO_TEST_CASE(PartitionAlert) |
|||
{ |
|||
// Test PartitionCheck
|
|||
CCriticalSection csDummy; |
|||
CBlockIndex indexDummy[400]; |
|||
CChainParams& params = Params(CBaseChainParams::MAIN); |
|||
int64_t nPowTargetSpacing = params.GetConsensus().nPowTargetSpacing; |
|||
|
|||
// Generate fake blockchain timestamps relative to
|
|||
// an arbitrary time:
|
|||
int64_t now = 1427379054; |
|||
SetMockTime(now); |
|||
for (int i = 0; i < 400; i++) |
|||
{ |
|||
indexDummy[i].phashBlock = NULL; |
|||
if (i == 0) indexDummy[i].pprev = NULL; |
|||
else indexDummy[i].pprev = &indexDummy[i-1]; |
|||
indexDummy[i].SetHeight(i); |
|||
indexDummy[i].nTime = now - (400-i)*nPowTargetSpacing; |
|||
// Other members don't matter, the partition check code doesn't
|
|||
// use them
|
|||
} |
|||
|
|||
// Test 1: chain with blocks every nPowTargetSpacing seconds,
|
|||
// as normal, no worries:
|
|||
PartitionCheck(falseFunc, csDummy, &indexDummy[399], nPowTargetSpacing); |
|||
BOOST_CHECK(strMiscWarning.empty()); |
|||
|
|||
// Test 2: go 3.5 hours without a block, expect a warning:
|
|||
now += 3*60*60+30*60; |
|||
SetMockTime(now); |
|||
PartitionCheck(falseFunc, csDummy, &indexDummy[399], nPowTargetSpacing); |
|||
BOOST_CHECK(!strMiscWarning.empty()); |
|||
BOOST_TEST_MESSAGE(std::string("Got alert text: ")+strMiscWarning); |
|||
strMiscWarning = ""; |
|||
|
|||
// Test 3: test the "partition alerts only go off once per day"
|
|||
// code:
|
|||
now += 60*10; |
|||
SetMockTime(now); |
|||
PartitionCheck(falseFunc, csDummy, &indexDummy[399], nPowTargetSpacing); |
|||
BOOST_CHECK(strMiscWarning.empty()); |
|||
|
|||
// Test 4: get 2.5 times as many blocks as expected:
|
|||
now += 60*60*24; // Pretend it is a day later
|
|||
SetMockTime(now); |
|||
int64_t quickSpacing = nPowTargetSpacing*2/5; |
|||
for (int i = 0; i < 400; i++) // Tweak chain timestamps:
|
|||
indexDummy[i].nTime = now - (400-i)*quickSpacing; |
|||
PartitionCheck(falseFunc, csDummy, &indexDummy[399], nPowTargetSpacing); |
|||
BOOST_CHECK(!strMiscWarning.empty()); |
|||
BOOST_TEST_MESSAGE(std::string("Got alert text: ")+strMiscWarning); |
|||
strMiscWarning = ""; |
|||
|
|||
SetMockTime(0); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_SUITE_END() |
|||
|
|||
#endif |
Loading…
Reference in new issue