diff options
Diffstat (limited to 'src')
109 files changed, 1787 insertions, 878 deletions
diff --git a/src/.clang-format b/src/.clang-format index 9a700b750d..2d2ee67035 100644 --- a/src/.clang-format +++ b/src/.clang-format @@ -46,6 +46,6 @@ SpacesInAngles: false SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInParentheses: false -Standard: Cpp03 +Standard: Cpp11 TabWidth: 8 UseTab: Never diff --git a/src/Makefile.am b/src/Makefile.am index a37c6a502b..06b09404a7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -128,6 +128,7 @@ BITCOIN_CORE_H = \ reverselock.h \ rpc/blockchain.h \ rpc/client.h \ + rpc/mining.h \ rpc/protocol.h \ rpc/server.h \ rpc/register.h \ diff --git a/src/addrdb.cpp b/src/addrdb.cpp index a3743cd0d4..7f85c16585 100644 --- a/src/addrdb.cpp +++ b/src/addrdb.cpp @@ -15,25 +15,31 @@ #include "tinyformat.h" #include "util.h" +namespace { -CBanDB::CBanDB() +template <typename Stream, typename Data> +bool SerializeDB(Stream& stream, const Data& data) { - pathBanlist = GetDataDir() / "banlist.dat"; + // Write and commit header, data + try { + CHashWriter hasher(SER_DISK, CLIENT_VERSION); + stream << FLATDATA(Params().MessageStart()) << data; + hasher << FLATDATA(Params().MessageStart()) << data; + stream << hasher.GetHash(); + } catch (const std::exception& e) { + return error("%s: Serialize or I/O error - %s", __func__, e.what()); + } + + return true; } -bool CBanDB::Write(const banmap_t& banSet) +template <typename Data> +bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data& data) { // Generate random temporary filename unsigned short randv = 0; GetRandBytes((unsigned char*)&randv, sizeof(randv)); - std::string tmpfn = strprintf("banlist.dat.%04x", randv); - - // serialize banlist, checksum data up to that point, then append csum - CDataStream ssBanlist(SER_DISK, CLIENT_VERSION); - ssBanlist << FLATDATA(Params().MessageStart()); - ssBanlist << banSet; - uint256 hash = Hash(ssBanlist.begin(), ssBanlist.end()); - ssBanlist << hash; + std::string tmpfn = strprintf("%s.%04x", prefix, randv); // open temp output file, and associate with CAutoFile fs::path pathTmp = GetDataDir() / tmpfn; @@ -42,69 +48,41 @@ bool CBanDB::Write(const banmap_t& banSet) if (fileout.IsNull()) return error("%s: Failed to open file %s", __func__, pathTmp.string()); - // Write and commit header, data - try { - fileout << ssBanlist; - } - catch (const std::exception& e) { - return error("%s: Serialize or I/O error - %s", __func__, e.what()); - } + // Serialize + if (!SerializeDB(fileout, data)) return false; FileCommit(fileout.Get()); fileout.fclose(); - // replace existing banlist.dat, if any, with new banlist.dat.XXXX - if (!RenameOver(pathTmp, pathBanlist)) + // replace existing file, if any, with new file + if (!RenameOver(pathTmp, path)) return error("%s: Rename-into-place failed", __func__); return true; } -bool CBanDB::Read(banmap_t& banSet) +template <typename Stream, typename Data> +bool DeserializeDB(Stream& stream, Data& data, bool fCheckSum = true) { - // open input file, and associate with CAutoFile - FILE *file = fsbridge::fopen(pathBanlist, "rb"); - CAutoFile filein(file, SER_DISK, CLIENT_VERSION); - if (filein.IsNull()) - return error("%s: Failed to open file %s", __func__, pathBanlist.string()); - - // use file size to size memory buffer - uint64_t fileSize = fs::file_size(pathBanlist); - uint64_t dataSize = 0; - // Don't try to resize to a negative number if file is small - if (fileSize >= sizeof(uint256)) - dataSize = fileSize - sizeof(uint256); - std::vector<unsigned char> vchData; - vchData.resize(dataSize); - uint256 hashIn; - - // read data and checksum from file - try { - filein.read((char *)&vchData[0], dataSize); - filein >> hashIn; - } - catch (const std::exception& e) { - return error("%s: Deserialize or I/O error - %s", __func__, e.what()); - } - filein.fclose(); - - CDataStream ssBanlist(vchData, SER_DISK, CLIENT_VERSION); - - // verify stored checksum matches input data - uint256 hashTmp = Hash(ssBanlist.begin(), ssBanlist.end()); - if (hashIn != hashTmp) - return error("%s: Checksum mismatch, data corrupted", __func__); - - unsigned char pchMsgTmp[4]; try { + CHashVerifier<Stream> verifier(&stream); // de-serialize file header (network specific magic number) and .. - ssBanlist >> FLATDATA(pchMsgTmp); - + unsigned char pchMsgTmp[4]; + verifier >> FLATDATA(pchMsgTmp); // ... verify the network matches ours if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) return error("%s: Invalid network magic number", __func__); - // de-serialize ban data - ssBanlist >> banSet; + // de-serialize data + verifier >> data; + + // verify checksum + if (fCheckSum) { + uint256 hashTmp; + stream >> hashTmp; + if (hashTmp != verifier.GetHash()) { + return error("%s: Checksum mismatch, data corrupted", __func__); + } + } } catch (const std::exception& e) { return error("%s: Deserialize or I/O error - %s", __func__, e.what()); @@ -113,106 +91,56 @@ bool CBanDB::Read(banmap_t& banSet) return true; } -CAddrDB::CAddrDB() +template <typename Data> +bool DeserializeFileDB(const fs::path& path, Data& data) { - pathAddr = GetDataDir() / "peers.dat"; + // open input file, and associate with CAutoFile + FILE *file = fsbridge::fopen(path, "rb"); + CAutoFile filein(file, SER_DISK, CLIENT_VERSION); + if (filein.IsNull()) + return error("%s: Failed to open file %s", __func__, path.string()); + + return DeserializeDB(filein, data); } -bool CAddrDB::Write(const CAddrMan& addr) -{ - // Generate random temporary filename - unsigned short randv = 0; - GetRandBytes((unsigned char*)&randv, sizeof(randv)); - std::string tmpfn = strprintf("peers.dat.%04x", randv); +} - // serialize addresses, checksum data up to that point, then append csum - CDataStream ssPeers(SER_DISK, CLIENT_VERSION); - ssPeers << FLATDATA(Params().MessageStart()); - ssPeers << addr; - uint256 hash = Hash(ssPeers.begin(), ssPeers.end()); - ssPeers << hash; +CBanDB::CBanDB() +{ + pathBanlist = GetDataDir() / "banlist.dat"; +} - // open temp output file, and associate with CAutoFile - fs::path pathTmp = GetDataDir() / tmpfn; - FILE *file = fsbridge::fopen(pathTmp, "wb"); - CAutoFile fileout(file, SER_DISK, CLIENT_VERSION); - if (fileout.IsNull()) - return error("%s: Failed to open file %s", __func__, pathTmp.string()); +bool CBanDB::Write(const banmap_t& banSet) +{ + return SerializeFileDB("banlist", pathBanlist, banSet); +} - // Write and commit header, data - try { - fileout << ssPeers; - } - catch (const std::exception& e) { - return error("%s: Serialize or I/O error - %s", __func__, e.what()); - } - FileCommit(fileout.Get()); - fileout.fclose(); +bool CBanDB::Read(banmap_t& banSet) +{ + return DeserializeFileDB(pathBanlist, banSet); +} - // replace existing peers.dat, if any, with new peers.dat.XXXX - if (!RenameOver(pathTmp, pathAddr)) - return error("%s: Rename-into-place failed", __func__); +CAddrDB::CAddrDB() +{ + pathAddr = GetDataDir() / "peers.dat"; +} - return true; +bool CAddrDB::Write(const CAddrMan& addr) +{ + return SerializeFileDB("peers", pathAddr, addr); } bool CAddrDB::Read(CAddrMan& addr) { - // open input file, and associate with CAutoFile - FILE *file = fsbridge::fopen(pathAddr, "rb"); - CAutoFile filein(file, SER_DISK, CLIENT_VERSION); - if (filein.IsNull()) - return error("%s: Failed to open file %s", __func__, pathAddr.string()); - - // use file size to size memory buffer - uint64_t fileSize = fs::file_size(pathAddr); - uint64_t dataSize = 0; - // Don't try to resize to a negative number if file is small - if (fileSize >= sizeof(uint256)) - dataSize = fileSize - sizeof(uint256); - std::vector<unsigned char> vchData; - vchData.resize(dataSize); - uint256 hashIn; - - // read data and checksum from file - try { - filein.read((char *)&vchData[0], dataSize); - filein >> hashIn; - } - catch (const std::exception& e) { - return error("%s: Deserialize or I/O error - %s", __func__, e.what()); - } - filein.fclose(); - - CDataStream ssPeers(vchData, SER_DISK, CLIENT_VERSION); - - // verify stored checksum matches input data - uint256 hashTmp = Hash(ssPeers.begin(), ssPeers.end()); - if (hashIn != hashTmp) - return error("%s: Checksum mismatch, data corrupted", __func__); - - return Read(addr, ssPeers); + return DeserializeFileDB(pathAddr, addr); } bool CAddrDB::Read(CAddrMan& addr, CDataStream& ssPeers) { - unsigned char pchMsgTmp[4]; - try { - // de-serialize file header (network specific magic number) and .. - ssPeers >> FLATDATA(pchMsgTmp); - - // ... verify the network matches ours - if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) - return error("%s: Invalid network magic number", __func__); - - // de-serialize address data into one CAddrMan object - ssPeers >> addr; - } - catch (const std::exception& e) { - // de-serialization has failed, ensure addrman is left in a clean state + bool ret = DeserializeDB(ssPeers, addr, false); + if (!ret) { + // Ensure addrman is left in a clean state addr.Clear(); - return error("%s: Deserialize or I/O error - %s", __func__, e.what()); } - - return true; + return ret; } diff --git a/src/addrdb.h b/src/addrdb.h index c3d509bd3a..6cb36dfac4 100644 --- a/src/addrdb.h +++ b/src/addrdb.h @@ -85,7 +85,7 @@ public: CAddrDB(); bool Write(const CAddrMan& addr); bool Read(CAddrMan& addr); - bool Read(CAddrMan& addr, CDataStream& ssPeers); + static bool Read(CAddrMan& addr, CDataStream& ssPeers); }; /** Access to the banlist database (banlist.dat) */ diff --git a/src/arith_uint256.cpp b/src/arith_uint256.cpp index dd34a313b7..b4952af6f4 100644 --- a/src/arith_uint256.cpp +++ b/src/arith_uint256.cpp @@ -15,6 +15,8 @@ template <unsigned int BITS> base_uint<BITS>::base_uint(const std::string& str) { + static_assert(BITS/32 > 0 && BITS%32 == 0, "Template parameter BITS must be a positive multiple of 32."); + SetHex(str); } diff --git a/src/arith_uint256.h b/src/arith_uint256.h index 0f6b3d4fba..c7734035df 100644 --- a/src/arith_uint256.h +++ b/src/arith_uint256.h @@ -31,12 +31,16 @@ public: base_uint() { + static_assert(BITS/32 > 0 && BITS%32 == 0, "Template parameter BITS must be a positive multiple of 32."); + for (int i = 0; i < WIDTH; i++) pn[i] = 0; } base_uint(const base_uint& b) { + static_assert(BITS/32 > 0 && BITS%32 == 0, "Template parameter BITS must be a positive multiple of 32."); + for (int i = 0; i < WIDTH; i++) pn[i] = b.pn[i]; } @@ -50,6 +54,8 @@ public: base_uint(uint64_t b) { + static_assert(BITS/32 > 0 && BITS%32 == 0, "Template parameter BITS must be a positive multiple of 32."); + pn[0] = (unsigned int)b; pn[1] = (unsigned int)(b >> 32); for (int i = 2; i < WIDTH; i++) @@ -174,7 +180,7 @@ public: { // prefix operator int i = 0; - while (++pn[i] == 0 && i < WIDTH-1) + while (i < WIDTH && ++pn[i] == 0) i++; return *this; } @@ -191,7 +197,7 @@ public: { // prefix operator int i = 0; - while (--pn[i] == (uint32_t)-1 && i < WIDTH-1) + while (i < WIDTH && --pn[i] == (uint32_t)-1) i++; return *this; } diff --git a/src/base58.cpp b/src/base58.cpp index 36b3523692..efa1beb1e4 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -225,7 +225,7 @@ public: bool operator()(const CNoDestination& no) const { return false; } }; -} // anon namespace +} // namespace bool CBitcoinAddress::Set(const CKeyID& id) { diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp index 61a0b31aed..226861aa7f 100644 --- a/src/bench/bench_bitcoin.cpp +++ b/src/bench/bench_bitcoin.cpp @@ -7,10 +7,12 @@ #include "key.h" #include "validation.h" #include "util.h" +#include "random.h" int main(int argc, char** argv) { + RandomInit(); ECC_Start(); SetupEnvironment(); fPrintToDebugLog = false; // don't want to write to debug.log file diff --git a/src/bench/checkblock.cpp b/src/bench/checkblock.cpp index 195388839e..7bb1b93668 100644 --- a/src/bench/checkblock.cpp +++ b/src/bench/checkblock.cpp @@ -11,7 +11,7 @@ namespace block_bench { #include "bench/data/block413567.raw.h" -} +} // namespace block_bench // These are the two major time-sinks which happen after we have fully received // a block off the wire, but before we can relay the block on to peers using diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 714ee555ec..6093f78fb1 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -299,7 +299,6 @@ static void MutateTxAddOutPubKey(CMutableTransaction& tx, const std::string& str if (!pubkey.IsFullyValid()) throw std::runtime_error("invalid TX output pubkey"); CScript scriptPubKey = GetScriptForRawPubKey(pubkey); - CBitcoinAddress addr(scriptPubKey); // Extract and validate FLAGS bool bSegWit = false; diff --git a/src/chain.cpp b/src/chain.cpp index a5b369c4fc..ffd58d471d 100644 --- a/src/chain.cpp +++ b/src/chain.cpp @@ -126,7 +126,7 @@ arith_uint256 GetBlockProof(const CBlockIndex& block) if (fNegative || fOverflow || bnTarget == 0) return 0; // We need to compute 2**256 / (bnTarget+1), but we can't represent 2**256 - // as it's too large for a arith_uint256. However, as 2**256 is at least as large + // as it's too large for an arith_uint256. However, as 2**256 is at least as large // as bnTarget+1, it is equal to ((2**256 - bnTarget - 1) / (bnTarget+1)) + 1, // or ~bnTarget / (nTarget+1) + 1. return (~bnTarget / (bnTarget + 1)) + 1; @@ -148,3 +148,22 @@ int64_t GetBlockProofEquivalentTime(const CBlockIndex& to, const CBlockIndex& fr } return sign * r.GetLow64(); } + +/** Find the last common ancestor two blocks have. + * Both pa and pb must be non-NULL. */ +const CBlockIndex* LastCommonAncestor(const CBlockIndex* pa, const CBlockIndex* pb) { + if (pa->nHeight > pb->nHeight) { + pa = pa->GetAncestor(pb->nHeight); + } else if (pb->nHeight > pa->nHeight) { + pb = pb->GetAncestor(pa->nHeight); + } + + while (pa != pb && pa && pb) { + pa = pa->pprev; + pb = pb->pprev; + } + + // Eventually all chain branches meet at the genesis block. + assert(pa == pb); + return pa; +} diff --git a/src/chain.h b/src/chain.h index de120d2d75..c5304b7d6f 100644 --- a/src/chain.h +++ b/src/chain.h @@ -362,6 +362,9 @@ public: arith_uint256 GetBlockProof(const CBlockIndex& block); /** Return the time it would take to redo the work difference between from and to, assuming the current hashrate corresponds to the difficulty at tip, in seconds. */ int64_t GetBlockProofEquivalentTime(const CBlockIndex& to, const CBlockIndex& from, const CBlockIndex& tip, const Consensus::Params&); +/** Find the forking point between two chain tips. */ +const CBlockIndex* LastCommonAncestor(const CBlockIndex* pa, const CBlockIndex* pb); + /** Used to marshal pointers into hashes for db storage. */ class CDiskBlockIndex : public CBlockIndex diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 3b42c5fb23..dc4d2621ee 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -124,12 +124,12 @@ public: assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")); // Note that of those with the service bits flag, most only support a subset of possible options - vSeeds.push_back(CDNSSeedData("bitcoin.sipa.be", "seed.bitcoin.sipa.be", true)); // Pieter Wuille, only supports x1, x5, x9, and xd - vSeeds.push_back(CDNSSeedData("bluematt.me", "dnsseed.bluematt.me", true)); // Matt Corallo, only supports x9 - vSeeds.push_back(CDNSSeedData("dashjr.org", "dnsseed.bitcoin.dashjr.org")); // Luke Dashjr - vSeeds.push_back(CDNSSeedData("bitcoinstats.com", "seed.bitcoinstats.com", true)); // Christian Decker, supports x1 - xf - vSeeds.push_back(CDNSSeedData("bitcoin.jonasschnelli.ch", "seed.bitcoin.jonasschnelli.ch", true)); // Jonas Schnelli, only supports x1, x5, x9, and xd - vSeeds.push_back(CDNSSeedData("petertodd.org", "seed.btc.petertodd.org", true)); // Peter Todd, only supports x1, x5, x9, and xd + vSeeds.emplace_back("seed.bitcoin.sipa.be", true); // Pieter Wuille, only supports x1, x5, x9, and xd + vSeeds.emplace_back("dnsseed.bluematt.me", true); // Matt Corallo, only supports x9 + vSeeds.emplace_back("dnsseed.bitcoin.dashjr.org", false); // Luke Dashjr + vSeeds.emplace_back("seed.bitcoinstats.com", true); // Christian Decker, supports x1 - xf + vSeeds.emplace_back("seed.bitcoin.jonasschnelli.ch", true); // Jonas Schnelli, only supports x1, x5, x9, and xd + vSeeds.emplace_back("seed.btc.petertodd.org", true); // Peter Todd, only supports x1, x5, x9, and xd base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,0); base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,5); @@ -225,10 +225,10 @@ public: vFixedSeeds.clear(); vSeeds.clear(); // nodes with support for servicebits filtering should be at the top - vSeeds.push_back(CDNSSeedData("testnetbitcoin.jonasschnelli.ch", "testnet-seed.bitcoin.jonasschnelli.ch", true)); - vSeeds.push_back(CDNSSeedData("petertodd.org", "seed.tbtc.petertodd.org", true)); - vSeeds.push_back(CDNSSeedData("bluematt.me", "testnet-seed.bluematt.me")); - vSeeds.push_back(CDNSSeedData("bitcoin.schildbach.de", "testnet-seed.bitcoin.schildbach.de")); + vSeeds.emplace_back("testnet-seed.bitcoin.jonasschnelli.ch", true); + vSeeds.emplace_back("seed.tbtc.petertodd.org", true); + vSeeds.emplace_back("testnet-seed.bluematt.me", false); + vSeeds.emplace_back("testnet-seed.bitcoin.schildbach.de", false); base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,111); base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,196); diff --git a/src/chainparams.h b/src/chainparams.h index a2f136171b..f55ae4cf7f 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -15,9 +15,9 @@ #include <vector> struct CDNSSeedData { - std::string name, host; + std::string host; bool supportsServiceBitsFiltering; - CDNSSeedData(const std::string &strName, const std::string &strHost, bool supportsServiceBitsFilteringIn = false) : name(strName), host(strHost), supportsServiceBitsFiltering(supportsServiceBitsFilteringIn) {} + CDNSSeedData(const std::string &strHost, bool supportsServiceBitsFilteringIn) : host(strHost), supportsServiceBitsFiltering(supportsServiceBitsFilteringIn) {} }; struct SeedSpec6 { diff --git a/src/coins.cpp b/src/coins.cpp index b45fc76338..b5dc6197bd 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -11,16 +11,22 @@ #include <assert.h> bool CCoinsView::GetCoin(const COutPoint &outpoint, Coin &coin) const { return false; } -bool CCoinsView::HaveCoin(const COutPoint &outpoint) const { return false; } uint256 CCoinsView::GetBestBlock() const { return uint256(); } +std::vector<uint256> CCoinsView::GetHeadBlocks() const { return std::vector<uint256>(); } bool CCoinsView::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return false; } CCoinsViewCursor *CCoinsView::Cursor() const { return 0; } +bool CCoinsView::HaveCoin(const COutPoint &outpoint) const +{ + Coin coin; + return GetCoin(outpoint, coin); +} CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) { } bool CCoinsViewBacked::GetCoin(const COutPoint &outpoint, Coin &coin) const { return base->GetCoin(outpoint, coin); } bool CCoinsViewBacked::HaveCoin(const COutPoint &outpoint) const { return base->HaveCoin(outpoint); } uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); } +std::vector<uint256> CCoinsViewBacked::GetHeadBlocks() const { return base->GetHeadBlocks(); } void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; } bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); } CCoinsViewCursor *CCoinsViewBacked::Cursor() const { return base->Cursor(); } @@ -55,7 +61,7 @@ bool CCoinsViewCache::GetCoin(const COutPoint &outpoint, Coin &coin) const { CCoinsMap::const_iterator it = FetchCoin(outpoint); if (it != cacheCoins.end()) { coin = it->second.coin; - return true; + return !coin.IsSpent(); } return false; } @@ -81,19 +87,20 @@ void CCoinsViewCache::AddCoin(const COutPoint &outpoint, Coin&& coin, bool possi cachedCoinsUsage += it->second.coin.DynamicMemoryUsage(); } -void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight) { +void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight, bool check) { bool fCoinbase = tx.IsCoinBase(); const uint256& txid = tx.GetHash(); for (size_t i = 0; i < tx.vout.size(); ++i) { - // Pass fCoinbase as the possible_overwrite flag to AddCoin, in order to correctly + bool overwrite = check ? cache.HaveCoin(COutPoint(txid, i)) : fCoinbase; + // Always set the possible_overwrite flag to AddCoin for coinbase txn, in order to correctly // deal with the pre-BIP30 occurrences of duplicate coinbase transactions. - cache.AddCoin(COutPoint(txid, i), Coin(tx.vout[i], nHeight, fCoinbase), fCoinbase); + cache.AddCoin(COutPoint(txid, i), Coin(tx.vout[i], nHeight, fCoinbase), overwrite); } } -void CCoinsViewCache::SpendCoin(const COutPoint &outpoint, Coin* moveout) { +bool CCoinsViewCache::SpendCoin(const COutPoint &outpoint, Coin* moveout) { CCoinsMap::iterator it = FetchCoin(outpoint); - if (it == cacheCoins.end()) return; + if (it == cacheCoins.end()) return false; cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage(); if (moveout) { *moveout = std::move(it->second.coin); @@ -104,6 +111,7 @@ void CCoinsViewCache::SpendCoin(const COutPoint &outpoint, Coin* moveout) { it->second.flags |= CCoinsCacheEntry::DIRTY; it->second.coin.Clear(); } + return true; } static const Coin coinEmpty; @@ -124,7 +132,7 @@ bool CCoinsViewCache::HaveCoin(const COutPoint &outpoint) const { bool CCoinsViewCache::HaveCoinInCache(const COutPoint &outpoint) const { CCoinsMap::const_iterator it = cacheCoins.find(outpoint); - return it != cacheCoins.end(); + return (it != cacheCoins.end() && !it->second.coin.IsSpent()); } uint256 CCoinsViewCache::GetBestBlock() const { diff --git a/src/coins.h b/src/coins.h index dc3210b8ac..efb5ce869c 100644 --- a/src/coins.h +++ b/src/coins.h @@ -145,16 +145,24 @@ private: class CCoinsView { public: - //! Retrieve the Coin (unspent transaction output) for a given outpoint. + /** Retrieve the Coin (unspent transaction output) for a given outpoint. + * Returns true only when an unspent coin was found, which is returned in coin. + * When false is returned, coin's value is unspecified. + */ virtual bool GetCoin(const COutPoint &outpoint, Coin &coin) const; - //! Just check whether we have data for a given outpoint. - //! This may (but cannot always) return true for spent outputs. + //! Just check whether a given outpoint is unspent. virtual bool HaveCoin(const COutPoint &outpoint) const; //! Retrieve the block hash whose state this CCoinsView currently represents virtual uint256 GetBestBlock() const; + //! Retrieve the range of blocks that may have been only partially written. + //! If the database is in a consistent state, the result is the empty vector. + //! Otherwise, a two-element vector is returned consisting of the new and + //! the old block hash, in that order. + virtual std::vector<uint256> GetHeadBlocks() const; + //! Do a bulk modification (multiple Coin changes + BestBlock change). //! The passed mapCoins can be modified. virtual bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock); @@ -181,6 +189,7 @@ public: bool GetCoin(const COutPoint &outpoint, Coin &coin) const override; bool HaveCoin(const COutPoint &outpoint) const override; uint256 GetBestBlock() const override; + std::vector<uint256> GetHeadBlocks() const override; void SetBackend(CCoinsView &viewIn); bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) override; CCoinsViewCursor *Cursor() const override; @@ -224,8 +233,13 @@ public: /** * Return a reference to Coin in the cache, or a pruned one if not found. This is - * more efficient than GetCoin. Modifications to other cache entries are - * allowed while accessing the returned pointer. + * more efficient than GetCoin. + * + * Generally, do not hold the reference returned for more than a short scope. + * While the current implementation allows for modifications to the contents + * of the cache while holding the reference, this behavior should not be relied + * on! To be safe, best to not hold the returned reference through any other + * calls to this cache. */ const Coin& AccessCoin(const COutPoint &output) const; @@ -240,7 +254,7 @@ public: * If no unspent output exists for the passed outpoint, this call * has no effect. */ - void SpendCoin(const COutPoint &outpoint, Coin* moveto = nullptr); + bool SpendCoin(const COutPoint &outpoint, Coin* moveto = nullptr); /** * Push the modifications applied to this cache to its base. @@ -284,12 +298,17 @@ private: }; //! Utility function to add all of a transaction's outputs to a cache. -// It assumes that overwrites are only possible for coinbase transactions, +// When check is false, this assumes that overwrites are only possible for coinbase transactions. +// When check is true, the underlying view may be queried to determine whether an addition is +// an overwrite. // TODO: pass in a boolean to limit these possible overwrites to known // (pre-BIP34) cases. -void AddCoins(CCoinsViewCache& cache, const CTransaction& tx, int nHeight); +void AddCoins(CCoinsViewCache& cache, const CTransaction& tx, int nHeight, bool check = false); //! Utility function to find any unspent output with a given txid. +// This function can be quite expensive because in the event of a transaction +// which is not found in the cache, it can cause up to MAX_OUTPUTS_PER_BLOCK +// lookups to database, so it should be used with care. const Coin& AccessByTxid(const CCoinsViewCache& cache, const uint256& txid); #endif // BITCOIN_COINS_H diff --git a/src/compat/glibc_sanity.cpp b/src/compat/glibc_sanity.cpp index d62d74d462..b4d1c90992 100644 --- a/src/compat/glibc_sanity.cpp +++ b/src/compat/glibc_sanity.cpp @@ -56,7 +56,7 @@ bool sanity_test_fdelt() } #endif -} // anon namespace +} // namespace bool glibc_sanity_test() { diff --git a/src/compat/glibcxx_sanity.cpp b/src/compat/glibcxx_sanity.cpp index cee8a98c7f..569fb1bbe8 100644 --- a/src/compat/glibcxx_sanity.cpp +++ b/src/compat/glibcxx_sanity.cpp @@ -38,7 +38,7 @@ bool sanity_test_list(unsigned int size) return true; } -} // anon namespace +} // namespace // trigger: string::at(x) on an empty string to trigger __throw_out_of_range_fmt. // test: force std::string to throw an out_of_range exception. Verify that diff --git a/src/consensus/consensus.h b/src/consensus/consensus.h index 351911a3a4..58b2ed4b3e 100644 --- a/src/consensus/consensus.h +++ b/src/consensus/consensus.h @@ -12,7 +12,13 @@ static const unsigned int MAX_BLOCK_SERIALIZED_SIZE = 4000000; /** The maximum allowed weight for a block, see BIP 141 (network rule) */ static const unsigned int MAX_BLOCK_WEIGHT = 4000000; -/** The maximum allowed size for a block excluding witness data, in bytes (network rule) */ +/** + * The maximum allowed size for a block excluding witness data, in bytes (network rule). + * This parameter is largely superfluous because it is directly implied by the above block + * weight limit, even when BIP 141 is not active. It continues to exist for use in + * various early tests that run before the witness data has been checked. + * All tests related to it could be removed without breaking consensus compatibility. + */ static const unsigned int MAX_BLOCK_BASE_SIZE = 1000000; /** The maximum allowed number of signature check operations in a block (network rule) */ static const int64_t MAX_BLOCK_SIGOPS_COST = 80000; diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp index bf68f8754b..0671cbc132 100644 --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -126,7 +126,9 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in unsigned int nSigOps = 0; for (unsigned int i = 0; i < tx.vin.size(); i++) { - const CTxOut &prevout = inputs.AccessCoin(tx.vin[i].prevout).out; + const Coin& coin = inputs.AccessCoin(tx.vin[i].prevout); + assert(!coin.IsSpent()); + const CTxOut &prevout = coin.out; if (prevout.scriptPubKey.IsPayToScriptHash()) nSigOps += prevout.scriptPubKey.GetSigOpCount(tx.vin[i].scriptSig); } @@ -146,7 +148,9 @@ int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& i for (unsigned int i = 0; i < tx.vin.size(); i++) { - const CTxOut &prevout = inputs.AccessCoin(tx.vin[i].prevout).out; + const Coin& coin = inputs.AccessCoin(tx.vin[i].prevout); + assert(!coin.IsSpent()); + const CTxOut &prevout = coin.out; nSigOps += CountWitnessSigOps(tx.vin[i].scriptSig, prevout.scriptPubKey, &tx.vin[i].scriptWitness, flags); } return nSigOps; diff --git a/src/cuckoocache.h b/src/cuckoocache.h index 5837549455..fd24d05ee7 100644 --- a/src/cuckoocache.h +++ b/src/cuckoocache.h @@ -176,7 +176,7 @@ private: */ mutable std::vector<bool> epoch_flags; - /** epoch_heuristic_counter is used to determine when a epoch might be aged + /** epoch_heuristic_counter is used to determine when an epoch might be aged * & an expensive scan should be done. epoch_heuristic_counter is * decremented on insert and reset to the new number of inserts which would * cause the epoch to reach epoch_size when it reaches zero. @@ -184,7 +184,7 @@ private: uint32_t epoch_heuristic_counter; /** epoch_size is set to be the number of elements supposed to be in a - * epoch. When the number of non-erased elements in a epoch + * epoch. When the number of non-erased elements in an epoch * exceeds epoch_size, a new epoch should be started and all * current entries demoted. epoch_size is set to be 45% of size because * we want to keep load around 90%, and we support 3 epochs at once -- @@ -206,6 +206,37 @@ private: /** compute_hashes is convenience for not having to write out this * expression everywhere we use the hash values of an Element. * + * We need to map the 32-bit input hash onto a hash bucket in a range [0, size) in a + * manner which preserves as much of the hash's uniformity as possible. Ideally + * this would be done by bitmasking but the size is usually not a power of two. + * + * The naive approach would be to use a mod -- which isn't perfectly uniform but so + * long as the hash is much larger than size it is not that bad. Unfortunately, + * mod/division is fairly slow on ordinary microprocessors (e.g. 90-ish cycles on + * haswell, ARM doesn't even have an instruction for it.); when the divisor is a + * constant the compiler will do clever tricks to turn it into a multiply+add+shift, + * but size is a run-time value so the compiler can't do that here. + * + * One option would be to implement the same trick the compiler uses and compute the + * constants for exact division based on the size, as described in "{N}-bit Unsigned + * Division via {N}-bit Multiply-Add" by Arch D. Robison in 2005. But that code is + * somewhat complicated and the result is still slower than other options: + * + * Instead we treat the 32-bit random number as a Q32 fixed-point number in the range + * [0,1) and simply multiply it by the size. Then we just shift the result down by + * 32-bits to get our bucket number. The results has non-uniformity the same as a + * mod, but it is much faster to compute. More about this technique can be found at + * http://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ + * + * The resulting non-uniformity is also more equally distributed which would be + * advantageous for something like linear probing, though it shouldn't matter + * one way or the other for a cuckoo table. + * + * The primary disadvantage of this approach is increased intermediate precision is + * required but for a 32-bit random number we only need the high 32 bits of a + * 32*32->64 multiply, which means the operation is reasonably fast even on a + * typical 32-bit processor. + * * @param e the element whose hashes will be returned * @returns std::array<uint32_t, 8> of deterministic hashes derived from e */ diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp index 3d2098c059..ba9e21cc1f 100644 --- a/src/dbwrapper.cpp +++ b/src/dbwrapper.cpp @@ -108,7 +108,7 @@ CDBWrapper::CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory, bo leveldb::Status result = leveldb::DestroyDB(path.string(), options); dbwrapper_private::HandleError(result); } - TryCreateDirectory(path); + TryCreateDirectories(path); LogPrintf("Opening LevelDB in %s\n", path.string()); } leveldb::Status status = leveldb::DB::Open(options, path.string(), &pdb); @@ -209,4 +209,4 @@ const std::vector<unsigned char>& GetObfuscateKey(const CDBWrapper &w) return w.obfuscate_key; } -}; +} // namespace dbwrapper_private diff --git a/src/httprpc.cpp b/src/httprpc.cpp index 39da26afe1..a207d5ece4 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -46,11 +46,11 @@ public: HTTPRPCTimerInterface(struct event_base* _base) : base(_base) { } - const char* Name() + const char* Name() override { return "HTTP"; } - RPCTimerBase* NewTimer(std::function<void(void)>& func, int64_t millis) + RPCTimerBase* NewTimer(std::function<void(void)>& func, int64_t millis) override { return new HTTPRPCTimer(base, func, millis); } @@ -91,35 +91,32 @@ static bool multiUserAuthorized(std::string strUserPass) std::string strUser = strUserPass.substr(0, strUserPass.find(":")); std::string strPass = strUserPass.substr(strUserPass.find(":") + 1); - if (gArgs.IsArgSet("-rpcauth")) { + for (const std::string& strRPCAuth : gArgs.GetArgs("-rpcauth")) { //Search for multi-user login/pass "rpcauth" from config - for (std::string strRPCAuth : gArgs.GetArgs("-rpcauth")) - { - std::vector<std::string> vFields; - boost::split(vFields, strRPCAuth, boost::is_any_of(":$")); - if (vFields.size() != 3) { - //Incorrect formatting in config file - continue; - } - - std::string strName = vFields[0]; - if (!TimingResistantEqual(strName, strUser)) { - continue; - } - - std::string strSalt = vFields[1]; - std::string strHash = vFields[2]; - - static const unsigned int KEY_SIZE = 32; - unsigned char out[KEY_SIZE]; - - CHMAC_SHA256(reinterpret_cast<const unsigned char*>(strSalt.c_str()), strSalt.size()).Write(reinterpret_cast<const unsigned char*>(strPass.c_str()), strPass.size()).Finalize(out); - std::vector<unsigned char> hexvec(out, out+KEY_SIZE); - std::string strHashFromPass = HexStr(hexvec); - - if (TimingResistantEqual(strHashFromPass, strHash)) { - return true; - } + std::vector<std::string> vFields; + boost::split(vFields, strRPCAuth, boost::is_any_of(":$")); + if (vFields.size() != 3) { + //Incorrect formatting in config file + continue; + } + + std::string strName = vFields[0]; + if (!TimingResistantEqual(strName, strUser)) { + continue; + } + + std::string strSalt = vFields[1]; + std::string strHash = vFields[2]; + + static const unsigned int KEY_SIZE = 32; + unsigned char out[KEY_SIZE]; + + CHMAC_SHA256(reinterpret_cast<const unsigned char*>(strSalt.c_str()), strSalt.size()).Write(reinterpret_cast<const unsigned char*>(strPass.c_str()), strPass.size()).Finalize(out); + std::vector<unsigned char> hexvec(out, out+KEY_SIZE); + std::string strHashFromPass = HexStr(hexvec); + + if (TimingResistantEqual(strHashFromPass, strHash)) { + return true; } } return false; diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 0d1cba3fd2..1c53d8d49d 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -21,13 +21,13 @@ #include <signal.h> #include <future> -#include <event2/event.h> -#include <event2/http.h> #include <event2/thread.h> #include <event2/buffer.h> #include <event2/util.h> #include <event2/keyvalq_struct.h> +#include "support/events.h" + #ifdef EVENT__HAVE_NETINET_IN_H #include <netinet/in.h> #ifdef _XOPEN_SOURCE_EXTENDED @@ -46,7 +46,7 @@ public: req(std::move(_req)), path(_path), func(_func) { } - void operator()() + void operator()() override { func(req.get(), path); } @@ -196,18 +196,16 @@ static bool InitHTTPAllowList() LookupHost("::1", localv6, false); rpc_allow_subnets.push_back(CSubNet(localv4, 8)); // always allow IPv4 local subnet rpc_allow_subnets.push_back(CSubNet(localv6)); // always allow IPv6 localhost - if (gArgs.IsArgSet("-rpcallowip")) { - for (const std::string& strAllow : gArgs.GetArgs("-rpcallowip")) { - CSubNet subnet; - LookupSubNet(strAllow.c_str(), subnet); - if (!subnet.IsValid()) { - uiInterface.ThreadSafeMessageBox( - strprintf("Invalid -rpcallowip subnet specification: %s. Valid are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24).", strAllow), - "", CClientUIInterface::MSG_ERROR); - return false; - } - rpc_allow_subnets.push_back(subnet); + for (const std::string& strAllow : gArgs.GetArgs("-rpcallowip")) { + CSubNet subnet; + LookupSubNet(strAllow.c_str(), subnet); + if (!subnet.IsValid()) { + uiInterface.ThreadSafeMessageBox( + strprintf("Invalid -rpcallowip subnet specification: %s. Valid are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24).", strAllow), + "", CClientUIInterface::MSG_ERROR); + return false; } + rpc_allow_subnets.push_back(subnet); } std::string strAllowed; for (const CSubNet& subnet : rpc_allow_subnets) @@ -367,9 +365,6 @@ static void libevent_log_cb(int severity, const char *msg) bool InitHTTPServer() { - struct evhttp* http = 0; - struct event_base* base = 0; - if (!InitHTTPAllowList()) return false; @@ -395,17 +390,13 @@ bool InitHTTPServer() evthread_use_pthreads(); #endif - base = event_base_new(); // XXX RAII - if (!base) { - LogPrintf("Couldn't create an event_base: exiting\n"); - return false; - } + raii_event_base base_ctr = obtain_event_base(); /* Create a new evhttp object to handle requests. */ - http = evhttp_new(base); // XXX RAII + raii_evhttp http_ctr = obtain_evhttp(base_ctr.get()); + struct evhttp* http = http_ctr.get(); if (!http) { LogPrintf("couldn't create evhttp. Exiting.\n"); - event_base_free(base); return false; } @@ -416,8 +407,6 @@ bool InitHTTPServer() if (!HTTPBindAddresses(http)) { LogPrintf("Unable to bind any endpoint for RPC server\n"); - evhttp_free(http); - event_base_free(base); return false; } @@ -426,8 +415,9 @@ bool InitHTTPServer() LogPrintf("HTTP: creating work queue of depth %d\n", workQueueDepth); workQueue = new WorkQueue<HTTPClosure>(workQueueDepth); - eventBase = base; - eventHTTP = http; + // tranfer ownership to eventBase/HTTP via .release() + eventBase = base_ctr.release(); + eventHTTP = http_ctr.release(); return true; } diff --git a/src/httpserver.h b/src/httpserver.h index 6be9950682..9df56e5fc5 100644 --- a/src/httpserver.h +++ b/src/httpserver.h @@ -86,7 +86,7 @@ public: /** * Get the request header specified by hdr, or an empty string. - * Return an pair (isPresent,string). + * Return a pair (isPresent,string). */ std::pair<bool, std::string> GetHeader(const std::string& hdr); @@ -125,7 +125,7 @@ public: virtual ~HTTPClosure() {} }; -/** Event class. This can be used either as an cross-thread trigger or as a timer. +/** Event class. This can be used either as a cross-thread trigger or as a timer. */ class HTTPEvent { diff --git a/src/init.cpp b/src/init.cpp index b6e4cd06f6..672ef77e80 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -88,14 +88,6 @@ static CZMQNotificationInterface* pzmqNotificationInterface = NULL; #define MIN_CORE_FILEDESCRIPTORS 150 #endif -/** Used to pass flags to the Bind() function */ -enum BindFlags { - BF_NONE = 0, - BF_EXPLICIT = (1U << 0), - BF_REPORT_ERROR = (1U << 1), - BF_WHITELIST = (1U << 2), -}; - static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat"; ////////////////////////////////////////////////////////////////////////////// @@ -296,17 +288,6 @@ static void registerSignalHandler(int signal, void(*handler)(int)) } #endif -bool static Bind(CConnman& connman, const CService &addr, unsigned int flags) { - if (!(flags & BF_EXPLICIT) && IsLimited(addr)) - return false; - std::string strError; - if (!connman.BindListenPort(addr, strError, (flags & BF_WHITELIST) != 0)) { - if (flags & BF_REPORT_ERROR) - return InitError(strError); - return false; - } - return true; -} void OnRPCStarted() { uiInterface.NotifyBlockTip.connect(&RPCNotifyBlockChange); @@ -355,6 +336,9 @@ std::string HelpMessage(HelpMessageMode mode) #endif } strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory")); + if (showDebug) { + strUsage += HelpMessageOpt("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize)); + } strUsage += HelpMessageOpt("-dbcache=<n>", strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache)); if (showDebug) strUsage += HelpMessageOpt("-feefilter", strprintf("Tell other nodes to filter invs to us by our mempool min fee (default: %u)", DEFAULT_FEEFILTER)); @@ -463,7 +447,7 @@ std::string HelpMessage(HelpMessageMode mode) { strUsage += HelpMessageOpt("-logtimemicros", strprintf("Add microsecond precision to debug timestamps (default: %u)", DEFAULT_LOGTIMEMICROS)); strUsage += HelpMessageOpt("-mocktime=<n>", "Replace actual time with <n> seconds since epoch (default: 0)"); - strUsage += HelpMessageOpt("-maxsigcachesize=<n>", strprintf("Limit size of signature cache to <n> MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_SIZE)); + strUsage += HelpMessageOpt("-maxsigcachesize=<n>", strprintf("Limit sum of signature cache and script execution cache sizes to <n> MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_SIZE)); strUsage += HelpMessageOpt("-maxtipage=<n>", strprintf("Maximum tip age in seconds to consider node in initial block download (default: %u)", DEFAULT_MAX_TIP_AGE)); } strUsage += HelpMessageOpt("-maxtxfee=<amt>", strprintf(_("Maximum total fees (in %s) to use in a single wallet transaction or raw transaction; setting this too low may abort large transactions (default: %s)"), @@ -822,7 +806,7 @@ int nUserMaxConnections; int nFD; ServiceFlags nLocalServices = NODE_NETWORK; -} +} // namespace [[noreturn]] static void new_handler_terminate() { @@ -844,8 +828,6 @@ bool AppInitBasicSetup() // Turn off Microsoft heap dump noise _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); _CrtSetReportFile(_CRT_WARN, CreateFileA("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0)); -#endif -#if _MSC_VER >= 1400 // Disable confusing "helpful" text message on abort, Ctrl-C _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); #endif @@ -900,10 +882,14 @@ bool AppInitParameterInteraction() return InitError(_("Prune mode is incompatible with -txindex.")); } + // -bind and -whitebind can't be set when not listening + size_t nUserBind = gArgs.GetArgs("-bind").size() + gArgs.GetArgs("-whitebind").size(); + if (nUserBind != 0 && !gArgs.GetBoolArg("-listen", DEFAULT_LISTEN)) { + return InitError("Cannot set -bind or -whitebind together with -listen=0"); + } + // Make sure enough file descriptors are available - int nBind = std::max( - (gArgs.IsArgSet("-bind") ? gArgs.GetArgs("-bind").size() : 0) + - (gArgs.IsArgSet("-whitebind") ? gArgs.GetArgs("-whitebind").size() : 0), size_t(1)); + int nBind = std::max(nUserBind, size_t(1)); nUserMaxConnections = GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS); nMaxConnections = std::max(nUserMaxConnections, 0); @@ -935,15 +921,13 @@ bool AppInitParameterInteraction() } // Now remove the logging categories which were explicitly excluded - if (gArgs.IsArgSet("-debugexclude")) { - for (const std::string& cat : gArgs.GetArgs("-debugexclude")) { - uint32_t flag = 0; - if (!GetLogCategory(&flag, &cat)) { - InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debugexclude", cat)); - continue; - } - logCategories &= ~flag; + for (const std::string& cat : gArgs.GetArgs("-debugexclude")) { + uint32_t flag = 0; + if (!GetLogCategory(&flag, &cat)) { + InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debugexclude", cat)); + continue; } + logCategories &= ~flag; } // Check for -debugnet @@ -1030,14 +1014,7 @@ bool AppInitParameterInteraction() if (nConnectTimeout <= 0) nConnectTimeout = DEFAULT_CONNECT_TIMEOUT; - // Fee-per-kilobyte amount required for mempool acceptance and relay - // If you are mining, be careful setting this: - // if you set it to zero then - // a transaction spammer can cheaply fill blocks using - // 0-fee transactions. It should be set above the real - // cost to you of processing a transaction. - if (IsArgSet("-minrelaytxfee")) - { + if (IsArgSet("-minrelaytxfee")) { CAmount n = 0; if (!ParseMoney(GetArg("-minrelaytxfee", ""), n)) { return InitError(AmountErrMsg("minrelaytxfee", GetArg("-minrelaytxfee", ""))); @@ -1170,6 +1147,7 @@ bool AppInitSanityChecks() // ********************************************************* Step 4: sanity checks // Initialize elliptic curve code + RandomInit(); ECC_Start(); globalVerifyHandle.reset(new ECCVerifyHandle()); @@ -1213,6 +1191,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) LogPrintf("Using at most %i automatic connections (%i file descriptors available)\n", nMaxConnections, nFD); InitSignatureCache(); + InitScriptExecutionCache(); LogPrintf("Using %u threads for script verification\n", nScriptCheckThreads); if (nScriptCheckThreads) { @@ -1259,13 +1238,10 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) // sanitize comments per BIP-0014, format user agent and check total size std::vector<std::string> uacomments; - if (gArgs.IsArgSet("-uacomment")) { - for (std::string cmt : gArgs.GetArgs("-uacomment")) - { - if (cmt != SanitizeString(cmt, SAFE_CHARS_UA_COMMENT)) - return InitError(strprintf(_("User Agent comment (%s) contains unsafe characters."), cmt)); - uacomments.push_back(cmt); - } + for (const std::string& cmt : gArgs.GetArgs("-uacomment")) { + if (cmt != SanitizeString(cmt, SAFE_CHARS_UA_COMMENT)) + return InitError(strprintf(_("User Agent comment (%s) contains unsafe characters."), cmt)); + uacomments.push_back(cmt); } strSubVersion = FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, uacomments); if (strSubVersion.size() > MAX_SUBVERSION_LENGTH) { @@ -1288,16 +1264,6 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) } } - if (gArgs.IsArgSet("-whitelist")) { - for (const std::string& net : gArgs.GetArgs("-whitelist")) { - CSubNet subnet; - LookupSubNet(net.c_str(), subnet); - if (!subnet.IsValid()) - return InitError(strprintf(_("Invalid netmask specified in -whitelist: '%s'"), net)); - connman.AddWhitelistedRange(subnet); - } - } - // Check for host lookup allowed before parsing any network related parameters fNameLookup = GetBoolArg("-dns", DEFAULT_NAME_LOOKUP); @@ -1348,44 +1314,12 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) fDiscover = GetBoolArg("-discover", true); fRelayTxes = !GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY); - if (fListen) { - bool fBound = false; - if (gArgs.IsArgSet("-bind")) { - for (const std::string& strBind : gArgs.GetArgs("-bind")) { - CService addrBind; - if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false)) - return InitError(ResolveErrMsg("bind", strBind)); - fBound |= Bind(connman, addrBind, (BF_EXPLICIT | BF_REPORT_ERROR)); - } - } - if (gArgs.IsArgSet("-whitebind")) { - for (const std::string& strBind : gArgs.GetArgs("-whitebind")) { - CService addrBind; - if (!Lookup(strBind.c_str(), addrBind, 0, false)) - return InitError(ResolveErrMsg("whitebind", strBind)); - if (addrBind.GetPort() == 0) - return InitError(strprintf(_("Need to specify a port with -whitebind: '%s'"), strBind)); - fBound |= Bind(connman, addrBind, (BF_EXPLICIT | BF_REPORT_ERROR | BF_WHITELIST)); - } - } - if (!gArgs.IsArgSet("-bind") && !gArgs.IsArgSet("-whitebind")) { - struct in_addr inaddr_any; - inaddr_any.s_addr = INADDR_ANY; - fBound |= Bind(connman, CService(in6addr_any, GetListenPort()), BF_NONE); - fBound |= Bind(connman, CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE); - } - if (!fBound) - return InitError(_("Failed to listen on any port. Use -listen=0 if you want this.")); - } - - if (gArgs.IsArgSet("-externalip")) { - for (const std::string& strAddr : gArgs.GetArgs("-externalip")) { - CService addrLocal; - if (Lookup(strAddr.c_str(), addrLocal, GetListenPort(), fNameLookup) && addrLocal.IsValid()) - AddLocal(addrLocal, LOCAL_MANUAL); - else - return InitError(ResolveErrMsg("externalip", strAddr)); - } + for (const std::string& strAddr : gArgs.GetArgs("-externalip")) { + CService addrLocal; + if (Lookup(strAddr.c_str(), addrLocal, GetListenPort(), fNameLookup) && addrLocal.IsValid()) + AddLocal(addrLocal, LOCAL_MANUAL); + else + return InitError(ResolveErrMsg("externalip", strAddr)); } #if ENABLE_ZMQ @@ -1407,8 +1341,6 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) fReindex = GetBoolArg("-reindex", false); bool fReindexChainState = GetBoolArg("-reindex-chainstate", false); - fs::create_directories(GetDataDir() / "blocks"); - // cache size calculations int64_t nTotalCache = (GetArg("-dbcache", nDefaultDbCache) << 20); nTotalCache = std::max(nTotalCache, nMinDbCache << 20); // total cache cannot be less than nMinDbCache @@ -1427,7 +1359,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) LogPrintf("* Using %.1fMiB for in-memory UTXO set (plus up to %.1fMiB of unused mempool space)\n", nCoinCacheUsage * (1.0 / 1024 / 1024), nMempoolSizeMax * (1.0 / 1024 / 1024)); bool fLoaded = false; - while (!fLoaded) { + while (!fLoaded && !fRequestShutdown) { bool fReset = fReindex; std::string strLoadError; @@ -1445,7 +1377,6 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex); pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex || fReindexChainState); pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview); - pcoinsTip = new CCoinsViewCache(pcoinscatcher); if (fReindex) { pblocktree->WriteReindexing(true); @@ -1459,6 +1390,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) break; } } + if (fRequestShutdown) break; if (!LoadBlockIndex(chainparams)) { strLoadError = _("Error loading block database"); @@ -1489,6 +1421,13 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) break; } + if (!ReplayBlocks(chainparams, pcoinsdbview)) { + strLoadError = _("Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate."); + break; + } + pcoinsTip = new CCoinsViewCache(pcoinscatcher); + LoadChainTip(chainparams); + if (!fReindex && chainActive.Tip() != NULL) { uiInterface.InitMessage(_("Rewinding blocks...")); if (!RewindBlockIndex(chainparams)) { @@ -1529,7 +1468,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) fLoaded = true; } while(false); - if (!fLoaded) { + if (!fLoaded && !fRequestShutdown) { // first suggest a reindex if (!fReset) { bool fRet = uiInterface.ThreadSafeQuestion( @@ -1616,10 +1555,8 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) uiInterface.NotifyBlockTip.connect(BlockNotifyCallback); std::vector<fs::path> vImportFiles; - if (gArgs.IsArgSet("-loadblock")) - { - for (const std::string& strFile : gArgs.GetArgs("-loadblock")) - vImportFiles.push_back(strFile); + for (const std::string& strFile : gArgs.GetArgs("-loadblock")) { + vImportFiles.push_back(strFile); } threadGroup.create_thread(boost::bind(&ThreadImport, vImportFiles)); @@ -1646,7 +1583,6 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) // Map ports with UPnP MapPort(GetBoolArg("-upnp", DEFAULT_UPNP)); - std::string strNodeError; CConnman::Options connOptions; connOptions.nLocalServices = nLocalServices; connOptions.nRelevantServices = nRelevantServices; @@ -1662,12 +1598,39 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) connOptions.nMaxOutboundTimeframe = nMaxOutboundTimeframe; connOptions.nMaxOutboundLimit = nMaxOutboundLimit; + for (const std::string& strBind : gArgs.GetArgs("-bind")) { + CService addrBind; + if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false)) { + return InitError(ResolveErrMsg("bind", strBind)); + } + connOptions.vBinds.push_back(addrBind); + } + for (const std::string& strBind : gArgs.GetArgs("-whitebind")) { + CService addrBind; + if (!Lookup(strBind.c_str(), addrBind, 0, false)) { + return InitError(ResolveErrMsg("whitebind", strBind)); + } + if (addrBind.GetPort() == 0) { + return InitError(strprintf(_("Need to specify a port with -whitebind: '%s'"), strBind)); + } + connOptions.vWhiteBinds.push_back(addrBind); + } + + for (const auto& net : gArgs.GetArgs("-whitelist")) { + CSubNet subnet; + LookupSubNet(net.c_str(), subnet); + if (!subnet.IsValid()) + return InitError(strprintf(_("Invalid netmask specified in -whitelist: '%s'"), net)); + connOptions.vWhitelistedRange.push_back(subnet); + } + if (gArgs.IsArgSet("-seednode")) { connOptions.vSeedNodes = gArgs.GetArgs("-seednode"); } - if (!connman.Start(scheduler, strNodeError, connOptions)) - return InitError(strNodeError); + if (!connman.Start(scheduler, connOptions)) { + return false; + } // ********************************************************* Step 12: finished diff --git a/src/keystore.h b/src/keystore.h index a2621f2de4..965ae0c79a 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -60,9 +60,9 @@ protected: WatchOnlySet setWatchOnly; public: - bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey); - bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const; - bool HaveKey(const CKeyID &address) const + bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override; + bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override; + bool HaveKey(const CKeyID &address) const override { bool result; { @@ -71,7 +71,7 @@ public: } return result; } - void GetKeys(std::set<CKeyID> &setAddress) const + void GetKeys(std::set<CKeyID> &setAddress) const override { setAddress.clear(); { @@ -84,7 +84,7 @@ public: } } } - bool GetKey(const CKeyID &address, CKey &keyOut) const + bool GetKey(const CKeyID &address, CKey &keyOut) const override { { LOCK(cs_KeyStore); @@ -97,14 +97,14 @@ public: } return false; } - virtual bool AddCScript(const CScript& redeemScript); - virtual bool HaveCScript(const CScriptID &hash) const; - virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const; + virtual bool AddCScript(const CScript& redeemScript) override; + virtual bool HaveCScript(const CScriptID &hash) const override; + virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const override; - virtual bool AddWatchOnly(const CScript &dest); - virtual bool RemoveWatchOnly(const CScript &dest); - virtual bool HaveWatchOnly(const CScript &dest) const; - virtual bool HaveWatchOnly() const; + virtual bool AddWatchOnly(const CScript &dest) override; + virtual bool RemoveWatchOnly(const CScript &dest) override; + virtual bool HaveWatchOnly(const CScript &dest) const override; + virtual bool HaveWatchOnly() const override; }; typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial; diff --git a/src/leveldb/db/version_set.h b/src/leveldb/db/version_set.h index c4e7ac360b..7935a965a7 100644 --- a/src/leveldb/db/version_set.h +++ b/src/leveldb/db/version_set.h @@ -376,7 +376,7 @@ class Compaction { // Each compaction reads inputs from "level_" and "level_+1" std::vector<FileMetaData*> inputs_[2]; // The two sets of inputs - // State used to check for number of of overlapping grandparent files + // State used to check for number of overlapping grandparent files // (parent == level_ + 1, grandparent == level_ + 2) std::vector<FileMetaData*> grandparents_; size_t grandparent_index_; // Index in grandparent_starts_ diff --git a/src/net.cpp b/src/net.cpp index 73f020273b..301cf58b87 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -64,6 +64,14 @@ #endif #endif +/** Used to pass flags to the Bind() function */ +enum BindFlags { + BF_NONE = 0, + BF_EXPLICIT = (1U << 0), + BF_REPORT_ERROR = (1U << 1), + BF_WHITELIST = (1U << 2), +}; + const static std::string NET_MESSAGE_COMMAND_OTHER = "*other*"; static const uint64_t RANDOMIZER_ID_NETGROUP = 0x6c0edd8036ef4036ULL; // SHA256("netgroup")[0:8] @@ -240,7 +248,7 @@ bool RemoveLocal(const CService& addr) /** Make a particular network entirely off-limits (no automatic connects to it) */ void SetLimited(enum Network net, bool fLimited) { - if (net == NET_UNROUTABLE) + if (net == NET_UNROUTABLE || net == NET_INTERNAL) return; LOCK(cs_mapLocalHost); vfLimited[net] = fLimited; @@ -601,7 +609,6 @@ void CConnman::SetBannedSetDirty(bool dirty) bool CConnman::IsWhitelistedRange(const CNetAddr &addr) { - LOCK(cs_vWhitelistedRange); for (const CSubNet& subnet : vWhitelistedRange) { if (subnet.Match(addr)) return true; @@ -609,12 +616,6 @@ bool CConnman::IsWhitelistedRange(const CNetAddr &addr) { return false; } -void CConnman::AddWhitelistedRange(const CSubNet &subnet) { - LOCK(cs_vWhitelistedRange); - vWhitelistedRange.push_back(subnet); -} - - std::string CNode::GetAddrName() const { LOCK(cs_addrName); return addrName; @@ -1604,7 +1605,12 @@ void CConnman::ThreadDNSAddressSeed() std::vector<CNetAddr> vIPs; std::vector<CAddress> vAdd; ServiceFlags requiredServiceBits = nRelevantServices; - if (LookupHost(GetDNSHost(seed, &requiredServiceBits).c_str(), vIPs, 0, true)) + std::string host = GetDNSHost(seed, &requiredServiceBits); + CNetAddr resolveSource; + if (!resolveSource.SetInternal(host)) { + continue; + } + if (LookupHost(host.c_str(), vIPs, 0, true)) { for (const CNetAddr& ip : vIPs) { @@ -1614,18 +1620,7 @@ void CConnman::ThreadDNSAddressSeed() vAdd.push_back(addr); found++; } - } - if (interruptNet) { - return; - } - // TODO: The seed name resolve may fail, yielding an IP of [::], which results in - // addrman assigning the same source to results from different seeds. - // This should switch to a hard-coded stable dummy IP for each seed name, so that the - // resolve is not required at all. - if (!vIPs.empty()) { - CService seedSource; - Lookup(seed.name.c_str(), seedSource, 0, true); - addrman.Add(vAdd, seedSource); + addrman.Add(vAdd, resolveSource); } } } @@ -1682,7 +1677,7 @@ void CConnman::ProcessOneShot() void CConnman::ThreadOpenConnections() { // Connect to specific addresses - if (gArgs.IsArgSet("-connect") && gArgs.GetArgs("-connect").size() > 0) + if (gArgs.IsArgSet("-connect")) { for (int64_t nLoop = 0;; nLoop++) { @@ -1724,7 +1719,7 @@ void CConnman::ThreadOpenConnections() if (!done) { LogPrintf("Adding fixed seed nodes as DNS doesn't seem to be available.\n"); CNetAddr local; - LookupHost("127.0.0.1", local, false); + local.SetInternal("fixedseeds"); addrman.Add(convertSeed6(Params().FixedSeeds()), local); done = true; } @@ -1908,8 +1903,7 @@ void CConnman::ThreadOpenAddedConnections() { { LOCK(cs_vAddedNodes); - if (gArgs.IsArgSet("-addnode")) - vAddedNodes = gArgs.GetArgs("-addnode"); + vAddedNodes = gArgs.GetArgs("-addnode"); } while (true) @@ -2226,7 +2220,38 @@ NodeId CConnman::GetNewNodeId() return nLastNodeId.fetch_add(1, std::memory_order_relaxed); } -bool CConnman::Start(CScheduler& scheduler, std::string& strNodeError, Options connOptions) + +bool CConnman::Bind(const CService &addr, unsigned int flags) { + if (!(flags & BF_EXPLICIT) && IsLimited(addr)) + return false; + std::string strError; + if (!BindListenPort(addr, strError, (flags & BF_WHITELIST) != 0)) { + if ((flags & BF_REPORT_ERROR) && clientInterface) { + clientInterface->ThreadSafeMessageBox(strError, "", CClientUIInterface::MSG_ERROR); + } + return false; + } + return true; +} + +bool CConnman::InitBinds(const std::vector<CService>& binds, const std::vector<CService>& whiteBinds) { + bool fBound = false; + for (const auto& addrBind : binds) { + fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR)); + } + for (const auto& addrBind : whiteBinds) { + fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR | BF_WHITELIST)); + } + if (binds.empty() && whiteBinds.empty()) { + struct in_addr inaddr_any; + inaddr_any.s_addr = INADDR_ANY; + fBound |= Bind(CService(in6addr_any, GetListenPort()), BF_NONE); + fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE); + } + return fBound; +} + +bool CConnman::Start(CScheduler& scheduler, Options connOptions) { nTotalBytesRecv = 0; nTotalBytesSent = 0; @@ -2248,11 +2273,23 @@ bool CConnman::Start(CScheduler& scheduler, std::string& strNodeError, Options c SetBestHeight(connOptions.nBestHeight); + clientInterface = connOptions.uiInterface; + + if (fListen && !InitBinds(connOptions.vBinds, connOptions.vWhiteBinds)) { + if (clientInterface) { + clientInterface->ThreadSafeMessageBox( + _("Failed to listen on any port. Use -listen=0 if you want this."), + "", CClientUIInterface::MSG_ERROR); + } + return false; + } + + vWhitelistedRange = connOptions.vWhitelistedRange; + for (const auto& strDest : connOptions.vSeedNodes) { AddOneShot(strDest); } - clientInterface = connOptions.uiInterface; if (clientInterface) { clientInterface->InitMessage(_("Loading P2P addresses...")); } @@ -143,13 +143,14 @@ public: uint64_t nMaxOutboundTimeframe = 0; uint64_t nMaxOutboundLimit = 0; std::vector<std::string> vSeedNodes; + std::vector<CSubNet> vWhitelistedRange; + std::vector<CService> vBinds, vWhiteBinds; }; CConnman(uint64_t seed0, uint64_t seed1); ~CConnman(); - bool Start(CScheduler& scheduler, std::string& strNodeError, Options options); + bool Start(CScheduler& scheduler, Options options); void Stop(); void Interrupt(); - bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false); bool GetNetworkActive() const { return fNetworkActive; }; void SetNetworkActive(bool active); bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false, bool fFeeler = false, bool fAddnode = false); @@ -243,8 +244,6 @@ public: unsigned int GetSendBufferSize() const; - void AddWhitelistedRange(const CSubNet &subnet); - ServiceFlags GetLocalServices() const; //!set the max outbound target in bytes @@ -288,6 +287,9 @@ private: ListenSocket(SOCKET socket_, bool whitelisted_) : socket(socket_), whitelisted(whitelisted_) {} }; + bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false); + bool Bind(const CService &addr, unsigned int flags); + bool InitBinds(const std::vector<CService>& binds, const std::vector<CService>& whiteBinds); void ThreadOpenAddedConnections(); void AddOneShot(const std::string& strDest); void ProcessOneShot(); @@ -345,7 +347,6 @@ private: // Whitelisted ranges. Any node connecting from these is automatically // whitelisted (as well as those connecting to whitelisted binds). std::vector<CSubNet> vWhitelistedRange; - CCriticalSection cs_vWhitelistedRange; unsigned int nSendBufferMaxSize; unsigned int nReceiveFloodSize; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index a8ad41dbab..a743f04dd1 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -121,7 +121,7 @@ namespace { MapRelay mapRelay; /** Expiration-time ordered list of (expire time, relay map entry) pairs, protected by cs_main). */ std::deque<std::pair<int64_t, MapRelay::iterator>> vRelayExpiration; -} // anon namespace +} // namespace ////////////////////////////////////////////////////////////////////////////// // @@ -343,7 +343,9 @@ bool MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const CBlockIndex* // Short-circuit most stuff in case its from the same node std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash); if (itInFlight != mapBlocksInFlight.end() && itInFlight->second.first == nodeid) { - *pit = &itInFlight->second.second; + if (pit) { + *pit = &itInFlight->second.second; + } return false; } @@ -451,25 +453,6 @@ bool PeerHasHeader(CNodeState *state, const CBlockIndex *pindex) return false; } -/** Find the last common ancestor two blocks have. - * Both pa and pb must be non-NULL. */ -const CBlockIndex* LastCommonAncestor(const CBlockIndex* pa, const CBlockIndex* pb) { - if (pa->nHeight > pb->nHeight) { - pa = pa->GetAncestor(pb->nHeight); - } else if (pb->nHeight > pa->nHeight) { - pb = pb->GetAncestor(pa->nHeight); - } - - while (pa != pb && pa && pb) { - pa = pa->pprev; - pb = pb->pprev; - } - - // Eventually all chain branches meet at the genesis block. - assert(pa == pb); - return pa; -} - /** Update pindexLastCommonBlock and add not-in-flight missing successors to vBlocks, until it has * at most count entries. */ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<const CBlockIndex*>& vBlocks, NodeId& nodeStaller, const Consensus::Params& consensusParams) { @@ -558,7 +541,7 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<con } } -} // anon namespace +} // namespace bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) { LOCK(cs_main); diff --git a/src/netaddress.cpp b/src/netaddress.cpp index 34a7029862..89f257c642 100644 --- a/src/netaddress.cpp +++ b/src/netaddress.cpp @@ -15,6 +15,9 @@ static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; static const unsigned char pchOnionCat[] = {0xFD,0x87,0xD8,0x7E,0xEB,0x43}; +// 0xFD + sha256("bitcoin")[0:5] +static const unsigned char g_internal_prefix[] = { 0xFD, 0x6B, 0x88, 0xC0, 0x87, 0x24 }; + void CNetAddr::Init() { memset(ip, 0, sizeof(ip)); @@ -42,6 +45,18 @@ void CNetAddr::SetRaw(Network network, const uint8_t *ip_in) } } +bool CNetAddr::SetInternal(const std::string &name) +{ + if (name.empty()) { + return false; + } + unsigned char hash[32] = {}; + CSHA256().Write((const unsigned char*)name.data(), name.size()).Finalize(hash); + memcpy(ip, g_internal_prefix, sizeof(g_internal_prefix)); + memcpy(ip + sizeof(g_internal_prefix), hash, sizeof(ip) - sizeof(g_internal_prefix)); + return true; +} + bool CNetAddr::SetSpecial(const std::string &strName) { if (strName.size()>6 && strName.substr(strName.size() - 6, 6) == ".onion") { @@ -84,7 +99,7 @@ bool CNetAddr::IsIPv4() const bool CNetAddr::IsIPv6() const { - return (!IsIPv4() && !IsTor()); + return (!IsIPv4() && !IsTor() && !IsInternal()); } bool CNetAddr::IsRFC1918() const @@ -199,6 +214,9 @@ bool CNetAddr::IsValid() const if (IsRFC3849()) return false; + if (IsInternal()) + return false; + if (IsIPv4()) { // INADDR_NONE @@ -217,11 +235,19 @@ bool CNetAddr::IsValid() const bool CNetAddr::IsRoutable() const { - return IsValid() && !(IsRFC1918() || IsRFC2544() || IsRFC3927() || IsRFC4862() || IsRFC6598() || IsRFC5737() || (IsRFC4193() && !IsTor()) || IsRFC4843() || IsLocal()); + return IsValid() && !(IsRFC1918() || IsRFC2544() || IsRFC3927() || IsRFC4862() || IsRFC6598() || IsRFC5737() || (IsRFC4193() && !IsTor()) || IsRFC4843() || IsLocal() || IsInternal()); +} + +bool CNetAddr::IsInternal() const +{ + return memcmp(ip, g_internal_prefix, sizeof(g_internal_prefix)) == 0; } enum Network CNetAddr::GetNetwork() const { + if (IsInternal()) + return NET_INTERNAL; + if (!IsRoutable()) return NET_UNROUTABLE; @@ -238,6 +264,8 @@ std::string CNetAddr::ToStringIP() const { if (IsTor()) return EncodeBase32(&ip[6], 10) + ".onion"; + if (IsInternal()) + return EncodeBase32(ip + sizeof(g_internal_prefix), sizeof(ip) - sizeof(g_internal_prefix)) + ".internal"; CService serv(*this, 0); struct sockaddr_storage sockaddr; socklen_t socklen = sizeof(sockaddr); @@ -305,9 +333,15 @@ std::vector<unsigned char> CNetAddr::GetGroup() const nClass = 255; nBits = 0; } - - // all unroutable addresses belong to the same group - if (!IsRoutable()) + // all internal-usage addresses get their own group + if (IsInternal()) + { + nClass = NET_INTERNAL; + nStartByte = sizeof(g_internal_prefix); + nBits = (sizeof(ip) - sizeof(g_internal_prefix)) * 8; + } + // all other unroutable addresses belong to the same group + else if (!IsRoutable()) { nClass = NET_UNROUTABLE; nBits = 0; @@ -393,7 +427,7 @@ int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const REACH_PRIVATE }; - if (!IsRoutable()) + if (!IsRoutable() || IsInternal()) return REACH_UNREACHABLE; int ourNet = GetExtNetwork(this); @@ -552,7 +586,7 @@ std::string CService::ToStringPort() const std::string CService::ToStringIPPort() const { - if (IsIPv4() || IsTor()) { + if (IsIPv4() || IsTor() || IsInternal()) { return ToStringIP() + ":" + ToStringPort(); } else { return "[" + ToStringIP() + "]:" + ToStringPort(); diff --git a/src/netaddress.h b/src/netaddress.h index fbc4d1a65f..80716600d1 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -22,6 +22,7 @@ enum Network NET_IPV4, NET_IPV6, NET_TOR, + NET_INTERNAL, NET_MAX, }; @@ -45,6 +46,12 @@ class CNetAddr */ void SetRaw(Network network, const uint8_t *data); + /** + * Transform an arbitrary string into a non-routable ipv6 address. + * Useful for mapping resolved addresses back to their source. + */ + bool SetInternal(const std::string& name); + bool SetSpecial(const std::string &strName); // for Tor addresses bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0) bool IsIPv6() const; // IPv6 address (not mapped IPv4, not Tor) @@ -64,6 +71,7 @@ class CNetAddr bool IsTor() const; bool IsLocal() const; bool IsRoutable() const; + bool IsInternal() const; bool IsValid() const; enum Network GetNetwork() const; std::string ToString() const; diff --git a/src/netbase.cpp b/src/netbase.cpp index 32557dd179..a23f92e1ed 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -108,17 +108,22 @@ bool static LookupIntern(const char *pszName, std::vector<CNetAddr>& vIP, unsign struct addrinfo *aiTrav = aiRes; while (aiTrav != NULL && (nMaxSolutions == 0 || vIP.size() < nMaxSolutions)) { + CNetAddr resolved; if (aiTrav->ai_family == AF_INET) { assert(aiTrav->ai_addrlen >= sizeof(sockaddr_in)); - vIP.push_back(CNetAddr(((struct sockaddr_in*)(aiTrav->ai_addr))->sin_addr)); + resolved = CNetAddr(((struct sockaddr_in*)(aiTrav->ai_addr))->sin_addr); } if (aiTrav->ai_family == AF_INET6) { assert(aiTrav->ai_addrlen >= sizeof(sockaddr_in6)); struct sockaddr_in6* s6 = (struct sockaddr_in6*) aiTrav->ai_addr; - vIP.push_back(CNetAddr(s6->sin6_addr, s6->sin6_scope_id)); + resolved = CNetAddr(s6->sin6_addr, s6->sin6_scope_id); + } + /* Never allow resolving to an internal address. Consider any such result invalid */ + if (!resolved.IsInternal()) { + vIP.push_back(resolved); } aiTrav = aiTrav->ai_next; diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp index 3c3a2fb651..771491770e 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -16,6 +16,26 @@ static constexpr double INF_FEERATE = 1e99; +std::string StringForFeeReason(FeeReason reason) { + static const std::map<FeeReason, std::string> fee_reason_strings = { + {FeeReason::NONE, "None"}, + {FeeReason::HALF_ESTIMATE, "Half Target 60% Threshold"}, + {FeeReason::FULL_ESTIMATE, "Target 85% Threshold"}, + {FeeReason::DOUBLE_ESTIMATE, "Double Target 95% Threshold"}, + {FeeReason::CONSERVATIVE, "Conservative Double Target longer horizon"}, + {FeeReason::MEMPOOL_MIN, "Mempool Min Fee"}, + {FeeReason::PAYTXFEE, "PayTxFee set"}, + {FeeReason::FALLBACK, "Fallback fee"}, + {FeeReason::REQUIRED, "Minimum Required Fee"}, + {FeeReason::MAXTXFEE, "MaxTxFee limit"} + }; + auto reason_string = fee_reason_strings.find(reason); + + if (reason_string == fee_reason_strings.end()) return "Unknown"; + + return reason_string->second; +} + /** * We will instantiate an instance of this class to track transactions that were * included in a block. We will lump transactions into a bucket according to their @@ -698,31 +718,36 @@ unsigned int CBlockPolicyEstimator::MaxUsableEstimate() const * time horizon which tracks confirmations up to the desired target. If * checkShorterHorizon is requested, also allow short time horizon estimates * for a lower target to reduce the given answer */ -double CBlockPolicyEstimator::estimateCombinedFee(unsigned int confTarget, double successThreshold, bool checkShorterHorizon) const +double CBlockPolicyEstimator::estimateCombinedFee(unsigned int confTarget, double successThreshold, bool checkShorterHorizon, EstimationResult *result) const { double estimate = -1; if (confTarget >= 1 && confTarget <= longStats->GetMaxConfirms()) { // Find estimate from shortest time horizon possible if (confTarget <= shortStats->GetMaxConfirms()) { // short horizon - estimate = shortStats->EstimateMedianVal(confTarget, SUFFICIENT_TXS_SHORT, successThreshold, true, nBestSeenHeight); + estimate = shortStats->EstimateMedianVal(confTarget, SUFFICIENT_TXS_SHORT, successThreshold, true, nBestSeenHeight, result); } else if (confTarget <= feeStats->GetMaxConfirms()) { // medium horizon - estimate = feeStats->EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, successThreshold, true, nBestSeenHeight); + estimate = feeStats->EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, successThreshold, true, nBestSeenHeight, result); } else { // long horizon - estimate = longStats->EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, successThreshold, true, nBestSeenHeight); + estimate = longStats->EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, successThreshold, true, nBestSeenHeight, result); } if (checkShorterHorizon) { + EstimationResult tempResult; // If a lower confTarget from a more recent horizon returns a lower answer use it. if (confTarget > feeStats->GetMaxConfirms()) { - double medMax = feeStats->EstimateMedianVal(feeStats->GetMaxConfirms(), SUFFICIENT_FEETXS, successThreshold, true, nBestSeenHeight); - if (medMax > 0 && (estimate == -1 || medMax < estimate)) + double medMax = feeStats->EstimateMedianVal(feeStats->GetMaxConfirms(), SUFFICIENT_FEETXS, successThreshold, true, nBestSeenHeight, &tempResult); + if (medMax > 0 && (estimate == -1 || medMax < estimate)) { estimate = medMax; + if (result) *result = tempResult; + } } if (confTarget > shortStats->GetMaxConfirms()) { - double shortMax = shortStats->EstimateMedianVal(shortStats->GetMaxConfirms(), SUFFICIENT_TXS_SHORT, successThreshold, true, nBestSeenHeight); - if (shortMax > 0 && (estimate == -1 || shortMax < estimate)) + double shortMax = shortStats->EstimateMedianVal(shortStats->GetMaxConfirms(), SUFFICIENT_TXS_SHORT, successThreshold, true, nBestSeenHeight, &tempResult); + if (shortMax > 0 && (estimate == -1 || shortMax < estimate)) { estimate = shortMax; + if (result) *result = tempResult; + } } } } @@ -732,16 +757,18 @@ double CBlockPolicyEstimator::estimateCombinedFee(unsigned int confTarget, doubl /** Ensure that for a conservative estimate, the DOUBLE_SUCCESS_PCT is also met * at 2 * target for any longer time horizons. */ -double CBlockPolicyEstimator::estimateConservativeFee(unsigned int doubleTarget) const +double CBlockPolicyEstimator::estimateConservativeFee(unsigned int doubleTarget, EstimationResult *result) const { double estimate = -1; + EstimationResult tempResult; if (doubleTarget <= shortStats->GetMaxConfirms()) { - estimate = feeStats->EstimateMedianVal(doubleTarget, SUFFICIENT_FEETXS, DOUBLE_SUCCESS_PCT, true, nBestSeenHeight); + estimate = feeStats->EstimateMedianVal(doubleTarget, SUFFICIENT_FEETXS, DOUBLE_SUCCESS_PCT, true, nBestSeenHeight, result); } if (doubleTarget <= feeStats->GetMaxConfirms()) { - double longEstimate = longStats->EstimateMedianVal(doubleTarget, SUFFICIENT_FEETXS, DOUBLE_SUCCESS_PCT, true, nBestSeenHeight); + double longEstimate = longStats->EstimateMedianVal(doubleTarget, SUFFICIENT_FEETXS, DOUBLE_SUCCESS_PCT, true, nBestSeenHeight, &tempResult); if (longEstimate > estimate) { estimate = longEstimate; + if (result) *result = tempResult; } } return estimate; @@ -754,12 +781,15 @@ double CBlockPolicyEstimator::estimateConservativeFee(unsigned int doubleTarget) * estimates, however, required the 95% threshold at 2 * target be met for any * longer time horizons also. */ -CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, int *answerFoundAtTarget, const CTxMemPool& pool, bool conservative) const +CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, FeeCalculation *feeCalc, const CTxMemPool& pool, bool conservative) const { - if (answerFoundAtTarget) - *answerFoundAtTarget = confTarget; + if (feeCalc) { + feeCalc->desiredTarget = confTarget; + feeCalc->returnedTarget = confTarget; + } double median = -1; + EstimationResult tempResult; { LOCK(cs_feeEstimator); @@ -780,7 +810,6 @@ CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, int *answerFoun } assert(confTarget > 0); //estimateCombinedFee and estimateConservativeFee take unsigned ints - /** true is passed to estimateCombined fee for target/2 and target so * that we check the max confirms for shorter time horizons as well. * This is necessary to preserve monotonically increasing estimates. @@ -791,32 +820,49 @@ CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, int *answerFoun * the purpose of conservative estimates is not to let short term * fluctuations lower our estimates by too much. */ - double halfEst = estimateCombinedFee(confTarget/2, HALF_SUCCESS_PCT, true); - double actualEst = estimateCombinedFee(confTarget, SUCCESS_PCT, true); - double doubleEst = estimateCombinedFee(2 * confTarget, DOUBLE_SUCCESS_PCT, !conservative); + double halfEst = estimateCombinedFee(confTarget/2, HALF_SUCCESS_PCT, true, &tempResult); + if (feeCalc) { + feeCalc->est = tempResult; + feeCalc->reason = FeeReason::HALF_ESTIMATE; + } median = halfEst; + double actualEst = estimateCombinedFee(confTarget, SUCCESS_PCT, true, &tempResult); if (actualEst > median) { median = actualEst; + if (feeCalc) { + feeCalc->est = tempResult; + feeCalc->reason = FeeReason::FULL_ESTIMATE; + } } + double doubleEst = estimateCombinedFee(2 * confTarget, DOUBLE_SUCCESS_PCT, !conservative, &tempResult); if (doubleEst > median) { median = doubleEst; + if (feeCalc) { + feeCalc->est = tempResult; + feeCalc->reason = FeeReason::DOUBLE_ESTIMATE; + } } if (conservative || median == -1) { - double consEst = estimateConservativeFee(2 * confTarget); + double consEst = estimateConservativeFee(2 * confTarget, &tempResult); if (consEst > median) { median = consEst; + if (feeCalc) { + feeCalc->est = tempResult; + feeCalc->reason = FeeReason::CONSERVATIVE; + } } } } // Must unlock cs_feeEstimator before taking mempool locks - if (answerFoundAtTarget) - *answerFoundAtTarget = confTarget; + if (feeCalc) feeCalc->returnedTarget = confTarget; // If mempool is limiting txs , return at least the min feerate from the mempool CAmount minPoolFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK(); - if (minPoolFee > 0 && minPoolFee > median) + if (minPoolFee > 0 && minPoolFee > median) { + if (feeCalc) feeCalc->reason = FeeReason::MEMPOOL_MIN; return CFeeRate(minPoolFee); + } if (median < 0) return CFeeRate(0); diff --git a/src/policy/fees.h b/src/policy/fees.h index e99fec2c39..2029ce3744 100644 --- a/src/policy/fees.h +++ b/src/policy/fees.h @@ -48,7 +48,7 @@ class TxConfirmStats; * in each bucket and the total amount of feerate paid in each bucket. Then we * calculate how many blocks Y it took each transaction to be mined. We convert * from a number of blocks to a number of periods Y' each encompassing "scale" - * blocks. This is is tracked in 3 different data sets each up to a maximum + * blocks. This is tracked in 3 different data sets each up to a maximum * number of periods. Within each data set we have an array of counters in each * feerate bucket and we increment all the counters from Y' up to max periods * representing that a tx was successfully confirmed in less than or equal to @@ -74,6 +74,22 @@ enum FeeEstimateHorizon { LONG_HALFLIFE = 2 }; +/* Enumeration of reason for returned fee estimate */ +enum class FeeReason { + NONE, + HALF_ESTIMATE, + FULL_ESTIMATE, + DOUBLE_ESTIMATE, + CONSERVATIVE, + MEMPOOL_MIN, + PAYTXFEE, + FALLBACK, + REQUIRED, + MAXTXFEE, +}; + +std::string StringForFeeReason(FeeReason reason); + /* Used to return detailed information about a feerate bucket */ struct EstimatorBucket { @@ -90,8 +106,16 @@ struct EstimationResult { EstimatorBucket pass; EstimatorBucket fail; - double decay; - unsigned int scale; + double decay = 0; + unsigned int scale = 0; +}; + +struct FeeCalculation +{ + EstimationResult est; + FeeReason reason = FeeReason::NONE; + int desiredTarget = 0; + int returnedTarget = 0; }; /** @@ -173,7 +197,7 @@ public: * the closest target where one can be given. 'conservative' estimates are * valid over longer time horizons also. */ - CFeeRate estimateSmartFee(int confTarget, int *answerFoundAtTarget, const CTxMemPool& pool, bool conservative = true) const; + CFeeRate estimateSmartFee(int confTarget, FeeCalculation *feeCalc, const CTxMemPool& pool, bool conservative = true) const; /** Return a specific fee estimate calculation with a given success * threshold and time horizon, and optionally return detailed data about @@ -223,9 +247,9 @@ private: bool processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry* entry); /** Helper for estimateSmartFee */ - double estimateCombinedFee(unsigned int confTarget, double successThreshold, bool checkShorterHorizon) const; + double estimateCombinedFee(unsigned int confTarget, double successThreshold, bool checkShorterHorizon, EstimationResult *result) const; /** Helper for estimateSmartFee */ - double estimateConservativeFee(unsigned int doubleTarget) const; + double estimateConservativeFee(unsigned int doubleTarget, EstimationResult *result) const; /** Number of blocks of data recorded while fee estimates have been running */ unsigned int BlockSpan() const; /** Number of blocks of recorded fee estimate data represented in saved data file */ diff --git a/src/protocol.cpp b/src/protocol.cpp index 6cd246ed53..da87e40091 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -39,7 +39,7 @@ const char *SENDCMPCT="sendcmpct"; const char *CMPCTBLOCK="cmpctblock"; const char *GETBLOCKTXN="getblocktxn"; const char *BLOCKTXN="blocktxn"; -}; +} // namespace NetMsgType /** All known message types. Keep this in the same order as the list of * messages above and in protocol.h. diff --git a/src/pubkey.cpp b/src/pubkey.cpp index e57fa238cb..a16457ea4e 100644 --- a/src/pubkey.cpp +++ b/src/pubkey.cpp @@ -11,7 +11,7 @@ namespace { /* Global secp256k1_context object used for verification. */ secp256k1_context* secp256k1_context_verify = NULL; -} +} // namespace /** This function is taken from the libsecp256k1 distribution and implements * DER parsing for ECDSA signatures, while supporting an arbitrary subset of diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 6d8760c071..8a745cadce 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -578,6 +578,7 @@ int main(int argc, char *argv[]) // Need to pass name here as CAmount is a typedef (see http://qt-project.org/doc/qt-5/qmetatype.html#qRegisterMetaType) // IMPORTANT if it is no longer a typedef use the normal variant above qRegisterMetaType< CAmount >("CAmount"); + qRegisterMetaType< std::function<void(void)> >("std::function<void(void)>"); /// 3. Application identification // must be set before OptionsModel is initialized or translations are loaded, diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index de00eacdb9..33f4535ee2 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -26,7 +26,6 @@ class CBlockIndex; -static const int64_t nClientStartupTime = GetTime(); static int64_t nLastHeaderTipUpdateNotification = 0; static int64_t nLastBlockTipUpdateNotification = 0; @@ -238,7 +237,7 @@ bool ClientModel::isReleaseVersion() const QString ClientModel::formatClientStartupTime() const { - return QDateTime::fromTime_t(nClientStartupTime).toString(); + return QDateTime::fromTime_t(GetStartupTime()).toString(); } QString ClientModel::dataDir() const @@ -303,7 +302,7 @@ static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, const CB } // if we are in-sync, update the UI regardless of last update time if (!initialSync || now - nLastUpdateNotification > MODEL_UPDATE_DELAY) { - //pass a async signal to the UI thread + //pass an async signal to the UI thread QMetaObject::invokeMethod(clientmodel, "numBlocksChanged", Qt::QueuedConnection, Q_ARG(int, pIndex->nHeight), Q_ARG(QDateTime, QDateTime::fromTime_t(pIndex->GetBlockTime())), diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 06b599f3e2..af9a888d94 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -499,7 +499,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) { // there is some fudging in these numbers related to the actual virtual transaction size calculation that will keep this estimate from being exact. // usually, the result will be an overestimate within a couple of satoshis so that the confirmation dialog ends up displaying a slightly smaller fee. - // also, the witness stack size value value is a variable sized integer. usually, the number of stack items will be well under the single byte var int limit. + // also, the witness stack size value is a variable sized integer. usually, the number of stack items will be well under the single byte var int limit. nBytes += 2; // account for the serialized marker and flag bytes nBytes += nQuantity; // account for the witness byte that holds the number of stack items for each input. } @@ -510,7 +510,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) nBytes -= 34; // Fee - nPayFee = CWallet::GetMinimumFee(nBytes, nTxConfirmTarget, ::mempool, ::feeEstimator); + nPayFee = CWallet::GetMinimumFee(nBytes, coinControl->nConfirmTarget, ::mempool, ::feeEstimator); if (nPayAmount > 0) { @@ -524,13 +524,10 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) CTxOut txout(nChange, (CScript)std::vector<unsigned char>(24, 0)); if (IsDust(txout, ::dustRelayFee)) { - if (CoinControlDialog::fSubtractFeeFromAmount) // dust-change will be raised until no dust - nChange = GetDustThreshold(txout, ::dustRelayFee); - else - { - nPayFee += nChange; - nChange = 0; - } + nPayFee += nChange; + nChange = 0; + if (CoinControlDialog::fSubtractFeeFromAmount) + nBytes -= 34; // we didn't detect lack of change above } } @@ -588,7 +585,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) if (payTxFee.GetFeePerK() > 0) dFeeVary = (double)std::max(CWallet::GetRequiredFee(1000), payTxFee.GetFeePerK()) / 1000; else { - dFeeVary = (double)std::max(CWallet::GetRequiredFee(1000), ::feeEstimator.estimateSmartFee(nTxConfirmTarget, NULL, ::mempool).GetFeePerK()) / 1000; + dFeeVary = (double)std::max(CWallet::GetRequiredFee(1000), ::feeEstimator.estimateSmartFee(coinControl->nConfirmTarget, NULL, ::mempool).GetFeePerK()) / 1000; } QString toolTip4 = tr("Can vary +/- %1 satoshi(s) per input.").arg(dFeeVary); diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index 0f1b3f4a73..14078b9ee8 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -315,7 +315,7 @@ <bool>false</bool> </property> <property name="toolTip"> - <string>Shows, if the supplied default SOCKS5 proxy is used to reach peers via this network type.</string> + <string>Shows if the supplied default SOCKS5 proxy is used to reach peers via this network type.</string> </property> <property name="text"> <string/> @@ -338,7 +338,7 @@ <bool>false</bool> </property> <property name="toolTip"> - <string>Shows, if the supplied default SOCKS5 proxy is used to reach peers via this network type.</string> + <string>Shows if the supplied default SOCKS5 proxy is used to reach peers via this network type.</string> </property> <property name="text"> <string/> @@ -361,7 +361,7 @@ <bool>false</bool> </property> <property name="toolTip"> - <string>Shows, if the supplied default SOCKS5 proxy is used to reach peers via this network type.</string> + <string>Shows if the supplied default SOCKS5 proxy is used to reach peers via this network type.</string> </property> <property name="text"> <string/> diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index 2460a59109..1e0d472b6a 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -214,7 +214,7 @@ bool Intro::pickDataDirectory() } dataDir = intro.getDataDirectory(); try { - TryCreateDirectory(GUIUtil::qstringToBoostPath(dataDir)); + TryCreateDirectories(GUIUtil::qstringToBoostPath(dataDir)); break; } catch (const fs::filesystem_error&) { QMessageBox::critical(0, tr(PACKAGE_NAME), diff --git a/src/qt/modaloverlay.cpp b/src/qt/modaloverlay.cpp index 4779ffa43f..a83f285034 100644 --- a/src/qt/modaloverlay.cpp +++ b/src/qt/modaloverlay.cpp @@ -126,7 +126,7 @@ void ModalOverlay::tipUpdate(int count, const QDateTime& blockDate, double nVeri return; // estimate the number of headers left based on nPowTargetSpacing - // and check if the gui is not aware of the the best header (happens rarely) + // and check if the gui is not aware of the best header (happens rarely) int estimateNumHeadersLeft = bestHeaderDate.secsTo(currentDate) / Params().GetConsensus().nPowTargetSpacing; bool hasBestHeader = bestHeaderHeight >= count; diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index b17693e1ca..ec0580b81c 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -672,7 +672,7 @@ void RPCConsole::setFontSize(int newSize) { QSettings settings; - //don't allow a insane font size + //don't allow an insane font size if (newSize < FONT_RANGE.width() || newSize > FONT_RANGE.height()) return; @@ -738,7 +738,7 @@ void RPCConsole::clear(bool clearHistory) tr("Use up and down arrows to navigate history, and %1 to clear screen.").arg("<b>"+clsKey+"</b>") + "<br>" + tr("Type <b>help</b> for an overview of available commands.")) + "<br><span class=\"secwarning\">" + - tr("WARNING: Scammers have been active, telling users to type commands here, stealing their wallet contents. Do not use this console without fully understanding the ramification of a command.") + + tr("WARNING: Scammers have been active, telling users to type commands here, stealing their wallet contents. Do not use this console without fully understanding the ramifications of a command.") + "</span>", true); } diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index cda33076f8..12d2d0f31c 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -651,8 +651,8 @@ void SendCoinsDialog::updateSmartFeeLabel() return; int nBlocksToConfirm = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2; - int estimateFoundAtBlocks = nBlocksToConfirm; - CFeeRate feeRate = ::feeEstimator.estimateSmartFee(nBlocksToConfirm, &estimateFoundAtBlocks, ::mempool); + FeeCalculation feeCalc; + CFeeRate feeRate = ::feeEstimator.estimateSmartFee(nBlocksToConfirm, &feeCalc, ::mempool); if (feeRate <= CFeeRate(0)) // not enough data => minfee { ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), @@ -670,7 +670,7 @@ void SendCoinsDialog::updateSmartFeeLabel() ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), std::max(feeRate.GetFeePerK(), CWallet::GetRequiredFee(1000))) + "/kB"); ui->labelSmartFee2->hide(); - ui->labelFeeEstimation->setText(tr("Estimated to begin confirmation within %n block(s).", "", estimateFoundAtBlocks)); + ui->labelFeeEstimation->setText(tr("Estimated to begin confirmation within %n block(s).", "", feeCalc.returnedTarget)); ui->fallbackFeeWarningLabel->setVisible(false); } @@ -822,6 +822,12 @@ void SendCoinsDialog::coinControlUpdateLabels() // set pay amounts CoinControlDialog::payAmounts.clear(); CoinControlDialog::fSubtractFeeFromAmount = false; + if (ui->radioSmartFee->isChecked()) { + CoinControlDialog::coinControl->nConfirmTarget = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2; + } else { + CoinControlDialog::coinControl->nConfirmTarget = model->getDefaultConfirmTarget(); + } + for(int i = 0; i < ui->entries->count(); ++i) { SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget()); diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp index 10966e42eb..1b7cc69231 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -131,6 +131,7 @@ SplashScreen::SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle) move(QApplication::desktop()->screenGeometry().center() - r.center()); subscribeToCoreSignals(); + installEventFilter(this); } SplashScreen::~SplashScreen() @@ -138,6 +139,16 @@ SplashScreen::~SplashScreen() unsubscribeFromCoreSignals(); } +bool SplashScreen::eventFilter(QObject * obj, QEvent * ev) { + if (ev->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast<QKeyEvent *>(ev); + if(keyEvent->text()[0] == 'q' && breakAction != nullptr) { + breakAction(); + } + } + return QObject::eventFilter(obj, ev); +} + void SplashScreen::slotFinish(QWidget *mainWin) { Q_UNUSED(mainWin); @@ -164,6 +175,18 @@ static void ShowProgress(SplashScreen *splash, const std::string &title, int nPr InitMessage(splash, title + strprintf("%d", nProgress) + "%"); } +void SplashScreen::setBreakAction(const std::function<void(void)> &action) +{ + breakAction = action; +} + +static void SetProgressBreakAction(SplashScreen *splash, const std::function<void(void)> &action) +{ + QMetaObject::invokeMethod(splash, "setBreakAction", + Qt::QueuedConnection, + Q_ARG(std::function<void(void)>, action)); +} + #ifdef ENABLE_WALLET void SplashScreen::ConnectWallet(CWallet* wallet) { @@ -177,6 +200,7 @@ void SplashScreen::subscribeToCoreSignals() // Connect signals to client uiInterface.InitMessage.connect(boost::bind(InitMessage, this, _1)); uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2)); + uiInterface.SetProgressBreakAction.connect(boost::bind(SetProgressBreakAction, this, _1)); #ifdef ENABLE_WALLET uiInterface.LoadWallet.connect(boost::bind(&SplashScreen::ConnectWallet, this, _1)); #endif diff --git a/src/qt/splashscreen.h b/src/qt/splashscreen.h index 95a65cc53c..a88ebb98a8 100644 --- a/src/qt/splashscreen.h +++ b/src/qt/splashscreen.h @@ -5,6 +5,7 @@ #ifndef BITCOIN_QT_SPLASHSCREEN_H #define BITCOIN_QT_SPLASHSCREEN_H +#include <functional> #include <QSplashScreen> class CWallet; @@ -35,6 +36,11 @@ public Q_SLOTS: /** Show message and progress */ void showMessage(const QString &message, int alignment, const QColor &color); + /** Sets the break action */ + void setBreakAction(const std::function<void(void)> &action); +protected: + bool eventFilter(QObject * obj, QEvent * ev); + private: /** Connect core signals to splash screen */ void subscribeToCoreSignals(); @@ -49,6 +55,8 @@ private: int curAlignment; QList<CWallet*> connectedWallets; + + std::function<void(void)> breakAction; }; #endif // BITCOIN_QT_SPLASHSCREEN_H diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp index dada689731..26dec3c610 100644 --- a/src/qt/test/rpcnestedtests.cpp +++ b/src/qt/test/rpcnestedtests.cpp @@ -33,8 +33,6 @@ static const CRPCCommand vRPCCommands[] = void RPCNestedTests::rpcNestedTests() { - UniValue jsonRPCError; - // do some test setup // could be moved to a more generic place when we add more tests on QT level const CChainParams& chainparams = Params(); diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index e3e070b27f..43d6e8826b 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -336,6 +336,10 @@ void TransactionView::changedAmount(const QString &amount) void TransactionView::exportClicked() { + if (!model || !model->getOptionsModel()) { + return; + } + // CSV is currently the only supported format QString filename = GUIUtil::getSaveFileName(this, tr("Export Transaction History"), QString(), diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 2b635e018c..7eff783fe8 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -338,7 +338,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran transaction_array.append(&(ssTx[0]), ssTx.size()); } - // Add addresses / update labels that we've sent to to the address book, + // Add addresses / update labels that we've sent to the address book, // and emit coinsSent signal for each recipient for (const SendCoinsRecipient &rcp : transaction.getRecipients()) { diff --git a/src/random.cpp b/src/random.cpp index e1ccfa5f24..67efc7d945 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -65,6 +65,70 @@ static inline int64_t GetPerformanceCounter() #endif } + +#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) +static std::atomic<bool> hwrand_initialized{false}; +static bool rdrand_supported = false; +static constexpr uint32_t CPUID_F1_ECX_RDRAND = 0x40000000; +static void RDRandInit() +{ + uint32_t eax, ecx, edx; +#if defined(__i386__) && ( defined(__PIC__) || defined(__PIE__)) + // Avoid clobbering ebx, as that is used for PIC on x86. + uint32_t tmp; + __asm__ ("mov %%ebx, %1; cpuid; mov %1, %%ebx": "=a"(eax), "=g"(tmp), "=c"(ecx), "=d"(edx) : "a"(1)); +#else + uint32_t ebx; + __asm__ ("cpuid": "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(1)); +#endif + //! When calling cpuid function #1, ecx register will have this set if RDRAND is available. + if (ecx & CPUID_F1_ECX_RDRAND) { + LogPrintf("Using RdRand as entropy source\n"); + rdrand_supported = true; + } + hwrand_initialized.store(true); +} +#else +static void RDRandInit() {} +#endif + +static bool GetHWRand(unsigned char* ent32) { +#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) + assert(hwrand_initialized.load(std::memory_order_relaxed)); + if (rdrand_supported) { + uint8_t ok; + // Not all assemblers support the rdrand instruction, write it in hex. +#ifdef __i386__ + for (int iter = 0; iter < 4; ++iter) { + uint32_t r1, r2; + __asm__ volatile (".byte 0x0f, 0xc7, 0xf0;" // rdrand %eax + ".byte 0x0f, 0xc7, 0xf2;" // rdrand %edx + "setc %2" : + "=a"(r1), "=d"(r2), "=q"(ok) :: "cc"); + if (!ok) return false; + WriteLE32(ent32 + 8 * iter, r1); + WriteLE32(ent32 + 8 * iter + 4, r2); + } +#else + uint64_t r1, r2, r3, r4; + __asm__ volatile (".byte 0x48, 0x0f, 0xc7, 0xf0, " // rdrand %rax + "0x48, 0x0f, 0xc7, 0xf3, " // rdrand %rbx + "0x48, 0x0f, 0xc7, 0xf1, " // rdrand %rcx + "0x48, 0x0f, 0xc7, 0xf2; " // rdrand %rdx + "setc %4" : + "=a"(r1), "=b"(r2), "=c"(r3), "=d"(r4), "=q"(ok) :: "cc"); + if (!ok) return false; + WriteLE64(ent32, r1); + WriteLE64(ent32 + 8, r2); + WriteLE64(ent32 + 16, r3); + WriteLE64(ent32 + 24, r4); +#endif + return true; + } +#endif + return false; +} + void RandAddSeed() { // Seed with CPU performance counter @@ -255,6 +319,11 @@ void GetStrongRandBytes(unsigned char* out, int num) GetOSRand(buf); hasher.Write(buf, 32); + // Third source: HW RNG, if available. + if (GetHWRand(buf)) { + hasher.Write(buf, 32); + } + // Combine with and update state { std::unique_lock<std::mutex> lock(cs_rng_state); @@ -381,3 +450,8 @@ FastRandomContext::FastRandomContext(bool fDeterministic) : requires_seed(!fDete uint256 seed; rng.SetKey(seed.begin(), 32); } + +void RandomInit() +{ + RDRandInit(); +} diff --git a/src/random.h b/src/random.h index dcb74eadb5..c60ab36179 100644 --- a/src/random.h +++ b/src/random.h @@ -140,4 +140,7 @@ void GetOSRand(unsigned char *ent32); */ bool Random_SanityCheck(); +/** Initialize the RNG. */ +void RandomInit(); + #endif // BITCOIN_RANDOM_H diff --git a/src/rest.cpp b/src/rest.cpp index 8fb0c13fa5..33e3fb4529 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -413,7 +413,7 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart) boost::split(uriParts, strUriParams, boost::is_any_of("/")); } - // throw exception in case of a empty request + // throw exception in case of an empty request std::string strRequestMutable = req->ReadBody(); if (strRequestMutable.length() == 0 && uriParts.size() == 0) return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request"); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 8f7f76841d..c17ca2fa3a 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -991,7 +991,7 @@ UniValue gettxout(const JSONRPCRequest& request) if (fMempool) { LOCK(mempool.cs); CCoinsViewMemPool view(pcoinsTip, mempool); - if (!view.GetCoin(out, coin) || mempool.isSpent(out)) { // TODO: filtering spent coins should be done by the CCoinsViewMemPool + if (!view.GetCoin(out, coin) || mempool.isSpent(out)) { return NullUniValue; } } else { diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index c5585a9fba..cb1539dce5 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -76,6 +76,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "listunspent", 0, "minconf" }, { "listunspent", 1, "maxconf" }, { "listunspent", 2, "addresses" }, + { "listunspent", 3, "include_unsafe" }, { "listunspent", 4, "query_options" }, { "getblock", 1, "verbosity" }, { "getblockheader", 1, "verbose" }, diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 9af29652cf..e50742f36e 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -18,6 +18,7 @@ #include "policy/fees.h" #include "pow.h" #include "rpc/blockchain.h" +#include "rpc/mining.h" #include "rpc/server.h" #include "txmempool.h" #include "util.h" @@ -141,42 +142,6 @@ UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGen return blockHashes; } -UniValue generate(const JSONRPCRequest& request) -{ - if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) - throw std::runtime_error( - "generate nblocks ( maxtries )\n" - "\nMine up to nblocks blocks immediately (before the RPC call returns)\n" - "\nArguments:\n" - "1. nblocks (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 = request.params[0].get_int(); - uint64_t nMaxTries = 1000000; - if (request.params.size() > 1) { - nMaxTries = request.params[1].get_int(); - } - - std::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 JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 2 || request.params.size() > 3) @@ -722,19 +687,16 @@ protected: UniValue submitblock(const JSONRPCRequest& request) { + // We allow 2 arguments for compliance with BIP22. Argument 2 is ignored. if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( - "submitblock \"hexdata\" ( \"jsonparametersobject\" )\n" + "submitblock \"hexdata\" ( \"dummy\" )\n" "\nAttempts to submit new block to network.\n" - "The 'jsonparametersobject' parameter is currently ignored.\n" "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n" "\nArguments\n" "1. \"hexdata\" (string, required) the hex-encoded block data to submit\n" - "2. \"parameters\" (string, optional) object of optional parameters\n" - " {\n" - " \"workid\" : \"id\" (string, optional) if the server provided a workid, it MUST be included with submissions\n" - " }\n" + "2. \"dummy\" (optional) dummy value, for compatibility with BIP22. This value is ignored.\n" "\nResult:\n" "\nExamples:\n" + HelpExampleCli("submitblock", "\"mydata\"") @@ -867,10 +829,10 @@ UniValue estimatesmartfee(const JSONRPCRequest& request) } UniValue result(UniValue::VOBJ); - int answerFound; - CFeeRate feeRate = ::feeEstimator.estimateSmartFee(nBlocks, &answerFound, ::mempool, conservative); + FeeCalculation feeCalc; + CFeeRate feeRate = ::feeEstimator.estimateSmartFee(nBlocks, &feeCalc, ::mempool, conservative); result.push_back(Pair("feerate", feeRate == CFeeRate(0) ? -1.0 : ValueFromAmount(feeRate.GetFeePerK()))); - result.push_back(Pair("blocks", answerFound)); + result.push_back(Pair("blocks", feeCalc.returnedTarget)); return result; } @@ -963,9 +925,8 @@ static const CRPCCommand commands[] = { "mining", "getmininginfo", &getmininginfo, true, {} }, { "mining", "prioritisetransaction", &prioritisetransaction, true, {"txid","dummy","fee_delta"} }, { "mining", "getblocktemplate", &getblocktemplate, true, {"template_request"} }, - { "mining", "submitblock", &submitblock, true, {"hexdata","parameters"} }, + { "mining", "submitblock", &submitblock, true, {"hexdata","dummy"} }, - { "generating", "generate", &generate, true, {"nblocks","maxtries"} }, { "generating", "generatetoaddress", &generatetoaddress, true, {"nblocks","address","maxtries"} }, { "util", "estimatefee", &estimatefee, true, {"nblocks"} }, diff --git a/src/rpc/mining.h b/src/rpc/mining.h new file mode 100644 index 0000000000..a148d851da --- /dev/null +++ b/src/rpc/mining.h @@ -0,0 +1,15 @@ +// Copyright (c) 2017 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_RPC_MINING_H +#define BITCOIN_RPC_MINING_H + +#include "script/script.h" + +#include <univalue.h> + +/** Generate blocks (mine) */ +UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGenerate, uint64_t nMaxTries, bool keepScript); + +#endif diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index c52ea75840..89e0c5dbc5 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -74,7 +74,7 @@ UniValue getpeerinfo(const JSONRPCRequest& request) "[\n" " {\n" " \"id\": n, (numeric) Peer index\n" - " \"addr\":\"host:port\", (string) The ip address and port of the peer\n" + " \"addr\":\"host:port\", (string) The IP address and port of the peer\n" " \"addrbind\":\"ip:port\", (string) Bind address of the connection to the peer\n" " \"addrlocal\":\"ip:port\", (string) Local address as reported by the peer\n" " \"services\":\"xxxxxxxxxxxxxxxx\", (string) The services offered\n" @@ -198,7 +198,7 @@ UniValue addnode(const JSONRPCRequest& request) (strCommand != "onetry" && strCommand != "add" && strCommand != "remove")) throw std::runtime_error( "addnode \"node\" \"add|remove|onetry\"\n" - "\nAttempts add or remove a node from the addnode list.\n" + "\nAttempts to add or remove a node from the addnode list.\n" "Or try a connection to a node once.\n" "\nArguments:\n" "1. \"node\" (string, required) The node (see getpeerinfo for nodes)\n" @@ -289,7 +289,7 @@ UniValue getaddednodeinfo(const JSONRPCRequest& request) "\nResult:\n" "[\n" " {\n" - " \"addednode\" : \"192.168.0.201\", (string) The node ip address or name (as provided to addnode)\n" + " \"addednode\" : \"192.168.0.201\", (string) The node IP address or name (as provided to addnode)\n" " \"connected\" : true|false, (boolean) If connected\n" " \"addresses\" : [ (list of objects) Only when connected = true\n" " {\n" @@ -396,7 +396,7 @@ static UniValue GetNetworksInfo() for(int n=0; n<NET_MAX; ++n) { enum Network network = static_cast<enum Network>(n); - if(network == NET_UNROUTABLE) + if(network == NET_UNROUTABLE || network == NET_INTERNAL) continue; proxyType proxy; UniValue obj(UniValue::VOBJ); @@ -496,12 +496,12 @@ UniValue setban(const JSONRPCRequest& request) (strCommand != "add" && strCommand != "remove")) throw std::runtime_error( "setban \"subnet\" \"add|remove\" (bantime) (absolute)\n" - "\nAttempts add or remove a IP/Subnet from the banned list.\n" + "\nAttempts to add or remove an IP/Subnet from the banned list.\n" "\nArguments:\n" - "1. \"subnet\" (string, required) The IP/Subnet (see getpeerinfo for nodes ip) with a optional netmask (default is /32 = single ip)\n" - "2. \"command\" (string, required) 'add' to add a IP/Subnet to the list, 'remove' to remove a IP/Subnet from the list\n" - "3. \"bantime\" (numeric, optional) time in seconds how long (or until when if [absolute] is set) the ip is banned (0 or empty means using the default time of 24h which can also be overwritten by the -bantime startup argument)\n" - "4. \"absolute\" (boolean, optional) If set, the bantime must be a absolute timestamp in seconds since epoch (Jan 1 1970 GMT)\n" + "1. \"subnet\" (string, required) The IP/Subnet (see getpeerinfo for nodes IP) with an optional netmask (default is /32 = single IP)\n" + "2. \"command\" (string, required) 'add' to add an IP/Subnet to the list, 'remove' to remove an IP/Subnet from the list\n" + "3. \"bantime\" (numeric, optional) time in seconds how long (or until when if [absolute] is set) the IP is banned (0 or empty means using the default time of 24h which can also be overwritten by the -bantime startup argument)\n" + "4. \"absolute\" (boolean, optional) If set, the bantime must be an absolute timestamp in seconds since epoch (Jan 1 1970 GMT)\n" "\nExamples:\n" + HelpExampleCli("setban", "\"192.168.0.6\" \"add\" 86400") + HelpExampleCli("setban", "\"192.168.0.0/24\" \"add\"") diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 42f3762bf6..527a4d6974 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -218,9 +218,13 @@ UniValue gettxoutproof(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); pblockindex = mapBlockIndex[hashBlock]; } else { - const Coin& coin = AccessByTxid(*pcoinsTip, oneTxid); - if (!coin.IsSpent() && coin.nHeight > 0 && coin.nHeight <= chainActive.Height()) { - pblockindex = chainActive[coin.nHeight]; + // Loop through txids and try to find which block they're in. Exit loop once a block is found. + for (const auto& tx : setTxids) { + const Coin& coin = AccessByTxid(*pcoinsTip, tx); + if (!coin.IsSpent()) { + pblockindex = chainActive[coin.nHeight]; + break; + } } } @@ -243,7 +247,7 @@ UniValue gettxoutproof(const JSONRPCRequest& request) if (setTxids.count(tx->GetHash())) ntxFound++; if (ntxFound != setTxids.size()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "(Not all) transactions not found in specified block"); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Not all transactions found in specified or retrieved block"); CDataStream ssMB(SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS); CMerkleBlock mb(block, setTxids); diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 6f7ab437b1..63e4e9c630 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -257,6 +257,22 @@ UniValue stop(const JSONRPCRequest& jsonRequest) return "Bitcoin server stopping"; } +UniValue uptime(const JSONRPCRequest& jsonRequest) +{ + if (jsonRequest.fHelp || jsonRequest.params.size() > 1) + throw std::runtime_error( + "uptime\n" + "\nReturns the total uptime of the server.\n" + "\nResult:\n" + "ttt (numeric) The number of seconds that the server has been running\n" + "\nExamples:\n" + + HelpExampleCli("uptime", "") + + HelpExampleRpc("uptime", "") + ); + + return GetTime() - GetStartupTime(); +} + /** * Call Table */ @@ -266,6 +282,7 @@ static const CRPCCommand vRPCCommands[] = /* Overall control/query calls */ { "control", "help", &help, true, {"command"} }, { "control", "stop", &stop, true, {} }, + { "control", "uptime", &uptime, true, {} }, }; CRPCTable::CRPCTable() diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index c4ab441e2c..4b71a42cdf 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -68,7 +68,7 @@ struct ECCryptoClosure }; ECCryptoClosure instance_of_eccryptoclosure; -} +} // namespace /** Check that all specified flags are part of the libconsensus interface. */ static bool verify_flags(unsigned int flags) diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 222cff59ea..7149c938fc 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -31,7 +31,7 @@ inline bool set_error(ScriptError* ret, const ScriptError serror) return false; } -} // anon namespace +} // namespace bool CastToBool(const valtype& vch) { @@ -1164,7 +1164,7 @@ uint256 GetOutputsHash(const CTransaction& txTo) { return ss.GetHash(); } -} // anon namespace +} // namespace PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo) { diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 60f6f711e6..ab1dc4e681 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -160,9 +160,9 @@ protected: public: TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(NULL) {} TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {} - bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const; - bool CheckLockTime(const CScriptNum& nLockTime) const; - bool CheckSequence(const CScriptNum& nSequence) const; + bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override; + bool CheckLockTime(const CScriptNum& nLockTime) const override; + bool CheckSequence(const CScriptNum& nSequence) const override; }; class MutableTransactionSignatureChecker : public TransactionSignatureChecker diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp index 7bb8d9941b..ceb573b2ec 100644 --- a/src/script/sigcache.cpp +++ b/src/script/sigcache.cpp @@ -66,7 +66,7 @@ public: * signatureCache could be made local to VerifySignature. */ static CSignatureCache signatureCache; -} +} // namespace // To be called once in AppInitMain/BasicTestingSetup to initialize the // signatureCache. @@ -74,10 +74,10 @@ void InitSignatureCache() { // nMaxCacheSize is unsigned. If -maxsigcachesize is set to zero, // setup_bytes creates the minimum possible cache (2 elements). - size_t nMaxCacheSize = std::min(std::max((int64_t)0, GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE)), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20); + size_t nMaxCacheSize = std::min(std::max((int64_t)0, GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) / 2), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20); size_t nElems = signatureCache.setup_bytes(nMaxCacheSize); - LogPrintf("Using %zu MiB out of %zu requested for signature cache, able to store %zu elements\n", - (nElems*sizeof(uint256)) >>20, nMaxCacheSize>>20, nElems); + LogPrintf("Using %zu MiB out of %zu/2 requested for signature cache, able to store %zu elements\n", + (nElems*sizeof(uint256)) >>20, (nMaxCacheSize*2)>>20, nElems); } bool CachingTransactionSignatureChecker::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const diff --git a/src/script/sigcache.h b/src/script/sigcache.h index 55cec4cc8d..5832b264b3 100644 --- a/src/script/sigcache.h +++ b/src/script/sigcache.h @@ -48,7 +48,7 @@ private: public: CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, bool storeIn, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nInIn, amountIn, txdataIn), store(storeIn) {} - bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const; + bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const override; }; void InitSignatureCache(); diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 94120e6408..dc50467d3f 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -392,13 +392,13 @@ class DummySignatureChecker : public BaseSignatureChecker public: DummySignatureChecker() {} - bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const + bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override { return true; } }; const DummySignatureChecker dummyChecker; -} +} // namespace const BaseSignatureChecker& DummySignatureCreator::Checker() const { diff --git a/src/script/sign.h b/src/script/sign.h index f3c0be4139..bd45862892 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -40,8 +40,8 @@ class TransactionSignatureCreator : public BaseSignatureCreator { public: TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn=SIGHASH_ALL); - const BaseSignatureChecker& Checker() const { return checker; } - bool CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const; + const BaseSignatureChecker& Checker() const override { return checker; } + bool CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override; }; class MutableTransactionSignatureCreator : public TransactionSignatureCreator { @@ -55,8 +55,8 @@ public: class DummySignatureCreator : public BaseSignatureCreator { public: DummySignatureCreator(const CKeyStore* keystoreIn) : BaseSignatureCreator(keystoreIn) {} - const BaseSignatureChecker& Checker() const; - bool CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const; + const BaseSignatureChecker& Checker() const override; + bool CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override; }; struct SignatureData { diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 3415a1ad7c..760a5305e5 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -272,7 +272,7 @@ public: return true; } }; -} +} // namespace CScript GetScriptForDestination(const CTxDestination& dest) { diff --git a/src/secp256k1/sage/group_prover.sage b/src/secp256k1/sage/group_prover.sage index ab580c5b23..5198724bea 100644 --- a/src/secp256k1/sage/group_prover.sage +++ b/src/secp256k1/sage/group_prover.sage @@ -3,7 +3,7 @@ # to independently set assumptions on input or intermediary variables. # # The general approach is: -# * A constraint is a tuple of two sets of of symbolic expressions: +# * A constraint is a tuple of two sets of symbolic expressions: # the first of which are required to evaluate to zero, the second of which # are required to evaluate to nonzero. # - A constraint is said to be conflicting if any of its nonzero expressions diff --git a/src/secp256k1/src/asm/field_10x26_arm.s b/src/secp256k1/src/asm/field_10x26_arm.s index 5df561f2fc..bd2b629e1c 100644 --- a/src/secp256k1/src/asm/field_10x26_arm.s +++ b/src/secp256k1/src/asm/field_10x26_arm.s @@ -11,7 +11,7 @@ Note: - To avoid unnecessary loads and make use of available registers, two 'passes' have every time been interleaved, with the odd passes accumulating c' and d' - which will be added to c and d respectively in the the even passes + which will be added to c and d respectively in the even passes */ diff --git a/src/support/events.h b/src/support/events.h index 4f2f3cf9ef..90690876ee 100644 --- a/src/support/events.h +++ b/src/support/events.h @@ -27,26 +27,26 @@ MAKE_RAII(evhttp); MAKE_RAII(evhttp_request); MAKE_RAII(evhttp_connection); -raii_event_base obtain_event_base() { +inline raii_event_base obtain_event_base() { auto result = raii_event_base(event_base_new()); if (!result.get()) throw std::runtime_error("cannot create event_base"); return result; } -raii_event obtain_event(struct event_base* base, evutil_socket_t s, short events, event_callback_fn cb, void* arg) { +inline raii_event obtain_event(struct event_base* base, evutil_socket_t s, short events, event_callback_fn cb, void* arg) { return raii_event(event_new(base, s, events, cb, arg)); } -raii_evhttp obtain_evhttp(struct event_base* base) { +inline raii_evhttp obtain_evhttp(struct event_base* base) { return raii_evhttp(evhttp_new(base)); } -raii_evhttp_request obtain_evhttp_request(void(*cb)(struct evhttp_request *, void *), void *arg) { +inline raii_evhttp_request obtain_evhttp_request(void(*cb)(struct evhttp_request *, void *), void *arg) { return raii_evhttp_request(evhttp_request_new(cb, arg)); } -raii_evhttp_connection obtain_evhttp_connection_base(struct event_base* base, std::string host, uint16_t port) { +inline raii_evhttp_connection obtain_evhttp_connection_base(struct event_base* base, std::string host, uint16_t port) { auto result = raii_evhttp_connection(evhttp_connection_base_new(base, NULL, host.c_str(), port)); if (!result.get()) throw std::runtime_error("create connection failed"); diff --git a/src/support/lockedpool.cpp b/src/support/lockedpool.cpp index 98c1581093..2df6b84a59 100644 --- a/src/support/lockedpool.cpp +++ b/src/support/lockedpool.cpp @@ -148,9 +148,9 @@ class Win32LockedPageAllocator: public LockedPageAllocator { public: Win32LockedPageAllocator(); - void* AllocateLocked(size_t len, bool *lockingSuccess); - void FreeLocked(void* addr, size_t len); - size_t GetLimit(); + void* AllocateLocked(size_t len, bool *lockingSuccess) override; + void FreeLocked(void* addr, size_t len) override; + size_t GetLimit() override; private: size_t page_size; }; @@ -200,9 +200,9 @@ class PosixLockedPageAllocator: public LockedPageAllocator { public: PosixLockedPageAllocator(); - void* AllocateLocked(size_t len, bool *lockingSuccess); - void FreeLocked(void* addr, size_t len); - size_t GetLimit(); + void* AllocateLocked(size_t len, bool *lockingSuccess) override; + void FreeLocked(void* addr, size_t len) override; + size_t GetLimit() override; private: size_t page_size; }; diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp index dc5372a070..bc6aef2c11 100644 --- a/src/test/addrman_tests.cpp +++ b/src/test/addrman_tests.cpp @@ -27,7 +27,7 @@ public: insecure_rand = FastRandomContext(true); } - int RandomInt(int nMax) + int RandomInt(int nMax) override { state = (CHashWriter(SER_GETHASH, 0) << state).GetHash().GetCheapHash(); return (unsigned int)(state % nMax); diff --git a/src/test/allocator_tests.cpp b/src/test/allocator_tests.cpp index 3f15a0dec1..4a533b5bf2 100644 --- a/src/test/allocator_tests.cpp +++ b/src/test/allocator_tests.cpp @@ -131,7 +131,7 @@ class TestLockedPageAllocator: public LockedPageAllocator { public: TestLockedPageAllocator(int count_in, int lockedcount_in): count(count_in), lockedcount(lockedcount_in) {} - void* AllocateLocked(size_t len, bool *lockingSuccess) + void* AllocateLocked(size_t len, bool *lockingSuccess) override { *lockingSuccess = false; if (count > 0) { @@ -146,10 +146,10 @@ public: } return 0; } - void FreeLocked(void* addr, size_t len) + void FreeLocked(void* addr, size_t len) override { } - size_t GetLimit() + size_t GetLimit() override { return std::numeric_limits<size_t>::max(); } diff --git a/src/test/arith_uint256_tests.cpp b/src/test/arith_uint256_tests.cpp index 45ae7d4636..2c98fbcfd6 100644 --- a/src/test/arith_uint256_tests.cpp +++ b/src/test/arith_uint256_tests.cpp @@ -219,7 +219,7 @@ BOOST_AUTO_TEST_CASE( unaryOperators ) // ! ~ - // Check if doing _A_ _OP_ _B_ results in the same as applying _OP_ onto each -// element of Aarray and Barray, and then converting the result into a arith_uint256. +// element of Aarray and Barray, and then converting the result into an arith_uint256. #define CHECKBITWISEOPERATOR(_A_,_B_,_OP_) \ for (unsigned int i = 0; i < 32; ++i) { TmpArray[i] = _A_##Array[i] _OP_ _B_##Array[i]; } \ BOOST_CHECK(arith_uint256V(std::vector<unsigned char>(TmpArray,TmpArray+32)) == (_A_##L _OP_ _B_##L)); diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index 33abfabe6b..e24431528a 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -50,12 +50,6 @@ public: return true; } - bool HaveCoin(const COutPoint& outpoint) const override - { - Coin coin; - return GetCoin(outpoint, coin); - } - uint256 GetBestBlock() const override { return hashBestBlock_; } bool BatchWrite(CCoinsMap& mapCoins, const uint256& hashBlock) override @@ -99,7 +93,7 @@ public: size_t& usage() { return cachedCoinsUsage; } }; -} +} // namespace BOOST_FIXTURE_TEST_SUITE(coins_tests, BasicTestingSetup) @@ -147,8 +141,22 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) { uint256 txid = txids[InsecureRandRange(txids.size())]; // txid we're going to modify in this iteration. Coin& coin = result[COutPoint(txid, 0)]; + + // Determine whether to test HaveCoin before or after Access* (or both). As these functions + // can influence each other's behaviour by pulling things into the cache, all combinations + // are tested. + bool test_havecoin_before = InsecureRandBits(2) == 0; + bool test_havecoin_after = InsecureRandBits(2) == 0; + + bool result_havecoin = test_havecoin_before ? stack.back()->HaveCoin(COutPoint(txid, 0)) : false; const Coin& entry = (InsecureRandRange(500) == 0) ? AccessByTxid(*stack.back(), txid) : stack.back()->AccessCoin(COutPoint(txid, 0)); BOOST_CHECK(coin == entry); + BOOST_CHECK(!test_havecoin_before || result_havecoin == !entry.IsSpent()); + + if (test_havecoin_after) { + bool ret = stack.back()->HaveCoin(COutPoint(txid, 0)); + BOOST_CHECK(ret == !entry.IsSpent()); + } if (InsecureRandRange(5) == 0 || coin.IsSpent()) { Coin newcoin; @@ -628,7 +636,7 @@ BOOST_AUTO_TEST_CASE(ccoins_access) CheckAccessCoin(ABSENT, VALUE2, VALUE2, FRESH , FRESH ); CheckAccessCoin(ABSENT, VALUE2, VALUE2, DIRTY , DIRTY ); CheckAccessCoin(ABSENT, VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH); - CheckAccessCoin(PRUNED, ABSENT, PRUNED, NO_ENTRY , FRESH ); + CheckAccessCoin(PRUNED, ABSENT, ABSENT, NO_ENTRY , NO_ENTRY ); CheckAccessCoin(PRUNED, PRUNED, PRUNED, 0 , 0 ); CheckAccessCoin(PRUNED, PRUNED, PRUNED, FRESH , FRESH ); CheckAccessCoin(PRUNED, PRUNED, PRUNED, DIRTY , DIRTY ); diff --git a/src/test/data/script_tests.json b/src/test/data/script_tests.json index e35a7ce569..0390d6806d 100644 --- a/src/test/data/script_tests.json +++ b/src/test/data/script_tests.json @@ -2506,7 +2506,7 @@ ], ["CHECKSEQUENCEVERIFY tests"], -["", "CHECKSEQUENCEVERIFY", "CHECKSEQUENCEVERIFY", "INVALID_STACK_OPERATION", "CSV automatically fails on a empty stack"], +["", "CHECKSEQUENCEVERIFY", "CHECKSEQUENCEVERIFY", "INVALID_STACK_OPERATION", "CSV automatically fails on an empty stack"], ["-1", "CHECKSEQUENCEVERIFY", "CHECKSEQUENCEVERIFY", "NEGATIVE_LOCKTIME", "CSV automatically fails if stack top is negative"], ["0x0100", "CHECKSEQUENCEVERIFY", "CHECKSEQUENCEVERIFY,MINIMALDATA", "UNKNOWN_ERROR", "CSV fails if stack top is not minimally encoded"], ["0", "CHECKSEQUENCEVERIFY", "CHECKSEQUENCEVERIFY", "UNSATISFIED_LOCKTIME", "CSV fails if stack top bit 1 << 31 is set and the tx version < 2"], diff --git a/src/test/data/tx_valid.json b/src/test/data/tx_valid.json index d70fa54333..e6b382af13 100644 --- a/src/test/data/tx_valid.json +++ b/src/test/data/tx_valid.json @@ -174,7 +174,7 @@ [[["5a6b0021a6042a686b6b94abc36b387bef9109847774e8b1e51eb8cc55c53921", 1, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]], "01000000012139c555ccb81ee5b1e87477840991ef7b386bc3ab946b6b682a04a621006b5a01000000fdb40148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f2204148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390175ac4830450220646b72c35beeec51f4d5bc1cbae01863825750d7f490864af354e6ea4f625e9c022100f04b98432df3a9641719dbced53393022e7249fb59db993af1118539830aab870148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a580039017521038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH"], -["Finally CHECKMULTISIG removes all signatures prior to hashing the script containing those signatures. In conjunction with the SIGHASH_SINGLE bug this lets us test whether or not FindAndDelete() is actually present in scriptPubKey/redeemScript evaluation by including a signature of the digest 0x01 We can compute in advance for our pubkey, embed it it in the scriptPubKey, and then also using a normal SIGHASH_ALL signature. If FindAndDelete() wasn't run, the 'bugged' signature would still be in the hashed script, and the normal signature would fail."], +["Finally CHECKMULTISIG removes all signatures prior to hashing the script containing those signatures. In conjunction with the SIGHASH_SINGLE bug this lets us test whether or not FindAndDelete() is actually present in scriptPubKey/redeemScript evaluation by including a signature of the digest 0x01 We can compute in advance for our pubkey, embed it in the scriptPubKey, and then also using a normal SIGHASH_ALL signature. If FindAndDelete() wasn't run, the 'bugged' signature would still be in the hashed script, and the normal signature would fail."], ["Here's an example on mainnet within a P2SH redeemScript. Remarkably it's a standard transaction in <0.9"], [[["b5b598de91787439afd5938116654e0b16b7a0d0f82742ba37564219c5afcbf9", 0, "DUP HASH160 0x14 0xf6f365c40f0739b61de827a44751e5e99032ed8f EQUALVERIFY CHECKSIG"], diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp index 66354699b2..095d86834c 100644 --- a/src/test/net_tests.cpp +++ b/src/test/net_tests.cpp @@ -29,7 +29,7 @@ public: class CAddrManUncorrupted : public CAddrManSerializationMock { public: - void Serialize(CDataStream& s) const + void Serialize(CDataStream& s) const override { CAddrMan::Serialize(s); } @@ -38,7 +38,7 @@ public: class CAddrManCorrupted : public CAddrManSerializationMock { public: - void Serialize(CDataStream& s) const + void Serialize(CDataStream& s) const override { // Produces corrupt output that claims addrman has 20 addrs when it only has one addr. unsigned char nVersion = 1; diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp index e4b4b85720..b45a7fcc57 100644 --- a/src/test/netbase_tests.cpp +++ b/src/test/netbase_tests.cpp @@ -25,6 +25,13 @@ static CSubNet ResolveSubNet(const char* subnet) return ret; } +static CNetAddr CreateInternal(const char* host) +{ + CNetAddr addr; + addr.SetInternal(host); + return addr; +} + BOOST_AUTO_TEST_CASE(netbase_networks) { BOOST_CHECK(ResolveIP("127.0.0.1").GetNetwork() == NET_UNROUTABLE); @@ -32,6 +39,7 @@ BOOST_AUTO_TEST_CASE(netbase_networks) BOOST_CHECK(ResolveIP("8.8.8.8").GetNetwork() == NET_IPV4); BOOST_CHECK(ResolveIP("2001::8888").GetNetwork() == NET_IPV6); BOOST_CHECK(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetNetwork() == NET_TOR); + BOOST_CHECK(CreateInternal("foo.com").GetNetwork() == NET_INTERNAL); } @@ -58,6 +66,8 @@ BOOST_AUTO_TEST_CASE(netbase_properties) BOOST_CHECK(ResolveIP("8.8.8.8").IsRoutable()); BOOST_CHECK(ResolveIP("2001::1").IsRoutable()); BOOST_CHECK(ResolveIP("127.0.0.1").IsValid()); + BOOST_CHECK(CreateInternal("FD6B:88C0:8724:edb1:8e4:3588:e546:35ca").IsInternal()); + BOOST_CHECK(CreateInternal("bar.com").IsInternal()); } @@ -103,6 +113,11 @@ BOOST_AUTO_TEST_CASE(netbase_lookupnumeric) BOOST_CHECK(TestParse("[::]:8333", "[::]:8333")); BOOST_CHECK(TestParse("[127.0.0.1]", "127.0.0.1:65535")); BOOST_CHECK(TestParse(":::", "[::]:0")); + + // verify that an internal address fails to resolve + BOOST_CHECK(TestParse("[fd6b:88c0:8724:1:2:3:4:5]", "[::]:0")); + // and that a one-off resolves correctly + BOOST_CHECK(TestParse("[fd6c:88c0:8724:1:2:3:4:5]", "[fd6c:88c0:8724:1:2:3:4:5]:65535")); } BOOST_AUTO_TEST_CASE(onioncat_test) @@ -281,6 +296,9 @@ BOOST_AUTO_TEST_CASE(netbase_getgroup) BOOST_CHECK(ResolveIP("2001:470:abcd:9999:9999:9999:9999:9999").GetGroup() == std::vector<unsigned char>({(unsigned char)NET_IPV6, 32, 1, 4, 112, 175})); //he.net BOOST_CHECK(ResolveIP("2001:2001:9999:9999:9999:9999:9999:9999").GetGroup() == std::vector<unsigned char>({(unsigned char)NET_IPV6, 32, 1, 32, 1})); //IPv6 + // baz.net sha256 hash: 12929400eb4607c4ac075f087167e75286b179c693eb059a01774b864e8fe505 + std::vector<unsigned char> internal_group = {NET_INTERNAL, 0x12, 0x92, 0x94, 0x00, 0xeb, 0x46, 0x07, 0xc4, 0xac, 0x07}; + BOOST_CHECK(CreateInternal("baz.net").GetGroup() == internal_group); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 4ddea9bae7..a18471588a 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -467,7 +467,7 @@ std::string JSONPrettyPrint(const UniValue& univalue) } return ret; } -} +} // namespace BOOST_AUTO_TEST_CASE(script_build) { diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index b70ee96966..579e96524c 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -33,10 +33,12 @@ extern void noui_connect(); BasicTestingSetup::BasicTestingSetup(const std::string& chainName) { + RandomInit(); ECC_Start(); SetupEnvironment(); SetupNetworking(); InitSignatureCache(); + InitScriptExecutionCache(); fPrintToDebugLog = false; // don't want to write to debug.log file fCheckBlockIndex = true; SelectParams(chainName); diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp index c5367208ba..a74f40251a 100644 --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -10,11 +10,17 @@ #include "txmempool.h" #include "random.h" #include "script/standard.h" +#include "script/sign.h" #include "test/test_bitcoin.h" #include "utiltime.h" +#include "core_io.h" +#include "keystore.h" +#include "policy/policy.h" #include <boost/test/unit_test.hpp> +bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks); + BOOST_AUTO_TEST_SUITE(tx_validationcache_tests) static bool @@ -84,4 +90,282 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup) BOOST_CHECK_EQUAL(mempool.size(), 0); } +// Run CheckInputs (using pcoinsTip) on the given transaction, for all script +// flags. Test that CheckInputs passes for all flags that don't overlap with +// the failing_flags argument, but otherwise fails. +// CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY (and future NOP codes that may +// get reassigned) have an interaction with DISCOURAGE_UPGRADABLE_NOPS: if +// the script flags used contain DISCOURAGE_UPGRADABLE_NOPS but don't contain +// CHECKLOCKTIMEVERIFY (or CHECKSEQUENCEVERIFY), but the script does contain +// OP_CHECKLOCKTIMEVERIFY (or OP_CHECKSEQUENCEVERIFY), then script execution +// should fail. +// Capture this interaction with the upgraded_nop argument: set it when evaluating +// any script flag that is implemented as an upgraded NOP code. +void ValidateCheckInputsForAllFlags(CMutableTransaction &tx, uint32_t failing_flags, bool add_to_cache, bool upgraded_nop) +{ + PrecomputedTransactionData txdata(tx); + // If we add many more flags, this loop can get too expensive, but we can + // rewrite in the future to randomly pick a set of flags to evaluate. + for (uint32_t test_flags=0; test_flags < (1U << 16); test_flags += 1) { + CValidationState state; + // Filter out incompatible flag choices + if ((test_flags & SCRIPT_VERIFY_CLEANSTACK)) { + // CLEANSTACK requires P2SH and WITNESS, see VerifyScript() in + // script/interpreter.cpp + test_flags |= SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS; + } + if ((test_flags & SCRIPT_VERIFY_WITNESS)) { + // WITNESS requires P2SH + test_flags |= SCRIPT_VERIFY_P2SH; + } + bool ret = CheckInputs(tx, state, pcoinsTip, true, test_flags, true, add_to_cache, txdata, nullptr); + // CheckInputs should succeed iff test_flags doesn't intersect with + // failing_flags + bool expected_return_value = !(test_flags & failing_flags); + if (expected_return_value && upgraded_nop) { + // If the script flag being tested corresponds to an upgraded NOP, + // then script execution should fail if DISCOURAGE_UPGRADABLE_NOPS + // is set. + expected_return_value = !(test_flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS); + } + BOOST_CHECK_EQUAL(ret, expected_return_value); + + // Test the caching + if (ret && add_to_cache) { + // Check that we get a cache hit if the tx was valid + std::vector<CScriptCheck> scriptchecks; + BOOST_CHECK(CheckInputs(tx, state, pcoinsTip, true, test_flags, true, add_to_cache, txdata, &scriptchecks)); + BOOST_CHECK(scriptchecks.empty()); + } else { + // Check that we get script executions to check, if the transaction + // was invalid, or we didn't add to cache. + std::vector<CScriptCheck> scriptchecks; + BOOST_CHECK(CheckInputs(tx, state, pcoinsTip, true, test_flags, true, add_to_cache, txdata, &scriptchecks)); + BOOST_CHECK_EQUAL(scriptchecks.size(), tx.vin.size()); + } + } +} + +BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) +{ + // Test that passing CheckInputs with one set of script flags doesn't imply + // that we would pass again with a different set of flags. + InitScriptExecutionCache(); + + CScript p2pk_scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; + CScript p2sh_scriptPubKey = GetScriptForDestination(CScriptID(p2pk_scriptPubKey)); + CScript p2pkh_scriptPubKey = GetScriptForDestination(coinbaseKey.GetPubKey().GetID()); + CScript p2wpkh_scriptPubKey = GetScriptForWitness(p2pkh_scriptPubKey); + + CBasicKeyStore keystore; + keystore.AddKey(coinbaseKey); + keystore.AddCScript(p2pk_scriptPubKey); + + // flags to test: SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, SCRIPT_VERIFY_CHECKSEQUENCE_VERIFY, SCRIPT_VERIFY_NULLDUMMY, uncompressed pubkey thing + + // Create 2 outputs that match the three scripts above, spending the first + // coinbase tx. + CMutableTransaction spend_tx; + + spend_tx.nVersion = 1; + spend_tx.vin.resize(1); + spend_tx.vin[0].prevout.hash = coinbaseTxns[0].GetHash(); + spend_tx.vin[0].prevout.n = 0; + spend_tx.vout.resize(4); + spend_tx.vout[0].nValue = 11*CENT; + spend_tx.vout[0].scriptPubKey = p2sh_scriptPubKey; + spend_tx.vout[1].nValue = 11*CENT; + spend_tx.vout[1].scriptPubKey = p2wpkh_scriptPubKey; + spend_tx.vout[2].nValue = 11*CENT; + spend_tx.vout[2].scriptPubKey = CScript() << OP_CHECKLOCKTIMEVERIFY << OP_DROP << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; + spend_tx.vout[3].nValue = 11*CENT; + spend_tx.vout[3].scriptPubKey = CScript() << OP_CHECKSEQUENCEVERIFY << OP_DROP << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; + + // Sign, with a non-DER signature + { + std::vector<unsigned char> vchSig; + uint256 hash = SignatureHash(p2pk_scriptPubKey, spend_tx, 0, SIGHASH_ALL, 0, SIGVERSION_BASE); + BOOST_CHECK(coinbaseKey.Sign(hash, vchSig)); + vchSig.push_back((unsigned char) 0); // padding byte makes this non-DER + vchSig.push_back((unsigned char)SIGHASH_ALL); + spend_tx.vin[0].scriptSig << vchSig; + } + + LOCK(cs_main); + + // Test that invalidity under a set of flags doesn't preclude validity + // under other (eg consensus) flags. + // spend_tx is invalid according to DERSIG + CValidationState state; + { + PrecomputedTransactionData ptd_spend_tx(spend_tx); + + BOOST_CHECK(!CheckInputs(spend_tx, state, pcoinsTip, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr)); + + // If we call again asking for scriptchecks (as happens in + // ConnectBlock), we should add a script check object for this -- we're + // not caching invalidity (if that changes, delete this test case). + std::vector<CScriptCheck> scriptchecks; + BOOST_CHECK(CheckInputs(spend_tx, state, pcoinsTip, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks)); + BOOST_CHECK_EQUAL(scriptchecks.size(), 1); + + // Test that CheckInputs returns true iff DERSIG-enforcing flags are + // not present. Don't add these checks to the cache, so that we can + // test later that block validation works fine in the absence of cached + // successes. + ValidateCheckInputsForAllFlags(spend_tx, SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC, false, false); + + // And if we produce a block with this tx, it should be valid (DERSIG not + // enabled yet), even though there's no cache entry. + CBlock block; + + block = CreateAndProcessBlock({spend_tx}, p2pk_scriptPubKey); + BOOST_CHECK(chainActive.Tip()->GetBlockHash() == block.GetHash()); + BOOST_CHECK(pcoinsTip->GetBestBlock() == block.GetHash()); + } + + // Test P2SH: construct a transaction that is valid without P2SH, and + // then test validity with P2SH. + { + CMutableTransaction invalid_under_p2sh_tx; + invalid_under_p2sh_tx.nVersion = 1; + invalid_under_p2sh_tx.vin.resize(1); + invalid_under_p2sh_tx.vin[0].prevout.hash = spend_tx.GetHash(); + invalid_under_p2sh_tx.vin[0].prevout.n = 0; + invalid_under_p2sh_tx.vout.resize(1); + invalid_under_p2sh_tx.vout[0].nValue = 11*CENT; + invalid_under_p2sh_tx.vout[0].scriptPubKey = p2pk_scriptPubKey; + std::vector<unsigned char> vchSig2(p2pk_scriptPubKey.begin(), p2pk_scriptPubKey.end()); + invalid_under_p2sh_tx.vin[0].scriptSig << vchSig2; + + ValidateCheckInputsForAllFlags(invalid_under_p2sh_tx, SCRIPT_VERIFY_P2SH, true, false); + } + + // Test CHECKLOCKTIMEVERIFY + { + CMutableTransaction invalid_with_cltv_tx; + invalid_with_cltv_tx.nVersion = 1; + invalid_with_cltv_tx.nLockTime = 100; + invalid_with_cltv_tx.vin.resize(1); + invalid_with_cltv_tx.vin[0].prevout.hash = spend_tx.GetHash(); + invalid_with_cltv_tx.vin[0].prevout.n = 2; + invalid_with_cltv_tx.vin[0].nSequence = 0; + invalid_with_cltv_tx.vout.resize(1); + invalid_with_cltv_tx.vout[0].nValue = 11*CENT; + invalid_with_cltv_tx.vout[0].scriptPubKey = p2pk_scriptPubKey; + + // Sign + std::vector<unsigned char> vchSig; + uint256 hash = SignatureHash(spend_tx.vout[2].scriptPubKey, invalid_with_cltv_tx, 0, SIGHASH_ALL, 0, SIGVERSION_BASE); + BOOST_CHECK(coinbaseKey.Sign(hash, vchSig)); + vchSig.push_back((unsigned char)SIGHASH_ALL); + invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 101; + + ValidateCheckInputsForAllFlags(invalid_with_cltv_tx, SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true); + + // Make it valid, and check again + invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 100; + CValidationState state; + PrecomputedTransactionData txdata(invalid_with_cltv_tx); + BOOST_CHECK(CheckInputs(invalid_with_cltv_tx, state, pcoinsTip, true, SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr)); + } + + // TEST CHECKSEQUENCEVERIFY + { + CMutableTransaction invalid_with_csv_tx; + invalid_with_csv_tx.nVersion = 2; + invalid_with_csv_tx.vin.resize(1); + invalid_with_csv_tx.vin[0].prevout.hash = spend_tx.GetHash(); + invalid_with_csv_tx.vin[0].prevout.n = 3; + invalid_with_csv_tx.vin[0].nSequence = 100; + invalid_with_csv_tx.vout.resize(1); + invalid_with_csv_tx.vout[0].nValue = 11*CENT; + invalid_with_csv_tx.vout[0].scriptPubKey = p2pk_scriptPubKey; + + // Sign + std::vector<unsigned char> vchSig; + uint256 hash = SignatureHash(spend_tx.vout[3].scriptPubKey, invalid_with_csv_tx, 0, SIGHASH_ALL, 0, SIGVERSION_BASE); + BOOST_CHECK(coinbaseKey.Sign(hash, vchSig)); + vchSig.push_back((unsigned char)SIGHASH_ALL); + invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 101; + + ValidateCheckInputsForAllFlags(invalid_with_csv_tx, SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true); + + // Make it valid, and check again + invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 100; + CValidationState state; + PrecomputedTransactionData txdata(invalid_with_csv_tx); + BOOST_CHECK(CheckInputs(invalid_with_csv_tx, state, pcoinsTip, true, SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr)); + } + + // TODO: add tests for remaining script flags + + // Test that passing CheckInputs with a valid witness doesn't imply success + // for the same tx with a different witness. + { + CMutableTransaction valid_with_witness_tx; + valid_with_witness_tx.nVersion = 1; + valid_with_witness_tx.vin.resize(1); + valid_with_witness_tx.vin[0].prevout.hash = spend_tx.GetHash(); + valid_with_witness_tx.vin[0].prevout.n = 1; + valid_with_witness_tx.vout.resize(1); + valid_with_witness_tx.vout[0].nValue = 11*CENT; + valid_with_witness_tx.vout[0].scriptPubKey = p2pk_scriptPubKey; + + // Sign + SignatureData sigdata; + ProduceSignature(MutableTransactionSignatureCreator(&keystore, &valid_with_witness_tx, 0, 11*CENT, SIGHASH_ALL), spend_tx.vout[1].scriptPubKey, sigdata); + UpdateTransaction(valid_with_witness_tx, 0, sigdata); + + // This should be valid under all script flags. + ValidateCheckInputsForAllFlags(valid_with_witness_tx, 0, true, false); + + // Remove the witness, and check that it is now invalid. + valid_with_witness_tx.vin[0].scriptWitness.SetNull(); + ValidateCheckInputsForAllFlags(valid_with_witness_tx, SCRIPT_VERIFY_WITNESS, true, false); + } + + { + // Test a transaction with multiple inputs. + CMutableTransaction tx; + + tx.nVersion = 1; + tx.vin.resize(2); + tx.vin[0].prevout.hash = spend_tx.GetHash(); + tx.vin[0].prevout.n = 0; + tx.vin[1].prevout.hash = spend_tx.GetHash(); + tx.vin[1].prevout.n = 1; + tx.vout.resize(1); + tx.vout[0].nValue = 22*CENT; + tx.vout[0].scriptPubKey = p2pk_scriptPubKey; + + // Sign + for (int i=0; i<2; ++i) { + SignatureData sigdata; + ProduceSignature(MutableTransactionSignatureCreator(&keystore, &tx, i, 11*CENT, SIGHASH_ALL), spend_tx.vout[i].scriptPubKey, sigdata); + UpdateTransaction(tx, i, sigdata); + } + + // This should be valid under all script flags + ValidateCheckInputsForAllFlags(tx, 0, true, false); + + // Check that if the second input is invalid, but the first input is + // valid, the transaction is not cached. + // Invalidate vin[1] + tx.vin[1].scriptWitness.SetNull(); + + CValidationState state; + PrecomputedTransactionData txdata(tx); + // This transaction is now invalid under segwit, because of the second input. + BOOST_CHECK(!CheckInputs(tx, state, pcoinsTip, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, nullptr)); + + std::vector<CScriptCheck> scriptchecks; + // Make sure this transaction was not cached (ie because the first + // input was valid) + BOOST_CHECK(CheckInputs(tx, state, pcoinsTip, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, &scriptchecks)); + // Should get 2 script checks back -- caching is on a whole-transaction basis. + BOOST_CHECK_EQUAL(scriptchecks.size(), 2); + } +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp index faa2383d14..722f6ae059 100644 --- a/src/test/versionbits_tests.cpp +++ b/src/test/versionbits_tests.cpp @@ -22,11 +22,11 @@ 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); } + int64_t BeginTime(const Consensus::Params& params) const override { return TestTime(10000); } + int64_t EndTime(const Consensus::Params& params) const override { return TestTime(20000); } + int Period(const Consensus::Params& params) const override { return 1000; } + int Threshold(const Consensus::Params& params) const override { return 900; } + bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override { return (pindex->nVersion & 0x100); } ThresholdState GetStateFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateFor(pindexPrev, paramsDummy, cache); } int GetStateSinceHeightFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateSinceHeightFor(pindexPrev, paramsDummy, cache); } diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index f273428625..3665e7e770 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -375,8 +375,10 @@ static std::pair<bool,std::string> ReadBinaryFile(const fs::path &filename, size while ((n=fread(buffer, 1, sizeof(buffer), f)) > 0) { // Check for reading errors so we don't return any data if we couldn't // read the entire file (or up to maxsize) - if (ferror(f)) + if (ferror(f)) { + fclose(f); return std::make_pair(false,""); + } retval.append(buffer, buffer+n); if (retval.size() > maxsize) break; @@ -404,7 +406,7 @@ static bool WriteBinaryFile(const fs::path &filename, const std::string &data) /****** Bitcoin specific TorController implementation ********/ /** Controller that connects to Tor control socket, authenticate, then create - * and maintain a ephemeral hidden service. + * and maintain an ephemeral hidden service. */ class TorController { diff --git a/src/txdb.cpp b/src/txdb.cpp index c8f5090293..002f6550bc 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -7,8 +7,12 @@ #include "chainparams.h" #include "hash.h" +#include "random.h" #include "pow.h" #include "uint256.h" +#include "util.h" +#include "ui_interface.h" +#include "init.h" #include <stdint.h> @@ -21,6 +25,7 @@ static const char DB_TXINDEX = 't'; static const char DB_BLOCK_INDEX = 'b'; static const char DB_BEST_BLOCK = 'B'; +static const char DB_HEAD_BLOCKS = 'H'; static const char DB_FLAG = 'F'; static const char DB_REINDEX_FLAG = 'R'; static const char DB_LAST_BLOCK = 'l'; @@ -68,10 +73,39 @@ uint256 CCoinsViewDB::GetBestBlock() const { return hashBestChain; } +std::vector<uint256> CCoinsViewDB::GetHeadBlocks() const { + std::vector<uint256> vhashHeadBlocks; + if (!db.Read(DB_HEAD_BLOCKS, vhashHeadBlocks)) { + return std::vector<uint256>(); + } + return vhashHeadBlocks; +} + bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { CDBBatch batch(db); size_t count = 0; size_t changed = 0; + size_t batch_size = (size_t)GetArg("-dbbatchsize", nDefaultDbBatchSize); + int crash_simulate = GetArg("-dbcrashratio", 0); + assert(!hashBlock.IsNull()); + + uint256 old_tip = GetBestBlock(); + if (old_tip.IsNull()) { + // We may be in the middle of replaying. + std::vector<uint256> old_heads = GetHeadBlocks(); + if (old_heads.size() == 2) { + assert(old_heads[0] == hashBlock); + old_tip = old_heads[1]; + } + } + + // In the first batch, mark the database as being in the middle of a + // transition from old_tip to hashBlock. + // A vector is used for future extensibility, as we may want to support + // interrupting after partial writes from multiple independent reorgs. + batch.Erase(DB_BEST_BLOCK); + batch.Write(DB_HEAD_BLOCKS, std::vector<uint256>{hashBlock, old_tip}); + for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) { if (it->second.flags & CCoinsCacheEntry::DIRTY) { CoinEntry entry(&it->first); @@ -84,10 +118,25 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { count++; CCoinsMap::iterator itOld = it++; mapCoins.erase(itOld); + if (batch.SizeEstimate() > batch_size) { + LogPrint(BCLog::COINDB, "Writing partial batch of %.2f MiB\n", batch.SizeEstimate() * (1.0 / 1048576.0)); + db.WriteBatch(batch); + batch.Clear(); + if (crash_simulate) { + static FastRandomContext rng; + if (rng.randrange(crash_simulate) == 0) { + LogPrintf("Simulating a crash. Goodbye.\n"); + _Exit(0); + } + } + } } - if (!hashBlock.IsNull()) - batch.Write(DB_BEST_BLOCK, hashBlock); + // In the last batch, mark the database as consistent with hashBlock again. + batch.Erase(DB_HEAD_BLOCKS); + batch.Write(DB_BEST_BLOCK, hashBlock); + + LogPrint(BCLog::COINDB, "Writing final batch of %.2f MiB\n", batch.SizeEstimate() * (1.0 / 1048576.0)); bool ret = db.WriteBatch(batch); LogPrint(BCLog::COINDB, "Committed %u changed transaction outputs (out of %u) to coin database...\n", (unsigned int)changed, (unsigned int)count); return ret; @@ -210,7 +259,7 @@ bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) { return true; } -bool CBlockTreeDB::LoadBlockIndexGuts(std::function<CBlockIndex*(const uint256&)> insertBlockIndex) +bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex) { std::unique_ptr<CDBIterator> pcursor(NewIterator()); @@ -238,12 +287,12 @@ bool CBlockTreeDB::LoadBlockIndexGuts(std::function<CBlockIndex*(const uint256&) pindexNew->nStatus = diskindex.nStatus; pindexNew->nTx = diskindex.nTx; - if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits, Params().GetConsensus())) - return error("LoadBlockIndex(): CheckProofOfWork failed: %s", pindexNew->ToString()); + if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits, consensusParams)) + return error("%s: CheckProofOfWork failed: %s", __func__, pindexNew->ToString()); pcursor->Next(); } else { - return error("LoadBlockIndex() : failed to read value"); + return error("%s: failed to read value", __func__); } } else { break; @@ -319,13 +368,30 @@ bool CCoinsViewDB::Upgrade() { return true; } - LogPrintf("Upgrading database...\n"); + int64_t count = 0; + LogPrintf("Upgrading utxo-set database...\n"); + LogPrintf("[0%%]..."); size_t batch_size = 1 << 24; CDBBatch batch(db); + uiInterface.SetProgressBreakAction(StartShutdown); + int reportDone = 0; while (pcursor->Valid()) { boost::this_thread::interruption_point(); + if (ShutdownRequested()) { + break; + } std::pair<unsigned char, uint256> key; if (pcursor->GetKey(key) && key.first == DB_COINS) { + if (count++ % 256 == 0) { + uint32_t high = 0x100 * *key.second.begin() + *(key.second.begin() + 1); + int percentageDone = (int)(high * 100.0 / 65536.0 + 0.5); + uiInterface.ShowProgress(_("Upgrading UTXO database") + "\n"+ _("(press q to shutdown and continue later)") + "\n", percentageDone); + if (reportDone < percentageDone/10) { + // report max. every 10% step + LogPrintf("[%d%%]...", percentageDone); + reportDone = percentageDone/10; + } + } CCoins old_coins; if (!pcursor->GetValue(old_coins)) { return error("%s: cannot parse CCoins record", __func__); @@ -350,5 +416,7 @@ bool CCoinsViewDB::Upgrade() { } } db.WriteBatch(batch); + uiInterface.SetProgressBreakAction(std::function<void(void)>()); + LogPrintf("[%s].\n", ShutdownRequested() ? "CANCELLED" : "DONE"); return true; } diff --git a/src/txdb.h b/src/txdb.h index 974dd4ebe3..adcbc73380 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -19,12 +19,12 @@ class CBlockIndex; class CCoinsViewDBCursor; class uint256; -//! Compensate for extra memory peak (x1.5-x1.9) at flush time. -static constexpr int DB_PEAK_USAGE_FACTOR = 2; //! No need to periodic flush if at least this much space still available. -static constexpr int MAX_BLOCK_COINSDB_USAGE = 10 * DB_PEAK_USAGE_FACTOR; +static constexpr int MAX_BLOCK_COINSDB_USAGE = 10; //! -dbcache default (MiB) static const int64_t nDefaultDbCache = 450; +//! -dbbatchsize default (bytes) +static const int64_t nDefaultDbBatchSize = 16 << 20; //! max. -dbcache (MiB) static const int64_t nMaxDbCache = sizeof(void*) > 4 ? 16384 : 1024; //! min. -dbcache (MiB) @@ -74,6 +74,7 @@ public: bool GetCoin(const COutPoint &outpoint, Coin &coin) const override; bool HaveCoin(const COutPoint &outpoint) const override; uint256 GetBestBlock() const override; + std::vector<uint256> GetHeadBlocks() const override; bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) override; CCoinsViewCursor *Cursor() const override; @@ -88,12 +89,12 @@ class CCoinsViewDBCursor: public CCoinsViewCursor public: ~CCoinsViewDBCursor() {} - bool GetKey(COutPoint &key) const; - bool GetValue(Coin &coin) const; - unsigned int GetValueSize() const; + bool GetKey(COutPoint &key) const override; + bool GetValue(Coin &coin) const override; + unsigned int GetValueSize() const override; - bool Valid() const; - void Next(); + bool Valid() const override; + void Next() override; private: CCoinsViewDBCursor(CDBIterator* pcursorIn, const uint256 &hashBlockIn): @@ -122,7 +123,7 @@ public: bool WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> > &list); bool WriteFlag(const std::string &name, bool fValue); bool ReadFlag(const std::string &name, bool &fValue); - bool LoadBlockIndexGuts(std::function<CBlockIndex*(const uint256&)> insertBlockIndex); + bool LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex); }; #endif // BITCOIN_TXDB_H diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 8ba48f3bb5..4a81055231 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -123,7 +123,7 @@ void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashes // accounted for in the state of their ancestors) std::set<uint256> setAlreadyIncluded(vHashesToUpdate.begin(), vHashesToUpdate.end()); - // Iterate in reverse, so that whenever we are looking at at a transaction + // Iterate in reverse, so that whenever we are looking at a transaction // we are sure that all in-mempool descendants have already been processed. // This maximizes the benefit of the descendant cache and guarantees that // setMemPoolChildren will be updated, an assumption made in @@ -770,7 +770,7 @@ public: return counta < countb; } }; -} +} // namespace std::vector<CTxMemPool::indexed_transaction_set::const_iterator> CTxMemPool::GetSortedDepthAndScore() const { @@ -904,11 +904,7 @@ bool CCoinsViewMemPool::GetCoin(const COutPoint &outpoint, Coin &coin) const { return false; } } - return (base->GetCoin(outpoint, coin) && !coin.IsSpent()); -} - -bool CCoinsViewMemPool::HaveCoin(const COutPoint &outpoint) const { - return mempool.exists(outpoint) || base->HaveCoin(outpoint); + return base->GetCoin(outpoint, coin); } size_t CTxMemPool::DynamicMemoryUsage() const { @@ -1051,9 +1047,7 @@ void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<COutPoint>* pvNoSpends for (const CTransaction& tx : txn) { for (const CTxIn& txin : tx.vin) { if (exists(txin.prevout.hash)) continue; - if (!mapNextTx.count(txin.prevout)) { - pvNoSpendsRemaining->push_back(txin.prevout); - } + pvNoSpendsRemaining->push_back(txin.prevout); } } } diff --git a/src/txmempool.h b/src/txmempool.h index 7ca3b18a1e..d272114a7c 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -618,13 +618,6 @@ public: return (mapTx.count(hash) != 0); } - bool exists(const COutPoint& outpoint) const - { - LOCK(cs); - auto it = mapTx.find(outpoint.hash); - return (it != mapTx.end() && outpoint.n < it->GetTx().vout.size()); - } - CTransactionRef get(const uint256& hash) const; TxMempoolInfo info(const uint256& hash) const; std::vector<TxMempoolInfo> infoAll() const; @@ -676,6 +669,13 @@ private: /** * CCoinsView that brings transactions from a memorypool into view. * It does not check for spendings by memory pool transactions. + * Instead, it provides access to all Coins which are either unspent in the + * base CCoinsView, or are outputs from any mempool transaction! + * This allows transaction replacement to work as expected, as you want to + * have all inputs "available" to check signatures, and any cycles in the + * dependency graph are checked directly in AcceptToMemoryPool. + * It also allows you to sign a double-spend directly in signrawtransaction, + * as long as the conflicting transaction is not yet confirmed. */ class CCoinsViewMemPool : public CCoinsViewBacked { @@ -684,8 +684,7 @@ protected: public: CCoinsViewMemPool(CCoinsView* baseIn, const CTxMemPool& mempoolIn); - bool GetCoin(const COutPoint &outpoint, Coin &coin) const; - bool HaveCoin(const COutPoint &outpoint) const; + bool GetCoin(const COutPoint &outpoint, Coin &coin) const override; }; /** diff --git a/src/ui_interface.h b/src/ui_interface.h index 090402aeed..762dd19b19 100644 --- a/src/ui_interface.h +++ b/src/ui_interface.h @@ -97,6 +97,9 @@ public: /** Show progress e.g. for verifychain */ boost::signals2::signal<void (const std::string &title, int nProgress)> ShowProgress; + /** Set progress break action (possible "cancel button" triggers that action) */ + boost::signals2::signal<void (std::function<void(void)> action)> SetProgressBreakAction; + /** New block has been accepted */ boost::signals2::signal<void (bool, const CBlockIndex *)> NotifyBlockTip; diff --git a/src/util.cpp b/src/util.cpp index 0a14e8bb9e..b76c173f90 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -84,6 +84,8 @@ #include <openssl/rand.h> #include <openssl/conf.h> +// Application startup time (used for uptime calculation) +const int64_t nStartupTime = GetTime(); const char * const BITCOIN_CONF_FILENAME = "bitcoin.conf"; const char * const BITCOIN_PID_FILENAME = "bitcoind.pid"; @@ -420,7 +422,9 @@ void ArgsManager::ParseParameters(int argc, const char* const argv[]) std::vector<std::string> ArgsManager::GetArgs(const std::string& strArg) { LOCK(cs_args); - return mapMultiArgs.at(strArg); + if (IsArgSet(strArg)) + return mapMultiArgs.at(strArg); + return {}; } bool ArgsManager::IsArgSet(const std::string& strArg) @@ -651,21 +655,21 @@ bool RenameOver(fs::path src, fs::path dest) } /** - * Ignores exceptions thrown by Boost's create_directory if the requested directory exists. + * Ignores exceptions thrown by Boost's create_directories if the requested directory exists. * Specifically handles case where path p exists, but it wasn't possible for the user to * write to the parent directory. */ -bool TryCreateDirectory(const fs::path& p) +bool TryCreateDirectories(const fs::path& p) { try { - return fs::create_directory(p); + return fs::create_directories(p); } catch (const fs::filesystem_error&) { if (!fs::exists(p) || !fs::is_directory(p)) throw; } - // create_directory didn't create the directory, it had to have existed already + // create_directories didn't create the directory, it had to have existed already return false; } @@ -889,3 +893,9 @@ std::string CopyrightHolders(const std::string& strPrefix) } return strCopyrightHolders; } + +// Obtain the application startup time (used for uptime calculation) +int64_t GetStartupTime() +{ + return nStartupTime; +} diff --git a/src/util.h b/src/util.h index 8f8b249749..824ad51ac4 100644 --- a/src/util.h +++ b/src/util.h @@ -5,7 +5,7 @@ /** * Server/client environment: argument handling, config file parsing, - * logging, thread wrappers + * logging, thread wrappers, startup time */ #ifndef BITCOIN_UTIL_H #define BITCOIN_UTIL_H @@ -29,6 +29,9 @@ #include <boost/signals2/signal.hpp> +// Application startup time (used for uptime calculation) +int64_t GetStartupTime(); + static const bool DEFAULT_LOGTIMEMICROS = false; static const bool DEFAULT_LOGIPS = false; static const bool DEFAULT_LOGTIMESTAMPS = true; @@ -122,6 +125,17 @@ int LogPrintStr(const std::string &str); /** Get format string from VA_ARGS for error reporting */ template<typename... Args> std::string FormatStringFromLogArgs(const char *fmt, const Args&... args) { return fmt; } +static inline void MarkUsed() {} +template<typename T, typename... Args> static inline void MarkUsed(const T& t, const Args&... args) +{ + (void)t; + MarkUsed(args...); +} + +#ifdef USE_COVERAGE +#define LogPrintf(...) do { MarkUsed(__VA_ARGS__); } while(0) +#define LogPrint(category, ...) do { MarkUsed(__VA_ARGS__); } while(0) +#else #define LogPrintf(...) do { \ std::string _log_msg_; /* Unlikely name to avoid shadowing variables */ \ try { \ @@ -138,6 +152,7 @@ template<typename... Args> std::string FormatStringFromLogArgs(const char *fmt, LogPrintf(__VA_ARGS__); \ } \ } while(0) +#endif template<typename... Args> bool error(const char* fmt, const Args&... args) @@ -152,7 +167,7 @@ bool TruncateFile(FILE *file, unsigned int length); int RaiseFileDescriptorLimit(int nMinFD); void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length); bool RenameOver(fs::path src, fs::path dest); -bool TryCreateDirectory(const fs::path& p); +bool TryCreateDirectories(const fs::path& p); fs::path GetDefaultDataDir(); const fs::path &GetDataDir(bool fNetSpecific = true); void ClearDatadirCache(); @@ -187,62 +202,63 @@ public: void ParseParameters(int argc, const char*const argv[]); void ReadConfigFile(const std::string& confPath); std::vector<std::string> GetArgs(const std::string& strArg); -/** - * Return true if the given argument has been manually set - * - * @param strArg Argument to get (e.g. "-foo") - * @return true if the argument has been set - */ -bool IsArgSet(const std::string& strArg); - -/** - * Return string argument or default value - * - * @param strArg Argument to get (e.g. "-foo") - * @param default (e.g. "1") - * @return command-line argument or default value - */ -std::string GetArg(const std::string& strArg, const std::string& strDefault); - -/** - * Return integer argument or default value - * - * @param strArg Argument to get (e.g. "-foo") - * @param default (e.g. 1) - * @return command-line argument (0 if invalid number) or default value - */ -int64_t GetArg(const std::string& strArg, int64_t nDefault); - -/** - * Return boolean argument or default value - * - * @param strArg Argument to get (e.g. "-foo") - * @param default (true or false) - * @return command-line argument or default value - */ -bool GetBoolArg(const std::string& strArg, bool fDefault); - -/** - * Set an argument if it doesn't already have a value - * - * @param strArg Argument to set (e.g. "-foo") - * @param strValue Value (e.g. "1") - * @return true if argument gets set, false if it already had a value - */ -bool SoftSetArg(const std::string& strArg, const std::string& strValue); - -/** - * Set a boolean argument if it doesn't already have a value - * - * @param strArg Argument to set (e.g. "-foo") - * @param fValue Value (e.g. false) - * @return true if argument gets set, false if it already had a value - */ -bool SoftSetBoolArg(const std::string& strArg, bool fValue); -// Forces an arg setting. Called by SoftSetArg() if the arg hasn't already -// been set. Also called directly in testing. -void ForceSetArg(const std::string& strArg, const std::string& strValue); + /** + * Return true if the given argument has been manually set + * + * @param strArg Argument to get (e.g. "-foo") + * @return true if the argument has been set + */ + bool IsArgSet(const std::string& strArg); + + /** + * Return string argument or default value + * + * @param strArg Argument to get (e.g. "-foo") + * @param default (e.g. "1") + * @return command-line argument or default value + */ + std::string GetArg(const std::string& strArg, const std::string& strDefault); + + /** + * Return integer argument or default value + * + * @param strArg Argument to get (e.g. "-foo") + * @param default (e.g. 1) + * @return command-line argument (0 if invalid number) or default value + */ + int64_t GetArg(const std::string& strArg, int64_t nDefault); + + /** + * Return boolean argument or default value + * + * @param strArg Argument to get (e.g. "-foo") + * @param default (true or false) + * @return command-line argument or default value + */ + bool GetBoolArg(const std::string& strArg, bool fDefault); + + /** + * Set an argument if it doesn't already have a value + * + * @param strArg Argument to set (e.g. "-foo") + * @param strValue Value (e.g. "1") + * @return true if argument gets set, false if it already had a value + */ + bool SoftSetArg(const std::string& strArg, const std::string& strValue); + + /** + * Set a boolean argument if it doesn't already have a value + * + * @param strArg Argument to set (e.g. "-foo") + * @param fValue Value (e.g. false) + * @return true if argument gets set, false if it already had a value + */ + bool SoftSetBoolArg(const std::string& strArg, bool fValue); + + // Forces an arg setting. Called by SoftSetArg() if the arg hasn't already + // been set. Also called directly in testing. + void ForceSetArg(const std::string& strArg, const std::string& strValue); }; extern ArgsManager gArgs; diff --git a/src/utilstrencodings.cpp b/src/utilstrencodings.cpp index 74bf66fbf6..93abaec04b 100644 --- a/src/utilstrencodings.cpp +++ b/src/utilstrencodings.cpp @@ -437,7 +437,7 @@ bool ParseInt32(const std::string& str, int32_t *out) errno = 0; // strtol will not set errno if valid long int n = strtol(str.c_str(), &endp, 10); if(out) *out = (int32_t)n; - // Note that strtol returns a *long int*, so even if strtol doesn't report a over/underflow + // Note that strtol returns a *long int*, so even if strtol doesn't report an over/underflow // we still have to check that the returned value is within the range of an *int32_t*. On 64-bit // platforms the size of these types may be different. return endp && *endp == 0 && !errno && @@ -453,7 +453,7 @@ bool ParseInt64(const std::string& str, int64_t *out) errno = 0; // strtoll will not set errno if valid long long int n = strtoll(str.c_str(), &endp, 10); if(out) *out = (int64_t)n; - // Note that strtoll returns a *long long int*, so even if strtol doesn't report a over/underflow + // Note that strtoll returns a *long long int*, so even if strtol doesn't report an over/underflow // we still have to check that the returned value is within the range of an *int64_t*. return endp && *endp == 0 && !errno && n >= std::numeric_limits<int64_t>::min() && @@ -470,7 +470,7 @@ bool ParseUInt32(const std::string& str, uint32_t *out) errno = 0; // strtoul will not set errno if valid unsigned long int n = strtoul(str.c_str(), &endp, 10); if(out) *out = (uint32_t)n; - // Note that strtoul returns a *unsigned long int*, so even if it doesn't report a over/underflow + // Note that strtoul returns a *unsigned long int*, so even if it doesn't report an over/underflow // we still have to check that the returned value is within the range of an *uint32_t*. On 64-bit // platforms the size of these types may be different. return endp && *endp == 0 && !errno && @@ -487,7 +487,7 @@ bool ParseUInt64(const std::string& str, uint64_t *out) errno = 0; // strtoull will not set errno if valid unsigned long long int n = strtoull(str.c_str(), &endp, 10); if(out) *out = (uint64_t)n; - // Note that strtoull returns a *unsigned long long int*, so even if it doesn't report a over/underflow + // Note that strtoull returns a *unsigned long long int*, so even if it doesn't report an over/underflow // we still have to check that the returned value is within the range of an *uint64_t*. return endp && *endp == 0 && !errno && n <= std::numeric_limits<uint64_t>::max(); diff --git a/src/validation.cpp b/src/validation.cpp index 04d180a084..09288be1ca 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -14,6 +14,7 @@ #include "consensus/merkle.h" #include "consensus/tx_verify.h" #include "consensus/validation.h" +#include "cuckoocache.h" #include "fs.h" #include "hash.h" #include "init.h" @@ -97,7 +98,7 @@ namespace { struct CBlockIndexWorkComparator { - bool operator()(CBlockIndex *pa, CBlockIndex *pb) const { + bool operator()(const CBlockIndex *pa, const CBlockIndex *pb) const { // First sort by most total work, ... if (pa->nChainWork > pb->nChainWork) return false; if (pa->nChainWork < pb->nChainWork) return true; @@ -190,7 +191,7 @@ enum FlushStateMode { static bool FlushStateToDisk(const CChainParams& chainParams, CValidationState &state, FlushStateMode mode, int nManualPruneHeight=0); static void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight); static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight); -static bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks = NULL); +bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks = nullptr); static FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false); bool CheckFinalTx(const CTransaction &tx, int flags) @@ -313,6 +314,9 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool return EvaluateSequenceLocks(index, lockPair); } +// Returns the script flags which should be checked for a given block +static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& chainparams); + static void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) { int expired = pool.Expire(GetTime() - age); if (expired != 0) { @@ -396,6 +400,42 @@ void UpdateMempoolForReorg(DisconnectedBlockTransactions &disconnectpool, bool f LimitMempoolSize(mempool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); } +// Used to avoid mempool polluting consensus critical paths if CCoinsViewMempool +// were somehow broken and returning the wrong scriptPubKeys +static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &view, CTxMemPool& pool, + unsigned int flags, bool cacheSigStore, PrecomputedTransactionData& txdata) { + AssertLockHeld(cs_main); + + // pool.cs should be locked already, but go ahead and re-take the lock here + // to enforce that mempool doesn't change between when we check the view + // and when we actually call through to CheckInputs + LOCK(pool.cs); + + assert(!tx.IsCoinBase()); + for (const CTxIn& txin : tx.vin) { + const Coin& coin = view.AccessCoin(txin.prevout); + + // At this point we haven't actually checked if the coins are all + // available (or shouldn't assume we have, since CheckInputs does). + // So we just return failure if the inputs are not available here, + // and then only have to check equivalence for available inputs. + if (coin.IsSpent()) return false; + + const CTransactionRef& txFrom = pool.get(txin.prevout.hash); + if (txFrom) { + assert(txFrom->GetHash() == txin.prevout.hash); + assert(txFrom->vout.size() > txin.prevout.n); + assert(txFrom->vout[txin.prevout.n] == coin.out); + } else { + const Coin& coinFromDisk = pcoinsTip->AccessCoin(txin.prevout); + assert(!coinFromDisk.IsSpent()); + assert(coinFromDisk.out == coin.out); + } + } + + return CheckInputs(tx, state, view, true, flags, cacheSigStore, true, txdata); +} + static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx, bool fLimitFree, bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced, bool fOverrideMempoolLimit, const CAmount& nAbsurdFee, std::vector<COutPoint>& coins_to_uncache) @@ -431,8 +471,9 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool return state.DoS(0, false, REJECT_NONSTANDARD, "non-final"); // is it already in the memory pool? - if (pool.exists(hash)) - return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-in-mempool"); + if (pool.exists(hash)) { + return state.Invalid(false, REJECT_DUPLICATE, "txn-already-in-mempool"); + } // Check for conflicts with in-memory transactions std::set<uint256> setConflicts; @@ -470,8 +511,9 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool } } } - if (fReplacementOptOut) - return state.Invalid(false, REJECT_CONFLICT, "txn-mempool-conflict"); + if (fReplacementOptOut) { + return state.Invalid(false, REJECT_DUPLICATE, "txn-mempool-conflict"); + } setConflicts.insert(ptxConflicting->GetHash()); } @@ -498,7 +540,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool if (!had_coin_in_cache) { coins_to_uncache.push_back(outpoint); } - return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-known"); + return state.Invalid(false, REJECT_DUPLICATE, "txn-already-known"); } } @@ -750,32 +792,51 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. PrecomputedTransactionData txdata(tx); - if (!CheckInputs(tx, state, view, true, scriptVerifyFlags, true, txdata)) { + if (!CheckInputs(tx, state, view, true, scriptVerifyFlags, true, false, txdata)) { // SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we // need to turn both off, and compare against just turning off CLEANSTACK // to see if the failure is specifically due to witness validation. CValidationState stateDummy; // Want reported failures to be from first CheckInputs - if (!tx.HasWitness() && CheckInputs(tx, stateDummy, view, true, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, txdata) && - !CheckInputs(tx, stateDummy, view, true, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, txdata)) { + if (!tx.HasWitness() && CheckInputs(tx, stateDummy, view, true, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, false, txdata) && + !CheckInputs(tx, stateDummy, view, true, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, false, txdata)) { // Only the witness is missing, so the transaction itself may be fine. state.SetCorruptionPossible(); } return false; // state filled in by CheckInputs } - // Check again against just the consensus-critical mandatory script - // verification flags, in case of bugs in the standard flags that cause + // Check again against the current block tip's script verification + // flags to cache our script execution flags. This is, of course, + // useless if the next block has different script flags from the + // previous one, but because the cache tracks script flags for us it + // will auto-invalidate and we'll just have a few blocks of extra + // misses on soft-fork activation. + // + // This is also useful in case of bugs in the standard flags that cause // transactions to pass as valid when they're actually invalid. For // instance the STRICTENC flag was incorrectly allowing certain // CHECKSIG NOT scripts to pass, even though they were invalid. // // There is a similar check in CreateNewBlock() to prevent creating - // invalid blocks, however allowing such transactions into the mempool - // can be exploited as a DoS attack. - if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata)) + // invalid blocks (using TestBlockValidity), however allowing such + // transactions into the mempool can be exploited as a DoS attack. + unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(chainActive.Tip(), Params().GetConsensus()); + if (!CheckInputsFromMempoolAndCache(tx, state, view, pool, currentBlockScriptVerifyFlags, true, txdata)) { - return error("%s: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s, %s", - __func__, hash.ToString(), FormatStateMessage(state)); + // If we're using promiscuousmempoolflags, we may hit this normally + // Check if current block has some flags that scriptVerifyFlags + // does not before printing an ominous warning + if (!(~scriptVerifyFlags & currentBlockScriptVerifyFlags)) { + return error("%s: BUG! PLEASE REPORT THIS! ConnectInputs failed against latest-block but not STANDARD flags %s, %s", + __func__, hash.ToString(), FormatStateMessage(state)); + } else { + if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, false, txdata)) { + return error("%s: ConnectInputs failed against MANDATORY but not STANDARD flags due to promiscuous mempool %s, %s", + __func__, hash.ToString(), FormatStateMessage(state)); + } else { + LogPrintf("Warning: -promiscuousmempool flags set to not include currently enforced soft forks, this may break mining or otherwise cause instability!\n"); + } + } } // Remove conflicting transactions from the mempool @@ -1124,7 +1185,8 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txund txundo.vprevout.reserve(tx.vin.size()); for (const CTxIn &txin : tx.vin) { txundo.vprevout.emplace_back(); - inputs.SpendCoin(txin.prevout, &txundo.vprevout.back()); + bool is_spent = inputs.SpendCoin(txin.prevout, &txundo.vprevout.back()); + assert(is_spent); } } // add outputs @@ -1150,12 +1212,34 @@ int GetSpendHeight(const CCoinsViewCache& inputs) return pindexPrev->nHeight + 1; } + +static CuckooCache::cache<uint256, SignatureCacheHasher> scriptExecutionCache; +static uint256 scriptExecutionCacheNonce(GetRandHash()); + +void InitScriptExecutionCache() { + // nMaxCacheSize is unsigned. If -maxsigcachesize is set to zero, + // setup_bytes creates the minimum possible cache (2 elements). + size_t nMaxCacheSize = std::min(std::max((int64_t)0, GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) / 2), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20); + size_t nElems = scriptExecutionCache.setup_bytes(nMaxCacheSize); + LogPrintf("Using %zu MiB out of %zu/2 requested for script execution cache, able to store %zu elements\n", + (nElems*sizeof(uint256)) >>20, (nMaxCacheSize*2)>>20, nElems); +} + /** * Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts) - * This does not modify the UTXO set. If pvChecks is not NULL, script checks are pushed onto it - * instead of being performed inline. + * This does not modify the UTXO set. + * + * If pvChecks is not NULL, script checks are pushed onto it instead of being performed inline. Any + * script checks which are not necessary (eg due to script execution cache hits) are, obviously, + * not pushed onto pvChecks/run. + * + * Setting cacheSigStore/cacheFullScriptStore to false will remove elements from the corresponding cache + * which are matched. This is useful for checking blocks where we will likely never need the cache + * entry again. + * + * Non-static (and re-declared) in src/test/txvalidationcache_tests.cpp */ -static bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks) +bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks) { if (!tx.IsCoinBase()) { @@ -1175,6 +1259,21 @@ static bool CheckInputs(const CTransaction& tx, CValidationState &state, const C // Of course, if an assumed valid block is invalid due to false scriptSigs // this optimization would allow an invalid chain to be accepted. if (fScriptChecks) { + // First check if script executions have been cached with the same + // flags. Note that this assumes that the inputs provided are + // correct (ie that the transaction hash which is in tx's prevouts + // properly commits to the scriptPubKey in the inputs view of that + // transaction). + uint256 hashCacheEntry; + // We only use the first 19 bytes of nonce to avoid a second SHA + // round - giving us 19 + 32 + 4 = 55 bytes (+ 8 + 1 = 64) + static_assert(55 - sizeof(flags) - 32 >= 128/8, "Want at least 128 bits of nonce for script execution cache"); + CSHA256().Write(scriptExecutionCacheNonce.begin(), 55 - sizeof(flags) - 32).Write(tx.GetWitnessHash().begin(), 32).Write((unsigned char*)&flags, sizeof(flags)).Finalize(hashCacheEntry.begin()); + AssertLockHeld(cs_main); //TODO: Remove this requirement by making CuckooCache not require external locks + if (scriptExecutionCache.contains(hashCacheEntry, !cacheFullScriptStore)) { + return true; + } + for (unsigned int i = 0; i < tx.vin.size(); i++) { const COutPoint &prevout = tx.vin[i].prevout; const Coin& coin = inputs.AccessCoin(prevout); @@ -1189,7 +1288,7 @@ static bool CheckInputs(const CTransaction& tx, CValidationState &state, const C const CAmount amount = coin.out.nValue; // Verify signature - CScriptCheck check(scriptPubKey, amount, tx, i, flags, cacheStore, &txdata); + CScriptCheck check(scriptPubKey, amount, tx, i, flags, cacheSigStore, &txdata); if (pvChecks) { pvChecks->push_back(CScriptCheck()); check.swap(pvChecks->back()); @@ -1202,12 +1301,12 @@ static bool CheckInputs(const CTransaction& tx, CValidationState &state, const C // avoid splitting the network between upgraded and // non-upgraded nodes. CScriptCheck check2(scriptPubKey, amount, tx, i, - flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore, &txdata); + flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheSigStore, &txdata); if (check2()) return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError()))); } // Failures of other flags indicate a transaction that is - // invalid in new blocks, e.g. a invalid P2SH. We DoS ban + // invalid in new blocks, e.g. an invalid P2SH. We DoS ban // such nodes as they are not following the protocol. That // said during an upgrade careful thought should be taken // as to the correct behavior - we may want to continue @@ -1216,6 +1315,12 @@ static bool CheckInputs(const CTransaction& tx, CValidationState &state, const C return state.DoS(100,false, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError()))); } } + + if (cacheFullScriptStore && !pvChecks) { + // We executed all of the provided scripts, and were told to + // cache the result. Do so now. + scriptExecutionCache.insert(hashCacheEntry); + } } } @@ -1295,7 +1400,7 @@ bool AbortNode(CValidationState& state, const std::string& strMessage, const std return state.Error(strMessage); } -} // anon namespace +} // namespace enum DisconnectResult { @@ -1329,17 +1434,19 @@ int ApplyTxInUndo(Coin&& undo, CCoinsViewCache& view, const COutPoint& out) return DISCONNECT_FAILED; // adding output for transaction without known metadata } } - view.AddCoin(out, std::move(undo), undo.fCoinBase); + // The potential_overwrite parameter to AddCoin is only allowed to be false if we know for + // sure that the coin did not already exist in the cache. As we have queried for that above + // using HaveCoin, we don't need to guess. When fClean is false, a coin already existed and + // it is an overwrite. + view.AddCoin(out, std::move(undo), !fClean); return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN; } /** Undo the effects of this block (with given index) on the UTXO set represented by coins. - * When UNCLEAN or FAILED is returned, view is left in an indeterminate state. */ + * When FAILED is returned, view is left in an indeterminate state. */ static DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view) { - assert(pindex->GetBlockHash() == view.GetBestBlock()); - bool fClean = true; CBlockUndo blockUndo; @@ -1362,6 +1469,7 @@ static DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* for (int i = block.vtx.size() - 1; i >= 0; i--) { const CTransaction &tx = *(block.vtx[i]); uint256 hash = tx.GetHash(); + bool is_coinbase = tx.IsCoinBase(); // Check that all outputs are available and match the outputs in the block itself // exactly. @@ -1369,8 +1477,8 @@ static DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* if (!tx.vout[o].scriptPubKey.IsUnspendable()) { COutPoint out(hash, o); Coin coin; - view.SpendCoin(out, &coin); - if (tx.vout[o] != coin.out) { + bool is_spent = view.SpendCoin(out, &coin); + if (!is_spent || tx.vout[o] != coin.out || pindex->nHeight != coin.nHeight || is_coinbase != coin.fCoinBase) { fClean = false; // transaction output mismatch } } @@ -1460,12 +1568,12 @@ private: 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; } + int64_t BeginTime(const Consensus::Params& params) const override { return 0; } + int64_t EndTime(const Consensus::Params& params) const override { return std::numeric_limits<int64_t>::max(); } + int Period(const Consensus::Params& params) const override { return params.nMinerConfirmationWindow; } + int Threshold(const Consensus::Params& params) const override { return params.nRuleChangeActivationThreshold; } - bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const + bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override { return ((pindex->nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) && ((pindex->nVersion >> bit) & 1) != 0 && @@ -1476,6 +1584,41 @@ public: // Protected by cs_main static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS]; +static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& consensusparams) { + AssertLockHeld(cs_main); + + // BIP16 didn't become active until Apr 1 2012 + int64_t nBIP16SwitchTime = 1333238400; + bool fStrictPayToScriptHash = (pindex->GetBlockTime() >= nBIP16SwitchTime); + + unsigned int flags = fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE; + + // Start enforcing the DERSIG (BIP66) rule + if (pindex->nHeight >= consensusparams.BIP66Height) { + flags |= SCRIPT_VERIFY_DERSIG; + } + + // Start enforcing CHECKLOCKTIMEVERIFY (BIP65) rule + if (pindex->nHeight >= consensusparams.BIP65Height) { + flags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY; + } + + // Start enforcing BIP68 (sequence locks) and BIP112 (CHECKSEQUENCEVERIFY) using versionbits logic. + if (VersionBitsState(pindex->pprev, consensusparams, Consensus::DEPLOYMENT_CSV, versionbitscache) == THRESHOLD_ACTIVE) { + flags |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY; + } + + // Start enforcing WITNESS rules using versionbits logic. + if (IsWitnessEnabled(pindex->pprev, consensusparams)) { + flags |= SCRIPT_VERIFY_WITNESS; + flags |= SCRIPT_VERIFY_NULLDUMMY; + } + + return flags; +} + + + static int64_t nTimeCheck = 0; static int64_t nTimeForks = 0; static int64_t nTimeVerify = 0; @@ -1579,34 +1722,14 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd } } - // BIP16 didn't become active until Apr 1 2012 - int64_t nBIP16SwitchTime = 1333238400; - bool fStrictPayToScriptHash = (pindex->GetBlockTime() >= nBIP16SwitchTime); - - unsigned int flags = fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE; - - // Start enforcing the DERSIG (BIP66) rule - if (pindex->nHeight >= chainparams.GetConsensus().BIP66Height) { - flags |= SCRIPT_VERIFY_DERSIG; - } - - // Start enforcing CHECKLOCKTIMEVERIFY (BIP65) rule - if (pindex->nHeight >= chainparams.GetConsensus().BIP65Height) { - flags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY; - } - // Start enforcing BIP68 (sequence locks) and BIP112 (CHECKSEQUENCEVERIFY) using versionbits logic. int nLockTimeFlags = 0; if (VersionBitsState(pindex->pprev, chainparams.GetConsensus(), Consensus::DEPLOYMENT_CSV, versionbitscache) == THRESHOLD_ACTIVE) { - flags |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY; nLockTimeFlags |= LOCKTIME_VERIFY_SEQUENCE; } - // Start enforcing WITNESS rules using versionbits logic. - if (IsWitnessEnabled(pindex->pprev, chainparams.GetConsensus())) { - flags |= SCRIPT_VERIFY_WITNESS; - flags |= SCRIPT_VERIFY_NULLDUMMY; - } + // Get the script flags for this block + unsigned int flags = GetBlockScriptFlags(pindex, chainparams.GetConsensus()); int64_t nTime2 = GetTimeMicros(); nTimeForks += nTime2 - nTime1; LogPrint(BCLog::BENCH, " - Fork checks: %.2fms [%.2fs]\n", 0.001 * (nTime2 - nTime1), nTimeForks * 0.000001); @@ -1667,7 +1790,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd std::vector<CScriptCheck> vChecks; bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */ - if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, txdata[i], nScriptCheckThreads ? &vChecks : NULL)) + if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, fCacheResults, txdata[i], nScriptCheckThreads ? &vChecks : NULL)) return error("ConnectBlock(): CheckInputs on %s failed with %s", tx.GetHash().ToString(), FormatStateMessage(state)); control.Add(vChecks); @@ -1777,7 +1900,7 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState & nLastSetChain = nNow; } int64_t nMempoolSizeMax = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; - int64_t cacheSize = pcoinsTip->DynamicMemoryUsage() * DB_PEAK_USAGE_FACTOR; + int64_t cacheSize = pcoinsTip->DynamicMemoryUsage(); int64_t nTotalSpace = nCoinCacheUsage + std::max<int64_t>(nMempoolSizeMax - nMempoolUsage, 0); // The cache is large and we're within 10% and 10 MiB of the limit, but we have time now (not in the middle of a block processing). bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize > std::max((9 * nTotalSpace) / 10, nTotalSpace - MAX_BLOCK_COINSDB_USAGE * 1024 * 1024); @@ -1944,6 +2067,7 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara int64_t nStart = GetTimeMicros(); { CCoinsViewCache view(pcoinsTip); + assert(view.GetBestBlock() == pindexDelete->GetBlockHash()); if (DisconnectBlock(block, pindexDelete, view) != DISCONNECT_OK) return error("DisconnectTip(): DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString()); bool flushed = view.Flush(); @@ -3323,7 +3447,7 @@ CBlockIndex * InsertBlockIndex(uint256 hash) bool static LoadBlockIndexDB(const CChainParams& chainparams) { - if (!pblocktree->LoadBlockIndexGuts(InsertBlockIndex)) + if (!pblocktree->LoadBlockIndexGuts(chainparams.GetConsensus(), InsertBlockIndex)) return false; boost::this_thread::interruption_point(); @@ -3415,20 +3539,25 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) pblocktree->ReadFlag("txindex", fTxIndex); LogPrintf("%s: transaction index %s\n", __func__, fTxIndex ? "enabled" : "disabled"); + return true; +} + +void LoadChainTip(const CChainParams& chainparams) +{ + if (chainActive.Tip() && chainActive.Tip()->GetBlockHash() == pcoinsTip->GetBestBlock()) return; + // Load pointer to end of best chain BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); if (it == mapBlockIndex.end()) - return true; + return; chainActive.SetTip(it->second); PruneBlockIndexCandidates(); - LogPrintf("%s: hashBestChain=%s height=%d date=%s progress=%f\n", __func__, + LogPrintf("Loaded best chain: hashBestChain=%s height=%d date=%s progress=%f\n", chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), GuessVerificationProgress(chainparams.TxData(), chainActive.Tip())); - - return true; } CVerifyDB::CVerifyDB() @@ -3497,6 +3626,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, } // check level 3: check for inconsistencies during memory-only disconnect of tip blocks if (nCheckLevel >= 3 && pindex == pindexState && (coins.DynamicMemoryUsage() + pcoinsTip->DynamicMemoryUsage()) <= nCoinCacheUsage) { + assert(coins.GetBestBlock() == pindex->GetBlockHash()); DisconnectResult res = DisconnectBlock(block, pindex, coins); if (res == DISCONNECT_FAILED) { return error("VerifyDB(): *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); @@ -3536,6 +3666,92 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, return true; } +/** Apply the effects of a block on the utxo cache, ignoring that it may already have been applied. */ +static bool RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params) +{ + // TODO: merge with ConnectBlock + CBlock block; + if (!ReadBlockFromDisk(block, pindex, params.GetConsensus())) { + return error("ReplayBlock(): ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); + } + + for (const CTransactionRef& tx : block.vtx) { + if (!tx->IsCoinBase()) { + for (const CTxIn &txin : tx->vin) { + inputs.SpendCoin(txin.prevout); + } + } + // Pass check = true as every addition may be an overwrite. + AddCoins(inputs, *tx, pindex->nHeight, true); + } + return true; +} + +bool ReplayBlocks(const CChainParams& params, CCoinsView* view) +{ + LOCK(cs_main); + + CCoinsViewCache cache(view); + + std::vector<uint256> hashHeads = view->GetHeadBlocks(); + if (hashHeads.empty()) return true; // We're already in a consistent state. + if (hashHeads.size() != 2) return error("ReplayBlocks(): unknown inconsistent state"); + + uiInterface.ShowProgress(_("Replaying blocks..."), 0); + LogPrintf("Replaying blocks\n"); + + const CBlockIndex* pindexOld = nullptr; // Old tip during the interrupted flush. + const CBlockIndex* pindexNew; // New tip during the interrupted flush. + const CBlockIndex* pindexFork = nullptr; // Latest block common to both the old and the new tip. + + if (mapBlockIndex.count(hashHeads[0]) == 0) { + return error("ReplayBlocks(): reorganization to unknown block requested"); + } + pindexNew = mapBlockIndex[hashHeads[0]]; + + if (!hashHeads[1].IsNull()) { // The old tip is allowed to be 0, indicating it's the first flush. + if (mapBlockIndex.count(hashHeads[1]) == 0) { + return error("ReplayBlocks(): reorganization from unknown block requested"); + } + pindexOld = mapBlockIndex[hashHeads[1]]; + pindexFork = LastCommonAncestor(pindexOld, pindexNew); + assert(pindexFork != nullptr); + } + + // Rollback along the old branch. + while (pindexOld != pindexFork) { + if (pindexOld->nHeight > 0) { // Never disconnect the genesis block. + CBlock block; + if (!ReadBlockFromDisk(block, pindexOld, params.GetConsensus())) { + return error("RollbackBlock(): ReadBlockFromDisk() failed at %d, hash=%s", pindexOld->nHeight, pindexOld->GetBlockHash().ToString()); + } + LogPrintf("Rolling back %s (%i)\n", pindexOld->GetBlockHash().ToString(), pindexOld->nHeight); + DisconnectResult res = DisconnectBlock(block, pindexOld, cache); + if (res == DISCONNECT_FAILED) { + return error("RollbackBlock(): DisconnectBlock failed at %d, hash=%s", pindexOld->nHeight, pindexOld->GetBlockHash().ToString()); + } + // If DISCONNECT_UNCLEAN is returned, it means a non-existing UTXO was deleted, or an existing UTXO was + // overwritten. It corresponds to cases where the block-to-be-disconnect never had all its operations + // applied to the UTXO set. However, as both writing a UTXO and deleting a UTXO are idempotent operations, + // the result is still a version of the UTXO set with the effects of that block undone. + } + pindexOld = pindexOld->pprev; + } + + // Roll forward from the forking point to the new tip. + int nForkHeight = pindexFork ? pindexFork->nHeight : 0; + for (int nHeight = nForkHeight + 1; nHeight <= pindexNew->nHeight; ++nHeight) { + const CBlockIndex* pindex = pindexNew->GetAncestor(nHeight); + LogPrintf("Rolling forward %s (%i)\n", pindex->GetBlockHash().ToString(), nHeight); + if (!RollforwardBlock(pindex, cache, params)) return false; + } + + cache.SetBestBlock(pindexNew->GetBlockHash()); + cache.Flush(); + uiInterface.ShowProgress("", 100); + return true; +} + bool RewindBlockIndex(const CChainParams& params) { LOCK(cs_main); @@ -3685,8 +3901,6 @@ bool InitBlockIndex(const CChainParams& chainparams) CBlockIndex *pindex = AddToBlockIndex(block); if (!ReceivedBlockTransactions(block, state, pindex, blockPos, chainparams.GetConsensus())) return error("LoadBlockIndex(): genesis block not accepted"); - // Force a chainstate write so that when we VerifyDB in a moment, it doesn't check stale data - return FlushStateToDisk(chainparams, state, FLUSH_STATE_ALWAYS); } catch (const std::runtime_error& e) { return error("LoadBlockIndex(): failed to initialize block database: %s", e.what()); } diff --git a/src/validation.h b/src/validation.h index 15e19bc511..a9f995abb8 100644 --- a/src/validation.h +++ b/src/validation.h @@ -260,6 +260,8 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB bool InitBlockIndex(const CChainParams& chainparams); /** Load the block tree and coins database from disk */ bool LoadBlockIndex(const CChainParams& chainparams); +/** Update the chain tip based on database information. */ +void LoadChainTip(const CChainParams& chainparams); /** Unload database information */ void UnloadBlockIndex(); /** Run an instance of the script checking thread */ @@ -391,6 +393,9 @@ public: ScriptError GetScriptError() const { return error; } }; +/** Initializes the script-execution cache */ +void InitScriptExecutionCache(); + /** Functions for disk access for blocks */ bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams); @@ -424,6 +429,9 @@ public: bool VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth); }; +/** Replay blocks that aren't fully applied to the database. */ +bool ReplayBlocks(const CChainParams& params, CCoinsView* view); + /** Find the last common block between the parameter chain and a locator. */ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator); @@ -469,10 +477,6 @@ int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Para static const unsigned int REJECT_INTERNAL = 0x100; /** Too high fee. Can not be triggered by P2P transactions */ static const unsigned int REJECT_HIGHFEE = 0x100; -/** Transaction is already known (either in mempool or blockchain) */ -static const unsigned int REJECT_ALREADY_KNOWN = 0x101; -/** Transaction conflicts with a transaction already known */ -static const unsigned int REJECT_CONFLICT = 0x102; /** Get block file info entry for one block file */ CBlockFileInfo* GetBlockFileInfo(size_t n); diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index 46d7c9b329..be2f20b863 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -21,12 +21,10 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) { g_signals.Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1)); g_signals.Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2)); g_signals.BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2)); - g_signals.ScriptForMining.connect(boost::bind(&CValidationInterface::GetScriptForMining, pwalletIn, _1)); g_signals.NewPoWValidBlock.connect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2)); } void UnregisterValidationInterface(CValidationInterface* pwalletIn) { - g_signals.ScriptForMining.disconnect(boost::bind(&CValidationInterface::GetScriptForMining, pwalletIn, _1)); g_signals.BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2)); g_signals.Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2)); g_signals.Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1)); @@ -39,7 +37,6 @@ void UnregisterValidationInterface(CValidationInterface* pwalletIn) { } void UnregisterAllValidationInterfaces() { - g_signals.ScriptForMining.disconnect_all_slots(); g_signals.BlockChecked.disconnect_all_slots(); g_signals.Broadcast.disconnect_all_slots(); g_signals.Inventory.disconnect_all_slots(); diff --git a/src/validationinterface.h b/src/validationinterface.h index 460aecf243..17545018df 100644 --- a/src/validationinterface.h +++ b/src/validationinterface.h @@ -40,7 +40,6 @@ protected: virtual void Inventory(const uint256 &hash) {} virtual void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) {} virtual void BlockChecked(const CBlock&, const CValidationState&) {} - virtual void GetScriptForMining(std::shared_ptr<CReserveScript>&) {}; virtual void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& block) {}; friend void ::RegisterValidationInterface(CValidationInterface*); friend void ::UnregisterValidationInterface(CValidationInterface*); @@ -72,8 +71,6 @@ struct CMainSignals { * callback was generated (not necessarily now) */ boost::signals2::signal<void (const CBlock&, const CValidationState&)> BlockChecked; - /** Notifies listeners that a key for mining is required (coinbase) */ - boost::signals2::signal<void (std::shared_ptr<CReserveScript>&)> ScriptForMining; /** * Notifies listeners that a block which builds directly on our current tip * has been received and connected to the headers tree, though not validated yet */ diff --git a/src/versionbits.cpp b/src/versionbits.cpp index 4bb352f23c..8047e17aa8 100644 --- a/src/versionbits.cpp +++ b/src/versionbits.cpp @@ -174,12 +174,12 @@ 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; } + int64_t BeginTime(const Consensus::Params& params) const override { return params.vDeployments[id].nStartTime; } + int64_t EndTime(const Consensus::Params& params) const override { return params.vDeployments[id].nTimeout; } + int Period(const Consensus::Params& params) const override { return params.nMinerConfirmationWindow; } + int Threshold(const Consensus::Params& params) const override { return params.nRuleChangeActivationThreshold; } - bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const + bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override { return (((pindex->nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) && (pindex->nVersion & Mask(params)) != 0); } @@ -189,7 +189,7 @@ public: uint32_t Mask(const Consensus::Params& params) const { return ((uint32_t)1) << params.vDeployments[id].bit; } }; -} +} // namespace ThresholdState VersionBitsState(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache) { diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp index 33be47273f..dcce88cedc 100644 --- a/src/wallet/crypter.cpp +++ b/src/wallet/crypter.cpp @@ -273,7 +273,6 @@ bool CCryptoKeyStore::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) co // Check for watch-only pubkeys return CBasicKeyStore::GetPubKey(address, vchPubKeyOut); } - return false; } bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn) diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h index f1c4f57428..1dc44e424f 100644 --- a/src/wallet/crypter.h +++ b/src/wallet/crypter.h @@ -157,8 +157,8 @@ public: bool Lock(); virtual bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret); - bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey); - bool HaveKey(const CKeyID &address) const + bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override; + bool HaveKey(const CKeyID &address) const override { { LOCK(cs_KeyStore); @@ -168,9 +168,9 @@ public: } return false; } - bool GetKey(const CKeyID &address, CKey& keyOut) const; - bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const; - void GetKeys(std::set<CKeyID> &setAddress) const + bool GetKey(const CKeyID &address, CKey& keyOut) const override; + bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override; + void GetKeys(std::set<CKeyID> &setAddress) const override { if (!IsCrypted()) { diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 3f495f23b9..da2d180756 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -73,7 +73,7 @@ bool CDBEnv::Open(const fs::path& pathIn) strPath = pathIn.string(); fs::path pathLogDir = pathIn / "database"; - TryCreateDirectory(pathLogDir); + TryCreateDirectories(pathLogDir); fs::path pathErrorFile = pathIn / "db.log"; LogPrintf("CDBEnv::Open: LogDir=%s ErrorFile=%s\n", pathLogDir.string(), pathErrorFile.string()); diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index 46ef87b7b1..6a9e6cf9ff 100644 --- a/src/wallet/feebumper.cpp +++ b/src/wallet/feebumper.cpp @@ -165,7 +165,7 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, int newConf nNewFee = totalFee; nNewFeeRate = CFeeRate(totalFee, maxNewTxSize); } else { - nNewFee = CWallet::GetMinimumFee(maxNewTxSize, newConfirmTarget, mempool, ::feeEstimator, ignoreGlobalPayTxFee); + nNewFee = CWallet::GetMinimumFee(maxNewTxSize, newConfirmTarget, mempool, ::feeEstimator, nullptr, ignoreGlobalPayTxFee); nNewFeeRate = CFeeRate(nNewFee, maxNewTxSize); // New fee rate must be at least old rate + minimum incremental relay rate diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 7627d625d0..9f42b1f266 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -147,7 +147,7 @@ UniValue importprivkey(const JSONRPCRequest& request) pwallet->UpdateTimeFirstKey(1); if (fRescan) { - pwallet->ScanForWalletTransactions(chainActive.Genesis(), true); + pwallet->RescanFromTime(TIMESTAMP_MIN, true /* update */); } } @@ -277,7 +277,7 @@ UniValue importaddress(const JSONRPCRequest& request) if (fRescan) { - pwallet->ScanForWalletTransactions(chainActive.Genesis(), true); + pwallet->RescanFromTime(TIMESTAMP_MIN, true /* update */); pwallet->ReacceptWalletTransactions(); } @@ -361,7 +361,7 @@ UniValue removeprunedfunds(const JSONRPCRequest& request) "\nExamples:\n" + HelpExampleCli("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"") + "\nAs a JSON-RPC call\n" - + HelpExampleRpc("removprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"") + + HelpExampleRpc("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"") ); LOCK2(cs_main, pwallet->cs_wallet); @@ -435,7 +435,7 @@ UniValue importpubkey(const JSONRPCRequest& request) if (fRescan) { - pwallet->ScanForWalletTransactions(chainActive.Genesis(), true); + pwallet->RescanFromTime(TIMESTAMP_MIN, true /* update */); pwallet->ReacceptWalletTransactions(); } @@ -535,11 +535,7 @@ UniValue importwallet(const JSONRPCRequest& request) file.close(); pwallet->ShowProgress("", 100); // hide progress dialog in GUI pwallet->UpdateTimeFirstKey(nTimeBegin); - - CBlockIndex *pindex = chainActive.FindEarliestAtLeast(nTimeBegin - TIMESTAMP_WINDOW); - - LogPrintf("Rescanning last %i blocks\n", pindex ? chainActive.Height() - pindex->nHeight + 1 : 0); - pwallet->ScanForWalletTransactions(pindex); + pwallet->RescanFromTime(nTimeBegin, false /* update */); pwallet->MarkDirty(); if (!fGood) @@ -1048,7 +1044,7 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) " \"redeemscript\": \"<script>\" , (string, optional) Allowed only if the scriptPubKey is a P2SH address or a P2SH scriptPubKey\n" " \"pubkeys\": [\"<pubKey>\", ... ] , (array, optional) Array of strings giving pubkeys that must occur in the output or redeemscript\n" " \"keys\": [\"<key>\", ... ] , (array, optional) Array of strings giving private keys whose corresponding public keys must occur in the output or redeemscript\n" - " \"internal\": <true> , (boolean, optional, default: false) Stating whether matching outputs should be be treated as not incoming payments\n" + " \"internal\": <true> , (boolean, optional, default: false) Stating whether matching outputs should be treated as not incoming payments\n" " \"watchonly\": <true> , (boolean, optional, default: false) Stating whether matching outputs should be considered watched even when they're not spendable, only allowed if keys are empty\n" " \"label\": <label> , (string, optional, default: '') Label to assign to the address (aka account name, for now), only allowed with internal=false\n" " }\n" @@ -1125,14 +1121,10 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) } if (fRescan && fRunScan && requests.size()) { - CBlockIndex* pindex = nLowestTimestamp > minimumTimestamp ? chainActive.FindEarliestAtLeast(std::max<int64_t>(nLowestTimestamp - TIMESTAMP_WINDOW, 0)) : chainActive.Genesis(); - CBlockIndex* scanFailed = nullptr; - if (pindex) { - scanFailed = pwallet->ScanForWalletTransactions(pindex, true); - pwallet->ReacceptWalletTransactions(); - } + int64_t scannedTime = pwallet->RescanFromTime(nLowestTimestamp, true /* update */); + pwallet->ReacceptWalletTransactions(); - if (scanFailed) { + if (scannedTime > nLowestTimestamp) { std::vector<UniValue> results = response.getValues(); response.clear(); response.setArray(); @@ -1142,7 +1134,7 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) // range, or if the import result already has an error set, let // the result stand unmodified. Otherwise replace the result // with an error message. - if (GetImportTimestamp(request, now) - TIMESTAMP_WINDOW > scanFailed->GetBlockTimeMax() || results.at(i).exists("error")) { + if (scannedTime <= GetImportTimestamp(request, now) || results.at(i).exists("error")) { response.push_back(results.at(i)); } else { UniValue result = UniValue(UniValue::VOBJ); @@ -1158,7 +1150,7 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) "caused by pruning or data corruption (see bitcoind log for details) and could " "be dealt with by downloading and rescanning the relevant blocks (see -reindex " "and -rescan options).", - GetImportTimestamp(request, now), scanFailed->GetBlockTimeMax(), TIMESTAMP_WINDOW))); + GetImportTimestamp(request, now), scannedTime - TIMESTAMP_WINDOW - 1, TIMESTAMP_WINDOW))); response.push_back(std::move(result)); } ++i; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 2e4105a569..e0c7ab9f0f 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -15,6 +15,7 @@ #include "policy/fees.h" #include "policy/policy.h" #include "policy/rbf.h" +#include "rpc/mining.h" #include "rpc/server.h" #include "script/sign.h" #include "timedata.h" @@ -1064,7 +1065,6 @@ public: bool operator()(const CNoDestination &dest) const { return false; } bool operator()(const CKeyID &keyID) { - CPubKey pubkey; if (pwallet) { CScript basescript = GetScriptForDestination(keyID); isminetype typ; @@ -2923,6 +2923,51 @@ UniValue bumpfee(const JSONRPCRequest& request) return result; } +UniValue generate(const JSONRPCRequest& request) +{ + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { + return NullUniValue; + } + + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { + throw std::runtime_error( + "generate nblocks ( maxtries )\n" + "\nMine up to nblocks blocks immediately (before the RPC call returns) to an address in the wallet.\n" + "\nArguments:\n" + "1. nblocks (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 num_generate = request.params[0].get_int(); + uint64_t max_tries = 1000000; + if (request.params.size() > 1 && !request.params[1].isNull()) { + max_tries = request.params[1].get_int(); + } + + std::shared_ptr<CReserveScript> coinbase_script; + pwallet->GetScriptForMining(coinbase_script); + + // If the keypool is exhausted, no script is returned at all. Catch this. + if (!coinbase_script) { + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); + } + + //throw an error if no script was provided + if (coinbase_script->reserveScript.empty()) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "No coinbase script available"); + } + + return generateBlocks(coinbase_script, num_generate, max_tries, true); +} + extern UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp extern UniValue importprivkey(const JSONRPCRequest& request); @@ -2986,6 +3031,8 @@ static const CRPCCommand commands[] = { "wallet", "walletpassphrasechange", &walletpassphrasechange, true, {"oldpassphrase","newpassphrase"} }, { "wallet", "walletpassphrase", &walletpassphrase, true, {"passphrase","timeout"} }, { "wallet", "removeprunedfunds", &removeprunedfunds, true, {"txid"} }, + + { "generating", "generate", &generate, true, {"nblocks","maxtries"} }, }; void RegisterWalletRPCCommands(CRPCTable &t) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 02de3cceed..0d1a86dd24 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -205,7 +205,6 @@ bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]); } - return false; } bool CWallet::LoadKeyMetadata(const CTxDestination& keyID, const CKeyMetadata &meta) @@ -221,6 +220,10 @@ bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigne return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret); } +/** + * Update wallet first key creation time. This should be called whenever keys + * are added to the wallet, with the oldest key creation time. + */ void CWallet::UpdateTimeFirstKey(int64_t nCreateTime) { AssertLockHeld(cs_wallet); @@ -1469,6 +1472,34 @@ void CWalletTx::GetAmounts(std::list<COutputEntry>& listReceived, } /** + * Scan active chain for relevant transactions after importing keys. This should + * be called whenever new keys are added to the wallet, with the oldest key + * creation time. + * + * @return Earliest timestamp that could be successfully scanned from. Timestamp + * returned will be higher than startTime if relevant blocks could not be read. + */ +int64_t CWallet::RescanFromTime(int64_t startTime, bool update) +{ + AssertLockHeld(cs_main); + AssertLockHeld(cs_wallet); + + // Find starting block. May be null if nCreateTime is greater than the + // highest blockchain timestamp, in which case there is nothing that needs + // to be scanned. + CBlockIndex* const startBlock = chainActive.FindEarliestAtLeast(startTime - TIMESTAMP_WINDOW); + LogPrintf("%s: Rescanning last %i blocks\n", __func__, startBlock ? chainActive.Height() - startBlock->nHeight + 1 : 0); + + if (startBlock) { + const CBlockIndex* const failedBlock = ScanForWalletTransactions(startBlock, update); + if (failedBlock) { + return failedBlock->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1; + } + } + return startTime; +} + +/** * Scan the block chain (starting in pindexStart) for transactions * from or to us. If fUpdate is true, found transactions that already * exist in the wallet will be updated. @@ -1489,11 +1520,6 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool f fAbortRescan = false; fScanningWallet = true; - // no need to read and scan block, if block was created before - // our wallet birthday (as adjusted for block time variability) - while (pindex && nTimeFirstKey && (pindex->GetBlockTime() < (nTimeFirstKey - TIMESTAMP_WINDOW))) - pindex = chainActive.Next(pindex); - ShowProgress(_("Rescanning..."), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup double dProgressStart = GuessVerificationProgress(chainParams.TxData(), pindex); double dProgressTip = GuessVerificationProgress(chainParams.TxData(), chainActive.Tip()); @@ -2534,7 +2560,8 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); assert(txNew.nLockTime < LOCKTIME_THRESHOLD); - + FeeCalculation feeCalc; + unsigned int nBytes; { std::set<CInputCoin> setCoins; LOCK2(cs_main, cs_wallet); @@ -2633,28 +2660,6 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT CTxOut newTxOut(nChange, scriptChange); - // We do not move dust-change to fees, because the sender would end up paying more than requested. - // This would be against the purpose of the all-inclusive feature. - // So instead we raise the change and deduct from the recipient. - if (nSubtractFeeFromAmount > 0 && IsDust(newTxOut, ::dustRelayFee)) - { - CAmount nDust = GetDustThreshold(newTxOut, ::dustRelayFee) - newTxOut.nValue; - newTxOut.nValue += nDust; // raise change until no more dust - for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient - { - if (vecSend[i].fSubtractFeeFromAmount) - { - txNew.vout[i].nValue -= nDust; - if (IsDust(txNew.vout[i], ::dustRelayFee)) - { - strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); - return false; - } - break; - } - } - } - // Never create dust outputs; if we would, just // add the dust to the fee. if (IsDust(newTxOut, ::dustRelayFee)) @@ -2706,9 +2711,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT return false; } - unsigned int nBytes = GetVirtualTransactionSize(txNew); - - CTransaction txNewConst(txNew); + nBytes = GetVirtualTransactionSize(txNew); // Remove scriptSigs to eliminate the fee calculation dummy signatures for (auto& vin : txNew.vin) { @@ -2721,7 +2724,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT if (coinControl && coinControl->nConfirmTarget > 0) currentConfirmationTarget = coinControl->nConfirmTarget; - CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, ::mempool, ::feeEstimator); + CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, ::mempool, ::feeEstimator, &feeCalc); if (coinControl && coinControl->fOverrideFeeRate) nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes); @@ -2818,6 +2821,15 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT return false; } } + + LogPrintf("Fee Calculation: Fee:%d Bytes:%u Tgt:%d (requested %d) Reason:\"%s\" Decay %.5f: Estimation: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n", + nFeeRet, nBytes, feeCalc.returnedTarget, feeCalc.desiredTarget, StringForFeeReason(feeCalc.reason), feeCalc.est.decay, + feeCalc.est.pass.start, feeCalc.est.pass.end, + 100 * feeCalc.est.pass.withinTarget / (feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool), + feeCalc.est.pass.withinTarget, feeCalc.est.pass.totalConfirmed, feeCalc.est.pass.inMempool, feeCalc.est.pass.leftMempool, + feeCalc.est.fail.start, feeCalc.est.fail.end, + 100 * feeCalc.est.fail.withinTarget / (feeCalc.est.fail.totalConfirmed + feeCalc.est.fail.inMempool + feeCalc.est.fail.leftMempool), + feeCalc.est.fail.withinTarget, feeCalc.est.fail.totalConfirmed, feeCalc.est.fail.inMempool, feeCalc.est.fail.leftMempool); return true; } @@ -2893,23 +2905,32 @@ CAmount CWallet::GetRequiredFee(unsigned int nTxBytes) return std::max(minTxFee.GetFee(nTxBytes), ::minRelayTxFee.GetFee(nTxBytes)); } -CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, bool ignoreGlobalPayTxFee) +CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc, bool ignoreGlobalPayTxFee) { // payTxFee is the user-set global for desired feerate CAmount nFeeNeeded = payTxFee.GetFee(nTxBytes); // User didn't set: use -txconfirmtarget to estimate... if (nFeeNeeded == 0 || ignoreGlobalPayTxFee) { - int estimateFoundTarget = nConfirmTarget; - nFeeNeeded = estimator.estimateSmartFee(nConfirmTarget, &estimateFoundTarget, pool).GetFee(nTxBytes); + nFeeNeeded = estimator.estimateSmartFee(nConfirmTarget, feeCalc, pool, true).GetFee(nTxBytes); // ... unless we don't have enough mempool data for estimatefee, then use fallbackFee - if (nFeeNeeded == 0) + if (nFeeNeeded == 0) { nFeeNeeded = fallbackFee.GetFee(nTxBytes); + if (feeCalc) feeCalc->reason = FeeReason::FALLBACK; + } + } else { + if (feeCalc) feeCalc->reason = FeeReason::PAYTXFEE; } // prevent user from paying a fee below minRelayTxFee or minTxFee - nFeeNeeded = std::max(nFeeNeeded, GetRequiredFee(nTxBytes)); + CAmount requiredFee = GetRequiredFee(nTxBytes); + if (requiredFee > nFeeNeeded) { + nFeeNeeded = requiredFee; + if (feeCalc) feeCalc->reason = FeeReason::REQUIRED; + } // But always obey the maximum - if (nFeeNeeded > maxTxFee) + if (nFeeNeeded > maxTxFee) { nFeeNeeded = maxTxFee; + if (feeCalc) feeCalc->reason = FeeReason::MAXTXFEE; + } return nFeeNeeded; } @@ -3847,11 +3868,11 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) else if (IsArgSet("-usehd")) { bool useHD = GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET); if (walletInstance->IsHDEnabled() && !useHD) { - InitError(strprintf(_("Error loading %s: You can't disable HD on a already existing HD wallet"), walletFile)); + InitError(strprintf(_("Error loading %s: You can't disable HD on an already existing HD wallet"), walletFile)); return NULL; } if (!walletInstance->IsHDEnabled() && useHD) { - InitError(strprintf(_("Error loading %s: You can't enable HD on a already existing non-HD wallet"), walletFile)); + InitError(strprintf(_("Error loading %s: You can't enable HD on an already existing non-HD wallet"), walletFile)); return NULL; } } @@ -3871,7 +3892,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) if (chainActive.Tip() && chainActive.Tip() != pindexRescan) { //We can't rescan beyond non-pruned blocks, stop and throw an error - //this might happen if a user uses a old wallet within a pruned node + //this might happen if a user uses an old wallet within a pruned node // or if he ran -disablewallet for a longer time, then decided to re-enable if (fPruneMode) { @@ -3887,6 +3908,13 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) uiInterface.InitMessage(_("Rescanning...")); LogPrintf("Rescanning last %i blocks (from block %i)...\n", chainActive.Height() - pindexRescan->nHeight, pindexRescan->nHeight); + + // No need to read and scan block if block was created before + // our wallet birthday (as adjusted for block time variability) + while (pindexRescan && walletInstance->nTimeFirstKey && (pindexRescan->GetBlockTime() < (walletInstance->nTimeFirstKey - TIMESTAMP_WINDOW))) { + pindexRescan = chainActive.Next(pindexRescan); + } + nStart = GetTimeMillis(); walletInstance->ScanForWalletTransactions(pindexRescan, true); LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 6c6eb69180..4f558adc77 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -68,6 +68,8 @@ static const bool DEFAULT_USE_HD_WALLET = true; extern const char * DEFAULT_WALLET_DAT; +static const int64_t TIMESTAMP_MIN = 0; + class CBlockIndex; class CCoinControl; class COutput; @@ -77,6 +79,7 @@ class CScheduler; class CTxMemPool; class CBlockPolicyEstimator; class CWalletTx; +struct FeeCalculation; /** (client) version numbers for particular wallet features */ enum WalletFeature @@ -918,6 +921,7 @@ public: void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex *pindex, const std::vector<CTransactionRef>& vtxConflicted) override; void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) override; bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate); + int64_t RescanFromTime(int64_t startTime, bool update); CBlockIndex* ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); void ReacceptWalletTransactions(); void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override; @@ -959,7 +963,7 @@ public: * Estimate the minimum fee considering user set parameters * and the required fee */ - static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, bool ignoreGlobalPayTxFee = false); + static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc = nullptr, bool ignoreGlobalPayTxFee = false); /** * Return the minimum required fee taking into account the * floating relay fee and user set minimum transaction fee @@ -1021,7 +1025,7 @@ public: } } - void GetScriptForMining(std::shared_ptr<CReserveScript> &script) override; + void GetScriptForMining(std::shared_ptr<CReserveScript> &script); unsigned int GetKeyPoolSize() { @@ -1149,7 +1153,7 @@ public: void ReturnKey(); bool GetReservedKey(CPubKey &pubkey, bool internal = false); void KeepKey(); - void KeepScript() { KeepKey(); } + void KeepScript() override { KeepKey(); } }; diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index bf4798264c..deb09a4771 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -75,8 +75,6 @@ bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey, const std::vector<unsigned char>& vchCryptedSecret, const CKeyMetadata &keyMeta) { - const bool fEraseUnencryptedKey = true; - if (!WriteIC(std::make_pair(std::string("keymeta"), vchPubKey), keyMeta)) { return false; } @@ -84,12 +82,8 @@ bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey, if (!WriteIC(std::make_pair(std::string("ckey"), vchPubKey), vchCryptedSecret, false)) { return false; } - if (fEraseUnencryptedKey) - { - EraseIC(std::make_pair(std::string("key"), vchPubKey)); - EraseIC(std::make_pair(std::string("wkey"), vchPubKey)); - } - + EraseIC(std::make_pair(std::string("key"), vchPubKey)); + EraseIC(std::make_pair(std::string("wkey"), vchPubKey)); return true; } diff --git a/src/zmq/zmqpublishnotifier.h b/src/zmq/zmqpublishnotifier.h index bcbecf1bde..1790fe5698 100644 --- a/src/zmq/zmqpublishnotifier.h +++ b/src/zmq/zmqpublishnotifier.h @@ -24,32 +24,32 @@ public: */ bool SendMessage(const char *command, const void* data, size_t size); - bool Initialize(void *pcontext); - void Shutdown(); + bool Initialize(void *pcontext) override; + void Shutdown() override; }; class CZMQPublishHashBlockNotifier : public CZMQAbstractPublishNotifier { public: - bool NotifyBlock(const CBlockIndex *pindex); + bool NotifyBlock(const CBlockIndex *pindex) override; }; class CZMQPublishHashTransactionNotifier : public CZMQAbstractPublishNotifier { public: - bool NotifyTransaction(const CTransaction &transaction); + bool NotifyTransaction(const CTransaction &transaction) override; }; class CZMQPublishRawBlockNotifier : public CZMQAbstractPublishNotifier { public: - bool NotifyBlock(const CBlockIndex *pindex); + bool NotifyBlock(const CBlockIndex *pindex) override; }; class CZMQPublishRawTransactionNotifier : public CZMQAbstractPublishNotifier { public: - bool NotifyTransaction(const CTransaction &transaction); + bool NotifyTransaction(const CTransaction &transaction) override; }; #endif // BITCOIN_ZMQ_ZMQPUBLISHNOTIFIER_H |