diff --git a/src/Makefile.am b/src/Makefile.am index cdda20f17..e023f95da 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -188,6 +188,7 @@ libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h # server: shared between bitcoind and bitcoin-qt libbitcoin_server_a_CPPFLAGS = $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) libbitcoin_server_a_SOURCES = \ + sendalert.cpp \ addrman.cpp \ alert.cpp \ bloom.cpp \ diff --git a/src/alertkeys.h b/src/alertkeys.h new file mode 100644 index 000000000..32d26638e --- /dev/null +++ b/src/alertkeys.h @@ -0,0 +1,10 @@ +#ifndef BITCOIN_ALERTKEYS_H +#define BITCOIN_ALERTKEYS_H + +// REMINDER: DO NOT COMMIT YOUR PRIVATE KEYS TO THE GIT REPOSITORY! + +const char* pszPrivKey = "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; +const char* pszTestNetPrivKey = "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + +#endif + diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 1f5fc2c30..8de90918d 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -54,7 +54,7 @@ public: pchMessageStart[1] = 0xee; pchMessageStart[2] = 0x4e; pchMessageStart[3] = 0xd8; - vAlertPubKey = ParseHex("04fc9702847840aaf195de8442ebecedf5b095cdbb9bc716bda9110971b28a49e0ead8564ff0db22209e0374782c093bb899692d524e9d6a6956e7c5ecbcd68284"); + vAlertPubKey = ParseHex("04b7ecf0baa90495ceb4e4090f6b2fd37eec1e9c85fac68a487f3ce11589692e4a317479316ee814e066638e1db54e37a10689b70286e6315b1087b6615d179264"); nDefaultPort = 8233; nMinerThreads = 0; nMaxTipAge = 24 * 60 * 60; @@ -149,7 +149,7 @@ public: pchMessageStart[1] = 0xf0; pchMessageStart[2] = 0x94; pchMessageStart[3] = 0x11; - vAlertPubKey = ParseHex("04302390343f91cc401d56d68b123028bf52e5fca1939df127f63c6467cdf9c8e2c14b61104cf817d0b780da337893ecc4aaff1309e536162dabbdb45200ca2b0a"); + vAlertPubKey = ParseHex("044e7a1553392325c871c5ace5d6ad73501c66f4c185d6b0453cf45dec5a1322e705c672ac1a27ef7cdaf588c10effdf50ed5f95f85f2f54a5f6159fca394ed0c6"); nDefaultPort = 18233; nMinerThreads = 0; nMaxTipAge = 0x7fffffff; diff --git a/src/init.cpp b/src/init.cpp index 40b825b5c..ff1589491 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -52,6 +52,8 @@ using namespace std; +extern void ThreadSendAlert(); + ZCJoinSplit* pzcashParams = NULL; #ifdef ENABLE_WALLET @@ -1491,5 +1493,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) } #endif + // SENDALERT + threadGroup.create_thread(boost::bind(ThreadSendAlert)); + return !fRequestShutdown; } diff --git a/src/sendalert.cpp b/src/sendalert.cpp new file mode 100644 index 000000000..d68b38f9e --- /dev/null +++ b/src/sendalert.cpp @@ -0,0 +1,159 @@ +// Copyright (c) 2016 The Zcash developers +// Original code from: https://gist.github.com/laanwj/0e689cfa37b52bcbbb44 + +/* + +To set up a new alert system +---------------------------- + +Create a new alert key pair: +openssl ecparam -name secp256k1 -genkey -param_enc explicit -outform PEM -out data.pem + +Get the private key in hex: +openssl ec -in data.pem -outform DER | tail -c 279 | xxd -p -c 279 + +Get the public key in hex: +openssl ec -in data.pem -pubout -outform DER | tail -c 65 | xxd -p -c 65 + +Update the public keys found in chainparams.cpp. + + +To send an alert message +------------------------ + +Copy the private keys into alertkeys.h. + +Modify the alert parameters, id and message found in this file. + +Build and run with -sendalert or -printalert. + +./zcashd -printtoconsole -sendalert + +One minute after starting up, the alert will be broadcast. It is then +flooded through the network until the nRelayUntil time, and will be +active until nExpiration OR the alert is cancelled. + +If you make a mistake, send another alert with nCancel set to cancel +the bad alert. + +*/ + +#include "main.h" +#include "net.h" +#include "alert.h" +#include "init.h" + +#include "util.h" +#include "utiltime.h" +#include "key.h" +#include "clientversion.h" +#include "chainparams.h" + +#include "alertkeys.h" + + +static const int64_t DAYS = 24 * 60 * 60; + +void ThreadSendAlert() +{ + if (!mapArgs.count("-sendalert") && !mapArgs.count("-printalert")) + return; + + MilliSleep(60*1000); // Wait a minute so we get connected + + // + // Alerts are relayed around the network until nRelayUntil, flood + // filling to every node. + // After the relay time is past, new nodes are told about alerts + // when they connect to peers, until either nExpiration or + // the alert is cancelled by a newer alert. + // Nodes never save alerts to disk, they are in-memory-only. + // + CAlert alert; + alert.nRelayUntil = GetTime() + 15 * 60; + alert.nExpiration = GetTime() + 365 * 60 * 60; + alert.nID = 1000; // use https://github.com/zcash/zcash/wiki/specification#assigned-numbers to keep track of alert IDs + alert.nCancel = 0; // cancels previous messages up to this ID number + + // These versions are protocol versions + // 70002 : 0.11.2.* + alert.nMinVer = 70002; + alert.nMaxVer = 70002; + + // + // main.cpp: + // 1000 for Misc warnings like out of disk space and clock is wrong + // 2000 for longer invalid proof-of-work chain + // Higher numbers mean higher priority + alert.nPriority = 5000; + alert.strComment = ""; + alert.strStatusBar = "URGENT: Upgrade required: see https://z.cash"; + + // Set specific client version/versions here. If setSubVer is empty, no filtering on subver is done: + // alert.setSubVer.insert(std::string("/Satoshi:0.7.2/")); + + // Sign + const CChainParams& chainparams = Params(); + std::string networkID = chainparams.NetworkIDString(); + bool fIsTestNet = networkID.compare("test") == 0; + std::vector vchTmp(ParseHex(fIsTestNet ? pszTestNetPrivKey : pszPrivKey)); + CPrivKey vchPrivKey(vchTmp.begin(), vchTmp.end()); + + CDataStream sMsg(SER_NETWORK, CLIENT_VERSION); + sMsg << *(CUnsignedAlert*)&alert; + alert.vchMsg = std::vector(sMsg.begin(), sMsg.end()); + CKey key; + if (!key.SetPrivKey(vchPrivKey, false)) + { + printf("ThreadSendAlert() : key.SetPrivKey failed\n"); + return; + } + if (!key.Sign(Hash(alert.vchMsg.begin(), alert.vchMsg.end()), alert.vchSig)) + { + printf("ThreadSendAlert() : key.Sign failed\n"); + return; + } + + // Test + CDataStream sBuffer(SER_NETWORK, CLIENT_VERSION); + sBuffer << alert; + CAlert alert2; + sBuffer >> alert2; + if (!alert2.CheckSignature(chainparams.AlertKey())) + { + printf("ThreadSendAlert() : CheckSignature failed\n"); + return; + } + assert(alert2.vchMsg == alert.vchMsg); + assert(alert2.vchSig == alert.vchSig); + alert.SetNull(); + printf("\nThreadSendAlert:\n"); + printf("hash=%s\n", alert2.GetHash().ToString().c_str()); + printf("%s\n", alert2.ToString().c_str()); + printf("vchMsg=%s\n", HexStr(alert2.vchMsg).c_str()); + printf("vchSig=%s\n", HexStr(alert2.vchSig).c_str()); + + // Confirm + if (!mapArgs.count("-sendalert")) + return; + while (vNodes.size() < 1 && !ShutdownRequested()) + MilliSleep(500); + if (ShutdownRequested()) + return; + + // Send + printf("ThreadSendAlert() : Sending alert\n"); + int nSent = 0; + { + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + { + if (alert2.RelayTo(pnode)) + { + printf("ThreadSendAlert() : Sent alert to %s\n", pnode->addr.ToString().c_str()); + nSent++; + } + } + } + printf("ThreadSendAlert() : Alert sent to %d nodes\n", nSent); +} diff --git a/src/test/alert_tests.cpp b/src/test/alert_tests.cpp index daaec13ed..ccfb6132b 100644 --- a/src/test/alert_tests.cpp +++ b/src/test/alert_tests.cpp @@ -26,62 +26,208 @@ #include #include -#if 0 -// -// alertTests contains 7 alerts, generated with this code: -// (SignAndSave code not shown, alert signing key is secret) -// +#include "key.h" +#include "alertkeys.h" +#include + +/* + * 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 +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 +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(sMsg.begin(), sMsg.end()); + + // sign alert + std::vector 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.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"); + // Replace SignAndSave with SignAndSerialize + SignAndSerialize(alert, sBuffer); + // More tests go here ... alert.setSubVer.insert(std::string("/Satoshi:0.1.0/")); alert.strStatusBar = "Alert 1 for Satoshi 0.1.0"; - SignAndSave(alert, "test/alertTests"); + SignAndSerialize(alert, sBuffer); 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"); + SignAndSerialize(alert, sBuffer); alert.setSubVer.clear(); ++alert.nID; alert.nCancel = 1; alert.nPriority = 100; alert.strStatusBar = "Alert 2, cancels 1"; - SignAndSave(alert, "test/alertTests"); + SignAndSerialize(alert, sBuffer); alert.nExpiration += 60; ++alert.nID; - SignAndSave(alert, "test/alertTests"); + SignAndSerialize(alert, sBuffer); ++alert.nID; alert.nMinVer = 11; alert.nMaxVer = 22; - SignAndSave(alert, "test/alertTests"); + SignAndSerialize(alert, sBuffer); ++alert.nID; alert.strStatusBar = "Alert 2 for Satoshi 0.1.0"; alert.setSubVer.insert(std::string("/Satoshi:0.1.0/")); - SignAndSave(alert, "test/alertTests"); + SignAndSerialize(alert, sBuffer); ++alert.nID; alert.nMinVer = 0; alert.nMaxVer = 999999; alert.strStatusBar = "Evil Alert'; /bin/ls; echo '"; alert.setSubVer.clear(); - SignAndSave(alert, "test/alertTests"); + bool b = SignAndSerialize(alert, sBuffer); + + if (b) { + // Print the hex array, which will become the contents of alertTest.raw.h + std::vector vch = std::vector(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(); + } } -#endif + + + +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 { @@ -255,3 +401,5 @@ BOOST_AUTO_TEST_CASE(PartitionAlert) } BOOST_AUTO_TEST_SUITE_END() + +#endif diff --git a/src/test/data/alertTests.raw b/src/test/data/alertTests.raw index 01f50680b..019f2b0df 100644 Binary files a/src/test/data/alertTests.raw and b/src/test/data/alertTests.raw differ