diff options
Diffstat (limited to 'src')
47 files changed, 1048 insertions, 780 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index fa7a78f330..6ad7aabae0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -87,7 +87,6 @@ endif # bitcoin core # BITCOIN_CORE_H = \ addrman.h \ - alert.h \ base58.h \ bloom.h \ chain.h \ @@ -152,6 +151,7 @@ BITCOIN_CORE_H = \ utilmoneystr.h \ utiltime.h \ validationinterface.h \ + versionbits.h \ wallet/crypter.h \ wallet/db.h \ wallet/rpcwallet.h \ @@ -175,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 \ @@ -204,6 +203,7 @@ libbitcoin_server_a_SOURCES = \ txdb.cpp \ txmempool.cpp \ validationinterface.cpp \ + versionbits.cpp \ $(BITCOIN_CORE_H) if ENABLE_ZMQ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 0c4e47a147..4481349e50 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -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) @@ -38,6 +38,7 @@ BITCOIN_TESTS =\ test/scriptnum10.h \ test/addrman_tests.cpp \ test/alert_tests.cpp \ + test/amount_tests.cpp \ test/allocator_tests.cpp \ test/base32_tests.cpp \ test/base58_tests.cpp \ @@ -83,6 +84,7 @@ BITCOIN_TESTS =\ test/timedata_tests.cpp \ test/transaction_tests.cpp \ test/txvalidationcache_tests.cpp \ + test/versionbits_tests.cpp \ test/uint256_tests.cpp \ test/univalue_tests.cpp \ test/util_tests.cpp diff --git a/src/alert.cpp b/src/alert.cpp deleted file mode 100644 index eb1cd5e7f6..0000000000 --- a/src/alert.cpp +++ /dev/null @@ -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); -} diff --git a/src/alert.h b/src/alert.h deleted file mode 100644 index 8cb86e338c..0000000000 --- a/src/alert.h +++ /dev/null @@ -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 diff --git a/src/amount.cpp b/src/amount.cpp index a3abd8cd83..68806ff062 100644 --- a/src/amount.cpp +++ b/src/amount.cpp @@ -19,10 +19,10 @@ CFeeRate::CFeeRate(const CAmount& nFeePaid, size_t nSize) CAmount CFeeRate::GetFee(size_t nSize) const { - CAmount nFee = nSatoshisPerK*nSize / 1000; + CAmount nFee = nSatoshisPerK * nSize / 1000; - if (nFee == 0 && nSatoshisPerK > 0) - nFee = nSatoshisPerK; + if (nFee == 0 && nSize != 0 && nSatoshisPerK > 0) + nFee = CAmount(1); return nFee; } diff --git a/src/amount.h b/src/amount.h index a48b17d514..9aba6525c7 100644 --- a/src/amount.h +++ b/src/amount.h @@ -42,10 +42,14 @@ public: explicit CFeeRate(const CAmount& _nSatoshisPerK): nSatoshisPerK(_nSatoshisPerK) { } CFeeRate(const CAmount& nFeePaid, size_t nSize); CFeeRate(const CFeeRate& other) { nSatoshisPerK = other.nSatoshisPerK; } - - CAmount GetFee(size_t size) const; // unit returned is satoshis - CAmount GetFeePerK() const { return GetFee(1000); } // satoshis-per-1000-bytes - + /** + * Return the fee in satoshis for the given size in bytes. + */ + CAmount GetFee(size_t size) const; + /** + * Return the fee in satoshis for a size of 1000 bytes + */ + CAmount GetFeePerK() const { return GetFee(1000); } friend bool operator<(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK < b.nSatoshisPerK; } friend bool operator>(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK > b.nSatoshisPerK; } friend bool operator==(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK == b.nSatoshisPerK; } diff --git a/src/base58.cpp b/src/base58.cpp index 5e26cf8d47..d81c26092c 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -68,26 +68,31 @@ std::string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend) { // Skip & count leading zeroes. int zeroes = 0; + int length = 0; while (pbegin != pend && *pbegin == 0) { pbegin++; zeroes++; } // Allocate enough space in big-endian base58 representation. - std::vector<unsigned char> b58((pend - pbegin) * 138 / 100 + 1); // log(256) / log(58), rounded up. + int size = (pend - pbegin) * 138 / 100 + 1; // log(256) / log(58), rounded up. + std::vector<unsigned char> b58(size); // Process the bytes. while (pbegin != pend) { int carry = *pbegin; + int i = 0; // Apply "b58 = b58 * 256 + ch". - for (std::vector<unsigned char>::reverse_iterator it = b58.rbegin(); it != b58.rend(); it++) { + for (std::vector<unsigned char>::reverse_iterator it = b58.rbegin(); (carry != 0 || i < length) && (it != b58.rend()); it++, i++) { carry += 256 * (*it); *it = carry % 58; carry /= 58; } + assert(carry == 0); + length = i; pbegin++; } // Skip leading zeroes in base58 result. - std::vector<unsigned char>::iterator it = b58.begin(); + std::vector<unsigned char>::iterator it = b58.begin() + (size - length); while (it != b58.end() && *it == 0) it++; // Translate the result into a string. diff --git a/src/chain.h b/src/chain.h index 9199983565..5b9605a80b 100644 --- a/src/chain.h +++ b/src/chain.h @@ -14,8 +14,6 @@ #include <vector> -#include <boost/foreach.hpp> - struct CDiskBlockPos { int nFile; diff --git a/src/chainparams.cpp b/src/chainparams.cpp index b962f6ac0a..508c4de167 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -81,6 +81,11 @@ public: consensus.nPowTargetSpacing = 10 * 60; consensus.fPowAllowMinDifficultyBlocks = false; consensus.fPowNoRetargeting = false; + consensus.nRuleChangeActivationThreshold = 1916; // 95% of 2016 + consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008 + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008 /** * The message start string is designed to be unlikely to occur in normal data. * The characters are rarely used upper ASCII, not valid as UTF-8, and produce @@ -90,7 +95,6 @@ public: pchMessageStart[1] = 0xbe; pchMessageStart[2] = 0xb4; pchMessageStart[3] = 0xd9; - vAlertPubKey = ParseHex("04fc9702847840aaf195de8442ebecedf5b095cdbb9bc716bda9110971b28a49e0ead8564ff0db22209e0374782c093bb899692d524e9d6a6956e7c5ecbcd68284"); nDefaultPort = 8333; nPruneAfterHeight = 100000; @@ -162,11 +166,15 @@ public: consensus.nPowTargetSpacing = 10 * 60; consensus.fPowAllowMinDifficultyBlocks = true; consensus.fPowNoRetargeting = false; + consensus.nRuleChangeActivationThreshold = 1512; // 75% for testchains + consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008 + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008 pchMessageStart[0] = 0x0b; pchMessageStart[1] = 0x11; pchMessageStart[2] = 0x09; pchMessageStart[3] = 0x07; - vAlertPubKey = ParseHex("04302390343f91cc401d56d68b123028bf52e5fca1939df127f63c6467cdf9c8e2c14b61104cf817d0b780da337893ecc4aaff1309e536162dabbdb45200ca2b0a"); nDefaultPort = 18333; nPruneAfterHeight = 1000; @@ -225,6 +233,11 @@ public: consensus.nPowTargetSpacing = 10 * 60; consensus.fPowAllowMinDifficultyBlocks = true; consensus.fPowNoRetargeting = true; + consensus.nRuleChangeActivationThreshold = 108; // 75% for testchains + consensus.nMinerConfirmationWindow = 144; // Faster than normal for regtest (144 instead of 2016) + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 0; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 999999999999ULL; pchMessageStart[0] = 0xfa; pchMessageStart[1] = 0xbf; diff --git a/src/chainparams.h b/src/chainparams.h index 88bc666765..59202f548a 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -54,7 +54,6 @@ public: const Consensus::Params& GetConsensus() const { return consensus; } const CMessageHeader::MessageStartChars& MessageStart() const { return pchMessageStart; } - const std::vector<unsigned char>& AlertKey() const { return vAlertPubKey; } int GetDefaultPort() const { return nDefaultPort; } const CBlock& GenesisBlock() const { return genesis; } @@ -80,8 +79,6 @@ protected: Consensus::Params consensus; CMessageHeader::MessageStartChars pchMessageStart; - //! Raw pub key bytes for the broadcast alert signing key. - std::vector<unsigned char> vAlertPubKey; int nDefaultPort; uint64_t nPruneAfterHeight; std::vector<CDNSSeedData> vSeeds; diff --git a/src/consensus/params.h b/src/consensus/params.h index 335750fe80..7c3a8e84c3 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -7,8 +7,29 @@ #define BITCOIN_CONSENSUS_PARAMS_H #include "uint256.h" +#include <map> +#include <string> namespace Consensus { + +enum DeploymentPos +{ + DEPLOYMENT_TESTDUMMY, + MAX_VERSION_BITS_DEPLOYMENTS +}; + +/** + * Struct for each individual consensus rule change using BIP9. + */ +struct BIP9Deployment { + /** Bit position to select the particular bit in nVersion. */ + int bit; + /** Start MedianTime for version bits miner confirmation. Can be a date in the past */ + int64_t nStartTime; + /** Timeout/expiry MedianTime for the deployment attempt. */ + int64_t nTimeout; +}; + /** * Parameters that influence chain consensus. */ @@ -22,6 +43,14 @@ struct Params { /** Block height and hash at which BIP34 becomes active */ int BIP34Height; uint256 BIP34Hash; + /** + * Minimum blocks including miner confirmation of the total of 2016 blocks in a retargetting period, + * (nPowTargetTimespan / nPowTargetSpacing) which is also used for BIP9 deployments. + * Examples: 1916 for 95%, 1512 for testchains. + */ + uint32_t nRuleChangeActivationThreshold; + uint32_t nMinerConfirmationWindow; + BIP9Deployment vDeployments[MAX_VERSION_BITS_DEPLOYMENTS]; /** Proof of work parameters */ uint256 powLimit; bool fPowAllowMinDifficultyBlocks; diff --git a/src/init.cpp b/src/init.cpp index 637b69ab05..38ac91b2af 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -315,7 +315,6 @@ std::string HelpMessage(HelpMessageMode mode) string strUsage = HelpMessageGroup(_("Options:")); strUsage += HelpMessageOpt("-?", _("Print this help message 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("-blocknotify=<cmd>", _("Execute command when the best block changes (%s in cmd is replaced by block hash)")); if (showDebug) @@ -331,6 +330,7 @@ std::string HelpMessage(HelpMessageMode mode) } strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory")); strUsage += HelpMessageOpt("-dbcache=<n>", strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache)); + strUsage += HelpMessageOpt("-feefilter", strprintf(_("Tell other nodes to filter invs to us by our mempool min fee (default: %u)"), DEFAULT_FEEFILTER)); strUsage += HelpMessageOpt("-loadblock=<file>", _("Imports blocks from external blk000??.dat file on startup")); strUsage += HelpMessageOpt("-maxorphantx=<n>", strprintf(_("Keep at most <n> unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS)); strUsage += HelpMessageOpt("-maxmempool=<n>", strprintf(_("Keep the transaction memory pool below <n> megabytes (default: %u)"), DEFAULT_MAX_MEMPOOL_SIZE)); @@ -369,8 +369,6 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-onlynet=<net>", _("Only connect to nodes in network <net> (ipv4, ipv6 or onion)")); strUsage += HelpMessageOpt("-permitbaremultisig", strprintf(_("Relay non-P2SH multisig (default: %u)"), DEFAULT_PERMIT_BAREMULTISIG)); strUsage += HelpMessageOpt("-peerbloomfilters", strprintf(_("Support filtering of blocks and transaction with bloom filters (default: %u)"), DEFAULT_PEERBLOOMFILTERS)); - if (showDebug) - strUsage += HelpMessageOpt("-enforcenodebloom", strprintf("Enforce minimum protocol version to limit use of bloom filters (default: %u)", DEFAULT_ENFORCENODEBLOOM)); strUsage += HelpMessageOpt("-port=<port>", strprintf(_("Listen for connections on <port> (default: %u or testnet: %u)"), Params(CBaseChainParams::MAIN).GetDefaultPort(), Params(CBaseChainParams::TESTNET).GetDefaultPort())); strUsage += HelpMessageOpt("-proxy=<ip:port>", _("Connect through SOCKS5 proxy")); strUsage += HelpMessageOpt("-proxyrandomize", strprintf(_("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)"), DEFAULT_PROXYRANDOMIZE)); @@ -466,7 +464,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-blockmaxsize=<n>", strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE)); strUsage += HelpMessageOpt("-blockprioritysize=<n>", strprintf(_("Set maximum size of high-priority/low-fee transactions in bytes (default: %d)"), DEFAULT_BLOCK_PRIORITY_SIZE)); if (showDebug) - strUsage += HelpMessageOpt("-blockversion=<n>", strprintf("Override block version to test forking scenarios (default: %d)", (int)CBlock::CURRENT_VERSION)); + strUsage += HelpMessageOpt("-blockversion=<n>", "Override block version to test forking scenarios"); strUsage += HelpMessageGroup(_("RPC server options:")); strUsage += HelpMessageOpt("-server", _("Accept command line and JSON-RPC commands")); @@ -1002,8 +1000,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) fAcceptDatacarrier = GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER); nMaxDatacarrierBytes = GetArg("-datacarriersize", nMaxDatacarrierBytes); - fAlerts = GetBoolArg("-alerts", DEFAULT_ALERTS); - // Option to startup with mocktime set (used for regression testing): SetMockTime(GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op diff --git a/src/main.cpp b/src/main.cpp index 924c2f9e0c..36189f4ffc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,7 +6,6 @@ #include "main.h" #include "addrman.h" -#include "alert.h" #include "arith_uint256.h" #include "chainparams.h" #include "checkpoints.h" @@ -18,10 +17,12 @@ #include "init.h" #include "merkleblock.h" #include "net.h" +#include "policy/fees.h" #include "policy/policy.h" #include "pow.h" #include "primitives/block.h" #include "primitives/transaction.h" +#include "random.h" #include "script/script.h" #include "script/sigcache.h" #include "script/standard.h" @@ -34,6 +35,7 @@ #include "utilmoneystr.h" #include "utilstrencodings.h" #include "validationinterface.h" +#include "versionbits.h" #include <sstream> @@ -74,7 +76,6 @@ bool fCheckBlockIndex = false; bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED; size_t nCoinCacheUsage = 5000 * 300; uint64_t nPruneTarget = 0; -bool fAlerts = DEFAULT_ALERTS; int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE; bool fEnableReplacement = DEFAULT_ENABLE_REPLACEMENT; @@ -82,6 +83,7 @@ CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE); CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE; CTxMemPool mempool(::minRelayTxFee); +FeeFilterRounder filterRounder(::minRelayTxFee); struct COrphanTx { CTransaction tx; @@ -988,7 +990,7 @@ std::string FormatStateMessage(const CValidationState &state) } bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const CTransaction& tx, bool fLimitFree, - bool* pfMissingInputs, bool fOverrideMempoolLimit, const CAmount nAbsurdFee, + bool* pfMissingInputs, CFeeRate* txFeeRate, bool fOverrideMempoolLimit, const CAmount& nAbsurdFee, std::vector<uint256>& vHashTxnToUncache) { const uint256 hash = tx.GetHash(); @@ -1145,6 +1147,9 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOps, lp); unsigned int nSize = entry.GetTxSize(); + if (txFeeRate) { + *txFeeRate = CFeeRate(nFees, nSize); + } // Check that the transaction doesn't have an excessive number of // sigops, making it impossible to mine. Since the coinbase transaction @@ -1393,10 +1398,10 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C } bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs, bool fOverrideMempoolLimit, const CAmount nAbsurdFee) + bool* pfMissingInputs, CFeeRate* txFeeRate, bool fOverrideMempoolLimit, const CAmount nAbsurdFee) { std::vector<uint256> vHashTxToUncache; - bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, fOverrideMempoolLimit, nAbsurdFee, vHashTxToUncache); + bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, txFeeRate, fOverrideMempoolLimit, nAbsurdFee, vHashTxToUncache); if (!res) { BOOST_FOREACH(const uint256& hashTx, vHashTxToUncache) pcoinsTip->Uncache(hashTx); @@ -1565,6 +1570,26 @@ bool fLargeWorkForkFound = false; bool fLargeWorkInvalidChainFound = false; CBlockIndex *pindexBestForkTip = NULL, *pindexBestForkBase = NULL; +static void AlertNotify(const std::string& strMessage, bool fThread) +{ + uiInterface.NotifyAlertChanged(); + 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); +} + void CheckForkWarningConditions() { AssertLockHeld(cs_main); @@ -1584,7 +1609,7 @@ void CheckForkWarningConditions() { std::string warning = std::string("'Warning: Large-work fork detected, forking after block ") + pindexBestForkBase->phashBlock->ToString() + std::string("'"); - CAlert::Notify(warning, true); + AlertNotify(warning, true); } if (pindexBestForkTip && pindexBestForkBase) { @@ -2115,11 +2140,56 @@ void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const if (!strWarning.empty()) { strMiscWarning = strWarning; - CAlert::Notify(strWarning, true); + AlertNotify(strWarning, true); lastAlertTime = now; } } +// Protected by cs_main +static VersionBitsCache versionbitscache; + +int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params) +{ + LOCK(cs_main); + int32_t nVersion = VERSIONBITS_TOP_BITS; + + for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; i++) { + ThresholdState state = VersionBitsState(pindexPrev, params, (Consensus::DeploymentPos)i, versionbitscache); + if (state == THRESHOLD_LOCKED_IN || state == THRESHOLD_STARTED) { + nVersion |= VersionBitsMask(params, (Consensus::DeploymentPos)i); + } + } + + return nVersion; +} + +/** + * Threshold condition checker that triggers when unknown versionbits are seen on the network. + */ +class WarningBitsConditionChecker : public AbstractThresholdConditionChecker +{ +private: + int bit; + +public: + WarningBitsConditionChecker(int bitIn) : bit(bitIn) {} + + int64_t BeginTime(const Consensus::Params& params) const { return 0; } + int64_t EndTime(const Consensus::Params& params) const { return std::numeric_limits<int64_t>::max(); } + int Period(const Consensus::Params& params) const { return params.nMinerConfirmationWindow; } + int Threshold(const Consensus::Params& params) const { return params.nRuleChangeActivationThreshold; } + + bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const + { + return ((pindex->nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) && + ((pindex->nVersion >> bit) & 1) != 0 && + ((ComputeBlockVersion(pindex->pprev, params) >> bit) & 1) == 0; + } +}; + +// Protected by cs_main +static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS]; + static int64_t nTimeCheck = 0; static int64_t nTimeForks = 0; static int64_t nTimeVerify = 0; @@ -2489,24 +2559,42 @@ void static UpdateTip(CBlockIndex *pindexNew) { // Check the version of the last 100 blocks to see if we need to upgrade: static bool fWarned = false; - if (!IsInitialBlockDownload() && !fWarned) + if (!IsInitialBlockDownload()) { int nUpgraded = 0; const CBlockIndex* pindex = chainActive.Tip(); + for (int bit = 0; bit < VERSIONBITS_NUM_BITS; bit++) { + WarningBitsConditionChecker checker(bit); + ThresholdState state = checker.GetStateFor(pindex, chainParams.GetConsensus(), warningcache[bit]); + if (state == THRESHOLD_ACTIVE || state == THRESHOLD_LOCKED_IN) { + if (state == THRESHOLD_ACTIVE) { + strMiscWarning = strprintf(_("Warning: unknown new rules activated (versionbit %i)"), bit); + if (!fWarned) { + AlertNotify(strMiscWarning, true); + fWarned = true; + } + } else { + LogPrintf("%s: unknown new rules are about to activate (versionbit %i)\n", __func__, bit); + } + } + } for (int i = 0; i < 100 && pindex != NULL; i++) { - if (pindex->nVersion > CBlock::CURRENT_VERSION) + int32_t nExpectedVersion = ComputeBlockVersion(pindex->pprev, chainParams.GetConsensus()); + if (pindex->nVersion > VERSIONBITS_LAST_OLD_BLOCK_VERSION && (pindex->nVersion & ~nExpectedVersion) != 0) ++nUpgraded; pindex = pindex->pprev; } if (nUpgraded > 0) - LogPrintf("%s: %d of last 100 blocks above version %d\n", __func__, nUpgraded, (int)CBlock::CURRENT_VERSION); + LogPrintf("%s: %d of last 100 blocks have unexpected version\n", __func__, nUpgraded); if (nUpgraded > 100/2) { // strMiscWarning is read by GetWarnings(), called by Qt and the JSON-RPC code to warn the user: - strMiscWarning = _("Warning: This version is obsolete; upgrade required!"); - CAlert::Notify(strMiscWarning, true); - fWarned = true; + strMiscWarning = _("Warning: Unknown block versions being mined! It's possible unknown rules are in effect"); + if (!fWarned) { + AlertNotify(strMiscWarning, true); + fWarned = true; + } } } } @@ -2538,7 +2626,7 @@ bool static DisconnectTip(CValidationState& state, const Consensus::Params& cons // ignore validation errors in resurrected transactions list<CTransaction> removed; CValidationState stateDummy; - if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL, true)) { + if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL, NULL, true)) { mempool.removeRecursive(tx, removed); } else if (mempool.exists(tx.GetHash())) { vHashUpdate.push_back(tx.GetHash()); @@ -3800,6 +3888,10 @@ void UnloadBlockIndex() setDirtyFileInfo.clear(); mapNodeState.clear(); recentRejects.reset(NULL); + versionbitscache.Clear(); + for (int b = 0; b < VERSIONBITS_NUM_BITS; b++) { + warningcache[b].clear(); + } BOOST_FOREACH(BlockMap::value_type& entry, mapBlockIndex) { delete entry.second; @@ -4145,14 +4237,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; @@ -4168,37 +4254,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") @@ -4414,7 +4483,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (pfrom->nVersion >= NO_BLOOM_VERSION) { Misbehaving(pfrom->GetId(), 100); return false; - } else if (GetBoolArg("-enforcenodebloom", DEFAULT_ENFORCENODEBLOOM)) { + } else { pfrom->fDisconnect = true; return false; } @@ -4520,13 +4589,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; @@ -4860,10 +4922,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->setAskFor.erase(inv.hash); mapAlreadyAskedFor.erase(inv); - if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs)) - { + CFeeRate txFeeRate = CFeeRate(0); + if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs, &txFeeRate)) { mempool.check(pcoinsTip); - RelayTransaction(tx); + RelayTransaction(tx, txFeeRate); vWorkQueue.push_back(inv.hash); LogPrint("mempool", "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n", @@ -4894,10 +4956,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (setMisbehaving.count(fromPeer)) continue; - if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2)) - { + CFeeRate orphanFeeRate = CFeeRate(0); + if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2, &orphanFeeRate)) { LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString()); - RelayTransaction(orphanTx); + RelayTransaction(orphanTx, orphanFeeRate); vWorkQueue.push_back(orphanHash); vEraseQueue.push_back(orphanHash); } @@ -4950,7 +5012,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int nDoS = 0; if (!state.IsInvalid(nDoS) || nDoS == 0) { LogPrintf("Force relaying tx %s from whitelisted peer=%d\n", tx.GetHash().ToString(), pfrom->id); - RelayTransaction(tx); + RelayTransaction(tx, txFeeRate); } else { LogPrintf("Not relaying invalid transaction %s from whitelisted peer=%d (%s)\n", tx.GetHash().ToString(), pfrom->id, FormatStateMessage(state)); } @@ -5144,6 +5206,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (!fInMemPool) continue; // another thread removed since queryHashes, maybe... if (!pfrom->pfilter->IsRelevantAndUpdate(tx)) continue; } + if (pfrom->minFeeFilter) { + CFeeRate feeRate; + mempool.lookupFeeRate(hash, feeRate); + LOCK(pfrom->cs_feeFilter); + if (feeRate.GetFeePerK() < pfrom->minFeeFilter) + continue; + } vInv.push_back(inv); if (vInv.size() == MAX_INV_SZ) { pfrom->PushMessage(NetMsgType::INV, vInv); @@ -5234,37 +5303,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; @@ -5337,8 +5375,19 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } } - else - { + else if (strCommand == NetMsgType::FEEFILTER) { + CAmount newFeeFilter = 0; + vRecv >> newFeeFilter; + if (MoneyRange(newFeeFilter)) { + { + LOCK(pfrom->cs_feeFilter); + pfrom->minFeeFilter = newFeeFilter; + } + LogPrint("net", "received: feefilter of %s from peer=%d\n", CFeeRate(newFeeFilter).ToString(), pfrom->id); + } + } + + else { // Ignore unknown commands for extensibility LogPrint("net", "Unknown command \"%s\" from peer=%d\n", SanitizeString(strCommand), pfrom->id); } @@ -5820,6 +5869,29 @@ bool SendMessages(CNode* pto) if (!vGetData.empty()) pto->PushMessage(NetMsgType::GETDATA, vGetData); + // + // Message: feefilter + // + // We don't want white listed peers to filter txs to us if we have -whitelistforcerelay + if (pto->nVersion >= FEEFILTER_VERSION && GetBoolArg("-feefilter", DEFAULT_FEEFILTER) && + !(pto->fWhitelisted && GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY))) { + CAmount currentFilter = mempool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK(); + int64_t timeNow = GetTimeMicros(); + if (timeNow > pto->nextSendTimeFeeFilter) { + CAmount filterToSend = filterRounder.round(currentFilter); + if (filterToSend != pto->lastSentFeeFilter) { + pto->PushMessage(NetMsgType::FEEFILTER, filterToSend); + pto->lastSentFeeFilter = filterToSend; + } + pto->nextSendTimeFeeFilter = PoissonNextSend(timeNow, AVG_FEEFILTER_BROADCAST_INTERVAL); + } + // If the fee filter has changed substantially and it's still more than MAX_FEEFILTER_CHANGE_DELAY + // until scheduled broadcast, then move the broadcast to within MAX_FEEFILTER_CHANGE_DELAY. + else if (timeNow + MAX_FEEFILTER_CHANGE_DELAY * 1000000 < pto->nextSendTimeFeeFilter && + (currentFilter < 3 * pto->lastSentFeeFilter / 4 || currentFilter > 4 * pto->lastSentFeeFilter / 3)) { + pto->nextSendTimeFeeFilter = timeNow + (insecure_rand() % MAX_FEEFILTER_CHANGE_DELAY) * 1000000; + } + } } return true; } @@ -5828,7 +5900,11 @@ bool SendMessages(CNode* pto) return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst), DateTimeStrFormat("%Y-%m-%d", nTimeLast)); } - +ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos) +{ + LOCK(cs_main); + return VersionBitsState(chainActive.Tip(), params, pos, versionbitscache); +} class CMainCleanup { diff --git a/src/main.h b/src/main.h index 85ec60ac61..0bfcfab213 100644 --- a/src/main.h +++ b/src/main.h @@ -16,6 +16,7 @@ #include "net.h" #include "script/script_error.h" #include "sync.h" +#include "versionbits.h" #include <algorithm> #include <exception> @@ -41,8 +42,6 @@ class CValidationState; struct CNodeStateStats; struct LockPoints; -/** Default for accepting alerts from the P2P network. */ -static const bool DEFAULT_ALERTS = true; /** Default for DEFAULT_WHITELISTRELAY. */ static const bool DEFAULT_WHITELISTRELAY = true; /** Default for DEFAULT_WHITELISTFORCERELAY. */ @@ -103,6 +102,10 @@ static const unsigned int AVG_ADDRESS_BROADCAST_INTERVAL = 30; /** Average delay between trickled inventory broadcasts in seconds. * Blocks, whitelisted receivers, and a random 25% of transactions bypass this. */ static const unsigned int AVG_INVENTORY_BROADCAST_INTERVAL = 5; +/** Average delay between feefilter broadcasts in seconds. */ +static const unsigned int AVG_FEEFILTER_BROADCAST_INTERVAL = 10 * 60; +/** Maximum feefilter broadcast delay after significant change. */ +static const unsigned int MAX_FEEFILTER_CHANGE_DELAY = 5 * 60; static const unsigned int DEFAULT_LIMITFREERELAY = 15; static const bool DEFAULT_RELAYPRIORITY = true; @@ -118,12 +121,13 @@ static const unsigned int DEFAULT_BANSCORE_THRESHOLD = 100; static const bool DEFAULT_TESTSAFEMODE = false; /** Default for -mempoolreplacement */ static const bool DEFAULT_ENABLE_REPLACEMENT = true; +/** Default for using fee filter */ +static const bool DEFAULT_FEEFILTER = true; /** Maximum number of headers to announce when relaying blocks with headers message.*/ static const unsigned int MAX_BLOCKS_TO_ANNOUNCE = 8; static const bool DEFAULT_PEERBLOOMFILTERS = true; -static const bool DEFAULT_ENFORCENODEBLOOM = false; struct BlockHasher { @@ -154,7 +158,6 @@ extern size_t nCoinCacheUsage; extern CFeeRate minRelayTxFee; /** Absolute maximum transaction fee (in satoshis) used by wallet and mempool (rejects high fee in sendrawtransaction) */ 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. */ extern int64_t nMaxTipAge; extern bool fEnableReplacement; @@ -285,11 +288,14 @@ void PruneAndFlush(); /** (try to) add transaction to memory pool **/ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs, bool fOverrideMempoolLimit=false, const CAmount nAbsurdFee=0); + bool* pfMissingInputs, CFeeRate* txFeeRate, bool fOverrideMempoolLimit=false, const CAmount nAbsurdFee=0); /** Convert CValidationState to a human-readable message for logging */ std::string FormatStateMessage(const CValidationState &state); +/** Get the BIP9 state for a given deployment at the current tip. */ +ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos); + struct CNodeStateStats { int nMisbehavior; int nSyncHeight; @@ -547,6 +553,11 @@ extern CBlockTreeDB *pblocktree; */ int GetSpendHeight(const CCoinsViewCache& inputs); +/** + * Determine what nVersion a new block should use. + */ +int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params); + /** Reject codes greater or equal to this can be returned by AcceptToMemPool * for transactions, to signal internal conditions. They cannot and should not * be sent over the P2P network. diff --git a/src/miner.cpp b/src/miner.cpp index ec87e84ca7..ef8fd4db43 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -79,11 +79,6 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s return NULL; CBlock *pblock = &pblocktemplate->block; // pointer for convenience - // -regtest only: allow overriding block.nVersion with - // -blockversion=N to test forking scenarios - if (chainparams.MineBlocksOnDemand()) - pblock->nVersion = GetArg("-blockversion", pblock->nVersion); - // Create coinbase tx CMutableTransaction txNew; txNew.vin.resize(1); @@ -137,6 +132,12 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s pblock->nTime = GetAdjustedTime(); const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast(); + pblock->nVersion = ComputeBlockVersion(pindexPrev, chainparams.GetConsensus()); + // -regtest only: allow overriding block.nVersion with + // -blockversion=N to test forking scenarios + if (chainparams.MineBlocksOnDemand()) + pblock->nVersion = GetArg("-blockversion", pblock->nVersion); + int64_t nLockTimeCutoff = (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST) ? nMedianTimePast : pblock->GetBlockTime(); diff --git a/src/net.cpp b/src/net.cpp index b589692d1b..e8cc753a48 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2053,20 +2053,15 @@ public: instance_of_cnetcleanup; - - - - - -void RelayTransaction(const CTransaction& tx) +void RelayTransaction(const CTransaction& tx, CFeeRate feerate) { CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss.reserve(10000); ss << tx; - RelayTransaction(tx, ss); + RelayTransaction(tx, feerate, ss); } -void RelayTransaction(const CTransaction& tx, const CDataStream& ss) +void RelayTransaction(const CTransaction& tx, CFeeRate feerate, const CDataStream& ss) { CInv inv(MSG_TX, tx.GetHash()); { @@ -2087,6 +2082,11 @@ void RelayTransaction(const CTransaction& tx, const CDataStream& ss) { if(!pnode->fRelayTxes) continue; + { + LOCK(pnode->cs_feeFilter); + if (feerate.GetFeePerK() < pnode->minFeeFilter) + continue; + } LOCK(pnode->cs_filter); if (pnode->pfilter) { @@ -2390,6 +2390,10 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa nPingUsecTime = 0; fPingQueued = false; nMinPingUsecTime = std::numeric_limits<int64_t>::max(); + minFeeFilter = 0; + lastSentFeeFilter = 0; + nextSendTimeFeeFilter = 0; + BOOST_FOREACH(const std::string &msg, getAllNetMessageTypes()) mapRecvBytesPerMsgCmd[msg] = 0; mapRecvBytesPerMsgCmd[NET_MESSAGE_COMMAND_OTHER] = 0; @@ -6,6 +6,7 @@ #ifndef BITCOIN_NET_H #define BITCOIN_NET_H +#include "amount.h" #include "bloom.h" #include "compat.h" #include "limitedmap.h" @@ -415,6 +416,11 @@ public: int64_t nMinPingUsecTime; // Whether a ping is requested. bool fPingQueued; + // Minimum fee rate with which to filter inv's to this node + CAmount minFeeFilter; + CCriticalSection cs_feeFilter; + CAmount lastSentFeeFilter; + int64_t nextSendTimeFeeFilter; CNode(SOCKET hSocketIn, const CAddress &addrIn, const std::string &addrNameIn = "", bool fInboundIn = false); ~CNode(); @@ -766,8 +772,8 @@ public: class CTransaction; -void RelayTransaction(const CTransaction& tx); -void RelayTransaction(const CTransaction& tx, const CDataStream& ss); +void RelayTransaction(const CTransaction& tx, CFeeRate feerate); +void RelayTransaction(const CTransaction& tx, CFeeRate feerate, const CDataStream& ss); /** Access to the (IP) address database (peers.dat) */ class CAddrDB diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp index de3c060d6a..7b0e8b7d08 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -8,6 +8,7 @@ #include "amount.h" #include "primitives/transaction.h" +#include "random.h" #include "streams.h" #include "txmempool.h" #include "util.h" @@ -580,3 +581,21 @@ void CBlockPolicyEstimator::Read(CAutoFile& filein) priStats.Read(filein); nBestSeenHeight = nFileBestSeenHeight; } + +FeeFilterRounder::FeeFilterRounder(const CFeeRate& minIncrementalFee) +{ + CAmount minFeeLimit = minIncrementalFee.GetFeePerK() / 2; + feeset.insert(0); + for (double bucketBoundary = minFeeLimit; bucketBoundary <= MAX_FEERATE; bucketBoundary *= FEE_SPACING) { + feeset.insert(bucketBoundary); + } +} + +CAmount FeeFilterRounder::round(CAmount currentMinFee) +{ + std::set<double>::iterator it = feeset.lower_bound(currentMinFee); + if ((it != feeset.begin() && insecure_rand() % 3 != 0) || it == feeset.end()) { + it--; + } + return *it; +} diff --git a/src/policy/fees.h b/src/policy/fees.h index 3fa31c39e7..cdd984de7d 100644 --- a/src/policy/fees.h +++ b/src/policy/fees.h @@ -286,4 +286,17 @@ private: CFeeRate feeLikely, feeUnlikely; double priLikely, priUnlikely; }; + +class FeeFilterRounder +{ +public: + /** Create new FeeFilterRounder */ + FeeFilterRounder(const CFeeRate& minIncrementalFee); + + /** Quantize a minimum fee for privacy purpose before broadcast **/ + CAmount round(CAmount currentMinFee); + +private: + std::set<double> feeset; +}; #endif /*BITCOIN_POLICYESTIMATOR_H */ diff --git a/src/primitives/block.h b/src/primitives/block.h index 0e93399c08..42276b2bc2 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -21,7 +21,6 @@ class CBlockHeader { public: // header - static const int32_t CURRENT_VERSION=4; int32_t nVersion; uint256 hashPrevBlock; uint256 hashMerkleRoot; @@ -49,7 +48,7 @@ public: void SetNull() { - nVersion = CBlockHeader::CURRENT_VERSION; + nVersion = 0; hashPrevBlock.SetNull(); hashMerkleRoot.SetNull(); nTime = 0; diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 07ae39e0b4..e124dca365 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -34,7 +34,8 @@ public: friend bool operator<(const COutPoint& a, const COutPoint& b) { - return (a.hash < b.hash || (a.hash == b.hash && a.n < b.n)); + int cmp = a.hash.Compare(b.hash); + return cmp < 0 || (cmp == 0 && a.n < b.n); } friend bool operator==(const COutPoint& a, const COutPoint& b) diff --git a/src/protocol.cpp b/src/protocol.cpp index c1c7c0b96b..8c4bd05725 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -28,13 +28,13 @@ const char *GETADDR="getaddr"; const char *MEMPOOL="mempool"; const char *PING="ping"; const char *PONG="pong"; -const char *ALERT="alert"; const char *NOTFOUND="notfound"; const char *FILTERLOAD="filterload"; const char *FILTERADD="filteradd"; const char *FILTERCLEAR="filterclear"; const char *REJECT="reject"; const char *SENDHEADERS="sendheaders"; +const char *FEEFILTER="feefilter"; }; static const char* ppszTypeName[] = @@ -64,13 +64,13 @@ const static std::string allNetMessageTypes[] = { NetMsgType::MEMPOOL, NetMsgType::PING, NetMsgType::PONG, - NetMsgType::ALERT, NetMsgType::NOTFOUND, NetMsgType::FILTERLOAD, NetMsgType::FILTERADD, NetMsgType::FILTERCLEAR, NetMsgType::REJECT, - NetMsgType::SENDHEADERS + NetMsgType::SENDHEADERS, + NetMsgType::FEEFILTER }; const static std::vector<std::string> allNetMessageTypesVec(allNetMessageTypes, allNetMessageTypes+ARRAYLEN(allNetMessageTypes)); diff --git a/src/protocol.h b/src/protocol.h index c8b8d20ead..1b049e52af 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -164,13 +164,6 @@ extern const char *PING; */ 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 * object the receiving node does not have available for relay. * @ince protocol version 70001. @@ -218,7 +211,12 @@ extern const char *REJECT; * @see https://bitcoin.org/en/developer-reference#sendheaders */ extern const char *SENDHEADERS; - +/** + * The feefilter message tells the receiving peer not to inv us any txs + * which do not meet the specified min fee rate. + * @since protocol version 70013 as described by BIP133 + */ +extern const char *FEEFILTER; }; /* Get a vector of all valid message types (see above) */ diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index fb502b3c81..d3edfedff2 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -8,7 +8,6 @@ #include "guiconstants.h" #include "peertablemodel.h" -#include "alert.h" #include "chainparams.h" #include "checkpoints.h" #include "clientversion.h" @@ -122,20 +121,8 @@ void ClientModel::updateNumConnections(int numConnections) Q_EMIT numConnectionsChanged(numConnections); } -void ClientModel::updateAlert(const QString &hash, int status) +void ClientModel::updateAlert() { - // 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()); } @@ -227,12 +214,10 @@ static void NotifyNumConnectionsChanged(ClientModel *clientmodel, int newNumConn Q_ARG(int, newNumConnections)); } -static void NotifyAlertChanged(ClientModel *clientmodel, const uint256 &hash, ChangeType status) +static void NotifyAlertChanged(ClientModel *clientmodel) { - qDebug() << "NotifyAlertChanged: " + QString::fromStdString(hash.GetHex()) + " status=" + QString::number(status); - QMetaObject::invokeMethod(clientmodel, "updateAlert", Qt::QueuedConnection, - Q_ARG(QString, QString::fromStdString(hash.GetHex())), - Q_ARG(int, status)); + qDebug() << "NotifyAlertChanged"; + QMetaObject::invokeMethod(clientmodel, "updateAlert", Qt::QueuedConnection); } static void BannedListChanged(ClientModel *clientmodel) @@ -266,7 +251,7 @@ void ClientModel::subscribeToCoreSignals() // Connect signals to client uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2)); uiInterface.NotifyNumConnectionsChanged.connect(boost::bind(NotifyNumConnectionsChanged, this, _1)); - uiInterface.NotifyAlertChanged.connect(boost::bind(NotifyAlertChanged, this, _1, _2)); + uiInterface.NotifyAlertChanged.connect(boost::bind(NotifyAlertChanged, this)); uiInterface.BannedListChanged.connect(boost::bind(BannedListChanged, this)); uiInterface.NotifyBlockTip.connect(boost::bind(BlockTipChanged, this, _1, _2)); } @@ -276,7 +261,7 @@ void ClientModel::unsubscribeFromCoreSignals() // Disconnect signals from client uiInterface.ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2)); uiInterface.NotifyNumConnectionsChanged.disconnect(boost::bind(NotifyNumConnectionsChanged, this, _1)); - uiInterface.NotifyAlertChanged.disconnect(boost::bind(NotifyAlertChanged, this, _1, _2)); + uiInterface.NotifyAlertChanged.disconnect(boost::bind(NotifyAlertChanged, this)); uiInterface.BannedListChanged.disconnect(boost::bind(BannedListChanged, this)); uiInterface.NotifyBlockTip.disconnect(boost::bind(BlockTipChanged, this, _1, _2)); } diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 62c9f71ac7..2fef6131c3 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -103,7 +103,7 @@ Q_SIGNALS: public Q_SLOTS: void updateTimer(); void updateNumConnections(int numConnections); - void updateAlert(const QString &hash, int status); + void updateAlert(); void updateBanlist(); }; diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index da57973dae..a110dff0d1 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -604,6 +604,20 @@ static UniValue SoftForkDesc(const std::string &name, int version, CBlockIndex* return rv; } +static UniValue BIP9SoftForkDesc(const std::string& name, const Consensus::Params& consensusParams, Consensus::DeploymentPos id) +{ + UniValue rv(UniValue::VOBJ); + rv.push_back(Pair("id", name)); + switch (VersionBitsTipState(consensusParams, id)) { + case THRESHOLD_DEFINED: rv.push_back(Pair("status", "defined")); break; + case THRESHOLD_STARTED: rv.push_back(Pair("status", "started")); break; + case THRESHOLD_LOCKED_IN: rv.push_back(Pair("status", "locked_in")); break; + case THRESHOLD_ACTIVE: rv.push_back(Pair("status", "active")); break; + case THRESHOLD_FAILED: rv.push_back(Pair("status", "failed")); break; + } + return rv; +} + UniValue getblockchaininfo(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 0) @@ -634,6 +648,12 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp) " },\n" " \"reject\": { ... } (object) progress toward rejecting pre-softfork blocks (same fields as \"enforce\")\n" " }, ...\n" + " ],\n" + " \"bip9_softforks\": [ (array) status of BIP9 softforks in progress\n" + " {\n" + " \"id\": \"xxxx\", (string) name of the softfork\n" + " \"status\": \"xxxx\", (string) one of \"defined\", \"started\", \"lockedin\", \"active\", \"failed\"\n" + " }\n" " ]\n" "}\n" "\nExamples:\n" @@ -657,10 +677,12 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp) const Consensus::Params& consensusParams = Params().GetConsensus(); CBlockIndex* tip = chainActive.Tip(); UniValue softforks(UniValue::VARR); + UniValue bip9_softforks(UniValue::VARR); softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams)); softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams)); softforks.push_back(SoftForkDesc("bip65", 4, tip, consensusParams)); obj.push_back(Pair("softforks", softforks)); + obj.push_back(Pair("bip9_softforks", bip9_softforks)); if (fPruneMode) { diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 45fb6c1642..89420b93d7 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -29,6 +29,9 @@ static const CRPCConvertParam vRPCConvertParams[] = { "getaddednodeinfo", 0 }, { "generate", 0 }, { "generate", 1 }, + { "generatetoaddress", 0 }, + { "generatetoaddress", 1 }, + { "generatetoaddress", 2 }, { "getnetworkhashps", 0 }, { "getnetworkhashps", 1 }, { "sendtoaddress", 1 }, diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index c33082fca0..a2abbb323d 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -3,6 +3,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "base58.h" #include "amount.h" #include "chain.h" #include "chainparams.h" @@ -93,42 +94,12 @@ UniValue getnetworkhashps(const UniValue& params, bool fHelp) return GetNetworkHashPS(params.size() > 0 ? params[0].get_int() : 120, params.size() > 1 ? params[1].get_int() : -1); } -UniValue generate(const UniValue& params, bool fHelp) +UniValue generateBlocks(boost::shared_ptr<CReserveScript> coinbaseScript, int nGenerate, uint64_t nMaxTries, bool keepScript) { - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "generate numblocks ( maxtries )\n" - "\nMine up to numblocks blocks immediately (before the RPC call returns)\n" - "\nArguments:\n" - "1. numblocks (numeric, required) How many blocks are generated immediately.\n" - "2. maxtries (numeric, optional) How many iterations to try (default = 1000000).\n" - "\nResult\n" - "[ blockhashes ] (array) hashes of blocks generated\n" - "\nExamples:\n" - "\nGenerate 11 blocks\n" - + HelpExampleCli("generate", "11") - ); - static const int nInnerLoopCount = 0x10000; int nHeightStart = 0; int nHeightEnd = 0; int nHeight = 0; - int nGenerate = params[0].get_int(); - uint64_t nMaxTries = 1000000; - if (params.size() > 1) { - nMaxTries = params[1].get_int(); - } - - boost::shared_ptr<CReserveScript> coinbaseScript; - GetMainSignals().ScriptForMining(coinbaseScript); - - // If the keypool is exhausted, no script is returned at all. Catch this. - if (!coinbaseScript) - throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); - - //throw an error if no script was provided - if (coinbaseScript->reserveScript.empty()) - throw JSONRPCError(RPC_INTERNAL_ERROR, "No coinbase script available (mining requires a wallet)"); { // Don't keep cs_main locked LOCK(cs_main); @@ -164,12 +135,84 @@ UniValue generate(const UniValue& params, bool fHelp) ++nHeight; blockHashes.push_back(pblock->GetHash().GetHex()); - //mark script as important because it was used at least for one coinbase output - coinbaseScript->KeepScript(); + //mark script as important because it was used at least for one coinbase output if the script came from the wallet + if (keepScript) + { + coinbaseScript->KeepScript(); + } } return blockHashes; } +UniValue generate(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "generate numblocks ( maxtries )\n" + "\nMine up to numblocks blocks immediately (before the RPC call returns)\n" + "\nArguments:\n" + "1. numblocks (numeric, required) How many blocks are generated immediately.\n" + "2. maxtries (numeric, optional) How many iterations to try (default = 1000000).\n" + "\nResult\n" + "[ blockhashes ] (array) hashes of blocks generated\n" + "\nExamples:\n" + "\nGenerate 11 blocks\n" + + HelpExampleCli("generate", "11") + ); + + int nGenerate = params[0].get_int(); + uint64_t nMaxTries = 1000000; + if (params.size() > 1) { + nMaxTries = params[1].get_int(); + } + + boost::shared_ptr<CReserveScript> coinbaseScript; + GetMainSignals().ScriptForMining(coinbaseScript); + + // If the keypool is exhausted, no script is returned at all. Catch this. + if (!coinbaseScript) + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); + + //throw an error if no script was provided + if (coinbaseScript->reserveScript.empty()) + throw JSONRPCError(RPC_INTERNAL_ERROR, "No coinbase script available (mining requires a wallet)"); + + return generateBlocks(coinbaseScript, nGenerate, nMaxTries, true); +} + +UniValue generatetoaddress(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 3) + throw runtime_error( + "generatetoaddress numblocks address (maxtries)\n" + "\nMine blocks immediately to a specified address (before the RPC call returns)\n" + "\nArguments:\n" + "1. numblocks (numeric, required) How many blocks are generated immediately.\n" + "2. address (string, required) The address to send the newly generated bitcoin to.\n" + "3. maxtries (numeric, optional) How many iterations to try (default = 1000000).\n" + "\nResult\n" + "[ blockhashes ] (array) hashes of blocks generated\n" + "\nExamples:\n" + "\nGenerate 11 blocks to myaddress\n" + + HelpExampleCli("generatetoaddress", "11 \"myaddress\"") + ); + + int nGenerate = params[0].get_int(); + uint64_t nMaxTries = 1000000; + if (params.size() > 2) { + nMaxTries = params[2].get_int(); + } + + CBitcoinAddress address(params[1].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Error: Invalid address"); + + boost::shared_ptr<CReserveScript> coinbaseScript(new CReserveScript()); + coinbaseScript->reserveScript = GetScriptForDestination(address.Get()); + + return generateBlocks(coinbaseScript, nGenerate, nMaxTries, false); +} + UniValue getmininginfo(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 0) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index de89fdeb0f..c72339313c 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -818,11 +818,12 @@ UniValue sendrawtransaction(const UniValue& params, bool fHelp) const CCoins* existingCoins = view.AccessCoins(hashTx); bool fHaveMempool = mempool.exists(hashTx); bool fHaveChain = existingCoins && existingCoins->nHeight < 1000000000; + CFeeRate txFeeRate = CFeeRate(0); if (!fHaveMempool && !fHaveChain) { // push to local node and sync with wallets CValidationState state; bool fMissingInputs; - if (!AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs, false, nMaxRawTxFee)) { + if (!AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs, &txFeeRate, false, nMaxRawTxFee)) { if (state.IsInvalid()) { throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason())); } else { @@ -835,7 +836,7 @@ UniValue sendrawtransaction(const UniValue& params, bool fHelp) } else if (fHaveChain) { throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain"); } - RelayTransaction(tx); + RelayTransaction(tx, txFeeRate); return hashTx.GetHex(); } diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 33fa1437e2..1303a3bb13 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -299,6 +299,7 @@ static const CRPCCommand vRPCCommands[] = /* Coin generation */ { "generating", "generate", &generate, true }, + { "generating", "generatetoaddress", &generatetoaddress, true }, /* Raw transactions */ { "rawtransactions", "createrawtransaction", &createrawtransaction, true }, diff --git a/src/rpc/server.h b/src/rpc/server.h index 38cb32e7f2..35e114feef 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -193,6 +193,7 @@ extern UniValue listbanned(const UniValue& params, bool fHelp); extern UniValue clearbanned(const UniValue& params, bool fHelp); extern UniValue generate(const UniValue& params, bool fHelp); +extern UniValue generatetoaddress(const UniValue& params, bool fHelp); extern UniValue getnetworkhashps(const UniValue& params, bool fHelp); extern UniValue getmininginfo(const UniValue& params, bool fHelp); extern UniValue prioritisetransaction(const UniValue& params, bool fHelp); diff --git a/src/test/alert_tests.cpp b/src/test/alert_tests.cpp index 87d35be412..70f1f12273 100644 --- a/src/test/alert_tests.cpp +++ b/src/test/alert_tests.cpp @@ -4,193 +4,16 @@ // 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"; - - 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_FIXTURE_TEST_SUITE(Alert_tests, TestingSetup) - 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; } @@ -253,4 +76,4 @@ BOOST_AUTO_TEST_CASE(PartitionAlert) SetMockTime(0); } -BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END()
\ No newline at end of file diff --git a/src/test/amount_tests.cpp b/src/test/amount_tests.cpp new file mode 100644 index 0000000000..59dab20633 --- /dev/null +++ b/src/test/amount_tests.cpp @@ -0,0 +1,42 @@ +// Copyright (c) 2016 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 "amount.h" +#include "test/test_bitcoin.h" + +#include <boost/test/unit_test.hpp> + +BOOST_FIXTURE_TEST_SUITE(amount_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(GetFeeTest) +{ + CFeeRate feeRate; + + feeRate = CFeeRate(0); + // Must always return 0 + BOOST_CHECK_EQUAL(feeRate.GetFee(0), 0); + BOOST_CHECK_EQUAL(feeRate.GetFee(1e5), 0); + + feeRate = CFeeRate(1000); + // Must always just return the arg + BOOST_CHECK_EQUAL(feeRate.GetFee(0), 0); + BOOST_CHECK_EQUAL(feeRate.GetFee(1), 1); + BOOST_CHECK_EQUAL(feeRate.GetFee(121), 121); + BOOST_CHECK_EQUAL(feeRate.GetFee(999), 999); + BOOST_CHECK_EQUAL(feeRate.GetFee(1e3), 1e3); + BOOST_CHECK_EQUAL(feeRate.GetFee(9e3), 9e3); + + feeRate = CFeeRate(123); + // Truncates the result, if not integer + BOOST_CHECK_EQUAL(feeRate.GetFee(0), 0); + BOOST_CHECK_EQUAL(feeRate.GetFee(8), 1); // Special case: returns 1 instead of 0 + BOOST_CHECK_EQUAL(feeRate.GetFee(9), 1); + BOOST_CHECK_EQUAL(feeRate.GetFee(121), 14); + BOOST_CHECK_EQUAL(feeRate.GetFee(122), 15); + BOOST_CHECK_EQUAL(feeRate.GetFee(999), 122); + BOOST_CHECK_EQUAL(feeRate.GetFee(1e3), 123); + BOOST_CHECK_EQUAL(feeRate.GetFee(9e3), 1107); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/data/alertTests.raw b/src/test/data/alertTests.raw Binary files differdeleted file mode 100644 index 01f50680b9..0000000000 --- a/src/test/data/alertTests.raw +++ /dev/null diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index f3297e074d..ab6485081c 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -247,13 +247,40 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) // subsidy changing int nHeight = chainActive.Height(); - chainActive.Tip()->nHeight = 209999; + // Create an actual 209999-long block chain (without valid blocks). + while (chainActive.Tip()->nHeight < 209999) { + CBlockIndex* prev = chainActive.Tip(); + CBlockIndex* next = new CBlockIndex(); + next->phashBlock = new uint256(GetRandHash()); + pcoinsTip->SetBestBlock(next->GetBlockHash()); + next->pprev = prev; + next->nHeight = prev->nHeight + 1; + next->BuildSkip(); + chainActive.SetTip(next); + } BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); delete pblocktemplate; - chainActive.Tip()->nHeight = 210000; + // Extend to a 210000-long block chain. + while (chainActive.Tip()->nHeight < 210000) { + CBlockIndex* prev = chainActive.Tip(); + CBlockIndex* next = new CBlockIndex(); + next->phashBlock = new uint256(GetRandHash()); + pcoinsTip->SetBestBlock(next->GetBlockHash()); + next->pprev = prev; + next->nHeight = prev->nHeight + 1; + next->BuildSkip(); + chainActive.SetTip(next); + } BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); delete pblocktemplate; - chainActive.Tip()->nHeight = nHeight; + // Delete the dummy blocks again. + while (chainActive.Tip()->nHeight > nHeight) { + CBlockIndex* del = chainActive.Tip(); + chainActive.SetTip(del->pprev); + pcoinsTip->SetBestBlock(del->pprev->GetBlockHash()); + delete del->phashBlock; + delete del; + } // non-final txs in mempool SetMockTime(chainActive.Tip()->GetMedianTimePast()+1); diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp index c29e30792a..237b26329b 100644 --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -23,7 +23,7 @@ ToMemPool(CMutableTransaction& tx) LOCK(cs_main); CValidationState state; - return AcceptToMemoryPool(mempool, state, tx, false, NULL, true, 0); + return AcceptToMemoryPool(mempool, state, tx, false, NULL, NULL, true, 0); } BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup) diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp new file mode 100644 index 0000000000..1f86a06a3f --- /dev/null +++ b/src/test/versionbits_tests.cpp @@ -0,0 +1,316 @@ +// Copyright (c) 2014-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 "chain.h" +#include "random.h" +#include "versionbits.h" +#include "test/test_bitcoin.h" +#include "chainparams.h" +#include "main.h" +#include "consensus/params.h" + +#include <boost/test/unit_test.hpp> + +/* Define a virtual block time, one block per 10 minutes after Nov 14 2014, 0:55:36am */ +int32_t TestTime(int nHeight) { return 1415926536 + 600 * nHeight; } + +static const Consensus::Params paramsDummy = Consensus::Params(); + +class TestConditionChecker : public AbstractThresholdConditionChecker +{ +private: + mutable ThresholdConditionCache cache; + +public: + int64_t BeginTime(const Consensus::Params& params) const { return TestTime(10000); } + int64_t EndTime(const Consensus::Params& params) const { return TestTime(20000); } + int Period(const Consensus::Params& params) const { return 1000; } + int Threshold(const Consensus::Params& params) const { return 900; } + bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const { return (pindex->nVersion & 0x100); } + + ThresholdState GetStateFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateFor(pindexPrev, paramsDummy, cache); } +}; + +#define CHECKERS 6 + +class VersionBitsTester +{ + // A fake blockchain + std::vector<CBlockIndex*> vpblock; + + // 6 independent checkers for the same bit. + // The first one performs all checks, the second only 50%, the third only 25%, etc... + // This is to test whether lack of cached information leads to the same results. + TestConditionChecker checker[CHECKERS]; + + // Test counter (to identify failures) + int num; + +public: + VersionBitsTester() : num(0) {} + + VersionBitsTester& Reset() { + for (unsigned int i = 0; i < vpblock.size(); i++) { + delete vpblock[i]; + } + for (unsigned int i = 0; i < CHECKERS; i++) { + checker[i] = TestConditionChecker(); + } + vpblock.clear(); + return *this; + } + + ~VersionBitsTester() { + Reset(); + } + + VersionBitsTester& Mine(unsigned int height, int32_t nTime, int32_t nVersion) { + while (vpblock.size() < height) { + CBlockIndex* pindex = new CBlockIndex(); + pindex->nHeight = vpblock.size(); + pindex->pprev = vpblock.size() > 0 ? vpblock.back() : NULL; + pindex->nTime = nTime; + pindex->nVersion = nVersion; + pindex->BuildSkip(); + vpblock.push_back(pindex); + } + return *this; + } + + VersionBitsTester& TestDefined() { + for (int i = 0; i < CHECKERS; i++) { + if ((insecure_rand() & ((1 << i) - 1)) == 0) { + BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? NULL : vpblock.back()) == THRESHOLD_DEFINED, strprintf("Test %i for DEFINED", num)); + } + } + num++; + return *this; + } + + VersionBitsTester& TestStarted() { + for (int i = 0; i < CHECKERS; i++) { + if ((insecure_rand() & ((1 << i) - 1)) == 0) { + BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? NULL : vpblock.back()) == THRESHOLD_STARTED, strprintf("Test %i for STARTED", num)); + } + } + num++; + return *this; + } + + VersionBitsTester& TestLockedIn() { + for (int i = 0; i < CHECKERS; i++) { + if ((insecure_rand() & ((1 << i) - 1)) == 0) { + BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? NULL : vpblock.back()) == THRESHOLD_LOCKED_IN, strprintf("Test %i for LOCKED_IN", num)); + } + } + num++; + return *this; + } + + VersionBitsTester& TestActive() { + for (int i = 0; i < CHECKERS; i++) { + if ((insecure_rand() & ((1 << i) - 1)) == 0) { + BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? NULL : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE", num)); + } + } + num++; + return *this; + } + + VersionBitsTester& TestFailed() { + for (int i = 0; i < CHECKERS; i++) { + if ((insecure_rand() & ((1 << i) - 1)) == 0) { + BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? NULL : vpblock.back()) == THRESHOLD_FAILED, strprintf("Test %i for FAILED", num)); + } + } + num++; + return *this; + } + + CBlockIndex * Tip() { return vpblock.size() ? vpblock.back() : NULL; } +}; + +BOOST_FIXTURE_TEST_SUITE(versionbits_tests, TestingSetup) + +BOOST_AUTO_TEST_CASE(versionbits_test) +{ + for (int i = 0; i < 64; i++) { + // DEFINED -> FAILED + VersionBitsTester().TestDefined() + .Mine(1, TestTime(1), 0x100).TestDefined() + .Mine(11, TestTime(11), 0x100).TestDefined() + .Mine(989, TestTime(989), 0x100).TestDefined() + .Mine(999, TestTime(20000), 0x100).TestDefined() + .Mine(1000, TestTime(20000), 0x100).TestFailed() + .Mine(1999, TestTime(30001), 0x100).TestFailed() + .Mine(2000, TestTime(30002), 0x100).TestFailed() + .Mine(2001, TestTime(30003), 0x100).TestFailed() + .Mine(2999, TestTime(30004), 0x100).TestFailed() + .Mine(3000, TestTime(30005), 0x100).TestFailed() + + // DEFINED -> STARTED -> FAILED + .Reset().TestDefined() + .Mine(1, TestTime(1), 0).TestDefined() + .Mine(1000, TestTime(10000) - 1, 0x100).TestDefined() // One second more and it would be defined + .Mine(2000, TestTime(10000), 0x100).TestStarted() // So that's what happens the next period + .Mine(2051, TestTime(10010), 0).TestStarted() // 51 old blocks + .Mine(2950, TestTime(10020), 0x100).TestStarted() // 899 new blocks + .Mine(3000, TestTime(20000), 0).TestFailed() // 50 old blocks (so 899 out of the past 1000) + .Mine(4000, TestTime(20010), 0x100).TestFailed() + + // DEFINED -> STARTED -> FAILED while threshold reached + .Reset().TestDefined() + .Mine(1, TestTime(1), 0).TestDefined() + .Mine(1000, TestTime(10000) - 1, 0x101).TestDefined() // One second more and it would be defined + .Mine(2000, TestTime(10000), 0x101).TestStarted() // So that's what happens the next period + .Mine(2999, TestTime(30000), 0x100).TestStarted() // 999 new blocks + .Mine(3000, TestTime(30000), 0x100).TestFailed() // 1 new block (so 1000 out of the past 1000 are new) + .Mine(3999, TestTime(30001), 0).TestFailed() + .Mine(4000, TestTime(30002), 0).TestFailed() + .Mine(14333, TestTime(30003), 0).TestFailed() + .Mine(24000, TestTime(40000), 0).TestFailed() + + // DEFINED -> STARTED -> LOCKEDIN at the last minute -> ACTIVE + .Reset().TestDefined() + .Mine(1, TestTime(1), 0).TestDefined() + .Mine(1000, TestTime(10000) - 1, 0x101).TestDefined() // One second more and it would be defined + .Mine(2000, TestTime(10000), 0x101).TestStarted() // So that's what happens the next period + .Mine(2050, TestTime(10010), 0x200).TestStarted() // 50 old blocks + .Mine(2950, TestTime(10020), 0x100).TestStarted() // 900 new blocks + .Mine(2999, TestTime(19999), 0x200).TestStarted() // 49 old blocks + .Mine(3000, TestTime(29999), 0x200).TestLockedIn() // 1 old block (so 900 out of the past 1000) + .Mine(3999, TestTime(30001), 0).TestLockedIn() + .Mine(4000, TestTime(30002), 0).TestActive() + .Mine(14333, TestTime(30003), 0).TestActive() + .Mine(24000, TestTime(40000), 0).TestActive(); + } + + // Sanity checks of version bit deployments + const Consensus::Params &mainnetParams = Params(CBaseChainParams::MAIN).GetConsensus(); + for (int i=0; i<(int) Consensus::MAX_VERSION_BITS_DEPLOYMENTS; i++) { + uint32_t bitmask = VersionBitsMask(mainnetParams, (Consensus::DeploymentPos)i); + // Make sure that no deployment tries to set an invalid bit. + BOOST_CHECK_EQUAL(bitmask & ~(uint32_t)VERSIONBITS_TOP_MASK, bitmask); + + // Verify that the deployment windows of different deployment using the + // same bit are disjoint. + // This test may need modification at such time as a new deployment + // is proposed that reuses the bit of an activated soft fork, before the + // end time of that soft fork. (Alternatively, the end time of that + // activated soft fork could be later changed to be earlier to avoid + // overlap.) + for (int j=i+1; j<(int) Consensus::MAX_VERSION_BITS_DEPLOYMENTS; j++) { + if (VersionBitsMask(mainnetParams, (Consensus::DeploymentPos)j) == bitmask) { + BOOST_CHECK(mainnetParams.vDeployments[j].nStartTime > mainnetParams.vDeployments[i].nTimeout || + mainnetParams.vDeployments[i].nStartTime > mainnetParams.vDeployments[j].nTimeout); + } + } + } +} + +BOOST_AUTO_TEST_CASE(versionbits_computeblockversion) +{ + // Check that ComputeBlockVersion will set the appropriate bit correctly + // on mainnet. + const Consensus::Params &mainnetParams = Params(CBaseChainParams::MAIN).GetConsensus(); + + // Use the TESTDUMMY deployment for testing purposes. + int64_t bit = mainnetParams.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit; + int64_t nStartTime = mainnetParams.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime; + int64_t nTimeout = mainnetParams.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout; + + assert(nStartTime < nTimeout); + + // In the first chain, test that the bit is set by CBV until it has failed. + // In the second chain, test the bit is set by CBV while STARTED and + // LOCKED-IN, and then no longer set while ACTIVE. + VersionBitsTester firstChain, secondChain; + + // Start generating blocks before nStartTime + int64_t nTime = nStartTime - 1; + + // Before MedianTimePast of the chain has crossed nStartTime, the bit + // should not be set. + CBlockIndex *lastBlock = NULL; + lastBlock = firstChain.Mine(2016, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip(); + BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0); + + // Mine 2011 more blocks at the old time, and check that CBV isn't setting the bit yet. + for (int i=1; i<2012; i++) { + lastBlock = firstChain.Mine(2016+i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip(); + // This works because VERSIONBITS_LAST_OLD_BLOCK_VERSION happens + // to be 4, and the bit we're testing happens to be bit 28. + BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0); + } + // Now mine 5 more blocks at the start time -- MTP should not have passed yet, so + // CBV should still not yet set the bit. + nTime = nStartTime; + for (int i=2012; i<=2016; i++) { + lastBlock = firstChain.Mine(2016+i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip(); + BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0); + } + + // Advance to the next period and transition to STARTED, + lastBlock = firstChain.Mine(6048, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip(); + // so ComputeBlockVersion should now set the bit, + BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0); + // and should also be using the VERSIONBITS_TOP_BITS. + BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS); + + // Check that ComputeBlockVersion will set the bit until nTimeout + nTime += 600; + int blocksToMine = 4032; // test blocks for up to 2 time periods + int nHeight = 6048; + // These blocks are all before nTimeout is reached. + while (nTime < nTimeout && blocksToMine > 0) { + lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip(); + BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0); + BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS); + blocksToMine--; + nTime += 600; + nHeight += 1; + }; + + nTime = nTimeout; + // FAILED is only triggered at the end of a period, so CBV should be setting + // the bit until the period transition. + for (int i=0; i<2015; i++) { + lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip(); + BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0); + nHeight += 1; + } + // The next block should trigger no longer setting the bit. + lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip(); + BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0); + + // On a new chain: + // verify that the bit will be set after lock-in, and then stop being set + // after activation. + nTime = nStartTime; + + // Mine one period worth of blocks, and check that the bit will be on for the + // next period. + lastBlock = secondChain.Mine(2016, nStartTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip(); + BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0); + + // Mine another period worth of blocks, signaling the new bit. + lastBlock = secondChain.Mine(4032, nStartTime, VERSIONBITS_TOP_BITS | (1<<bit)).Tip(); + // After one period of setting the bit on each block, it should have locked in. + // We keep setting the bit for one more period though, until activation. + BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0); + + // Now check that we keep mining the block until the end of this period, and + // then stop at the beginning of the next period. + lastBlock = secondChain.Mine(6047, nStartTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip(); + BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0); + lastBlock = secondChain.Mine(6048, nStartTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip(); + BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0); + + // Finally, verify that after a soft fork has activated, CBV no longer uses + // VERSIONBITS_LAST_OLD_BLOCK_VERSION. + //BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS); +} + + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 088e5edde5..52c7793118 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -771,6 +771,16 @@ bool CTxMemPool::lookup(uint256 hash, CTransaction& result) const return true; } +bool CTxMemPool::lookupFeeRate(const uint256& hash, CFeeRate& feeRate) const +{ + LOCK(cs); + indexed_transaction_set::const_iterator i = mapTx.find(hash); + if (i == mapTx.end()) + return false; + feeRate = CFeeRate(i->GetFee(), i->GetTxSize()); + return true; +} + CFeeRate CTxMemPool::estimateFee(int nBlocks) const { LOCK(cs); diff --git a/src/txmempool.h b/src/txmempool.h index 665bb44cf3..9dbb37dad0 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -600,6 +600,7 @@ public: } bool lookup(uint256 hash, CTransaction& result) const; + bool lookupFeeRate(const uint256& hash, CFeeRate& feeRate) const; /** Estimate fee rate needed to get into the next nBlocks * If no answer can be given at nBlocks, return an estimate diff --git a/src/ui_interface.h b/src/ui_interface.h index 967d243270..0b51d52e65 100644 --- a/src/ui_interface.h +++ b/src/ui_interface.h @@ -83,10 +83,9 @@ public: boost::signals2::signal<void (int newNumConnections)> NotifyNumConnectionsChanged; /** - * New, updated or cancelled alert. - * @note called with lock cs_mapAlerts held. + * Status bar alerts changed. */ - boost::signals2::signal<void (const uint256 &hash, ChangeType status)> NotifyAlertChanged; + boost::signals2::signal<void ()> NotifyAlertChanged; /** A wallet has been loaded. */ boost::signals2::signal<void (CWallet* wallet)> LoadWallet; diff --git a/src/uint256.h b/src/uint256.h index 4495000f2f..bcdb6dd7c2 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -42,9 +42,11 @@ public: memset(data, 0, sizeof(data)); } - friend inline bool operator==(const base_blob& a, const base_blob& b) { return memcmp(a.data, b.data, sizeof(a.data)) == 0; } - friend inline bool operator!=(const base_blob& a, const base_blob& b) { return memcmp(a.data, b.data, sizeof(a.data)) != 0; } - friend inline bool operator<(const base_blob& a, const base_blob& b) { return memcmp(a.data, b.data, sizeof(a.data)) < 0; } + inline int Compare(const base_blob& other) const { return memcmp(data, other.data, sizeof(data)); } + + friend inline bool operator==(const base_blob& a, const base_blob& b) { return a.Compare(b) == 0; } + friend inline bool operator!=(const base_blob& a, const base_blob& b) { return a.Compare(b) != 0; } + friend inline bool operator<(const base_blob& a, const base_blob& b) { return a.Compare(b) < 0; } std::string GetHex() const; void SetHex(const char* psz); diff --git a/src/version.h b/src/version.h index af2eb8eab6..0e1d8a63ce 100644 --- a/src/version.h +++ b/src/version.h @@ -9,7 +9,7 @@ * network protocol versioning */ -static const int PROTOCOL_VERSION = 70012; +static const int PROTOCOL_VERSION = 70013; //! initial proto version, to be increased after version/verack negotiation static const int INIT_PROTO_VERSION = 209; @@ -36,4 +36,7 @@ static const int NO_BLOOM_VERSION = 70011; //! "sendheaders" command and announcing blocks with headers starts with this version static const int SENDHEADERS_VERSION = 70012; +//! "feefilter" tells peers to filter invs to you by fee starts with this version +static const int FEEFILTER_VERSION = 70013; + #endif // BITCOIN_VERSION_H diff --git a/src/versionbits.cpp b/src/versionbits.cpp new file mode 100644 index 0000000000..fbb60c0fc5 --- /dev/null +++ b/src/versionbits.cpp @@ -0,0 +1,133 @@ +// Copyright (c) 2016 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 "versionbits.h" + +ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const +{ + int nPeriod = Period(params); + int nThreshold = Threshold(params); + int64_t nTimeStart = BeginTime(params); + int64_t nTimeTimeout = EndTime(params); + + // A block's state is always the same as that of the first of its period, so it is computed based on a pindexPrev whose height equals a multiple of nPeriod - 1. + if (pindexPrev != NULL) { + pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod)); + } + + // Walk backwards in steps of nPeriod to find a pindexPrev whose information is known + std::vector<const CBlockIndex*> vToCompute; + while (cache.count(pindexPrev) == 0) { + if (pindexPrev == NULL) { + // The genesis block is by definition defined. + cache[pindexPrev] = THRESHOLD_DEFINED; + break; + } + if (pindexPrev->GetMedianTimePast() < nTimeStart) { + // Optimizaton: don't recompute down further, as we know every earlier block will be before the start time + cache[pindexPrev] = THRESHOLD_DEFINED; + break; + } + vToCompute.push_back(pindexPrev); + pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - nPeriod); + } + + // At this point, cache[pindexPrev] is known + assert(cache.count(pindexPrev)); + ThresholdState state = cache[pindexPrev]; + + // Now walk forward and compute the state of descendants of pindexPrev + while (!vToCompute.empty()) { + ThresholdState stateNext = state; + pindexPrev = vToCompute.back(); + vToCompute.pop_back(); + + switch (state) { + case THRESHOLD_DEFINED: { + if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) { + stateNext = THRESHOLD_FAILED; + } else if (pindexPrev->GetMedianTimePast() >= nTimeStart) { + stateNext = THRESHOLD_STARTED; + } + break; + } + case THRESHOLD_STARTED: { + if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) { + stateNext = THRESHOLD_FAILED; + break; + } + // We need to count + const CBlockIndex* pindexCount = pindexPrev; + int count = 0; + for (int i = 0; i < nPeriod; i++) { + if (Condition(pindexCount, params)) { + count++; + } + pindexCount = pindexCount->pprev; + } + if (count >= nThreshold) { + stateNext = THRESHOLD_LOCKED_IN; + } + break; + } + case THRESHOLD_LOCKED_IN: { + // Always progresses into ACTIVE. + stateNext = THRESHOLD_ACTIVE; + break; + } + case THRESHOLD_FAILED: + case THRESHOLD_ACTIVE: { + // Nothing happens, these are terminal states. + break; + } + } + cache[pindexPrev] = state = stateNext; + } + + return state; +} + +namespace +{ +/** + * Class to implement versionbits logic. + */ +class VersionBitsConditionChecker : public AbstractThresholdConditionChecker { +private: + const Consensus::DeploymentPos id; + +protected: + int64_t BeginTime(const Consensus::Params& params) const { return params.vDeployments[id].nStartTime; } + int64_t EndTime(const Consensus::Params& params) const { return params.vDeployments[id].nTimeout; } + int Period(const Consensus::Params& params) const { return params.nMinerConfirmationWindow; } + int Threshold(const Consensus::Params& params) const { return params.nRuleChangeActivationThreshold; } + + bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const + { + return (((pindex->nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) && (pindex->nVersion & Mask(params)) != 0); + } + +public: + VersionBitsConditionChecker(Consensus::DeploymentPos id_) : id(id_) {} + uint32_t Mask(const Consensus::Params& params) const { return ((uint32_t)1) << params.vDeployments[id].bit; } +}; + +} + +ThresholdState VersionBitsState(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache) +{ + return VersionBitsConditionChecker(pos).GetStateFor(pindexPrev, params, cache.caches[pos]); +} + +uint32_t VersionBitsMask(const Consensus::Params& params, Consensus::DeploymentPos pos) +{ + return VersionBitsConditionChecker(pos).Mask(params); +} + +void VersionBitsCache::Clear() +{ + for (unsigned int d = 0; d < Consensus::MAX_VERSION_BITS_DEPLOYMENTS; d++) { + caches[d].clear(); + } +} diff --git a/src/versionbits.h b/src/versionbits.h new file mode 100644 index 0000000000..04f4738272 --- /dev/null +++ b/src/versionbits.h @@ -0,0 +1,59 @@ +// Copyright (c) 2016 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_CONSENSUS_VERSIONBITS +#define BITCOIN_CONSENSUS_VERSIONBITS + +#include "chain.h" +#include <map> + +/** What block version to use for new blocks (pre versionbits) */ +static const int32_t VERSIONBITS_LAST_OLD_BLOCK_VERSION = 4; +/** What bits to set in version for versionbits blocks */ +static const int32_t VERSIONBITS_TOP_BITS = 0x20000000UL; +/** What bitmask determines whether versionbits is in use */ +static const int32_t VERSIONBITS_TOP_MASK = 0xE0000000UL; +/** Total bits available for versionbits */ +static const int32_t VERSIONBITS_NUM_BITS = 29; + +enum ThresholdState { + THRESHOLD_DEFINED, + THRESHOLD_STARTED, + THRESHOLD_LOCKED_IN, + THRESHOLD_ACTIVE, + THRESHOLD_FAILED, +}; + +// A map that gives the state for blocks whose height is a multiple of Period(). +// The map is indexed by the block's parent, however, so all keys in the map +// will either be NULL or a block with (height + 1) % Period() == 0. +typedef std::map<const CBlockIndex*, ThresholdState> ThresholdConditionCache; + +/** + * Abstract class that implements BIP9-style threshold logic, and caches results. + */ +class AbstractThresholdConditionChecker { +protected: + virtual bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const =0; + virtual int64_t BeginTime(const Consensus::Params& params) const =0; + virtual int64_t EndTime(const Consensus::Params& params) const =0; + virtual int Period(const Consensus::Params& params) const =0; + virtual int Threshold(const Consensus::Params& params) const =0; + +public: + // Note that the function below takes a pindexPrev as input: they compute information for block B based on its parent. + ThresholdState GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const; +}; + +struct VersionBitsCache +{ + ThresholdConditionCache caches[Consensus::MAX_VERSION_BITS_DEPLOYMENTS]; + + void Clear(); +}; + +ThresholdState VersionBitsState(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache); +uint32_t VersionBitsMask(const Consensus::Params& params, Consensus::DeploymentPos pos); + +#endif diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 9ec28e7b91..6e50f92428 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -192,7 +192,7 @@ UniValue importaddress(const UniValue& params, bool fHelp) "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n" "4. p2sh (boolean, optional, default=false) Add the P2SH version of the script as well\n" "\nNote: This call can take minutes to complete if rescan is true.\n" - "If you have the full public key, you should call importpublickey instead of this.\n" + "If you have the full public key, you should call importpubkey instead of this.\n" "\nExamples:\n" "\nImport a script with rescan\n" + HelpExampleCli("importaddress", "\"myscript\"") + diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index f1e61c710f..7d1928dd6a 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1268,7 +1268,9 @@ bool CWalletTx::RelayWalletTransaction() { if (GetDepthInMainChain() == 0 && !isAbandoned() && InMempool()) { LogPrintf("Relaying wtx %s\n", GetHash().ToString()); - RelayTransaction((CTransaction)*this); + CFeeRate feeRate; + mempool.lookupFeeRate(GetHash(), feeRate); + RelayTransaction((CTransaction)*this, feeRate); return true; } } @@ -1844,10 +1846,9 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int return true; } -bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet, const CCoinControl* coinControl) const +bool CWallet::SelectCoins(const vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet, const CCoinControl* coinControl) const { - vector<COutput> vCoins; - AvailableCoins(vCoins, true, coinControl); + vector<COutput> vCoins(vAvailableCoins); // coin control -> return all selected outputs (we want all selected to go into the transaction for sure) if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs) @@ -2013,6 +2014,9 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt { LOCK2(cs_main, cs_wallet); { + std::vector<COutput> vAvailableCoins; + AvailableCoins(vAvailableCoins, true, coinControl); + nFeeRet = 0; // Start with no fee and loop until there is enough fee while (true) @@ -2062,7 +2066,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt // Choose coins to use set<pair<const CWalletTx*,unsigned int> > setCoins; CAmount nValueIn = 0; - if (!SelectCoins(nValueToSelect, setCoins, nValueIn, coinControl)) + if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl)) { strFailReason = _("Insufficient funds"); return false; @@ -3236,5 +3240,5 @@ int CMerkleTx::GetBlocksToMaturity() const bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, CAmount nAbsurdFee) { CValidationState state; - return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL, false, nAbsurdFee); + return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL, NULL, false, nAbsurdFee); } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index d009211a92..e37d972a15 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -544,7 +544,7 @@ private: * all coins from coinControl are selected; Never select unconfirmed coins * if they are not ours */ - bool SelectCoins(const CAmount& nTargetValue, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet, const CCoinControl *coinControl = NULL) const; + bool SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet, const CCoinControl *coinControl = NULL) const; CWalletDB *pwalletdbEncryption; |