diff options
Diffstat (limited to 'src')
116 files changed, 1711 insertions, 1100 deletions
diff --git a/src/.clang-format b/src/.clang-format index 5918819d13..2d2ee67035 100644 --- a/src/.clang-format +++ b/src/.clang-format @@ -23,7 +23,6 @@ ContinuationIndentWidth: 4 Cpp11BracedListStyle: true DerivePointerAlignment: false DisableFormat: false -ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH, BOOST_REVERSE_FOREACH ] IndentCaseLabels: false IndentFunctionDeclarationAfterType: false IndentWidth: 4 diff --git a/src/Makefile.am b/src/Makefile.am index 9b9dd89f68..06b09404a7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -124,9 +124,11 @@ BITCOIN_CORE_H = \ pow.h \ protocol.h \ random.h \ + reverse_iterator.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/Makefile.leveldb.include b/src/Makefile.leveldb.include index ac38141f43..833f3d2a10 100644 --- a/src/Makefile.leveldb.include +++ b/src/Makefile.leveldb.include @@ -142,7 +142,7 @@ leveldb_libmemenv_a_SOURCES += leveldb/helpers/memenv/memenv.h leveldb_libleveldb_sse42_a_CPPFLAGS = $(leveldb_libleveldb_a_CPPFLAGS) leveldb_libleveldb_sse42_a_CXXFLAGS = $(leveldb_libleveldb_a_CXXFLAGS) -if ENABLE_SSE42 +if ENABLE_HWCRC32 leveldb_libleveldb_sse42_a_CPPFLAGS += -DLEVELDB_PLATFORM_POSIX_SSE leveldb_libleveldb_sse42_a_CXXFLAGS += $(SSE42_CXXFLAGS) endif diff --git a/src/base58.cpp b/src/base58.cpp index efa1beb1e4..17022a6bc1 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -110,7 +110,7 @@ std::string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend) std::string EncodeBase58(const std::vector<unsigned char>& vch) { - return EncodeBase58(&vch[0], &vch[0] + vch.size()); + return EncodeBase58(vch.data(), vch.data() + vch.size()); } bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet) @@ -160,7 +160,7 @@ void CBase58Data::SetData(const std::vector<unsigned char>& vchVersionIn, const vchVersion = vchVersionIn; vchData.resize(nSize); if (!vchData.empty()) - memcpy(&vchData[0], pdata, nSize); + memcpy(vchData.data(), pdata, nSize); } void CBase58Data::SetData(const std::vector<unsigned char>& vchVersionIn, const unsigned char* pbegin, const unsigned char* pend) @@ -180,8 +180,8 @@ bool CBase58Data::SetString(const char* psz, unsigned int nVersionBytes) vchVersion.assign(vchTemp.begin(), vchTemp.begin() + nVersionBytes); vchData.resize(vchTemp.size() - nVersionBytes); if (!vchData.empty()) - memcpy(&vchData[0], &vchTemp[nVersionBytes], vchData.size()); - memory_cleanse(&vchTemp[0], vchTemp.size()); + memcpy(vchData.data(), vchTemp.data() + nVersionBytes, vchData.size()); + memory_cleanse(vchTemp.data(), vchTemp.size()); return true; } @@ -262,7 +262,7 @@ CTxDestination CBitcoinAddress::Get() const if (!IsValid()) return CNoDestination(); uint160 id; - memcpy(&id, &vchData[0], 20); + memcpy(&id, vchData.data(), 20); if (vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS)) return CKeyID(id); else if (vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS)) @@ -276,7 +276,7 @@ bool CBitcoinAddress::GetKeyID(CKeyID& keyID) const if (!IsValid() || vchVersion != Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS)) return false; uint160 id; - memcpy(&id, &vchData[0], 20); + memcpy(&id, vchData.data(), 20); keyID = CKeyID(id); return true; } diff --git a/src/base58.h b/src/base58.h index 3998283bb1..4de5cc6ce5 100644 --- a/src/base58.h +++ b/src/base58.h @@ -148,7 +148,7 @@ public: K ret; if (vchData.size() == Size) { // If base58 encoded data does not hold an ext key, return a !IsValid() key - ret.Decode(&vchData[0]); + ret.Decode(vchData.data()); } return ret; } diff --git a/src/bench/base58.cpp b/src/bench/base58.cpp index 3319c179bf..65e27a615d 100644 --- a/src/bench/base58.cpp +++ b/src/bench/base58.cpp @@ -7,34 +7,37 @@ #include "validation.h" #include "base58.h" +#include <array> #include <vector> #include <string> static void Base58Encode(benchmark::State& state) { - unsigned char buff[32] = { - 17, 79, 8, 99, 150, 189, 208, 162, 22, 23, 203, 163, 36, 58, 147, - 227, 139, 2, 215, 100, 91, 38, 11, 141, 253, 40, 117, 21, 16, 90, - 200, 24 + static const std::array<unsigned char, 32> buff = { + { + 17, 79, 8, 99, 150, 189, 208, 162, 22, 23, 203, 163, 36, 58, 147, + 227, 139, 2, 215, 100, 91, 38, 11, 141, 253, 40, 117, 21, 16, 90, + 200, 24 + } }; - unsigned char* b = buff; while (state.KeepRunning()) { - EncodeBase58(b, b + 32); + EncodeBase58(buff.begin(), buff.end()); } } static void Base58CheckEncode(benchmark::State& state) { - unsigned char buff[32] = { - 17, 79, 8, 99, 150, 189, 208, 162, 22, 23, 203, 163, 36, 58, 147, - 227, 139, 2, 215, 100, 91, 38, 11, 141, 253, 40, 117, 21, 16, 90, - 200, 24 + static const std::array<unsigned char, 32> buff = { + { + 17, 79, 8, 99, 150, 189, 208, 162, 22, 23, 203, 163, 36, 58, 147, + 227, 139, 2, 215, 100, 91, 38, 11, 141, 253, 40, 117, 21, 16, 90, + 200, 24 + } }; - unsigned char* b = buff; std::vector<unsigned char> vch; - vch.assign(b, b + 32); + vch.assign(buff.begin(), buff.end()); while (state.KeepRunning()) { EncodeBase58Check(vch); } diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp index 942942c299..f8956508f6 100644 --- a/src/bench/coin_selection.cpp +++ b/src/bench/coin_selection.cpp @@ -5,7 +5,6 @@ #include "bench.h" #include "wallet/wallet.h" -#include <boost/foreach.hpp> #include <set> static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<COutput>& vCoins) diff --git a/src/bench/verify_script.cpp b/src/bench/verify_script.cpp index 23bbadc88d..ef7381c120 100644 --- a/src/bench/verify_script.cpp +++ b/src/bench/verify_script.cpp @@ -11,6 +11,8 @@ #include "script/sign.h" #include "streams.h" +#include <array> + // FIXME: Dedup with BuildCreditingTransaction in test/script_tests.cpp. static CMutableTransaction BuildCreditingTransaction(const CScript& scriptPubKey) { @@ -55,8 +57,12 @@ static void VerifyScriptBench(benchmark::State& state) // Keypair. CKey key; - const unsigned char vchKey[32] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; - key.Set(vchKey, vchKey + 32, false); + static const std::array<unsigned char, 32> vchKey = { + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 + } + }; + key.Set(vchKey.begin(), vchKey.end(), false); CPubKey pubkey = key.GetPubKey(); uint160 pubkeyHash; CHash160().Write(pubkey.begin(), pubkey.size()).Finalize(pubkeyHash.begin()); diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 885b787b4d..8b48c7f8e4 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -191,8 +191,14 @@ static void http_error_cb(enum evhttp_request_error err, void *ctx) UniValue CallRPC(const std::string& strMethod, const UniValue& params) { - std::string host = GetArg("-rpcconnect", DEFAULT_RPCCONNECT); - int port = GetArg("-rpcport", BaseParams().RPCPort()); + std::string host; + // In preference order, we choose the following for the port: + // 1. -rpcport + // 2. port in -rpcconnect (ie following : in ipv4 or ]: in ipv6) + // 3. default port for chain + int port = BaseParams().RPCPort(); + SplitHostPort(GetArg("-rpcconnect", DEFAULT_RPCCONNECT), port, host); + port = GetArg("-rpcport", port); // Obtain event base raii_event_base base = obtain_event_base(); diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 6093f78fb1..9acb3fd30e 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -77,7 +77,7 @@ static int AppInitRawTx(int argc, char* argv[]) strUsage += HelpMessageOpt("in=TXID:VOUT(:SEQUENCE_NUMBER)", _("Add input to TX")); strUsage += HelpMessageOpt("locktime=N", _("Set TX lock time to N")); strUsage += HelpMessageOpt("nversion=N", _("Set TX version to N")); - strUsage += HelpMessageOpt("rbfoptin(=N)", _("Set RBF opt-in sequence number for input N (if not provided, opt-in all available inputs)")); + strUsage += HelpMessageOpt("replaceable(=N)", _("Set RBF opt-in sequence number for input N (if not provided, opt-in all available inputs)")); strUsage += HelpMessageOpt("outaddr=VALUE:ADDRESS", _("Add address-based output to TX")); strUsage += HelpMessageOpt("outpubkey=VALUE:PUBKEY[:FLAGS]", _("Add pay-to-pubkey output to TX") + ". " + _("Optionally add the \"W\" flag to produce a pay-to-witness-pubkey-hash output") + ". " + @@ -239,7 +239,7 @@ static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInpu uint256 txid(uint256S(strTxid)); static const unsigned int minTxOutSz = 9; - static const unsigned int maxVout = MAX_BLOCK_BASE_SIZE / minTxOutSz; + static const unsigned int maxVout = MAX_BLOCK_WEIGHT / (WITNESS_SCALE_FACTOR * minTxOutSz); // extract and validate vout std::string strVout = vStrInputParts[1]; @@ -673,7 +673,7 @@ static void MutateTx(CMutableTransaction& tx, const std::string& command, MutateTxVersion(tx, commandVal); else if (command == "locktime") MutateTxLocktime(tx, commandVal); - else if (command == "rbfoptin") { + else if (command == "replaceable") { MutateTxRBFOptIn(tx, commandVal); } diff --git a/src/blockencodings.cpp b/src/blockencodings.cpp index 9cac10d2b8..6f27b7b9dc 100644 --- a/src/blockencodings.cpp +++ b/src/blockencodings.cpp @@ -15,8 +15,6 @@ #include <unordered_map> -#define MIN_TRANSACTION_BASE_SIZE (::GetSerializeSize(CTransaction(), SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS)) - CBlockHeaderAndShortTxIDs::CBlockHeaderAndShortTxIDs(const CBlock& block, bool fUseWTXID) : nonce(GetRand(std::numeric_limits<uint64_t>::max())), shorttxids(block.vtx.size() - 1), prefilledtxn(1), header(block) { @@ -50,7 +48,7 @@ uint64_t CBlockHeaderAndShortTxIDs::GetShortID(const uint256& txhash) const { ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& cmpctblock, const std::vector<std::pair<uint256, CTransactionRef>>& extra_txn) { if (cmpctblock.header.IsNull() || (cmpctblock.shorttxids.empty() && cmpctblock.prefilledtxn.empty())) return READ_STATUS_INVALID; - if (cmpctblock.shorttxids.size() + cmpctblock.prefilledtxn.size() > MAX_BLOCK_BASE_SIZE / MIN_TRANSACTION_BASE_SIZE) + if (cmpctblock.shorttxids.size() + cmpctblock.prefilledtxn.size() > MAX_BLOCK_WEIGHT / MIN_SERIALIZABLE_TRANSACTION_WEIGHT) return READ_STATUS_INVALID; assert(header.IsNull() && txn_available.empty()); diff --git a/src/bloom.cpp b/src/bloom.cpp index cc3baa9185..fa884f0bf3 100644 --- a/src/bloom.cpp +++ b/src/bloom.cpp @@ -14,7 +14,6 @@ #include <math.h> #include <stdlib.h> -#include <boost/foreach.hpp> #define LN2SQUARED 0.4804530139182014246671025263266649717305529515945455 #define LN2 0.6931471805599453094172321214581765680755001343602552 diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index 13b5876530..e6b5fb72a7 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -6,12 +6,12 @@ #include "chain.h" #include "chainparams.h" +#include "reverse_iterator.h" #include "validation.h" #include "uint256.h" #include <stdint.h> -#include <boost/foreach.hpp> namespace Checkpoints { @@ -19,7 +19,7 @@ namespace Checkpoints { { const MapCheckpoints& checkpoints = data.mapCheckpoints; - BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, checkpoints) + for (const MapCheckpoints::value_type& i : reverse_iterate(checkpoints)) { const uint256& hash = i.second; BlockMap::const_iterator t = mapBlockIndex.find(hash); diff --git a/src/checkqueue.h b/src/checkqueue.h index d7b7b836dc..408e278d21 100644 --- a/src/checkqueue.h +++ b/src/checkqueue.h @@ -10,7 +10,6 @@ #include <algorithm> #include <vector> -#include <boost/foreach.hpp> #include <boost/thread/condition_variable.hpp> #include <boost/thread/mutex.hpp> diff --git a/src/coins.cpp b/src/coins.cpp index b5dc6197bd..e30bda930a 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -245,7 +245,8 @@ bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const return true; } -static const size_t MAX_OUTPUTS_PER_BLOCK = MAX_BLOCK_BASE_SIZE / ::GetSerializeSize(CTxOut(), SER_NETWORK, PROTOCOL_VERSION); // TODO: merge with similar definition in undo.h. +static const size_t MIN_TRANSACTION_OUTPUT_WEIGHT = WITNESS_SCALE_FACTOR * ::GetSerializeSize(CTxOut(), SER_NETWORK, PROTOCOL_VERSION); +static const size_t MAX_OUTPUTS_PER_BLOCK = MAX_BLOCK_WEIGHT / MIN_TRANSACTION_OUTPUT_WEIGHT; const Coin& AccessByTxid(const CCoinsViewCache& view, const uint256& txid) { diff --git a/src/compressor.cpp b/src/compressor.cpp index 20c154fc1e..f4c12f38d2 100644 --- a/src/compressor.cpp +++ b/src/compressor.cpp @@ -93,7 +93,7 @@ bool CScriptCompressor::Decompress(unsigned int nSize, const std::vector<unsigne script[0] = OP_DUP; script[1] = OP_HASH160; script[2] = 20; - memcpy(&script[3], &in[0], 20); + memcpy(&script[3], in.data(), 20); script[23] = OP_EQUALVERIFY; script[24] = OP_CHECKSIG; return true; @@ -101,7 +101,7 @@ bool CScriptCompressor::Decompress(unsigned int nSize, const std::vector<unsigne script.resize(23); script[0] = OP_HASH160; script[1] = 20; - memcpy(&script[2], &in[0], 20); + memcpy(&script[2], in.data(), 20); script[22] = OP_EQUAL; return true; case 0x02: @@ -109,14 +109,14 @@ bool CScriptCompressor::Decompress(unsigned int nSize, const std::vector<unsigne script.resize(35); script[0] = 33; script[1] = nSize; - memcpy(&script[2], &in[0], 32); + memcpy(&script[2], in.data(), 32); script[34] = OP_CHECKSIG; return true; case 0x04: case 0x05: unsigned char vch[33] = {}; vch[0] = nSize - 2; - memcpy(&vch[1], &in[0], 32); + memcpy(&vch[1], in.data(), 32); CPubKey pubkey(&vch[0], &vch[33]); if (!pubkey.Decompress()) return false; diff --git a/src/consensus/consensus.h b/src/consensus/consensus.h index 58b2ed4b3e..ddd4ee9fab 100644 --- a/src/consensus/consensus.h +++ b/src/consensus/consensus.h @@ -6,25 +6,23 @@ #ifndef BITCOIN_CONSENSUS_CONSENSUS_H #define BITCOIN_CONSENSUS_CONSENSUS_H +#include <stdlib.h> #include <stdint.h> /** The maximum allowed size for a serialized block, in bytes (only for buffer size limits) */ 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). - * 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; /** Coinbase transaction outputs can only be spent after this number of new blocks (network rule) */ static const int COINBASE_MATURITY = 100; +static const int WITNESS_SCALE_FACTOR = 4; + +static const size_t MIN_TRANSACTION_WEIGHT = WITNESS_SCALE_FACTOR * 60; // 60 is the lower bound for the size of a valid serialized CTransaction +static const size_t MIN_SERIALIZABLE_TRANSACTION_WEIGHT = WITNESS_SCALE_FACTOR * 10; // 10 is the lower bound for the size of a serialized CTransaction + /** Flags for nSequence and nLockTime locks */ enum { /* Interpret sequence numbers as relative lock-time constraints. */ diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp index 0671cbc132..0a71915d1d 100644 --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -164,7 +164,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe if (tx.vout.empty()) return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty"); // Size limits (this doesn't take the witness into account, as that hasn't been checked for malleability) - if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) > MAX_BLOCK_BASE_SIZE) + if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT) return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize"); // Check for negative or overflow output values diff --git a/src/consensus/validation.h b/src/consensus/validation.h index 8fc3ef1b66..5494ce40ea 100644 --- a/src/consensus/validation.h +++ b/src/consensus/validation.h @@ -7,6 +7,10 @@ #define BITCOIN_CONSENSUS_VALIDATION_H #include <string> +#include "version.h" +#include "consensus/consensus.h" +#include "primitives/transaction.h" +#include "primitives/block.h" /** "reject" message codes */ static const unsigned char REJECT_MALFORMED = 0x01; @@ -85,4 +89,18 @@ public: std::string GetDebugMessage() const { return strDebugMessage; } }; +static inline int64_t GetTransactionWeight(const CTransaction& tx) +{ + return ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR -1) + ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); +} + +static inline int64_t GetBlockWeight(const CBlock& block) +{ + // This implements the weight = (stripped_size * 4) + witness_size formula, + // using only serialization with and without witness data. As witness_size + // is equal to total_size - stripped_size, this formula is identical to: + // weight = (stripped_size * 3) + total_size. + return ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION); +} + #endif // BITCOIN_CONSENSUS_VALIDATION_H diff --git a/src/core_memusage.h b/src/core_memusage.h index e4ccd54c42..f038e7b154 100644 --- a/src/core_memusage.h +++ b/src/core_memusage.h @@ -10,7 +10,7 @@ #include "memusage.h" static inline size_t RecursiveDynamicUsage(const CScript& script) { - return memusage::DynamicUsage(*static_cast<const CScriptBase*>(&script)); + return memusage::DynamicUsage(script); } static inline size_t RecursiveDynamicUsage(const COutPoint& out) { diff --git a/src/core_read.cpp b/src/core_read.cpp index dd9b5726a3..7018131a13 100644 --- a/src/core_read.cpp +++ b/src/core_read.cpp @@ -27,7 +27,7 @@ CScript ParseScript(const std::string& s) if (mapOpNames.empty()) { - for (int op = 0; op <= OP_NOP10; op++) + for (unsigned int op = 0; op <= MAX_OPCODE; op++) { // Allow OP_RESERVED to get into mapOpNames if (op < OP_NOP && op != OP_RESERVED) diff --git a/src/core_write.cpp b/src/core_write.cpp index 553ef44874..a366ef933c 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -5,7 +5,8 @@ #include "core_io.h" #include "base58.h" -#include "primitives/transaction.h" +#include "consensus/consensus.h" +#include "consensus/validation.h" #include "script/script.h" #include "script/standard.h" #include "serialize.h" @@ -15,8 +16,6 @@ #include "utilmoneystr.h" #include "utilstrencodings.h" -#include <boost/foreach.hpp> - std::string FormatScript(const CScript& script) { std::string ret; diff --git a/src/hash.cpp b/src/hash.cpp index b361c90d16..5a15600be5 100644 --- a/src/hash.cpp +++ b/src/hash.cpp @@ -17,36 +17,34 @@ unsigned int MurmurHash3(unsigned int nHashSeed, const std::vector<unsigned char { // The following is MurmurHash3 (x86_32), see http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp uint32_t h1 = nHashSeed; - if (vDataToHash.size() > 0) - { - const uint32_t c1 = 0xcc9e2d51; - const uint32_t c2 = 0x1b873593; + const uint32_t c1 = 0xcc9e2d51; + const uint32_t c2 = 0x1b873593; - const int nblocks = vDataToHash.size() / 4; + const int nblocks = vDataToHash.size() / 4; - //---------- - // body - const uint8_t* blocks = &vDataToHash[0] + nblocks * 4; + //---------- + // body + const uint8_t* blocks = vDataToHash.data(); - for (int i = -nblocks; i; i++) { - uint32_t k1 = ReadLE32(blocks + i*4); + for (int i = 0; i < nblocks; ++i) { + uint32_t k1 = ReadLE32(blocks + i*4); - k1 *= c1; - k1 = ROTL32(k1, 15); - k1 *= c2; + k1 *= c1; + k1 = ROTL32(k1, 15); + k1 *= c2; - h1 ^= k1; - h1 = ROTL32(h1, 13); - h1 = h1 * 5 + 0xe6546b64; - } + h1 ^= k1; + h1 = ROTL32(h1, 13); + h1 = h1 * 5 + 0xe6546b64; + } - //---------- - // tail - const uint8_t* tail = (const uint8_t*)(&vDataToHash[0] + nblocks * 4); + //---------- + // tail + const uint8_t* tail = vDataToHash.data() + nblocks * 4; - uint32_t k1 = 0; + uint32_t k1 = 0; - switch (vDataToHash.size() & 3) { + switch (vDataToHash.size() & 3) { case 3: k1 ^= tail[2] << 16; case 2: @@ -57,7 +55,6 @@ unsigned int MurmurHash3(unsigned int nHashSeed, const std::vector<unsigned char k1 = ROTL32(k1, 15); k1 *= c2; h1 ^= k1; - } } //---------- diff --git a/src/httprpc.cpp b/src/httprpc.cpp index 8c2e0da32f..a207d5ece4 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -18,7 +18,6 @@ #include <stdio.h> #include <boost/algorithm/string.hpp> // boost::trim -#include <boost/foreach.hpp> /** WWW-Authenticate to present with 401 Unauthorized response */ static const char* WWW_AUTH_HEADER_DATA = "Basic realm=\"jsonrpc\""; diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 1c53d8d49d..290a2efca2 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -7,6 +7,7 @@ #include "chainparamsbase.h" #include "compat.h" #include "util.h" +#include "utilstrencodings.h" #include "netbase.h" #include "rpc/protocol.h" // For HTTP status codes #include "sync.h" diff --git a/src/init.cpp b/src/init.cpp index 672ef77e80..1e85642019 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -215,6 +215,19 @@ void Shutdown() fFeeEstimatesInitialized = false; } + // FlushStateToDisk generates a SetBestChain callback, which we should avoid missing + FlushStateToDisk(); + + // After there are no more peers/RPC left to give us new data which may generate + // CValidationInterface callbacks, flush them... + GetMainSignals().FlushBackgroundCallbacks(); + + // Any future callbacks will be dropped. This should absolutely be safe - if + // missing a callback results in an unrecoverable situation, unclean shutdown + // would too. The only reason to do the above flushes is to let the wallet catch + // up with our current chain to avoid any strange pruning edge cases and make + // next startup faster by avoiding rescan. + { LOCK(cs_main); if (pcoinsTip != NULL) { @@ -251,6 +264,7 @@ void Shutdown() } #endif UnregisterAllValidationInterfaces(); + GetMainSignals().UnregisterBackgroundSignalScheduler(); #ifdef ENABLE_WALLET for (CWalletRef pwallet : vpwallets) { delete pwallet; @@ -1203,6 +1217,8 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) CScheduler::Function serviceLoop = boost::bind(&CScheduler::serviceQueue, &scheduler); threadGroup.create_thread(boost::bind(&TraceThread<CScheduler::Function>, "scheduler", serviceLoop)); + GetMainSignals().RegisterBackgroundSignalScheduler(scheduler); + /* Start the RPC server already. It will be started in "warmup" mode * and not really process calls already (but it will signify connections * that the server is there and will be ready later). Warmup mode will @@ -1496,7 +1512,9 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) LogPrintf("Shutdown requested. Exiting.\n"); return false; } - LogPrintf(" block index %15dms\n", GetTimeMillis() - nStart); + if (fLoaded) { + LogPrintf(" block index %15dms\n", GetTimeMillis() - nStart); + } fs::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME; CAutoFile est_filein(fsbridge::fopen(est_path, "rb"), SER_DISK, CLIENT_VERSION); diff --git a/src/key.cpp b/src/key.cpp index 5a75647f1a..5a991fc1d2 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -138,7 +138,7 @@ CPrivKey CKey::GetPrivKey() const { size_t privkeylen; privkey.resize(279); privkeylen = 279; - ret = ec_privkey_export_der(secp256k1_context_sign, (unsigned char*)&privkey[0], &privkeylen, begin(), fCompressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED); + ret = ec_privkey_export_der(secp256k1_context_sign, (unsigned char*) privkey.data(), &privkeylen, begin(), fCompressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED); assert(ret); privkey.resize(privkeylen); return privkey; @@ -167,7 +167,7 @@ bool CKey::Sign(const uint256 &hash, std::vector<unsigned char>& vchSig, uint32_ secp256k1_ecdsa_signature sig; int ret = secp256k1_ecdsa_sign(secp256k1_context_sign, &sig, hash.begin(), begin(), secp256k1_nonce_function_rfc6979, test_case ? extra_entropy : NULL); assert(ret); - secp256k1_ecdsa_signature_serialize_der(secp256k1_context_sign, (unsigned char*)&vchSig[0], &nSigLen, &sig); + secp256k1_ecdsa_signature_serialize_der(secp256k1_context_sign, (unsigned char*)vchSig.data(), &nSigLen, &sig); vchSig.resize(nSigLen); return true; } @@ -202,7 +202,7 @@ bool CKey::SignCompact(const uint256 &hash, std::vector<unsigned char>& vchSig) } bool CKey::Load(CPrivKey &privkey, CPubKey &vchPubKey, bool fSkipCheck=false) { - if (!ec_privkey_import_der(secp256k1_context_sign, (unsigned char*)begin(), &privkey[0], privkey.size())) + if (!ec_privkey_import_der(secp256k1_context_sign, (unsigned char*)begin(), privkey.data(), privkey.size())) return false; fCompressed = vchPubKey.IsCompressed(); fValid = true; @@ -245,8 +245,8 @@ void CExtKey::SetMaster(const unsigned char *seed, unsigned int nSeedLen) { static const unsigned char hashkey[] = {'B','i','t','c','o','i','n',' ','s','e','e','d'}; std::vector<unsigned char, secure_allocator<unsigned char>> vout(64); CHMAC_SHA512(hashkey, sizeof(hashkey)).Write(seed, nSeedLen).Finalize(vout.data()); - key.Set(&vout[0], &vout[32], true); - memcpy(chaincode.begin(), &vout[32], 32); + key.Set(vout.data(), vout.data() + 32, true); + memcpy(chaincode.begin(), vout.data() + 32, 32); nDepth = 0; nChild = 0; memset(vchFingerprint, 0, sizeof(vchFingerprint)); diff --git a/src/memusage.h b/src/memusage.h index 710120d285..93fd6a0eb5 100644 --- a/src/memusage.h +++ b/src/memusage.h @@ -15,7 +15,6 @@ #include <unordered_map> #include <unordered_set> -#include <boost/foreach.hpp> namespace memusage { diff --git a/src/merkleblock.cpp b/src/merkleblock.cpp index 78d7cd6001..ba5f7b400c 100644 --- a/src/merkleblock.cpp +++ b/src/merkleblock.cpp @@ -153,7 +153,7 @@ uint256 CPartialMerkleTree::ExtractMatches(std::vector<uint256> &vMatch, std::ve if (nTransactions == 0) return uint256(); // check for excessively high numbers of transactions - if (nTransactions > MAX_BLOCK_BASE_SIZE / 60) // 60 is the lower bound for the size of a serialized CTransaction + if (nTransactions > MAX_BLOCK_WEIGHT / MIN_TRANSACTION_WEIGHT) return uint256(); // there can never be more hashes provided than one for every txid if (vHash.size() > nTransactions) diff --git a/src/net.cpp b/src/net.cpp index 301cf58b87..5bf3af7ea3 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2876,5 +2876,5 @@ uint64_t CConnman::CalculateKeyedNetGroup(const CAddress& ad) const { std::vector<unsigned char> vchNetGroup(ad.GetGroup()); - return GetDeterministicRandomizer(RANDOMIZER_ID_NETGROUP).Write(&vchNetGroup[0], vchNetGroup.size()).Finalize(); + return GetDeterministicRandomizer(RANDOMIZER_ID_NETGROUP).Write(vchNetGroup.data(), vchNetGroup.size()).Finalize(); } @@ -33,7 +33,6 @@ #include <arpa/inet.h> #endif -#include <boost/foreach.hpp> #include <boost/signals2/signal.hpp> class CScheduler; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 4d832f3711..a743f04dd1 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -22,6 +22,7 @@ #include "primitives/block.h" #include "primitives/transaction.h" #include "random.h" +#include "reverse_iterator.h" #include "tinyformat.h" #include "txmempool.h" #include "ui_interface.h" @@ -827,7 +828,7 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB // Relay inventory, but don't relay old inventory during initial block download. connman->ForEachNode([nNewHeight, &vHashes](CNode* pnode) { if (nNewHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 0)) { - BOOST_REVERSE_FOREACH(const uint256& hash, vHashes) { + for (const uint256& hash : reverse_iterate(vHashes)) { pnode->PushBlockHash(hash); } } @@ -2338,7 +2339,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr } else { std::vector<CInv> vGetData; // Download as much as possible, from earliest to latest. - BOOST_REVERSE_FOREACH(const CBlockIndex *pindex, vToFetch) { + for (const CBlockIndex *pindex : reverse_iterate(vToFetch)) { if (nodestate->nBlocksInFlight >= MAX_BLOCKS_IN_TRANSIT_PER_PEER) { // Can't download any more from this peer break; diff --git a/src/netaddress.cpp b/src/netaddress.cpp index 89f257c642..110e778fbd 100644 --- a/src/netaddress.cpp +++ b/src/netaddress.cpp @@ -573,7 +573,7 @@ std::vector<unsigned char> CService::GetKey() const { std::vector<unsigned char> vKey; vKey.resize(18); - memcpy(&vKey[0], ip, 16); + memcpy(vKey.data(), ip, 16); vKey[16] = port / 0x100; vKey[17] = port & 0x0FF; return vKey; diff --git a/src/netbase.cpp b/src/netbase.cpp index a23f92e1ed..1f668a5d4c 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -58,25 +58,6 @@ std::string GetNetworkName(enum Network net) { } } -void SplitHostPort(std::string in, int &portOut, std::string &hostOut) { - size_t colon = in.find_last_of(':'); - // if a : is found, and it either follows a [...], or no other : is in the string, treat it as port separator - bool fHaveColon = colon != in.npos; - bool fBracketed = fHaveColon && (in[0]=='[' && in[colon-1]==']'); // if there is a colon, and in[0]=='[', colon is not 0, so in[colon-1] is safe - bool fMultiColon = fHaveColon && (in.find_last_of(':',colon-1) != in.npos); - if (fHaveColon && (colon==0 || fBracketed || !fMultiColon)) { - int32_t n; - if (ParseInt32(in.substr(colon + 1), &n) && n > 0 && n < 0x10000) { - in = in.substr(0, colon); - portOut = n; - } - } - if (in.size()>0 && in[0] == '[' && in[in.size()-1] == ']') - hostOut = in.substr(1, in.size()-2); - else - hostOut = in; -} - bool static LookupIntern(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup) { vIP.clear(); @@ -563,7 +544,7 @@ static bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDe // do socks negotiation if (proxy.randomize_credentials) { ProxyCredentials random_auth; - static std::atomic_int counter; + static std::atomic_int counter(0); random_auth.username = random_auth.password = strprintf("%i", counter++); if (!Socks5(strDest, (unsigned short)port, &random_auth, hSocket)) return false; diff --git a/src/netbase.h b/src/netbase.h index c9d108aadd..fd4b34c8f1 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -39,7 +39,6 @@ public: enum Network ParseNetwork(std::string net); std::string GetNetworkName(enum Network net); -void SplitHostPort(std::string in, int &portOut, std::string &hostOut); bool SetProxy(enum Network net, const proxyType &addrProxy); bool GetProxy(enum Network net, proxyType &proxyInfoOut); bool IsProxy(const CNetAddr &addr); diff --git a/src/policy/feerate.h b/src/policy/feerate.h index e82268b095..565da6c154 100644 --- a/src/policy/feerate.h +++ b/src/policy/feerate.h @@ -40,6 +40,7 @@ public: friend bool operator==(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK == b.nSatoshisPerK; } friend bool operator<=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK <= b.nSatoshisPerK; } friend bool operator>=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK >= b.nSatoshisPerK; } + friend bool operator!=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK != b.nSatoshisPerK; } CFeeRate& operator+=(const CFeeRate& a) { nSatoshisPerK += a.nSatoshisPerK; return *this; } std::string ToString() const; diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp index 771491770e..0f186fa845 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -16,6 +16,19 @@ static constexpr double INF_FEERATE = 1e99; +std::string StringForFeeEstimateHorizon(FeeEstimateHorizon horizon) { + static const std::map<FeeEstimateHorizon, std::string> horizon_strings = { + {FeeEstimateHorizon::SHORT_HALFLIFE, "short"}, + {FeeEstimateHorizon::MED_HALFLIFE, "medium"}, + {FeeEstimateHorizon::LONG_HALFLIFE, "long"}, + }; + auto horizon_string = horizon_strings.find(horizon); + + if (horizon_string == horizon_strings.end()) return "unknown"; + + return horizon_string->second; +} + std::string StringForFeeReason(FeeReason reason) { static const std::map<FeeReason, std::string> fee_reason_strings = { {FeeReason::NONE, "None"}, @@ -36,6 +49,20 @@ std::string StringForFeeReason(FeeReason reason) { return reason_string->second; } +bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode) { + static const std::map<std::string, FeeEstimateMode> fee_modes = { + {"UNSET", FeeEstimateMode::UNSET}, + {"ECONOMICAL", FeeEstimateMode::ECONOMICAL}, + {"CONSERVATIVE", FeeEstimateMode::CONSERVATIVE}, + }; + auto mode = fee_modes.find(mode_string); + + if (mode == fee_modes.end()) return false; + + fee_estimate_mode = mode->second; + return true; +} + /** * 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 @@ -90,7 +117,7 @@ public: * Create new TxConfirmStats. This is called by BlockPolicyEstimator's * constructor with default values. * @param defaultBuckets contains the upper limits for the bucket boundaries - * @param maxConfirms max number of confirms to track + * @param maxPeriods max number of periods to track * @param decay how much to decay the historical moving average per block */ TxConfirmStats(const std::vector<double>& defaultBuckets, const std::map<double, unsigned int>& defaultBucketMap, @@ -671,7 +698,7 @@ CFeeRate CBlockPolicyEstimator::estimateRawFee(int confTarget, double successThr break; } default: { - return CFeeRate(0); + throw std::out_of_range("CBlockPolicyEstimator::estimateRawFee unknown FeeEstimateHorizon"); } } @@ -690,6 +717,24 @@ CFeeRate CBlockPolicyEstimator::estimateRawFee(int confTarget, double successThr return CFeeRate(median); } +unsigned int CBlockPolicyEstimator::HighestTargetTracked(FeeEstimateHorizon horizon) const +{ + switch (horizon) { + case FeeEstimateHorizon::SHORT_HALFLIFE: { + return shortStats->GetMaxConfirms(); + } + case FeeEstimateHorizon::MED_HALFLIFE: { + return feeStats->GetMaxConfirms(); + } + case FeeEstimateHorizon::LONG_HALFLIFE: { + return longStats->GetMaxConfirms(); + } + default: { + throw std::out_of_range("CBlockPolicyEstimator::HighestTargetTracked unknown FeeEstimateHorizon"); + } + } +} + unsigned int CBlockPolicyEstimator::BlockSpan() const { if (firstRecordedHeight == 0) return 0; @@ -781,8 +826,10 @@ 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, FeeCalculation *feeCalc, const CTxMemPool& pool, bool conservative) const +CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, FeeCalculation *feeCalc, bool conservative) const { + LOCK(cs_feeEstimator); + if (feeCalc) { feeCalc->desiredTarget = confTarget; feeCalc->returnedTarget = confTarget; @@ -790,80 +837,70 @@ CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, FeeCalculation double median = -1; EstimationResult tempResult; - { - LOCK(cs_feeEstimator); - // Return failure if trying to analyze a target we're not tracking - if (confTarget <= 0 || (unsigned int)confTarget > longStats->GetMaxConfirms()) - return CFeeRate(0); + // Return failure if trying to analyze a target we're not tracking + if (confTarget <= 0 || (unsigned int)confTarget > longStats->GetMaxConfirms()) + return CFeeRate(0); - // It's not possible to get reasonable estimates for confTarget of 1 - if (confTarget == 1) - confTarget = 2; + // It's not possible to get reasonable estimates for confTarget of 1 + if (confTarget == 1) + confTarget = 2; - unsigned int maxUsableEstimate = MaxUsableEstimate(); - if (maxUsableEstimate <= 1) - return CFeeRate(0); + unsigned int maxUsableEstimate = MaxUsableEstimate(); + if (maxUsableEstimate <= 1) + return CFeeRate(0); - if ((unsigned int)confTarget > maxUsableEstimate) { - confTarget = maxUsableEstimate; - } + if ((unsigned int)confTarget > maxUsableEstimate) { + confTarget = maxUsableEstimate; + } - 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. - * For non-conservative estimates we do the same thing for 2*target, but - * for conservative estimates we want to skip these shorter horizons - * checks for 2*target because we are taking the max over all time - * horizons so we already have monotonically increasing estimates and - * 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, &tempResult); + 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. + * For non-conservative estimates we do the same thing for 2*target, but + * for conservative estimates we want to skip these shorter horizons + * checks for 2*target because we are taking the max over all time + * horizons so we already have monotonically increasing estimates and + * 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, &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::HALF_ESTIMATE; + feeCalc->reason = FeeReason::FULL_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; } - double doubleEst = estimateCombinedFee(2 * confTarget, DOUBLE_SUCCESS_PCT, !conservative, &tempResult); - if (doubleEst > median) { - median = doubleEst; + } + + if (conservative || median == -1) { + double consEst = estimateConservativeFee(2 * confTarget, &tempResult); + if (consEst > median) { + median = consEst; if (feeCalc) { feeCalc->est = tempResult; - feeCalc->reason = FeeReason::DOUBLE_ESTIMATE; + feeCalc->reason = FeeReason::CONSERVATIVE; } } - - if (conservative || median == -1) { - 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 (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 (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 2029ce3744..f4ef793643 100644 --- a/src/policy/fees.h +++ b/src/policy/fees.h @@ -74,6 +74,8 @@ enum FeeEstimateHorizon { LONG_HALFLIFE = 2 }; +std::string StringForFeeEstimateHorizon(FeeEstimateHorizon horizon); + /* Enumeration of reason for returned fee estimate */ enum class FeeReason { NONE, @@ -90,6 +92,15 @@ enum class FeeReason { std::string StringForFeeReason(FeeReason reason); +/* Used to determine type of fee estimation requested */ +enum class FeeEstimateMode { + UNSET, //! Use default settings based on other criteria + ECONOMICAL, //! Force estimateSmartFee to use non-conservative estimates + CONSERVATIVE, //! Force estimateSmartFee to use conservative estimates +}; + +bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode); + /* Used to return detailed information about a feerate bucket */ struct EstimatorBucket { @@ -197,7 +208,7 @@ public: * the closest target where one can be given. 'conservative' estimates are * valid over longer time horizons also. */ - CFeeRate estimateSmartFee(int confTarget, FeeCalculation *feeCalc, const CTxMemPool& pool, bool conservative = true) const; + CFeeRate estimateSmartFee(int confTarget, FeeCalculation *feeCalc, bool conservative) const; /** Return a specific fee estimate calculation with a given success * threshold and time horizon, and optionally return detailed data about @@ -214,6 +225,9 @@ public: /** Empty mempool transactions on shutdown to record failure to confirm for txs still in mempool */ void FlushUnconfirmed(CTxMemPool& pool); + /** Calculation of highest target that estimates are tracked for */ + unsigned int HighestTargetTracked(FeeEstimateHorizon horizon) const; + private: unsigned int nBestSeenHeight; unsigned int firstRecordedHeight; diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 5f68c09a86..9f2d623e76 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -7,13 +7,13 @@ #include "policy/policy.h" +#include "consensus/validation.h" #include "validation.h" #include "coins.h" #include "tinyformat.h" #include "util.h" #include "utilstrencodings.h" -#include <boost/foreach.hpp> CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn) { diff --git a/src/prevector.h b/src/prevector.h index dc17e7ce4b..46640d6fff 100644 --- a/src/prevector.h +++ b/src/prevector.h @@ -132,7 +132,7 @@ public: typedef const T* pointer; typedef const T& reference; typedef std::bidirectional_iterator_tag iterator_category; - const_reverse_iterator(T* ptr_) : ptr(ptr_) {} + const_reverse_iterator(const T* ptr_) : ptr(ptr_) {} const_reverse_iterator(reverse_iterator x) : ptr(&(*x)) {} const T& operator*() const { return *ptr; } const T* operator->() const { return ptr; } @@ -220,7 +220,7 @@ public: } } - prevector() : _size(0) {} + prevector() : _size(0), _union{{}} {} explicit prevector(size_type n) : _size(0) { resize(n); diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp index 9a979094cc..24be67c84f 100644 --- a/src/primitives/block.cpp +++ b/src/primitives/block.cpp @@ -31,12 +31,3 @@ std::string CBlock::ToString() const } return s.str(); } - -int64_t GetBlockWeight(const CBlock& block) -{ - // This implements the weight = (stripped_size * 4) + witness_size formula, - // using only serialization with and without witness data. As witness_size - // is equal to total_size - stripped_size, this formula is identical to: - // weight = (stripped_size * 3) + total_size. - return ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION); -} diff --git a/src/primitives/block.h b/src/primitives/block.h index f03cf48504..c90a1dfa64 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -152,7 +152,4 @@ struct CBlockLocator } }; -/** Compute the consensus-critical block weight (see BIP 141). */ -int64_t GetBlockWeight(const CBlock& tx); - #endif // BITCOIN_PRIMITIVES_BLOCK_H diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index a0d7793f97..f87934d586 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -114,8 +114,3 @@ std::string CTransaction::ToString() const str += " " + vout[i].ToString() + "\n"; return str; } - -int64_t GetTransactionWeight(const CTransaction& tx) -{ - return ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR -1) + ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); -} diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 00ac0b92b5..041034bb8b 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -6,6 +6,7 @@ #ifndef BITCOIN_PRIMITIVES_TRANSACTION_H #define BITCOIN_PRIMITIVES_TRANSACTION_H +#include <stdint.h> #include "amount.h" #include "script/script.h" #include "serialize.h" @@ -13,8 +14,6 @@ static const int SERIALIZE_TRANSACTION_NO_WITNESS = 0x40000000; -static const int WITNESS_SCALE_FACTOR = 4; - /** An outpoint - a combination of a transaction hash and an index n into its vout */ class COutPoint { @@ -107,7 +106,7 @@ public: template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action) { READWRITE(prevout); - READWRITE(*(CScriptBase*)(&scriptSig)); + READWRITE(scriptSig); READWRITE(nSequence); } @@ -147,7 +146,7 @@ public: template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action) { READWRITE(nValue); - READWRITE(*(CScriptBase*)(&scriptPubKey)); + READWRITE(scriptPubKey); } void SetNull() @@ -411,7 +410,4 @@ typedef std::shared_ptr<const CTransaction> CTransactionRef; static inline CTransactionRef MakeTransactionRef() { return std::make_shared<const CTransaction>(); } template <typename Tx> static inline CTransactionRef MakeTransactionRef(Tx&& txIn) { return std::make_shared<const CTransaction>(std::forward<Tx>(txIn)); } -/** Compute the weight of a transaction, as defined by BIP 141 */ -int64_t GetTransactionWeight(const CTransaction &tx); - #endif // BITCOIN_PRIMITIVES_TRANSACTION_H diff --git a/src/protocol.h b/src/protocol.h index eba39ab1e5..7890bb627d 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -163,7 +163,7 @@ extern const char *PONG; /** * The notfound message is a reply to a getdata message which requested an * object the receiving node does not have available for relay. - * @ince protocol version 70001. + * @since protocol version 70001. * @see https://bitcoin.org/en/developer-reference#notfound */ extern const char *NOTFOUND; diff --git a/src/pubkey.cpp b/src/pubkey.cpp index a16457ea4e..91af4e56f2 100644 --- a/src/pubkey.cpp +++ b/src/pubkey.cpp @@ -172,10 +172,7 @@ bool CPubKey::Verify(const uint256 &hash, const std::vector<unsigned char>& vchS if (!secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, &(*this)[0], size())) { return false; } - if (vchSig.size() == 0) { - return false; - } - if (!ecdsa_signature_parse_der_lax(secp256k1_context_verify, &sig, &vchSig[0], vchSig.size())) { + if (!ecdsa_signature_parse_der_lax(secp256k1_context_verify, &sig, vchSig.data(), vchSig.size())) { return false; } /* libsecp256k1's ECDSA verification requires lower-S signatures, which have @@ -274,7 +271,7 @@ bool CExtPubKey::Derive(CExtPubKey &out, unsigned int _nChild) const { /* static */ bool CPubKey::CheckLowS(const std::vector<unsigned char>& vchSig) { secp256k1_ecdsa_signature sig; - if (!ecdsa_signature_parse_der_lax(secp256k1_context_verify, &sig, &vchSig[0], vchSig.size())) { + if (!ecdsa_signature_parse_der_lax(secp256k1_context_verify, &sig, vchSig.data(), vchSig.size())) { return false; } return (!secp256k1_ecdsa_signature_normalize(secp256k1_context_verify, NULL, &sig)); diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index d3ad24da01..2fa032abdc 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -10,7 +10,6 @@ #include "base58.h" #include "wallet/wallet.h" -#include <boost/foreach.hpp> #include <QFont> #include <QDebug> diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp index a1e5cccc0b..b3d2cf1d55 100644 --- a/src/qt/bitcoinstrings.cpp +++ b/src/qt/bitcoinstrings.cpp @@ -21,9 +21,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "A fee rate (in %s/kB) that will be used when fee estimation has insufficient " "data (default: %s)"), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Accept connections from outside (default: 1 if no -proxy or -connect/-" -"noconnect)"), -QT_TRANSLATE_NOOP("bitcoin-core", "" "Accept relayed transactions received from whitelisted peers even when not " "relaying transactions (default: %d)"), QT_TRANSLATE_NOOP("bitcoin-core", "" @@ -37,14 +34,16 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "Bind to given address and whitelist peers connecting to it. Use [host]:port " "notation for IPv6"), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Bind to given address to listen for JSON-RPC connections. Use [host]:port " -"notation for IPv6. This option can be specified multiple times (default: " -"bind to all interfaces)"), +"Bind to given address to listen for JSON-RPC connections. This option is " +"ignored unless -rpcallowip is also passed. Port is optional and overrides -" +"rpcport. Use [host]:port notation for IPv6. This option can be specified " +"multiple times (default: 127.0.0.1 and ::1 i.e., localhost, or if -" +"rpcallowip has been specified, 0.0.0.0 and :: i.e., all addresses)"), QT_TRANSLATE_NOOP("bitcoin-core", "" "Cannot obtain a lock on data directory %s. %s is probably already running."), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Connect only to the specified node(s); -noconnect or -connect=0 alone to " -"disable automatic connections"), +"Connect only to the specified node(s); -connect=0 disables automatic " +"connections"), QT_TRANSLATE_NOOP("bitcoin-core", "" "Create new files with system default permissions, instead of umask 077 (only " "effective with disabled wallet functionality)"), @@ -62,13 +61,17 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" QT_TRANSLATE_NOOP("bitcoin-core", "" "Equivalent bytes per sigop in transactions for relay and mining (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Error loading %s: You can't enable HD on a already existing non-HD wallet"), +"Error loading %s: You can't enable HD on an already existing non-HD wallet"), QT_TRANSLATE_NOOP("bitcoin-core", "" "Error reading %s! All keys read correctly, but transaction data or address " "book entries might be missing or incorrect."), QT_TRANSLATE_NOOP("bitcoin-core", "" "Error: Listening for incoming connections failed (listen returned error %s)"), QT_TRANSLATE_NOOP("bitcoin-core", "" +"Exclude debugging information for a category. Can be used in conjunction " +"with -debug=1 to output debug logs for all categories except one or more " +"specified categories."), +QT_TRANSLATE_NOOP("bitcoin-core", "" "Execute command when a relevant alert is received or we see a really long " "fork (%s in cmd is replaced by message)"), QT_TRANSLATE_NOOP("bitcoin-core", "" @@ -134,7 +137,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "reindex (download the whole blockchain again in case of pruned node)"), QT_TRANSLATE_NOOP("bitcoin-core", "" "Query for peer addresses via DNS lookup, if low on addresses (default: 1 " -"unless -connect/-noconnect)"), +"unless -connect used)"), QT_TRANSLATE_NOOP("bitcoin-core", "" "Randomize credentials for every proxy connection. This enables Tor stream " "isolation (default: %u)"), @@ -154,8 +157,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "Set lowest fee rate (in %s/kB) for transactions to be included in block " "creation. (default: %s)"), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Set maximum size of high-priority/low-fee transactions in bytes (default: %d)"), -QT_TRANSLATE_NOOP("bitcoin-core", "" "Set the number of script verification threads (%u to %d, 0 = auto, <0 = " "leave that many cores free, default: %d)"), QT_TRANSLATE_NOOP("bitcoin-core", "" @@ -186,6 +187,9 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = " "no limit (default: %d)"), QT_TRANSLATE_NOOP("bitcoin-core", "" +"Unable to replay blocks. You will need to rebuild the database using -" +"reindex-chainstate."), +QT_TRANSLATE_NOOP("bitcoin-core", "" "Unable to rewind the database to a pre-fork state. You will need to " "redownload the blockchain"), QT_TRANSLATE_NOOP("bitcoin-core", "" @@ -225,6 +229,8 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "Warning: We do not appear to fully agree with our peers! You may need to " "upgrade, or other nodes may need to upgrade."), QT_TRANSLATE_NOOP("bitcoin-core", "" +"Whether to save the mempool on shutdown and load on restart (default: %u)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" "Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or CIDR " "notated network (e.g. 1.2.3.0/24). Can be specified multiple times."), QT_TRANSLATE_NOOP("bitcoin-core", "" @@ -235,13 +241,17 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "mode. This will redownload the entire blockchain"), QT_TRANSLATE_NOOP("bitcoin-core", "" "You need to rebuild the database using -reindex-chainstate to change -txindex"), +QT_TRANSLATE_NOOP("bitcoin-core", "%d of last 100 blocks have unexpected version"), QT_TRANSLATE_NOOP("bitcoin-core", "%s corrupt, salvage failed"), QT_TRANSLATE_NOOP("bitcoin-core", "%s is set very high!"), QT_TRANSLATE_NOOP("bitcoin-core", "(default: %s)"), QT_TRANSLATE_NOOP("bitcoin-core", "(default: %u)"), +QT_TRANSLATE_NOOP("bitcoin-core", "(press q to shutdown and continue later)"), QT_TRANSLATE_NOOP("bitcoin-core", "-maxmempool must be at least %d MB"), +QT_TRANSLATE_NOOP("bitcoin-core", "-wallet parameter must only specify a filename (not a path)"), QT_TRANSLATE_NOOP("bitcoin-core", "<category> can be:"), QT_TRANSLATE_NOOP("bitcoin-core", "Accept command line and JSON-RPC commands"), +QT_TRANSLATE_NOOP("bitcoin-core", "Accept connections from outside (default: 1 if no -proxy or -connect)"), QT_TRANSLATE_NOOP("bitcoin-core", "Accept public REST requests (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "Add a node to connect to and attempt to keep the connection open"), QT_TRANSLATE_NOOP("bitcoin-core", "Allow DNS lookups for -addnode, -seednode and -connect"), @@ -274,10 +284,11 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Error initializing wallet database environmen QT_TRANSLATE_NOOP("bitcoin-core", "Error loading %s"), QT_TRANSLATE_NOOP("bitcoin-core", "Error loading %s: Wallet corrupted"), QT_TRANSLATE_NOOP("bitcoin-core", "Error loading %s: Wallet requires newer version of %s"), -QT_TRANSLATE_NOOP("bitcoin-core", "Error loading %s: You can't disable HD on a already existing HD wallet"), +QT_TRANSLATE_NOOP("bitcoin-core", "Error loading %s: You can't disable HD on an already existing HD wallet"), QT_TRANSLATE_NOOP("bitcoin-core", "Error loading block database"), QT_TRANSLATE_NOOP("bitcoin-core", "Error opening block database"), QT_TRANSLATE_NOOP("bitcoin-core", "Error reading from database, shutting down."), +QT_TRANSLATE_NOOP("bitcoin-core", "Error upgrading chainstate database"), QT_TRANSLATE_NOOP("bitcoin-core", "Error"), QT_TRANSLATE_NOOP("bitcoin-core", "Error: A fatal internal error occurred, see debug.log for details"), QT_TRANSLATE_NOOP("bitcoin-core", "Error: Disk space is low!"), @@ -291,18 +302,19 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Incorrect or no genesis block found. Wrong da QT_TRANSLATE_NOOP("bitcoin-core", "Information"), QT_TRANSLATE_NOOP("bitcoin-core", "Initialization sanity check failed. %s is shutting down."), QT_TRANSLATE_NOOP("bitcoin-core", "Insufficient funds"), -QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -onion address: '%s'"), -QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -proxy address: '%s'"), +QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -onion address or hostname: '%s'"), +QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -proxy address or hostname: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -%s=<amount>: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -fallbackfee=<amount>: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Invalid characters in -wallet filename"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid netmask specified in -whitelist: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Keep at most <n> unconnectable transactions in memory (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "Keep the transaction memory pool below <n> megabytes (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "Keypool ran out, please call keypoolrefill first"), QT_TRANSLATE_NOOP("bitcoin-core", "Listen for JSON-RPC connections on <port> (default: %u or testnet: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "Listen for connections on <port> (default: %u or testnet: %u)"), -QT_TRANSLATE_NOOP("bitcoin-core", "Loading addresses..."), +QT_TRANSLATE_NOOP("bitcoin-core", "Loading P2P addresses..."), QT_TRANSLATE_NOOP("bitcoin-core", "Loading banlist..."), QT_TRANSLATE_NOOP("bitcoin-core", "Loading block index..."), QT_TRANSLATE_NOOP("bitcoin-core", "Loading wallet..."), @@ -329,12 +341,12 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Rebuild chain state from the currently indexe QT_TRANSLATE_NOOP("bitcoin-core", "Reducing -maxconnections from %d to %d, because of system limitations."), QT_TRANSLATE_NOOP("bitcoin-core", "Relay and mine data carrier transactions (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "Relay non-P2SH multisig (default: %u)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Replaying blocks..."), QT_TRANSLATE_NOOP("bitcoin-core", "Rescan the block chain for missing wallet transactions on startup"), QT_TRANSLATE_NOOP("bitcoin-core", "Rescanning..."), QT_TRANSLATE_NOOP("bitcoin-core", "Rewinding blocks..."), QT_TRANSLATE_NOOP("bitcoin-core", "Run in the background as a daemon and accept commands"), QT_TRANSLATE_NOOP("bitcoin-core", "Send trace/debug info to console instead of debug.log file"), -QT_TRANSLATE_NOOP("bitcoin-core", "Send transactions as zero-fee transactions if possible (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "Send transactions with full-RBF opt-in enabled (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "Set database cache size in megabytes (%d to %d, default: %d)"), QT_TRANSLATE_NOOP("bitcoin-core", "Set key pool size to <n> (default: %u)"), @@ -374,13 +386,15 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Unknown network specified in -onlynet: '%s'") QT_TRANSLATE_NOOP("bitcoin-core", "Unsupported argument -benchmark ignored, use -debug=bench."), QT_TRANSLATE_NOOP("bitcoin-core", "Unsupported argument -debugnet ignored, use -debug=net."), QT_TRANSLATE_NOOP("bitcoin-core", "Unsupported argument -tor found, use -onion."), +QT_TRANSLATE_NOOP("bitcoin-core", "Unsupported logging category %s=%s."), QT_TRANSLATE_NOOP("bitcoin-core", "Upgrade wallet to latest format on startup"), +QT_TRANSLATE_NOOP("bitcoin-core", "Upgrading UTXO database"), QT_TRANSLATE_NOOP("bitcoin-core", "Use UPnP to map the listening port (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "Use the test chain"), QT_TRANSLATE_NOOP("bitcoin-core", "User Agent comment (%s) contains unsafe characters."), QT_TRANSLATE_NOOP("bitcoin-core", "Username for JSON-RPC connections"), QT_TRANSLATE_NOOP("bitcoin-core", "Verifying blocks..."), -QT_TRANSLATE_NOOP("bitcoin-core", "Verifying wallet..."), +QT_TRANSLATE_NOOP("bitcoin-core", "Verifying wallet(s)..."), QT_TRANSLATE_NOOP("bitcoin-core", "Wallet %s resides outside data directory %s"), QT_TRANSLATE_NOOP("bitcoin-core", "Wallet debugging/testing options:"), QT_TRANSLATE_NOOP("bitcoin-core", "Wallet needed to be rewritten: restart %s to complete"), diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index af9a888d94..f3ee0fbe39 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -510,7 +510,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) nBytes -= 34; // Fee - nPayFee = CWallet::GetMinimumFee(nBytes, coinControl->nConfirmTarget, ::mempool, ::feeEstimator); + nPayFee = CWallet::GetMinimumFee(nBytes, *coinControl, ::mempool, ::feeEstimator, nullptr /* FeeCalculation */); if (nPayAmount > 0) { @@ -581,12 +581,8 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) QString toolTipDust = tr("This label turns red if any recipient receives an amount smaller than the current dust threshold."); // how many satoshis the estimated fee can vary per byte we guess wrong - double dFeeVary; - if (payTxFee.GetFeePerK() > 0) - dFeeVary = (double)std::max(CWallet::GetRequiredFee(1000), payTxFee.GetFeePerK()) / 1000; - else { - dFeeVary = (double)std::max(CWallet::GetRequiredFee(1000), ::feeEstimator.estimateSmartFee(coinControl->nConfirmTarget, NULL, ::mempool).GetFeePerK()) / 1000; - } + double dFeeVary = (double)nPayFee / nBytes; + QString toolTip4 = tr("Can vary +/- %1 satoshi(s) per input.").arg(dFeeVary); l3->setToolTip(toolTip4); diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 89f9c25d14..e82a3c1533 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -1068,44 +1068,15 @@ <number>30</number> </property> <item> - <widget class="QSlider" name="sliderSmartFee"> - <property name="minimum"> + <layout class="QHBoxLayout" name="horizontalLayoutConfTarget"> + <property name="bottomMargin"> <number>0</number> </property> - <property name="maximum"> - <number>23</number> - </property> - <property name="pageStep"> - <number>1</number> - </property> - <property name="value"> - <number>0</number> - </property> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="invertedAppearance"> - <bool>false</bool> - </property> - <property name="invertedControls"> - <bool>false</bool> - </property> - <property name="tickPosition"> - <enum>QSlider::NoTicks</enum> - </property> - </widget> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayoutFee10"> <item> - <widget class="QLabel" name="labelSmartFeeNormal"> - <property name="text"> - <string>normal</string> - </property> - </widget> + <widget class="QComboBox" name="confTargetSelector"/> </item> <item> - <spacer name="horizontalSpacer_7"> + <spacer name="horizontalSpacerConfTarget"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> @@ -1117,33 +1088,6 @@ </property> </spacer> </item> - <item> - <widget class="QLabel" name="confirmationTargetLabel"> - <property name="text"> - <string notr="true">(count)</string> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_3"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QLabel" name="labelSmartFeeFast"> - <property name="text"> - <string>fast</string> - </property> - </widget> - </item> </layout> </item> </layout> diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts index f62f1e4a73..9183075067 100644 --- a/src/qt/locale/bitcoin_en.ts +++ b/src/qt/locale/bitcoin_en.ts @@ -127,7 +127,7 @@ <context> <name>AddressTableModel</name> <message> - <location filename="../addresstablemodel.cpp" line="+170"/> + <location filename="../addresstablemodel.cpp" line="+169"/> <source>Label</source> <translation type="unfinished"></translation> </message> @@ -304,12 +304,12 @@ <translation>Sign &message...</translation> </message> <message> - <location line="+427"/> + <location line="+429"/> <source>Synchronizing with network...</source> <translation>Synchronizing with network...</translation> </message> <message> - <location line="-505"/> + <location line="-507"/> <source>&Overview</source> <translation>&Overview</translation> </message> @@ -404,7 +404,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+357"/> + <location line="+359"/> <source>Click to disable network activity.</source> <translation type="unfinished"></translation> </message> @@ -429,7 +429,7 @@ <translation>Reindexing blocks on disk...</translation> </message> <message> - <location line="-508"/> + <location line="-510"/> <source>Send coins to a Bitcoin address</source> <translation>Send coins to a Bitcoin address</translation> </message> @@ -459,12 +459,12 @@ <translation>&Verify message...</translation> </message> <message> - <location line="+514"/> + <location line="+516"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> <message> - <location line="-739"/> + <location line="-741"/> <source>Wallet</source> <translation>Wallet</translation> </message> @@ -549,7 +549,7 @@ <translation type="unfinished"></translation> </message> <message numerus="yes"> - <location line="+354"/> + <location line="+356"/> <source>%n active connection(s) to Bitcoin network</source> <translation> <numerusform>%n active connection to Bitcoin network</numerusform> @@ -610,12 +610,12 @@ <translation>Up to date</translation> </message> <message> - <location line="-438"/> + <location line="-440"/> <source>Show the %1 help message to get a list with possible Bitcoin command-line options</source> <translation type="unfinished"></translation> </message> <message> - <location line="+197"/> + <location line="+199"/> <source>%1 client</source> <translation type="unfinished"></translation> </message> @@ -690,7 +690,7 @@ <translation>Wallet is <b>encrypted</b> and currently <b>locked</b></translation> </message> <message> - <location filename="../bitcoin.cpp" line="+518"/> + <location filename="../bitcoin.cpp" line="+524"/> <source>A fatal error occurred. Bitcoin can no longer continue safely and will quit.</source> <translation type="unfinished"></translation> </message> @@ -783,7 +783,7 @@ <translation type="unfinished">Confirmed</translation> </message> <message> - <location filename="../coincontroldialog.cpp" line="+55"/> + <location filename="../coincontroldialog.cpp" line="+54"/> <source>Copy address</source> <translation type="unfinished"></translation> </message> @@ -849,7 +849,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+183"/> + <location line="+155"/> <source>yes</source> <translation type="unfinished"></translation> </message> @@ -956,7 +956,7 @@ <context> <name>FreespaceChecker</name> <message> - <location filename="../intro.cpp" line="+78"/> + <location filename="../intro.cpp" line="+76"/> <source>A new data directory will be created.</source> <translation>A new data directory will be created.</translation> </message> @@ -1068,12 +1068,22 @@ <translation type="unfinished"></translation> </message> <message> + <location line="+157"/> + <source>When you click OK, %1 will begin to download and process the full %4 block chain (%2GB) starting with the earliest transactions in %3 when %4 initially launched.</source> + <translation type="unfinished"></translation> + </message> + <message> <location line="+10"/> - <source>%1 will download and store a copy of the Bitcoin block chain. At least %2GB of data will be stored in this directory, and it will grow over time. The wallet will also be stored in this directory.</source> + <source>This initial synchronisation is very demanding, and may expose hardware problems with your computer that had previously gone unnoticed. Each time you run %1, it will continue downloading where it left off.</source> <translation type="unfinished"></translation> </message> <message> <location line="+10"/> + <source>If you have chosen to limit block chain storage (pruning), the historical data must still be downloaded and processed, but will be deleted afterward to keep your disk usage low.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="-160"/> <source>Use the default data directory</source> <translation>Use the default data directory</translation> </message> @@ -1083,7 +1093,32 @@ <translation>Use a custom data directory:</translation> </message> <message> - <location filename="../intro.cpp" line="+94"/> + <location filename="../intro.cpp" line="+20"/> + <source>Bitcoin</source> + <translation type="unfinished">Bitcoin</translation> + </message> + <message> + <location line="+6"/> + <source>At least %1 GB of data will be stored in this directory, and it will grow over time.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+5"/> + <source>Approximately %1 GB of data will be stored in this directory.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+8"/> + <source>%1 will download and store a copy of the Bitcoin block chain.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> + <source>The wallet will also be stored in this directory.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+70"/> <source>Error: Specified data directory "%1" cannot be created.</source> <translation type="unfinished"></translation> </message> @@ -1257,7 +1292,14 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+94"/> + <location line="-118"/> + <location line="+23"/> + <location line="+23"/> + <source>Shows if the supplied default SOCKS5 proxy is used to reach peers via this network type.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+166"/> <source>Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu.</source> <translation type="unfinished"></translation> </message> @@ -1278,7 +1320,17 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+43"/> + <location line="+45"/> + <source>Open the %1 configuration file from the working directory.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> + <source>Open Configuration File</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+10"/> <source>Reset all client options to default.</source> <translation>Reset all client options to default.</translation> </message> @@ -1288,7 +1340,7 @@ <translation>&Reset Options</translation> </message> <message> - <location line="-514"/> + <location line="-529"/> <source>&Network</source> <translation>&Network</translation> </message> @@ -1366,14 +1418,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+13"/> <location line="+23"/> - <location line="+23"/> - <source>Shows, if the supplied default SOCKS5 proxy is used to reach peers via this network type.</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="-36"/> <source>IPv4</source> <translation type="unfinished"></translation> </message> @@ -1458,7 +1503,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+612"/> + <location line="+646"/> <source>&OK</source> <translation>&OK</translation> </message> @@ -1468,7 +1513,7 @@ <translation>&Cancel</translation> </message> <message> - <location filename="../optionsdialog.cpp" line="+86"/> + <location filename="../optionsdialog.cpp" line="+84"/> <source>default</source> <translation>default</translation> </message> @@ -1484,22 +1529,42 @@ </message> <message> <location line="+1"/> - <location line="+43"/> + <location line="+55"/> <source>Client restart required to activate changes.</source> <translation type="unfinished"></translation> </message> <message> - <location line="-43"/> + <location line="-55"/> <source>Client will be shut down. Do you want to proceed?</source> <translation type="unfinished"></translation> </message> <message> - <location line="+47"/> + <location line="+15"/> + <source>Configuration options</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>The configuration file is used to specify advanced user options which override GUI settings. Additionally, any command-line options will override this configuration file.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+5"/> + <source>Error</source> + <translation type="unfinished">Error</translation> + </message> + <message> + <location line="+0"/> + <source>The configuration file could not be opened.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+38"/> <source>This change would require a client restart.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+25"/> + <location line="+28"/> <source>The supplied proxy address is invalid.</source> <translation>The supplied proxy address is invalid.</translation> </message> @@ -1755,12 +1820,12 @@ <translation type="unfinished">Amount</translation> </message> <message> - <location filename="../guiutil.cpp" line="+136"/> + <location filename="../guiutil.cpp" line="+130"/> <source>Enter a Bitcoin address (e.g. %1)</source> <translation type="unfinished"></translation> </message> <message> - <location line="+759"/> + <location line="+766"/> <source>%1 d</source> <translation type="unfinished"></translation> </message> @@ -1850,7 +1915,7 @@ </translation> </message> <message> - <location filename="../bitcoin.cpp" line="+172"/> + <location filename="../bitcoin.cpp" line="+173"/> <source>%1 didn't yet exit safely...</source> <translation type="unfinished"></translation> </message> @@ -2007,7 +2072,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+404"/> + <location line="+324"/> + <source>&Reset</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+80"/> <location line="+558"/> <source>Received</source> <translation type="unfinished"></translation> @@ -2030,8 +2100,8 @@ </message> <message> <location line="+60"/> - <location filename="../rpcconsole.cpp" line="+456"/> - <location line="+719"/> + <location filename="../rpcconsole.cpp" line="+458"/> + <location line="+728"/> <source>Select a peer to view detailed information.</source> <translation type="unfinished"></translation> </message> @@ -2157,12 +2227,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+52"/> - <source>&Clear</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+16"/> + <location line="+68"/> <source>Totals</source> <translation type="unfinished"></translation> </message> @@ -2187,7 +2252,7 @@ <translation>Clear console</translation> </message> <message> - <location filename="../rpcconsole.cpp" line="-214"/> + <location filename="../rpcconsole.cpp" line="-223"/> <source>1 &hour</source> <translation type="unfinished"></translation> </message> @@ -2225,23 +2290,23 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+126"/> + <location line="+135"/> <source>Welcome to the %1 RPC console.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+1"/> - <source>Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen.</source> - <translation>Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen.</translation> - </message> - <message> - <location line="+1"/> + <location line="+2"/> <source>Type <b>help</b> for an overview of available commands.</source> <translation>Type <b>help</b> for an overview of available commands.</translation> </message> <message> - <location line="+2"/> - <source>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.</source> + <location line="-1"/> + <source>Use up and down arrows to navigate history, and %1 to clear screen.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> + <source>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.</source> <translation type="unfinished"></translation> </message> <message> @@ -2494,7 +2559,7 @@ <context> <name>RecentRequestsTableModel</name> <message> - <location filename="../recentrequeststablemodel.cpp" line="+29"/> + <location filename="../recentrequeststablemodel.cpp" line="+28"/> <source>Date</source> <translation type="unfinished">Date</translation> </message> @@ -2509,7 +2574,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+40"/> + <location line="+39"/> <source>(no label)</source> <translation type="unfinished"></translation> </message> @@ -2533,7 +2598,7 @@ <name>SendCoinsDialog</name> <message> <location filename="../forms/sendcoinsdialog.ui" line="+14"/> - <location filename="../sendcoinsdialog.cpp" line="+554"/> + <location filename="../sendcoinsdialog.cpp" line="+565"/> <source>Send Coins</source> <translation>Send Coins</translation> </message> @@ -2608,7 +2673,17 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+37"/> + <location line="+24"/> + <source>Using the fallbackfee can result in sending a transaction that will take several hours or days (or never) to confirm. Consider choosing your fee manually or wait until your have validated the complete chain.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+9"/> + <source>Warning: Fee estimation is currently not possible.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+26"/> <source>collapse fee-settings</source> <translation type="unfinished"></translation> </message> @@ -2619,22 +2694,16 @@ </message> <message> <location line="-3"/> - <location line="+16"/> <source>If the custom fee is set to 1000 satoshis and the transaction is only 250 bytes, then "per kilobyte" only pays 250 satoshis in fee, while "total at least" pays 1000 satoshis. For transactions bigger than a kilobyte both pay by kilobyte.</source> <translation type="unfinished"></translation> </message> <message> - <location line="-64"/> + <location line="-48"/> <source>Hide</source> <translation type="unfinished"></translation> </message> <message> - <location line="+67"/> - <source>total at least</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+30"/> + <location line="+84"/> <location line="+13"/> <source>Paying only the minimum fee is just fine as long as there is less transaction volume than space in the blocks. But be aware that this can end up in a never confirming transaction once there is more demand for bitcoin transactions than the network can process.</source> <translation type="unfinished"></translation> @@ -2670,7 +2739,17 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+102"/> + <location line="+30"/> + <source>Request Replace-By-Fee</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> + <source>Indicates that the sender may wish to replace this transaction with a new one paying higher fees (prior to being confirmed).</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+79"/> <source>Send to multiple recipients at once</source> <translation>Send to multiple recipients at once</translation> </message> @@ -2685,17 +2764,17 @@ <translation type="unfinished"></translation> </message> <message> - <location line="-876"/> + <location line="-895"/> <source>Dust:</source> <translation type="unfinished"></translation> </message> <message> - <location line="+691"/> + <location line="+700"/> <source>Confirmation time target:</source> <translation type="unfinished"></translation> </message> <message> - <location line="+188"/> + <location line="+198"/> <source>Clear &All</source> <translation>Clear &All</translation> </message> @@ -2715,7 +2794,7 @@ <translation>S&end</translation> </message> <message> - <location filename="../sendcoinsdialog.cpp" line="-486"/> + <location filename="../sendcoinsdialog.cpp" line="-497"/> <source>Copy quantity</source> <translation type="unfinished"></translation> </message> @@ -2750,7 +2829,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+205"/> + <location line="+209"/> <location line="+5"/> <location line="+5"/> <location line="+4"/> @@ -2778,7 +2857,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+2"/> + <location line="+5"/> + <source>This transaction signals replaceability (optin-RBF).</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+4"/> <source>Confirm send coins</source> <translation type="unfinished"></translation> </message> @@ -2828,7 +2912,7 @@ <translation type="unfinished"></translation> </message> <message numerus="yes"> - <location line="+67"/> + <location line="+63"/> <source>%n block(s)</source> <translation> <numerusform>%n block</numerusform> @@ -2836,12 +2920,12 @@ </translation> </message> <message> - <location line="+28"/> + <location line="+24"/> <source>Pay only the required fee of %1</source> <translation type="unfinished"></translation> </message> <message numerus="yes"> - <location line="+25"/> + <location line="+30"/> <source>Estimated to begin confirmation within %n block(s).</source> <translation> <numerusform>Estimated to begin confirmation within %n block.</numerusform> @@ -2849,7 +2933,7 @@ </translation> </message> <message> - <location line="+102"/> + <location line="+103"/> <source>Warning: Invalid Bitcoin address</source> <translation type="unfinished"></translation> </message> @@ -2986,7 +3070,7 @@ <context> <name>SendConfirmationDialog</name> <message> - <location filename="../sendcoinsdialog.cpp" line="+95"/> + <location filename="../sendcoinsdialog.cpp" line="+86"/> <location line="+5"/> <source>Yes</source> <translation type="unfinished"></translation> @@ -3198,7 +3282,7 @@ <context> <name>TrafficGraphWidget</name> <message> - <location filename="../trafficgraphwidget.cpp" line="+79"/> + <location filename="../trafficgraphwidget.cpp" line="+80"/> <source>KB/s</source> <translation type="unfinished"></translation> </message> @@ -3427,7 +3511,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+21"/> + <location line="+20"/> <source>Amount</source> <translation type="unfinished">Amount</translation> </message> @@ -3460,7 +3544,7 @@ <context> <name>TransactionTableModel</name> <message> - <location filename="../transactiontablemodel.cpp" line="+246"/> + <location filename="../transactiontablemodel.cpp" line="+248"/> <source>Date</source> <translation type="unfinished">Date</translation> </message> @@ -3606,7 +3690,7 @@ <context> <name>TransactionView</name> <message> - <location filename="../transactionview.cpp" line="+69"/> + <location filename="../transactionview.cpp" line="+70"/> <location line="+16"/> <source>All</source> <translation type="unfinished"></translation> @@ -3677,12 +3761,17 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+36"/> + <location line="+37"/> <source>Abandon transaction</source> <translation type="unfinished"></translation> </message> <message> <location line="+1"/> + <source>Increase transaction fee</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> <source>Copy address</source> <translation type="unfinished"></translation> </message> @@ -3722,7 +3811,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+186"/> + <location line="+193"/> <source>Export Transaction History</source> <translation type="unfinished"></translation> </message> @@ -3787,7 +3876,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+147"/> + <location line="+166"/> <source>Range:</source> <translation type="unfinished"></translation> </message> @@ -3816,10 +3905,57 @@ <context> <name>WalletModel</name> <message> - <location filename="../walletmodel.cpp" line="+291"/> + <location filename="../walletmodel.cpp" line="+289"/> <source>Send Coins</source> <translation type="unfinished">Send Coins</translation> </message> + <message> + <location line="+385"/> + <location line="+46"/> + <location line="+9"/> + <source>Fee bump error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="-55"/> + <source>Increasing transaction fee failed</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+6"/> + <source>Do you want to increase the fee?</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+6"/> + <source>Current fee:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+4"/> + <source>Increase:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+4"/> + <source>New fee:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+4"/> + <source>Confirm fee bump</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+22"/> + <source>Can't sign transaction.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+9"/> + <source>Could not commit transaction</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>WalletView</name> @@ -3867,7 +4003,7 @@ <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+318"/> + <location filename="../bitcoinstrings.cpp" line="+330"/> <source>Options:</source> <translation>Options:</translation> </message> @@ -3877,37 +4013,27 @@ <translation>Specify data directory</translation> </message> <message> - <location line="-90"/> + <location line="-92"/> <source>Connect to a node to retrieve peer addresses, and disconnect</source> <translation>Connect to a node to retrieve peer addresses, and disconnect</translation> </message> <message> - <location line="+93"/> + <location line="+95"/> <source>Specify your own public address</source> <translation>Specify your own public address</translation> </message> <message> - <location line="-108"/> + <location line="-111"/> <source>Accept command line and JSON-RPC commands</source> <translation>Accept command line and JSON-RPC commands</translation> </message> <message> - <location line="-221"/> - <source>Accept connections from outside (default: 1 if no -proxy or -connect/-noconnect)</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+22"/> - <source>Connect only to the specified node(s); -noconnect or -connect=0 alone to disable automatic connections</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+12"/> + <location line="-197"/> <source>Distributed under the MIT software license, see the accompanying file %s or %s</source> <translation type="unfinished"></translation> </message> <message> - <location line="+37"/> + <location line="+41"/> <source>If <category> is not supplied or if <category> = 1, output all debugging information.</source> <translation type="unfinished"></translation> </message> @@ -3927,7 +4053,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+132"/> + <location line="+140"/> <source>Error: A fatal internal error occurred, see debug.log for details</source> <translation type="unfinished"></translation> </message> @@ -3937,22 +4063,22 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+40"/> + <location line="+41"/> <source>Pruning blockstore...</source> <translation type="unfinished"></translation> </message> <message> - <location line="+10"/> + <location line="+11"/> <source>Run in the background as a daemon and accept commands</source> <translation>Run in the background as a daemon and accept commands</translation> </message> <message> - <location line="+37"/> + <location line="+36"/> <source>Unable to start HTTP server. See debug log for details.</source> <translation type="unfinished"></translation> </message> <message> - <location line="-360"/> + <location line="-372"/> <source>Bitcoin Core</source> <translation type="unfinished">Bitcoin Core</translation> </message> @@ -3967,7 +4093,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+6"/> + <location line="+3"/> <source>Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: %d)</source> <translation type="unfinished"></translation> </message> @@ -3977,7 +4103,7 @@ <translation>Bind to given address and always listen on it. Use [host]:port notation for IPv6</translation> </message> <message> - <location line="+10"/> + <location line="+12"/> <source>Cannot obtain a lock on data directory %s. %s is probably already running.</source> <translation type="unfinished"></translation> </message> @@ -3987,17 +4113,17 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+13"/> - <source>Error loading %s: You can't enable HD on a already existing non-HD wallet</source> + <location line="+15"/> + <source>Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+2"/> - <source>Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</source> + <location line="+5"/> + <source>Exclude debugging information for a category. Can be used in conjunction with -debug=1 to output debug logs for all categories except one or more specified categories.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+8"/> + <location line="+7"/> <source>Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)</source> <translation>Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)</translation> </message> @@ -4032,7 +4158,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+14"/> + <location line="+8"/> + <source>Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect used)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+6"/> <source>Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >%u = automatically prune block files to stay under the specified target size in MiB)</source> <translation type="unfinished"></translation> </message> @@ -4042,7 +4173,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+5"/> + <location line="+3"/> <source>Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)</source> <translation type="unfinished"></translation> </message> @@ -4058,6 +4189,11 @@ </message> <message> <location line="+15"/> + <source>Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> <source>Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain</source> <translation type="unfinished"></translation> </message> @@ -4087,27 +4223,52 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+12"/> + <location line="+3"/> + <source>Whether to save the mempool on shutdown and load on restart (default: %u)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+11"/> <source>You need to rebuild the database using -reindex-chainstate to change -txindex</source> <translation type="unfinished"></translation> </message> <message> <location line="+2"/> + <source>%d of last 100 blocks have unexpected version</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> <source>%s corrupt, salvage failed</source> <translation type="unfinished"></translation> </message> <message> <location line="+4"/> + <source>(press q to shutdown and continue later)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> <source>-maxmempool must be at least %d MB</source> <translation type="unfinished"></translation> </message> <message> <location line="+1"/> + <source>-wallet parameter must only specify a filename (not a path)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> <source><category> can be:</source> <translation type="unfinished"></translation> </message> <message> - <location line="+6"/> + <location line="+2"/> + <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+5"/> <source>Append comment to the user agent string</source> <translation type="unfinished"></translation> </message> @@ -4217,12 +4378,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+1"/> - <source>Error loading %s: You can't disable HD on a already existing HD wallet</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+1"/> + <location line="+2"/> <source>Error loading block database</source> <translation>Error loading block database</translation> </message> @@ -4232,7 +4388,7 @@ <translation>Error opening block database</translation> </message> <message> - <location line="+4"/> + <location line="+5"/> <source>Error: Disk space is low!</source> <translation>Error: Disk space is low!</translation> </message> @@ -4257,27 +4413,32 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+2"/> - <source>Invalid -onion address: '%s'</source> + <location line="+4"/> + <source>Invalid amount for -%s=<amount>: '%s'</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Invalid amount for -fallbackfee=<amount>: '%s'</source> <translation type="unfinished"></translation> </message> <message> <location line="+2"/> - <source>Invalid amount for -%s=<amount>: '%s'</source> + <source>Invalid characters in -wallet filename</source> <translation type="unfinished"></translation> </message> <message> - <location line="+1"/> - <source>Invalid amount for -fallbackfee=<amount>: '%s'</source> + <location line="+3"/> + <source>Keep the transaction memory pool below <n> megabytes (default: %u)</source> <translation type="unfinished"></translation> </message> <message> <location line="+4"/> - <source>Keep the transaction memory pool below <n> megabytes (default: %u)</source> + <source>Loading P2P addresses...</source> <translation type="unfinished"></translation> </message> <message> - <location line="+5"/> + <location line="+1"/> <source>Loading banlist...</source> <translation type="unfinished"></translation> </message> @@ -4327,12 +4488,17 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+6"/> + <location line="+4"/> + <source>Replaying blocks...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> <source>Rewinding blocks...</source> <translation type="unfinished"></translation> </message> <message> - <location line="+5"/> + <location line="+4"/> <source>Set database cache size in megabytes (%d to %d, default: %d)</source> <translation type="unfinished"></translation> </message> @@ -4372,7 +4538,17 @@ <translation type="unfinished"></translation> </message> <message> + <location line="+1"/> + <source>Unsupported logging category %s=%s.</source> + <translation type="unfinished"></translation> + </message> + <message> <location line="+2"/> + <source>Upgrading UTXO database</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> <source>Use UPnP to map the listening port (default: %u)</source> <translation type="unfinished"></translation> </message> @@ -4392,12 +4568,7 @@ <translation>Verifying blocks...</translation> </message> <message> - <location line="+1"/> - <source>Verifying wallet...</source> - <translation>Verifying wallet...</translation> - </message> - <message> - <location line="+1"/> + <location line="+2"/> <source>Wallet %s resides outside data directory %s</source> <translation>Wallet %s resides outside data directory %s</translation> </message> @@ -4417,7 +4588,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="-358"/> + <location line="-375"/> <source>Allow JSON-RPC connections from specified source. Valid for <ip> 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). This option can be specified multiple times</source> <translation type="unfinished"></translation> </message> @@ -4427,12 +4598,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+3"/> - <source>Bind to given address to listen for JSON-RPC connections. Use [host]:port notation for IPv6. This option can be specified multiple times (default: bind to all interfaces)</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+9"/> + <location line="+14"/> <source>Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)</source> <translation type="unfinished"></translation> </message> @@ -4447,7 +4613,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+2"/> + <location line="+6"/> <source>Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)</source> <translation>Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)</translation> </message> @@ -4477,22 +4643,17 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+18"/> - <source>Set maximum size of high-priority/low-fee transactions in bytes (default: %d)</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+15"/> + <location line="+31"/> <source>The transaction amount is too small to send after the fee has been deducted</source> <translation type="unfinished"></translation> </message> <message> - <location line="+28"/> + <location line="+31"/> <source>Use hierarchical deterministic key generation (HD) after BIP32. Only has effect during wallet creation/first start</source> <translation type="unfinished"></translation> </message> <message> - <location line="+31"/> + <location line="+33"/> <source>Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway</source> <translation type="unfinished"></translation> </message> @@ -4502,12 +4663,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+8"/> + <location line="+9"/> <source>(default: %u)</source> <translation type="unfinished"></translation> </message> <message> - <location line="+4"/> + <location line="+7"/> <source>Accept public REST requests (default: %u)</source> <translation type="unfinished"></translation> </message> @@ -4522,11 +4683,21 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+22"/> + <location line="+19"/> + <source>Error loading %s: You can't disable HD on an already existing HD wallet</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> <source>Error reading from database, shutting down.</source> <translation type="unfinished"></translation> </message> <message> + <location line="+1"/> + <source>Error upgrading chainstate database</source> + <translation type="unfinished"></translation> + </message> + <message> <location line="+8"/> <source>Imports blocks from external blk000??.dat file on startup</source> <translation type="unfinished"></translation> @@ -4537,12 +4708,22 @@ <translation>Information</translation> </message> <message> - <location line="+7"/> - <source>Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)</source> + <location line="+3"/> + <source>Invalid -onion address or hostname: '%s'</source> <translation type="unfinished"></translation> </message> <message> <location line="+1"/> + <source>Invalid -proxy address or hostname: '%s'</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> + <source>Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> <source>Invalid netmask specified in -whitelist: '%s'</source> <translation type="unfinished"></translation> </message> @@ -4572,7 +4753,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+3"/> + <location line="+4"/> <source>Rescan the block chain for missing wallet transactions on startup</source> <translation type="unfinished"></translation> </message> @@ -4582,11 +4763,6 @@ <translation>Send trace/debug info to console instead of debug.log file</translation> </message> <message> - <location line="+1"/> - <source>Send transactions as zero-fee transactions if possible (default: %u)</source> - <translation type="unfinished"></translation> - </message> - <message> <location line="+7"/> <source>Show all debugging options (usage: --help -help-debug)</source> <translation type="unfinished"></translation> @@ -4642,17 +4818,22 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+7"/> + <location line="+8"/> <source>Upgrade wallet to latest format on startup</source> <translation type="unfinished"></translation> </message> <message> - <location line="+4"/> + <location line="+5"/> <source>Username for JSON-RPC connections</source> <translation>Username for JSON-RPC connections</translation> </message> <message> - <location line="+7"/> + <location line="+2"/> + <source>Verifying wallet(s)...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+5"/> <source>Warning</source> <translation>Warning</translation> </message> @@ -4677,27 +4858,22 @@ <translation type="unfinished"></translation> </message> <message> - <location line="-73"/> + <location line="-75"/> <source>Password for JSON-RPC connections</source> <translation>Password for JSON-RPC connections</translation> </message> <message> - <location line="-242"/> + <location line="-251"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation>Execute command when the best block changes (%s in cmd is replaced by block hash)</translation> </message> <message> - <location line="+170"/> + <location line="+177"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>Allow DNS lookups for -addnode, -seednode and -connect</translation> </message> <message> - <location line="+58"/> - <source>Loading addresses...</source> - <translation>Loading addresses...</translation> - </message> - <message> - <location line="-291"/> + <location line="-243"/> <source>(1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)</source> <translation type="unfinished"></translation> </message> @@ -4707,7 +4883,17 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+43"/> + <location line="+19"/> + <source>Bind to given address to listen for JSON-RPC connections. This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -rpcport. Use [host]:port notation for IPv6. This option can be specified multiple times (default: 127.0.0.1 and ::1 i.e., localhost, or if -rpcallowip has been specified, 0.0.0.0 and :: i.e., all addresses)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+8"/> + <source>Connect only to the specified node(s); -connect=0 disables automatic connections</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+15"/> <source>Do not keep transactions in the mempool longer than <n> hours (default: %u)</source> <translation type="unfinished"></translation> </message> @@ -4717,7 +4903,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+24"/> + <location line="+2"/> + <source>Error loading %s: You can't enable HD on an already existing non-HD wallet</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+26"/> <source>Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)</source> <translation type="unfinished"></translation> </message> @@ -4747,12 +4938,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+13"/> - <source>Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect/-noconnect)</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+26"/> + <location line="+37"/> <source>Sets the serialization of raw transaction or block hex returned in non-verbose mode, non-segwit(0) or segwit(1) (default: %d)</source> <translation type="unfinished"></translation> </message> @@ -4782,7 +4968,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+6"/> + <location line="+9"/> <source>Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported.</source> <translation type="unfinished"></translation> </message> @@ -4807,12 +4993,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+7"/> + <location line="+9"/> <source>Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or CIDR notated network (e.g. 1.2.3.0/24). Can be specified multiple times.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+12"/> + <location line="+13"/> <source>%s is set very high!</source> <translation type="unfinished"></translation> </message> @@ -4822,12 +5008,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+8"/> + <location line="+11"/> <source>Always query for peer addresses via DNS lookup (default: %u)</source> <translation type="unfinished"></translation> </message> <message> - <location line="+38"/> + <location line="+39"/> <source>How many blocks to check at startup (default: %u, 0 = all)</source> <translation type="unfinished"></translation> </message> @@ -4837,12 +5023,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+6"/> - <source>Invalid -proxy address: '%s'</source> - <translation>Invalid -proxy address: '%s'</translation> - </message> - <message> - <location line="+7"/> + <location line="+14"/> <source>Keypool ran out, please call keypoolrefill first</source> <translation type="unfinished"></translation> </message> @@ -4977,27 +5158,27 @@ <translation>Unknown network specified in -onlynet: '%s'</translation> </message> <message> - <location line="-80"/> + <location line="-81"/> <source>Insufficient funds</source> <translation>Insufficient funds</translation> </message> <message> - <location line="+14"/> + <location line="+15"/> <source>Loading block index...</source> <translation>Loading block index...</translation> </message> <message> - <location line="-61"/> + <location line="-63"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>Add a node to connect to and attempt to keep the connection open</translation> </message> <message> - <location line="+62"/> + <location line="+64"/> <source>Loading wallet...</source> <translation>Loading wallet...</translation> </message> <message> - <location line="-55"/> + <location line="-57"/> <source>Cannot downgrade wallet</source> <translation>Cannot downgrade wallet</translation> </message> @@ -5007,17 +5188,17 @@ <translation>Cannot write default address</translation> </message> <message> - <location line="+78"/> + <location line="+81"/> <source>Rescanning...</source> <translation>Rescanning...</translation> </message> <message> - <location line="-67"/> + <location line="-70"/> <source>Done loading</source> <translation>Done loading</translation> </message> <message> - <location line="+15"/> + <location line="+16"/> <source>Error</source> <translation>Error</translation> </message> diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp index 470fb6b377..4e88c8802c 100644 --- a/src/qt/recentrequeststablemodel.cpp +++ b/src/qt/recentrequeststablemodel.cpp @@ -11,7 +11,6 @@ #include "clientversion.h" #include "streams.h" -#include <boost/foreach.hpp> RecentRequestsTableModel::RecentRequestsTableModel(CWallet *wallet, WalletModel *parent) : QAbstractTableModel(parent), walletModel(parent) diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 12d2d0f31c..a01886c3ea 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -31,6 +31,25 @@ #include <QTextDocument> #include <QTimer> +static const std::array<int, 9> confTargets = { {2, 4, 6, 12, 24, 48, 144, 504, 1008} }; +int getConfTargetForIndex(int index) { + if (index+1 > static_cast<int>(confTargets.size())) { + return confTargets.back(); + } + if (index < 0) { + return confTargets[0]; + } + return confTargets[index]; +} +int getIndexForConfTarget(int target) { + for (unsigned int i = 0; i < confTargets.size(); i++) { + if (confTargets[i] >= target) { + return i; + } + } + return confTargets.size() - 1; +} + SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *parent) : QDialog(parent), ui(new Ui::SendCoinsDialog), @@ -152,35 +171,41 @@ void SendCoinsDialog::setModel(WalletModel *_model) coinControlUpdateLabels(); // fee section - connect(ui->sliderSmartFee, SIGNAL(valueChanged(int)), this, SLOT(updateSmartFeeLabel())); - connect(ui->sliderSmartFee, SIGNAL(valueChanged(int)), this, SLOT(updateGlobalFeeVariables())); - connect(ui->sliderSmartFee, SIGNAL(valueChanged(int)), this, SLOT(coinControlUpdateLabels())); + for (const int &n : confTargets) { + ui->confTargetSelector->addItem(tr("%1 (%2 blocks)").arg(GUIUtil::formatNiceTimeOffset(n*Params().GetConsensus().nPowTargetSpacing)).arg(n)); + } + connect(ui->confTargetSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(updateSmartFeeLabel())); + connect(ui->confTargetSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(coinControlUpdateLabels())); connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(updateFeeSectionControls())); - connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(updateGlobalFeeVariables())); connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels())); - connect(ui->groupCustomFee, SIGNAL(buttonClicked(int)), this, SLOT(updateGlobalFeeVariables())); connect(ui->groupCustomFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels())); - connect(ui->customFee, SIGNAL(valueChanged()), this, SLOT(updateGlobalFeeVariables())); connect(ui->customFee, SIGNAL(valueChanged()), this, SLOT(coinControlUpdateLabels())); connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(setMinimumFee())); connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(updateFeeSectionControls())); - connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(updateGlobalFeeVariables())); connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels())); + connect(ui->optInRBF, SIGNAL(stateChanged(int)), this, SLOT(updateSmartFeeLabel())); + connect(ui->optInRBF, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels())); ui->customFee->setSingleStep(CWallet::GetRequiredFee(1000)); updateFeeSectionControls(); updateMinFeeLabel(); updateSmartFeeLabel(); - updateGlobalFeeVariables(); // set default rbf checkbox state ui->optInRBF->setCheckState(model->getDefaultWalletRbf() ? Qt::Checked : Qt::Unchecked); // set the smartfee-sliders default value (wallets default conf.target or last stored value) QSettings settings; - if (settings.value("nSmartFeeSliderPosition").toInt() == 0) - ui->sliderSmartFee->setValue(ui->sliderSmartFee->maximum() - model->getDefaultConfirmTarget() + 2); + if (settings.value("nSmartFeeSliderPosition").toInt() != 0) { + // migrate nSmartFeeSliderPosition to nConfTarget + // nConfTarget is available since 0.15 (replaced nSmartFeeSliderPosition) + int nConfirmTarget = 25 - settings.value("nSmartFeeSliderPosition").toInt(); // 25 == old slider range + settings.setValue("nConfTarget", nConfirmTarget); + settings.remove("nSmartFeeSliderPosition"); + } + if (settings.value("nConfTarget").toInt() == 0) + ui->confTargetSelector->setCurrentIndex(getIndexForConfTarget(model->getDefaultConfirmTarget())); else - ui->sliderSmartFee->setValue(settings.value("nSmartFeeSliderPosition").toInt()); + ui->confTargetSelector->setCurrentIndex(getIndexForConfTarget(settings.value("nConfTarget").toInt())); } } @@ -190,7 +215,7 @@ SendCoinsDialog::~SendCoinsDialog() settings.setValue("fFeeSectionMinimized", fFeeMinimized); settings.setValue("nFeeRadio", ui->groupFee->checkedId()); settings.setValue("nCustomFeeRadio", ui->groupCustomFee->checkedId()); - settings.setValue("nSmartFeeSliderPosition", ui->sliderSmartFee->value()); + settings.setValue("nConfTarget", getConfTargetForIndex(ui->confTargetSelector->currentIndex())); settings.setValue("nTransactionFee", (qint64)ui->customFee->value()); settings.setValue("fPayOnlyMinFee", ui->checkBoxMinimumFee->isChecked()); @@ -243,14 +268,10 @@ void SendCoinsDialog::on_sendButton_clicked() CCoinControl ctrl; if (model->getOptionsModel()->getCoinControlFeatures()) ctrl = *CoinControlDialog::coinControl; - if (ui->radioSmartFee->isChecked()) - ctrl.nConfirmTarget = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2; - else - ctrl.nConfirmTarget = 0; - ctrl.signalRbf = ui->optInRBF->isChecked(); + updateCoinControlState(ctrl); - prepareStatus = model->prepareTransaction(currentTransaction, &ctrl); + prepareStatus = model->prepareTransaction(currentTransaction, ctrl); // process prepareStatus and on error generate message shown to user processSendCoinsReturn(prepareStatus, @@ -594,36 +615,17 @@ void SendCoinsDialog::setMinimumFee() void SendCoinsDialog::updateFeeSectionControls() { - ui->sliderSmartFee ->setEnabled(ui->radioSmartFee->isChecked()); + ui->confTargetSelector ->setEnabled(ui->radioSmartFee->isChecked()); ui->labelSmartFee ->setEnabled(ui->radioSmartFee->isChecked()); ui->labelSmartFee2 ->setEnabled(ui->radioSmartFee->isChecked()); ui->labelSmartFee3 ->setEnabled(ui->radioSmartFee->isChecked()); ui->labelFeeEstimation ->setEnabled(ui->radioSmartFee->isChecked()); - ui->labelSmartFeeNormal ->setEnabled(ui->radioSmartFee->isChecked()); - ui->labelSmartFeeFast ->setEnabled(ui->radioSmartFee->isChecked()); - ui->confirmationTargetLabel ->setEnabled(ui->radioSmartFee->isChecked()); ui->checkBoxMinimumFee ->setEnabled(ui->radioCustomFee->isChecked()); ui->labelMinFeeWarning ->setEnabled(ui->radioCustomFee->isChecked()); ui->radioCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked()); ui->customFee ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked()); } -void SendCoinsDialog::updateGlobalFeeVariables() -{ - if (ui->radioSmartFee->isChecked()) - { - int nConfirmTarget = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2; - payTxFee = CFeeRate(0); - - // show the estimated required time for confirmation - ui->confirmationTargetLabel->setText(GUIUtil::formatDurationStr(nConfirmTarget * Params().GetConsensus().nPowTargetSpacing) + " / " + tr("%n block(s)", "", nConfirmTarget)); - } - else - { - payTxFee = CFeeRate(ui->customFee->value()); - } -} - void SendCoinsDialog::updateFeeMinimizedLabel() { if(!model || !model->getOptionsModel()) @@ -645,18 +647,32 @@ void SendCoinsDialog::updateMinFeeLabel() ); } +void SendCoinsDialog::updateCoinControlState(CCoinControl& ctrl) +{ + if (ui->radioCustomFee->isChecked()) { + ctrl.m_feerate = CFeeRate(ui->customFee->value()); + } else { + ctrl.m_feerate.reset(); + } + // Avoid using global defaults when sending money from the GUI + // Either custom fee will be used or if not selected, the confirmation target from dropdown box + ctrl.m_confirm_target = getConfTargetForIndex(ui->confTargetSelector->currentIndex()); + ctrl.signalRbf = ui->optInRBF->isChecked(); +} + void SendCoinsDialog::updateSmartFeeLabel() { if(!model || !model->getOptionsModel()) return; - - int nBlocksToConfirm = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2; + CCoinControl coin_control; + updateCoinControlState(coin_control); + coin_control.m_feerate.reset(); // Explicitly use only fee estimation rate for smart fee labels 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(), - std::max(CWallet::fallbackFee.GetFeePerK(), CWallet::GetRequiredFee(1000))) + "/kB"); + CFeeRate feeRate = CFeeRate(CWallet::GetMinimumFee(1000, coin_control, ::mempool, ::feeEstimator, &feeCalc)); + + ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), feeRate.GetFeePerK()) + "/kB"); + + if (feeCalc.reason == FeeReason::FALLBACK) { ui->labelSmartFee2->show(); // (Smart fee not initialized yet. This usually takes a few blocks...) ui->labelFeeEstimation->setText(""); ui->fallbackFeeWarningLabel->setVisible(true); @@ -667,8 +683,6 @@ void SendCoinsDialog::updateSmartFeeLabel() } else { - 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).", "", feeCalc.returnedTarget)); ui->fallbackFeeWarningLabel->setVisible(false); @@ -727,8 +741,6 @@ void SendCoinsDialog::coinControlFeatureChanged(bool checked) if (!checked && model) // coin control features disabled CoinControlDialog::coinControl->SetNull(); - // make sure we set back the confirmation target - updateGlobalFeeVariables(); coinControlUpdateLabels(); } @@ -819,14 +831,11 @@ void SendCoinsDialog::coinControlUpdateLabels() if (!model || !model->getOptionsModel()) return; + updateCoinControlState(*CoinControlDialog::coinControl); + // 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) { diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index ff7040ac5b..70b4aa5a03 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -68,6 +68,8 @@ private: void processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg = QString()); void minimizeFeeSection(bool fMinimize); void updateFeeMinimizedLabel(); + // Update the passed in CCoinControl with state from the GUI + void updateCoinControlState(CCoinControl& ctrl); private Q_SLOTS: void on_sendButton_clicked(); @@ -91,7 +93,6 @@ private Q_SLOTS: void updateFeeSectionControls(); void updateMinFeeLabel(); void updateSmartFeeLabel(); - void updateGlobalFeeVariables(); Q_SIGNALS: // Fired when a message should be reported to the user diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp index 26dec3c610..fbad9e544a 100644 --- a/src/qt/test/rpcnestedtests.cpp +++ b/src/qt/test/rpcnestedtests.cpp @@ -12,6 +12,7 @@ #include "rpc/server.h" #include "rpcconsole.h" #include "test/testutil.h" +#include "test/test_bitcoin.h" #include "univalue.h" #include "util.h" @@ -35,8 +36,6 @@ void RPCNestedTests::rpcNestedTests() { // do some test setup // could be moved to a more generic place when we add more tests on QT level - const CChainParams& chainparams = Params(); - RegisterAllCoreRPCCommands(tableRPC); tableRPC.appendCommand("rpcNestedTest", &vRPCCommands[0]); ClearDatadirCache(); std::string path = QDir::tempPath().toStdString() + "/" + strprintf("test_bitcoin_qt_%lu_%i", (unsigned long)GetTime(), (int)(GetRand(100000))); @@ -44,15 +43,8 @@ void RPCNestedTests::rpcNestedTests() dir.mkpath("."); ForceSetArg("-datadir", path); //mempool.setSanityCheck(1.0); - pblocktree = new CBlockTreeDB(1 << 20, true); - pcoinsdbview = new CCoinsViewDB(1 << 23, true); - pcoinsTip = new CCoinsViewCache(pcoinsdbview); - InitBlockIndex(chainparams); - { - CValidationState state; - bool ok = ActivateBestChain(state, chainparams); - QVERIFY(ok); - } + + TestingSetup test; SetRPCWarmupFinished(); @@ -145,13 +137,5 @@ void RPCNestedTests::rpcNestedTests() QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest(abc,,)"), std::runtime_error); //don't tollerate empty arguments when using , #endif - UnloadBlockIndex(); - delete pcoinsTip; - pcoinsTip = nullptr; - delete pcoinsdbview; - pcoinsdbview = nullptr; - delete pblocktree; - pblocktree = nullptr; - fs::remove_all(fs::path(path)); } diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index da070da084..03fd734e92 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -12,7 +12,6 @@ #include <stdint.h> -#include <boost/foreach.hpp> /* Return positive answer if transaction should be shown in list. */ diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 6538a80233..ba0e1da0c7 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -19,10 +19,12 @@ #include "keystore.h" #include "validation.h" #include "net.h" // for g_connman +#include "policy/fees.h" #include "policy/rbf.h" #include "sync.h" #include "ui_interface.h" #include "util.h" // for GetBoolArg +#include "wallet/coincontrol.h" #include "wallet/feebumper.h" #include "wallet/wallet.h" #include "wallet/walletdb.h" // for BackupWallet @@ -34,7 +36,6 @@ #include <QSet> #include <QTimer> -#include <boost/foreach.hpp> WalletModel::WalletModel(const PlatformStyle *platformStyle, CWallet *_wallet, OptionsModel *_optionsModel, QObject *parent) : QObject(parent), wallet(_wallet), optionsModel(_optionsModel), addressTableModel(0), @@ -191,7 +192,7 @@ bool WalletModel::validateAddress(const QString &address) return addressParsed.IsValid(); } -WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl *coinControl) +WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl& coinControl) { CAmount total = 0; bool fSubtractFeeFromAmount = false; @@ -258,7 +259,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact return DuplicateAddress; } - CAmount nBalance = getBalance(coinControl); + CAmount nBalance = getBalance(&coinControl); if(total > nBalance) { @@ -667,8 +668,10 @@ bool WalletModel::bumpFee(uint256 hash) { std::unique_ptr<CFeeBumper> feeBump; { + CCoinControl coin_control; + coin_control.signalRbf = true; LOCK2(cs_main, wallet->cs_wallet); - feeBump.reset(new CFeeBumper(wallet, hash, nTxConfirmTarget, false, 0, true)); + feeBump.reset(new CFeeBumper(wallet, hash, coin_control, 0)); } if (feeBump->getResult() != BumpFeeResult::OK) { diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 16b0caed4e..5258dc6699 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -154,7 +154,7 @@ public: }; // prepare transaction for getting txfee before sending coins - SendCoinsReturn prepareTransaction(WalletModelTransaction &transaction, const CCoinControl *coinControl = NULL); + SendCoinsReturn prepareTransaction(WalletModelTransaction &transaction, const CCoinControl& coinControl); // Send coins to a list of recipients SendCoinsReturn sendCoins(WalletModelTransaction &transaction); diff --git a/src/random.cpp b/src/random.cpp index 67efc7d945..1ee6158e4b 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -36,6 +36,10 @@ #include <mutex> +#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) +#include <cpuid.h> +#endif + #include <openssl/err.h> #include <openssl/rand.h> @@ -72,18 +76,9 @@ 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"); + uint32_t eax, ebx, ecx, edx; + if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) && (ecx & CPUID_F1_ECX_RDRAND)) { + LogPrintf("Using RdRand as an additional entropy source\n"); rdrand_supported = true; } hwrand_initialized.store(true); diff --git a/src/reverse_iterator.h b/src/reverse_iterator.h new file mode 100644 index 0000000000..409f895ce0 --- /dev/null +++ b/src/reverse_iterator.h @@ -0,0 +1,39 @@ +// Taken from https://gist.github.com/arvidsson/7231973 + +#ifndef BITCOIN_REVERSE_ITERATOR_HPP +#define BITCOIN_REVERSE_ITERATOR_HPP + +/** + * Template used for reverse iteration in C++11 range-based for loops. + * + * std::vector<int> v = {1, 2, 3, 4, 5}; + * for (auto x : reverse_iterate(v)) + * std::cout << x << " "; + */ + +template <typename T> +class reverse_range +{ + T &x; + +public: + reverse_range(T &x) : x(x) {} + + auto begin() const -> decltype(this->x.rbegin()) + { + return x.rbegin(); + } + + auto end() const -> decltype(this->x.rend()) + { + return x.rend(); + } +}; + +template <typename T> +reverse_range<T> reverse_iterate(T &x) +{ + return reverse_range<T>(x); +} + +#endif // BITCOIN_REVERSE_ITERATOR_HPP diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index c17ca2fa3a..6178a1c7ab 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -798,7 +798,7 @@ static void ApplyStats(CCoinsStats &stats, CHashWriter& ss, const uint256& hash, stats.nTransactions++; for (const auto output : outputs) { ss << VARINT(output.first + 1); - ss << *(const CScriptBase*)(&output.second.out.scriptPubKey); + ss << output.second.out.scriptPubKey; ss << VARINT(output.second.out.nValue); stats.nTransactionOutputs++; stats.nTotalAmount += output.second.out.nValue; diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index cb1539dce5..775ad4b6c9 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -37,6 +37,8 @@ static const CRPCConvertParam vRPCConvertParams[] = { "getnetworkhashps", 1, "height" }, { "sendtoaddress", 1, "amount" }, { "sendtoaddress", 4, "subtractfeefromamount" }, + { "sendtoaddress", 5 , "replaceable" }, + { "sendtoaddress", 6 , "conf_target" }, { "settxfee", 0, "amount" }, { "getreceivedbyaddress", 1, "minconf" }, { "getreceivedbyaccount", 1, "minconf" }, @@ -69,6 +71,8 @@ static const CRPCConvertParam vRPCConvertParams[] = { "sendmany", 1, "amounts" }, { "sendmany", 2, "minconf" }, { "sendmany", 4, "subtractfeefrom" }, + { "sendmany", 5 , "replaceable" }, + { "sendmany", 6 , "conf_target" }, { "addmultisigaddress", 0, "nrequired" }, { "addmultisigaddress", 1, "keys" }, { "createmultisig", 0, "nrequired" }, @@ -79,6 +83,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "listunspent", 3, "include_unsafe" }, { "listunspent", 4, "query_options" }, { "getblock", 1, "verbosity" }, + { "getblock", 1, "verbose" }, { "getblockheader", 1, "verbose" }, { "getchaintxstats", 0, "nblocks" }, { "gettransaction", 1, "include_watchonly" }, @@ -86,7 +91,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "createrawtransaction", 0, "inputs" }, { "createrawtransaction", 1, "outputs" }, { "createrawtransaction", 2, "locktime" }, - { "createrawtransaction", 3, "optintorbf" }, + { "createrawtransaction", 3, "replaceable" }, { "signrawtransaction", 1, "prevtxs" }, { "signrawtransaction", 2, "privkeys" }, { "sendrawtransaction", 1, "allowhighfees" }, @@ -112,7 +117,6 @@ static const CRPCConvertParam vRPCConvertParams[] = { "estimatesmartfee", 1, "conservative" }, { "estimaterawfee", 0, "nblocks" }, { "estimaterawfee", 1, "threshold" }, - { "estimaterawfee", 2, "horizon" }, { "prioritisetransaction", 1, "dummy" }, { "prioritisetransaction", 2, "fee_delta" }, { "setban", 2, "bantime" }, diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 8c682592c5..b8c94d32ec 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" @@ -29,6 +30,16 @@ #include <univalue.h> +unsigned int ParseConfirmTarget(const UniValue& value) +{ + int target = value.get_int(); + unsigned int max_target = ::feeEstimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE); + if (target < 1 || (unsigned int)target > max_target) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid conf_target, must be between %u - %u", 1, max_target)); + } + return (unsigned int)target; +} + /** * Return average network hashes per second based on the last 'lookup' blocks, * or from the last difficulty change if 'lookup' is nonpositive. @@ -141,42 +152,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) @@ -680,15 +655,16 @@ UniValue getblocktemplate(const JSONRPCRequest& request) result.push_back(Pair("mutable", aMutable)); result.push_back(Pair("noncerange", "00000000ffffffff")); int64_t nSigOpLimit = MAX_BLOCK_SIGOPS_COST; + int64_t nSizeLimit = MAX_BLOCK_SERIALIZED_SIZE; if (fPreSegWit) { assert(nSigOpLimit % WITNESS_SCALE_FACTOR == 0); nSigOpLimit /= WITNESS_SCALE_FACTOR; + assert(nSizeLimit % WITNESS_SCALE_FACTOR == 0); + nSizeLimit /= WITNESS_SCALE_FACTOR; } result.push_back(Pair("sigoplimit", nSigOpLimit)); - if (fPreSegWit) { - result.push_back(Pair("sizelimit", (int64_t)MAX_BLOCK_BASE_SIZE)); - } else { - result.push_back(Pair("sizelimit", (int64_t)MAX_BLOCK_SERIALIZED_SIZE)); + result.push_back(Pair("sizelimit", nSizeLimit)); + if (!fPreSegWit) { result.push_back(Pair("weightlimit", (int64_t)MAX_BLOCK_WEIGHT)); } result.push_back(Pair("curtime", pblock->GetBlockTime())); @@ -849,7 +825,6 @@ UniValue estimatesmartfee(const JSONRPCRequest& request) "\n" "A negative value is returned if not enough transactions and blocks\n" "have been observed to make an estimate for any number of blocks.\n" - "However it will not return a value below the mempool reject fee.\n" "\nExample:\n" + HelpExampleCli("estimatesmartfee", "6") ); @@ -865,7 +840,7 @@ UniValue estimatesmartfee(const JSONRPCRequest& request) UniValue result(UniValue::VOBJ); FeeCalculation feeCalc; - CFeeRate feeRate = ::feeEstimator.estimateSmartFee(nBlocks, &feeCalc, ::mempool, conservative); + CFeeRate feeRate = ::feeEstimator.estimateSmartFee(nBlocks, &feeCalc, conservative); result.push_back(Pair("feerate", feeRate == CFeeRate(0) ? -1.0 : ValueFromAmount(feeRate.GetFeePerK()))); result.push_back(Pair("blocks", feeCalc.returnedTarget)); return result; @@ -873,9 +848,9 @@ UniValue estimatesmartfee(const JSONRPCRequest& request) UniValue estimaterawfee(const JSONRPCRequest& request) { - if (request.fHelp || request.params.size() < 1|| request.params.size() > 3) + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw std::runtime_error( - "estimaterawfee nblocks (threshold horizon)\n" + "estimaterawfee nblocks (threshold)\n" "\nWARNING: This interface is unstable and may disappear or change!\n" "\nWARNING: This is an advanced API call that is tightly coupled to the specific\n" " implementation of fee estimation. The parameters it can be called with\n" @@ -884,72 +859,95 @@ UniValue estimaterawfee(const JSONRPCRequest& request) "confirmation within nblocks blocks if possible. Uses virtual transaction size as defined\n" "in BIP 141 (witness data is discounted).\n" "\nArguments:\n" - "1. nblocks (numeric)\n" + "1. nblocks (numeric) Confirmation target in blocks (1 - 1008)\n" "2. threshold (numeric, optional) The proportion of transactions in a given feerate range that must have been\n" " confirmed within nblocks in order to consider those feerates as high enough and proceed to check\n" " lower buckets. Default: 0.95\n" - "3. horizon (numeric, optional) How long a history of estimates to consider. 0=short, 1=medium, 2=long.\n" - " Default: 1\n" "\nResult:\n" "{\n" - " \"feerate\" : x.x, (numeric) estimate fee-per-kilobyte (in BTC)\n" - " \"decay\" : x.x, (numeric) exponential decay (per block) for historical moving average of confirmation data\n" - " \"scale\" : x, (numeric) The resolution of confirmation targets at this time horizon\n" - " \"pass\" : { (json object) information about the lowest range of feerates to succeed in meeting the threshold\n" - " \"startrange\" : x.x, (numeric) start of feerate range\n" - " \"endrange\" : x.x, (numeric) end of feerate range\n" - " \"withintarget\" : x.x, (numeric) number of txs over history horizon in the feerate range that were confirmed within target\n" - " \"totalconfirmed\" : x.x, (numeric) number of txs over history horizon in the feerate range that were confirmed at any point\n" - " \"inmempool\" : x.x, (numeric) current number of txs in mempool in the feerate range unconfirmed for at least target blocks\n" - " \"leftmempool\" : x.x, (numeric) number of txs over history horizon in the feerate range that left mempool unconfirmed after target\n" - " }\n" - " \"fail\" : { ... } (json object) information about the highest range of feerates to fail to meet the threshold\n" + " \"short\" : { (json object, optional) estimate for short time horizon\n" + " \"feerate\" : x.x, (numeric, optional) estimate fee-per-kilobyte (in BTC)\n" + " \"decay\" : x.x, (numeric) exponential decay (per block) for historical moving average of confirmation data\n" + " \"scale\" : x, (numeric) The resolution of confirmation targets at this time horizon\n" + " \"pass\" : { (json object, optional) information about the lowest range of feerates to succeed in meeting the threshold\n" + " \"startrange\" : x.x, (numeric) start of feerate range\n" + " \"endrange\" : x.x, (numeric) end of feerate range\n" + " \"withintarget\" : x.x, (numeric) number of txs over history horizon in the feerate range that were confirmed within target\n" + " \"totalconfirmed\" : x.x, (numeric) number of txs over history horizon in the feerate range that were confirmed at any point\n" + " \"inmempool\" : x.x, (numeric) current number of txs in mempool in the feerate range unconfirmed for at least target blocks\n" + " \"leftmempool\" : x.x, (numeric) number of txs over history horizon in the feerate range that left mempool unconfirmed after target\n" + " },\n" + " \"fail\" : { ... }, (json object, optional) information about the highest range of feerates to fail to meet the threshold\n" + " \"errors\": [ str... ] (json array of strings, optional) Errors encountered during processing\n" + " },\n" + " \"medium\" : { ... }, (json object, optional) estimate for medium time horizon\n" + " \"long\" : { ... } (json object) estimate for long time horizon\n" "}\n" "\n" - "A negative feerate is returned if no answer can be given.\n" + "Results are returned for any horizon which tracks blocks up to the confirmation target.\n" "\nExample:\n" - + HelpExampleCli("estimaterawfee", "6 0.9 1") + + HelpExampleCli("estimaterawfee", "6 0.9") ); RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VNUM, UniValue::VNUM}, true); RPCTypeCheckArgument(request.params[0], UniValue::VNUM); int nBlocks = request.params[0].get_int(); + if (nBlocks < 1 || (unsigned int)nBlocks > ::feeEstimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid nblocks"); + } double threshold = 0.95; - if (!request.params[1].isNull()) + if (!request.params[1].isNull()) { threshold = request.params[1].get_real(); - FeeEstimateHorizon horizon = FeeEstimateHorizon::MED_HALFLIFE; - if (!request.params[2].isNull()) { - int horizonInt = request.params[2].get_int(); - if (horizonInt < 0 || horizonInt > 2) { - throw JSONRPCError(RPC_TYPE_ERROR, "Invalid horizon for fee estimates"); - } else { - horizon = (FeeEstimateHorizon)horizonInt; - } } + if (threshold < 0 || threshold > 1) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid threshold"); + } + UniValue result(UniValue::VOBJ); - CFeeRate feeRate; - EstimationResult buckets; - feeRate = ::feeEstimator.estimateRawFee(nBlocks, threshold, horizon, &buckets); - result.push_back(Pair("feerate", feeRate == CFeeRate(0) ? -1.0 : ValueFromAmount(feeRate.GetFeePerK()))); - result.push_back(Pair("decay", buckets.decay)); - result.push_back(Pair("scale", (int)buckets.scale)); - UniValue passbucket(UniValue::VOBJ); - passbucket.push_back(Pair("startrange", round(buckets.pass.start))); - passbucket.push_back(Pair("endrange", round(buckets.pass.end))); - passbucket.push_back(Pair("withintarget", round(buckets.pass.withinTarget * 100.0) / 100.0)); - passbucket.push_back(Pair("totalconfirmed", round(buckets.pass.totalConfirmed * 100.0) / 100.0)); - passbucket.push_back(Pair("inmempool", round(buckets.pass.inMempool * 100.0) / 100.0)); - passbucket.push_back(Pair("leftmempool", round(buckets.pass.leftMempool * 100.0) / 100.0)); - result.push_back(Pair("pass", passbucket)); - UniValue failbucket(UniValue::VOBJ); - failbucket.push_back(Pair("startrange", round(buckets.fail.start))); - failbucket.push_back(Pair("endrange", round(buckets.fail.end))); - failbucket.push_back(Pair("withintarget", round(buckets.fail.withinTarget * 100.0) / 100.0)); - failbucket.push_back(Pair("totalconfirmed", round(buckets.fail.totalConfirmed * 100.0) / 100.0)); - failbucket.push_back(Pair("inmempool", round(buckets.fail.inMempool * 100.0) / 100.0)); - failbucket.push_back(Pair("leftmempool", round(buckets.fail.leftMempool * 100.0) / 100.0)); - result.push_back(Pair("fail", failbucket)); + for (FeeEstimateHorizon horizon : {FeeEstimateHorizon::SHORT_HALFLIFE, FeeEstimateHorizon::MED_HALFLIFE, FeeEstimateHorizon::LONG_HALFLIFE}) { + CFeeRate feeRate; + EstimationResult buckets; + + // Only output results for horizons which track the target + if ((unsigned int)nBlocks > ::feeEstimator.HighestTargetTracked(horizon)) continue; + + feeRate = ::feeEstimator.estimateRawFee(nBlocks, threshold, horizon, &buckets); + UniValue horizon_result(UniValue::VOBJ); + UniValue errors(UniValue::VARR); + UniValue passbucket(UniValue::VOBJ); + passbucket.push_back(Pair("startrange", round(buckets.pass.start))); + passbucket.push_back(Pair("endrange", round(buckets.pass.end))); + passbucket.push_back(Pair("withintarget", round(buckets.pass.withinTarget * 100.0) / 100.0)); + passbucket.push_back(Pair("totalconfirmed", round(buckets.pass.totalConfirmed * 100.0) / 100.0)); + passbucket.push_back(Pair("inmempool", round(buckets.pass.inMempool * 100.0) / 100.0)); + passbucket.push_back(Pair("leftmempool", round(buckets.pass.leftMempool * 100.0) / 100.0)); + UniValue failbucket(UniValue::VOBJ); + failbucket.push_back(Pair("startrange", round(buckets.fail.start))); + failbucket.push_back(Pair("endrange", round(buckets.fail.end))); + failbucket.push_back(Pair("withintarget", round(buckets.fail.withinTarget * 100.0) / 100.0)); + failbucket.push_back(Pair("totalconfirmed", round(buckets.fail.totalConfirmed * 100.0) / 100.0)); + failbucket.push_back(Pair("inmempool", round(buckets.fail.inMempool * 100.0) / 100.0)); + failbucket.push_back(Pair("leftmempool", round(buckets.fail.leftMempool * 100.0) / 100.0)); + + // CFeeRate(0) is used to indicate error as a return value from estimateRawFee + if (feeRate != CFeeRate(0)) { + horizon_result.push_back(Pair("feerate", ValueFromAmount(feeRate.GetFeePerK()))); + horizon_result.push_back(Pair("decay", buckets.decay)); + horizon_result.push_back(Pair("scale", (int)buckets.scale)); + horizon_result.push_back(Pair("pass", passbucket)); + // buckets.fail.start == -1 indicates that all buckets passed, there is no fail bucket to output + if (buckets.fail.start != -1) horizon_result.push_back(Pair("fail", failbucket)); + } else { + // Output only information that is still meaningful in the event of error + horizon_result.push_back(Pair("decay", buckets.decay)); + horizon_result.push_back(Pair("scale", (int)buckets.scale)); + horizon_result.push_back(Pair("fail", failbucket)); + errors.push_back("Insufficient data or no feerate found which meets threshold"); + horizon_result.push_back(Pair("errors",errors)); + } + result.push_back(Pair(StringForFeeEstimateHorizon(horizon), horizon_result)); + } return result; } @@ -962,13 +960,12 @@ static const CRPCCommand commands[] = { "mining", "getblocktemplate", &getblocktemplate, true, {"template_request"} }, { "mining", "submitblock", &submitblock, true, {"hexdata","dummy"} }, - { "generating", "generate", &generate, true, {"nblocks","maxtries"} }, { "generating", "generatetoaddress", &generatetoaddress, true, {"nblocks","address","maxtries"} }, { "util", "estimatefee", &estimatefee, true, {"nblocks"} }, { "util", "estimatesmartfee", &estimatesmartfee, true, {"nblocks", "conservative"} }, - { "hidden", "estimaterawfee", &estimaterawfee, true, {"nblocks", "threshold", "horizon"} }, + { "hidden", "estimaterawfee", &estimaterawfee, true, {"nblocks", "threshold"} }, }; void RegisterMiningRPCCommands(CRPCTable &t) diff --git a/src/rpc/mining.h b/src/rpc/mining.h new file mode 100644 index 0000000000..868d7002b5 --- /dev/null +++ b/src/rpc/mining.h @@ -0,0 +1,18 @@ +// 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); + +/** Check bounds on a command line confirm target */ +unsigned int ParseConfirmTarget(const UniValue& value); + +#endif diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index ef19e481c2..fcbbe1ceed 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -174,6 +174,14 @@ UniValue validateaddress(const JSONRPCRequest& request) " \"ismine\" : true|false, (boolean) If the address is yours or not\n" " \"iswatchonly\" : true|false, (boolean) If the address is watchonly\n" " \"isscript\" : true|false, (boolean) If the key is a script\n" + " \"script\" : \"type\" (string, optional) The output script type. Possible types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash, witness_v0_scripthash\n" + " \"hex\" : \"hex\", (string, optional) The redeemscript for the p2sh address\n" + " \"addresses\" (string, optional) Array of addresses associated with the known redeemscript\n" + " [\n" + " \"address\"\n" + " ,...\n" + " ]\n" + " \"sigsrequired\" : xxxxx (numeric, optional) Number of signatures required to spend multisig output\n" " \"pubkey\" : \"publickeyhex\", (string) The hex value of the raw public key\n" " \"iscompressed\" : true|false, (boolean) If the address is compressed\n" " \"account\" : \"account\" (string) DEPRECATED. The account associated with the address, \"\" is the default account\n" diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 5cab0ad5bd..ed452fcb02 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -19,7 +19,6 @@ #include "utilstrencodings.h" #include "version.h" -#include <boost/foreach.hpp> #include <univalue.h> @@ -302,9 +301,8 @@ UniValue getaddednodeinfo(const JSONRPCRequest& request) " ,...\n" "]\n" "\nExamples:\n" - + HelpExampleCli("getaddednodeinfo", "true") - + HelpExampleCli("getaddednodeinfo", "true \"192.168.0.201\"") - + HelpExampleRpc("getaddednodeinfo", "true, \"192.168.0.201\"") + + HelpExampleCli("getaddednodeinfo", "\"192.168.0.201\"") + + HelpExampleRpc("getaddednodeinfo", "\"192.168.0.201\"") ); if(!g_connman) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 527a4d6974..b878624df8 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -294,7 +294,7 @@ UniValue createrawtransaction(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) throw std::runtime_error( - "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,\"data\":\"hex\",...} ( locktime ) ( optintorbf )\n" + "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,\"data\":\"hex\",...} ( locktime ) ( replaceable )\n" "\nCreate a transaction spending the given inputs and creating new outputs.\n" "Outputs can be addresses or data.\n" "Returns hex-encoded raw transaction.\n" @@ -318,7 +318,8 @@ UniValue createrawtransaction(const JSONRPCRequest& request) " ,...\n" " }\n" "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n" - "4. optintorbf (boolean, optional, default=false) Allow this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\n" + "4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125 replaceable.\n" + " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\n" "\nResult:\n" "\"transaction\" (string) hex string of the transaction\n" @@ -412,7 +413,7 @@ UniValue createrawtransaction(const JSONRPCRequest& request) } if (request.params.size() > 3 && rbfOptIn != SignalsOptInRBF(rawTx)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict optintorbf option"); + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option"); } return EncodeHexTx(rawTx); @@ -900,7 +901,7 @@ static const CRPCCommand commands[] = { // category name actor (function) okSafeMode // --------------------- ------------------------ ----------------------- ---------- { "rawtransactions", "getrawtransaction", &getrawtransaction, true, {"txid","verbose"} }, - { "rawtransactions", "createrawtransaction", &createrawtransaction, true, {"inputs","outputs","locktime"} }, + { "rawtransactions", "createrawtransaction", &createrawtransaction, true, {"inputs","outputs","locktime","replaceable"} }, { "rawtransactions", "decoderawtransaction", &decoderawtransaction, true, {"hexstring"} }, { "rawtransactions", "decodescript", &decodescript, true, {"hexstring"} }, { "rawtransactions", "sendrawtransaction", &sendrawtransaction, false, {"hexstring","allowhighfees"} }, diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index c320d20453..63e4e9c630 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -17,7 +17,6 @@ #include <univalue.h> #include <boost/bind.hpp> -#include <boost/foreach.hpp> #include <boost/signals2/signal.hpp> #include <boost/algorithm/string/case_conv.hpp> // for to_upper() #include <boost/algorithm/string/classification.hpp> diff --git a/src/scheduler.cpp b/src/scheduler.cpp index 923ba2c231..36a6d5110d 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -139,3 +139,69 @@ size_t CScheduler::getQueueInfo(boost::chrono::system_clock::time_point &first, } return result; } + +bool CScheduler::AreThreadsServicingQueue() const { + return nThreadsServicingQueue; +} + + +void SingleThreadedSchedulerClient::MaybeScheduleProcessQueue() { + { + LOCK(m_cs_callbacks_pending); + // Try to avoid scheduling too many copies here, but if we + // accidentally have two ProcessQueue's scheduled at once its + // not a big deal. + if (m_are_callbacks_running) return; + if (m_callbacks_pending.empty()) return; + } + m_pscheduler->schedule(std::bind(&SingleThreadedSchedulerClient::ProcessQueue, this)); +} + +void SingleThreadedSchedulerClient::ProcessQueue() { + std::function<void (void)> callback; + { + LOCK(m_cs_callbacks_pending); + if (m_are_callbacks_running) return; + if (m_callbacks_pending.empty()) return; + m_are_callbacks_running = true; + + callback = std::move(m_callbacks_pending.front()); + m_callbacks_pending.pop_front(); + } + + // RAII the setting of fCallbacksRunning and calling MaybeScheduleProcessQueue + // to ensure both happen safely even if callback() throws. + struct RAIICallbacksRunning { + SingleThreadedSchedulerClient* instance; + RAIICallbacksRunning(SingleThreadedSchedulerClient* _instance) : instance(_instance) {} + ~RAIICallbacksRunning() { + { + LOCK(instance->m_cs_callbacks_pending); + instance->m_are_callbacks_running = false; + } + instance->MaybeScheduleProcessQueue(); + } + } raiicallbacksrunning(this); + + callback(); +} + +void SingleThreadedSchedulerClient::AddToProcessQueue(std::function<void (void)> func) { + assert(m_pscheduler); + + { + LOCK(m_cs_callbacks_pending); + m_callbacks_pending.emplace_back(std::move(func)); + } + MaybeScheduleProcessQueue(); +} + +void SingleThreadedSchedulerClient::EmptyQueue() { + assert(!m_pscheduler->AreThreadsServicingQueue()); + bool should_continue = true; + while (should_continue) { + ProcessQueue(); + LOCK(m_cs_callbacks_pending); + should_continue = !m_callbacks_pending.empty(); + } +} diff --git a/src/scheduler.h b/src/scheduler.h index 27412a15b4..0365d668b2 100644 --- a/src/scheduler.h +++ b/src/scheduler.h @@ -14,6 +14,8 @@ #include <boost/thread.hpp> #include <map> +#include "sync.h" + // // Simple class for background tasks that should be run // periodically or once "after a while" @@ -41,7 +43,7 @@ public: typedef std::function<void(void)> Function; // Call func at/after time t - void schedule(Function f, boost::chrono::system_clock::time_point t); + void schedule(Function f, boost::chrono::system_clock::time_point t=boost::chrono::system_clock::now()); // Convenience method: call f once deltaSeconds from now void scheduleFromNow(Function f, int64_t deltaMilliSeconds); @@ -69,6 +71,9 @@ public: size_t getQueueInfo(boost::chrono::system_clock::time_point &first, boost::chrono::system_clock::time_point &last) const; + // Returns true if there are threads actively running in serviceQueue() + bool AreThreadsServicingQueue() const; + private: std::multimap<boost::chrono::system_clock::time_point, Function> taskQueue; boost::condition_variable newTaskScheduled; @@ -79,4 +84,30 @@ private: bool shouldStop() { return stopRequested || (stopWhenEmpty && taskQueue.empty()); } }; +/** + * Class used by CScheduler clients which may schedule multiple jobs + * which are required to be run serially. Does not require such jobs + * to be executed on the same thread, but no two jobs will be executed + * at the same time. + */ +class SingleThreadedSchedulerClient { +private: + CScheduler *m_pscheduler; + + CCriticalSection m_cs_callbacks_pending; + std::list<std::function<void (void)>> m_callbacks_pending; + bool m_are_callbacks_running = false; + + void MaybeScheduleProcessQueue(); + void ProcessQueue(); + +public: + SingleThreadedSchedulerClient(CScheduler *pschedulerIn) : m_pscheduler(pschedulerIn) {} + void AddToProcessQueue(std::function<void (void)> func); + + // Processes all remaining queue members on the calling thread, blocking until queue is empty + // Must be called after the CScheduler has no remaining processing threads! + void EmptyQueue(); +}; + #endif diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 7149c938fc..8a121774a0 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1099,7 +1099,7 @@ public: // Serialize the script if (nInput != nIn) // Blank out other inputs' signatures - ::Serialize(s, CScriptBase()); + ::Serialize(s, CScript()); else SerializeScriptCode(s); // Serialize the nSequence @@ -1207,7 +1207,7 @@ uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsig // The prevout may already be contained in hashPrevout, and the nSequence // may already be contain in hashSequence. ss << txTo.vin[nIn].prevout; - ss << static_cast<const CScriptBase&>(scriptCode); + ss << scriptCode; ss << amount; ss << txTo.vin[nIn].nSequence; // Outputs (none/one/all, depending on flags) diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp index 35b534344e..0a39619734 100644 --- a/src/script/ismine.cpp +++ b/src/script/ismine.cpp @@ -11,7 +11,6 @@ #include "script/standard.h" #include "script/sign.h" -#include <boost/foreach.hpp> typedef std::vector<unsigned char> valtype; diff --git a/src/script/script.h b/src/script/script.h index bbb37f049e..d16bfd0e00 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -8,6 +8,7 @@ #include "crypto/common.h" #include "prevector.h" +#include "serialize.h" #include <assert.h> #include <climits> @@ -404,6 +405,13 @@ public: CScript(std::vector<unsigned char>::const_iterator pbegin, std::vector<unsigned char>::const_iterator pend) : CScriptBase(pbegin, pend) { } CScript(const unsigned char* pbegin, const unsigned char* pend) : CScriptBase(pbegin, pend) { } + ADD_SERIALIZE_METHODS; + + template <typename Stream, typename Operation> + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(static_cast<CScriptBase&>(*this)); + } + CScript& operator+=(const CScript& b) { insert(end(), b.begin(), b.end()); diff --git a/src/script/sign.cpp b/src/script/sign.cpp index ec93c5451b..dc50467d3f 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -12,7 +12,6 @@ #include "script/standard.h" #include "uint256.h" -#include <boost/foreach.hpp> typedef std::vector<unsigned char> valtype; diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 8e08acf0c6..760a5305e5 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -10,7 +10,6 @@ #include "util.h" #include "utilstrencodings.h" -#include <boost/foreach.hpp> typedef std::vector<unsigned char> valtype; diff --git a/src/serialize.h b/src/serialize.h index e82ddf2c5a..8b86a07a76 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -450,7 +450,7 @@ public: } string.resize(size); if (size != 0) - s.read((char*)&string[0], size); + s.read((char*)string.data(), size); } template<typename Stream> @@ -458,7 +458,7 @@ public: { WriteCompactSize(s, string.size()); if (!string.empty()) - s.write((char*)&string[0], string.size()); + s.write((char*)string.data(), string.size()); } }; @@ -556,7 +556,7 @@ void Serialize(Stream& os, const std::basic_string<C>& str) { WriteCompactSize(os, str.size()); if (!str.empty()) - os.write((char*)&str[0], str.size() * sizeof(str[0])); + os.write((char*)str.data(), str.size() * sizeof(C)); } template<typename Stream, typename C> @@ -565,7 +565,7 @@ void Unserialize(Stream& is, std::basic_string<C>& str) unsigned int nSize = ReadCompactSize(is); str.resize(nSize); if (nSize != 0) - is.read((char*)&str[0], nSize * sizeof(str[0])); + is.read((char*)str.data(), nSize * sizeof(C)); } @@ -578,7 +578,7 @@ void Serialize_impl(Stream& os, const prevector<N, T>& v, const unsigned char&) { WriteCompactSize(os, v.size()); if (!v.empty()) - os.write((char*)&v[0], v.size() * sizeof(T)); + os.write((char*)v.data(), v.size() * sizeof(T)); } template<typename Stream, unsigned int N, typename T, typename V> @@ -646,7 +646,7 @@ void Serialize_impl(Stream& os, const std::vector<T, A>& v, const unsigned char& { WriteCompactSize(os, v.size()); if (!v.empty()) - os.write((char*)&v[0], v.size() * sizeof(T)); + os.write((char*)v.data(), v.size() * sizeof(T)); } template<typename Stream, typename T, typename A, typename V> diff --git a/src/streams.h b/src/streams.h index 8dc5a19ead..245fb9cd8f 100644 --- a/src/streams.h +++ b/src/streams.h @@ -389,7 +389,7 @@ public: { // Special case: stream << stream concatenates like stream += stream if (!vch.empty()) - s.write((char*)&vch[0], vch.size() * sizeof(vch[0])); + s.write((char*)vch.data(), vch.size() * sizeof(value_type)); } template<typename T> diff --git a/src/sync.cpp b/src/sync.cpp index 94f2cafa98..c359e8220b 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -9,7 +9,6 @@ #include <stdio.h> -#include <boost/foreach.hpp> #include <boost/thread.hpp> #ifdef DEBUG_LOCKCONTENTION diff --git a/src/test/data/script_tests.json b/src/test/data/script_tests.json index 0390d6806d..698e898231 100644 --- a/src/test/data/script_tests.json +++ b/src/test/data/script_tests.json @@ -240,7 +240,7 @@ ["0", "IF NOP10 ENDIF 1", "P2SH,STRICTENC,DISCOURAGE_UPGRADABLE_NOPS", "OK", "Discouraged NOPs are allowed if not executed"], -["0", "IF 0xba ELSE 1 ENDIF", "P2SH,STRICTENC", "OK", "opcodes above NOP10 invalid if executed"], +["0", "IF 0xba ELSE 1 ENDIF", "P2SH,STRICTENC", "OK", "opcodes above MAX_OPCODE invalid if executed"], ["0", "IF 0xbb ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], ["0", "IF 0xbc ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], ["0", "IF 0xbd ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], @@ -878,7 +878,7 @@ "P2SH,DISCOURAGE_UPGRADABLE_NOPS", "DISCOURAGE_UPGRADABLE_NOPS", "Discouraged NOP10 in redeemScript"], ["0x50","1", "P2SH,STRICTENC", "BAD_OPCODE", "opcode 0x50 is reserved"], -["1", "IF 0xba ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE", "opcodes above NOP10 invalid if executed"], +["1", "IF 0xba ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE", "opcodes above MAX_OPCODE invalid if executed"], ["1", "IF 0xbb ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], ["1", "IF 0xbc ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], ["1", "IF 0xbd ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], @@ -1001,7 +1001,7 @@ ["1","RESERVED", "P2SH,STRICTENC", "BAD_OPCODE", "OP_RESERVED is reserved"], ["1","RESERVED1", "P2SH,STRICTENC", "BAD_OPCODE", "OP_RESERVED1 is reserved"], ["1","RESERVED2", "P2SH,STRICTENC", "BAD_OPCODE", "OP_RESERVED2 is reserved"], -["1","0xba", "P2SH,STRICTENC", "BAD_OPCODE", "0xba == OP_NOP10 + 1"], +["1","0xba", "P2SH,STRICTENC", "BAD_OPCODE", "0xba == MAX_OPCODE + 1"], ["2147483648", "1ADD 1", "P2SH,STRICTENC", "UNKNOWN_ERROR", "We cannot do math on 5-byte integers"], ["2147483648", "NEGATE 1", "P2SH,STRICTENC", "UNKNOWN_ERROR", "We cannot do math on 5-byte integers"], diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp index be631ce7a6..6ed6e7744e 100644 --- a/src/test/dbwrapper_tests.cpp +++ b/src/test/dbwrapper_tests.cpp @@ -24,8 +24,7 @@ BOOST_FIXTURE_TEST_SUITE(dbwrapper_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(dbwrapper) { // Perform tests both obfuscated and non-obfuscated. - for (int i = 0; i < 2; i++) { - bool obfuscate = (bool)i; + for (bool obfuscate : {false, true}) { fs::path ph = fs::temp_directory_path() / fs::unique_path(); CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate); char key = 'k'; @@ -45,8 +44,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper) BOOST_AUTO_TEST_CASE(dbwrapper_batch) { // Perform tests both obfuscated and non-obfuscated. - for (int i = 0; i < 2; i++) { - bool obfuscate = (bool)i; + for (bool obfuscate : {false, true}) { fs::path ph = fs::temp_directory_path() / fs::unique_path(); CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate); @@ -82,8 +80,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper_batch) BOOST_AUTO_TEST_CASE(dbwrapper_iterator) { // Perform tests both obfuscated and non-obfuscated. - for (int i = 0; i < 2; i++) { - bool obfuscate = (bool)i; + for (bool obfuscate : {false, true}) { fs::path ph = fs::temp_directory_path() / fs::unique_path(); CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate); @@ -95,7 +92,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper_iterator) uint256 in2 = InsecureRand256(); BOOST_CHECK(dbw.Write(key2, in2)); - std::unique_ptr<CDBIterator> it(const_cast<CDBWrapper*>(&dbw)->NewIterator()); + std::unique_ptr<CDBIterator> it(const_cast<CDBWrapper&>(dbw).NewIterator()); // Be sure to seek past the obfuscation key (if it exists) it->Seek(key); @@ -210,13 +207,8 @@ BOOST_AUTO_TEST_CASE(iterator_ordering) BOOST_CHECK(dbw.Write(key, value)); } - std::unique_ptr<CDBIterator> it(const_cast<CDBWrapper*>(&dbw)->NewIterator()); - for (int c=0; c<2; ++c) { - int seek_start; - if (c == 0) - seek_start = 0x00; - else - seek_start = 0x80; + std::unique_ptr<CDBIterator> it(const_cast<CDBWrapper&>(dbw).NewIterator()); + for (int seek_start : {0x00, 0x80}) { it->Seek((uint8_t)seek_start); for (int x=seek_start; x<256; ++x) { uint8_t key; @@ -286,13 +278,8 @@ BOOST_AUTO_TEST_CASE(iterator_string_ordering) } } - std::unique_ptr<CDBIterator> it(const_cast<CDBWrapper*>(&dbw)->NewIterator()); - for (int c=0; c<2; ++c) { - int seek_start; - if (c == 0) - seek_start = 0; - else - seek_start = 5; + std::unique_ptr<CDBIterator> it(const_cast<CDBWrapper&>(dbw).NewIterator()); + for (int seek_start : {0, 5}) { snprintf(buf, sizeof(buf), "%d", seek_start); StringContentsSerializer seek_key(buf); it->Seek(seek_key); diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp index 01cc5ed831..c79675f5a6 100644 --- a/src/test/getarg_tests.cpp +++ b/src/test/getarg_tests.cpp @@ -9,7 +9,6 @@ #include <vector> #include <boost/algorithm/string.hpp> -#include <boost/foreach.hpp> #include <boost/test/unit_test.hpp> BOOST_FIXTURE_TEST_SUITE(getarg_tests, BasicTestingSetup) diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index 6d8aab887b..c686f679c2 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -14,7 +14,6 @@ #include "test/test_bitcoin.h" -#include <boost/foreach.hpp> #include <boost/test/unit_test.hpp> typedef std::vector<unsigned char> valtype; diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp index b45a7fcc57..1baf7643e5 100644 --- a/src/test/netbase_tests.cpp +++ b/src/test/netbase_tests.cpp @@ -4,6 +4,7 @@ #include "netbase.h" #include "test/test_bitcoin.h" +#include "utilstrencodings.h" #include <string> diff --git a/src/test/policyestimator_tests.cpp b/src/test/policyestimator_tests.cpp index 6bfd315647..fd8f7191f4 100644 --- a/src/test/policyestimator_tests.cpp +++ b/src/test/policyestimator_tests.cpp @@ -177,16 +177,6 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) for (int i = 2; i < 9; i++) { // At 9, the original estimate was already at the bottom (b/c scale = 2) BOOST_CHECK(feeEst.estimateFee(i).GetFeePerK() < origFeeEst[i-1] - deltaFee); } - - // Test that if the mempool is limited, estimateSmartFee won't return a value below the mempool min fee - mpool.addUnchecked(tx.GetHash(), entry.Fee(feeV[5]).Time(GetTime()).Height(blocknum).FromTx(tx)); - // evict that transaction which should set a mempool min fee of minRelayTxFee + feeV[5] - mpool.TrimToSize(1); - BOOST_CHECK(mpool.GetMinFee(1).GetFeePerK() > feeV[5]); - for (int i = 1; i < 10; i++) { - BOOST_CHECK(feeEst.estimateSmartFee(i, NULL, mpool).GetFeePerK() >= feeEst.estimateRawFee(i, 0.85, FeeEstimateHorizon::MED_HALFLIFE).GetFeePerK()); - BOOST_CHECK(feeEst.estimateSmartFee(i, NULL, mpool).GetFeePerK() >= mpool.GetMinFee(1).GetFeePerK()); - } } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp index 11bb11d1e9..345c4a2148 100644 --- a/src/test/prevector_tests.cpp +++ b/src/test/prevector_tests.cpp @@ -5,6 +5,7 @@ #include <vector> #include "prevector.h" +#include "reverse_iterator.h" #include "serialize.h" #include "streams.h" @@ -56,13 +57,13 @@ class prevector_tester { for (const T& v : pre_vector) { local_check(v == real_vector[pos++]); } - BOOST_REVERSE_FOREACH(const T& v, pre_vector) { + for (const T& v : reverse_iterate(pre_vector)) { local_check(v == real_vector[--pos]); } for (const T& v : const_pre_vector) { local_check(v == real_vector[pos++]); } - BOOST_REVERSE_FOREACH(const T& v, const_pre_vector) { + for (const T& v : reverse_iterate(const_pre_vector)) { local_check(v == real_vector[--pos]); } CDataStream ss1(SER_DISK, 0); diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 1bb191c73d..a18471588a 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -24,7 +24,6 @@ #include <string> #include <vector> -#include <boost/foreach.hpp> #include <boost/test/unit_test.hpp> #include <univalue.h> diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp index eddb80aed5..d3b8b07228 100644 --- a/src/test/sigopcount_tests.cpp +++ b/src/test/sigopcount_tests.cpp @@ -3,6 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "consensus/tx_verify.h" +#include "consensus/validation.h" #include "pubkey.h" #include "key.h" #include "script/script.h" diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 579e96524c..3ba81ed17b 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -62,6 +62,12 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha pathTemp = GetTempPath() / strprintf("test_bitcoin_%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(100000))); fs::create_directories(pathTemp); ForceSetArg("-datadir", pathTemp.string()); + + // Note that because we don't bother running a scheduler thread here, + // callbacks via CValidationInterface are unreliable, but that's OK, + // our unit tests aren't testing multiple parts of the code at once. + GetMainSignals().RegisterBackgroundSignalScheduler(scheduler); + mempool.setSanityCheck(1.0); pblocktree = new CBlockTreeDB(1 << 20, true); pcoinsdbview = new CCoinsViewDB(1 << 23, true); @@ -88,6 +94,8 @@ TestingSetup::~TestingSetup() UnregisterNodeSignals(GetNodeSignals()); threadGroup.interrupt_all(); threadGroup.join_all(); + GetMainSignals().FlushBackgroundCallbacks(); + GetMainSignals().UnregisterBackgroundSignalScheduler(); UnloadBlockIndex(); delete pcoinsTip; delete pcoinsdbview; diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h index c9e4a3427f..dd3b13c8c8 100644 --- a/src/test/test_bitcoin.h +++ b/src/test/test_bitcoin.h @@ -10,6 +10,7 @@ #include "key.h" #include "pubkey.h" #include "random.h" +#include "scheduler.h" #include "txdb.h" #include "txmempool.h" @@ -53,6 +54,7 @@ struct TestingSetup: public BasicTestingSetup { fs::path pathTemp; boost::thread_group threadGroup; CConnman* connman; + CScheduler scheduler; TestingSetup(const std::string& chainName = CBaseChainParams::MAIN); ~TestingSetup(); diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 778d2fd742..39f9f58604 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -27,7 +27,6 @@ #include <boost/algorithm/string/classification.hpp> #include <boost/algorithm/string/split.hpp> #include <boost/test/unit_test.hpp> -#include <boost/foreach.hpp> #include <univalue.h> diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp index a74f40251a..f609cb1af4 100644 --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -196,8 +196,8 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) // 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; { + 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)); diff --git a/src/timedata.cpp b/src/timedata.cpp index d736baa213..099ed7f042 100644 --- a/src/timedata.cpp +++ b/src/timedata.cpp @@ -15,7 +15,6 @@ #include "utilstrencodings.h" #include "warnings.h" -#include <boost/foreach.hpp> static CCriticalSection cs_nTimeOffset; static int64_t nTimeOffset = 0; diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index 4cd64bf028..ac13f73e70 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -17,7 +17,6 @@ #include <boost/bind.hpp> #include <boost/signals2/signal.hpp> -#include <boost/foreach.hpp> #include <boost/algorithm/string/split.hpp> #include <boost/algorithm/string/classification.hpp> #include <boost/algorithm/string/replace.hpp> @@ -663,7 +662,7 @@ void TorController::protocolinfo_cb(TorControlConnection& _conn, const TorContro // _conn.Command("AUTHENTICATE " + HexStr(status_cookie.second), boost::bind(&TorController::auth_cb, this, _1, _2)); cookie = std::vector<uint8_t>(status_cookie.second.begin(), status_cookie.second.end()); clientNonce = std::vector<uint8_t>(TOR_NONCE_SIZE, 0); - GetRandBytes(&clientNonce[0], TOR_NONCE_SIZE); + GetRandBytes(clientNonce.data(), TOR_NONCE_SIZE); _conn.Command("AUTHCHALLENGE SAFECOOKIE " + HexStr(clientNonce), boost::bind(&TorController::authchallenge_cb, this, _1, _2)); } else { if (status_cookie.first) { diff --git a/src/txdb.cpp b/src/txdb.cpp index 002f6550bc..aa0b73a417 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -172,7 +172,7 @@ bool CBlockTreeDB::ReadLastBlockFile(int &nFile) { CCoinsViewCursor *CCoinsViewDB::Cursor() const { - CCoinsViewDBCursor *i = new CCoinsViewDBCursor(const_cast<CDBWrapper*>(&db)->NewIterator(), GetBestBlock()); + CCoinsViewDBCursor *i = new CCoinsViewDBCursor(const_cast<CDBWrapper&>(db).NewIterator(), GetBestBlock()); /* It seems that there are no "const iterators" for LevelDB. Since we only need read operations on it, use a const-cast to get around that restriction. */ diff --git a/src/txmempool.cpp b/src/txmempool.cpp index dcfc5ffde0..4a81055231 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -11,6 +11,7 @@ #include "validation.h" #include "policy/policy.h" #include "policy/fees.h" +#include "reverse_iterator.h" #include "streams.h" #include "timedata.h" #include "util.h" @@ -127,7 +128,7 @@ void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashes // This maximizes the benefit of the descendant cache and guarantees that // setMemPoolChildren will be updated, an assumption made in // UpdateForDescendants. - BOOST_REVERSE_FOREACH(const uint256 &hash, vHashesToUpdate) { + for (const uint256 &hash : reverse_iterate(vHashesToUpdate)) { // we cache the in-mempool children to avoid duplicate updates setEntries setChildren; // calculate children from mapNextTx diff --git a/src/undo.h b/src/undo.h index 3749d5d7a8..0f9d041bbd 100644 --- a/src/undo.h +++ b/src/undo.h @@ -60,7 +60,8 @@ public: TxInUndoDeserializer(Coin* coin) : txout(coin) {} }; -static const size_t MAX_INPUTS_PER_BLOCK = MAX_BLOCK_BASE_SIZE / ::GetSerializeSize(CTxIn(), SER_NETWORK, PROTOCOL_VERSION); +static const size_t MIN_TRANSACTION_INPUT_WEIGHT = WITNESS_SCALE_FACTOR * ::GetSerializeSize(CTxIn(), SER_NETWORK, PROTOCOL_VERSION); +static const size_t MAX_INPUTS_PER_BLOCK = MAX_BLOCK_WEIGHT / MIN_TRANSACTION_INPUT_WEIGHT; /** Undo information for a CTransaction */ class CTxUndo diff --git a/src/util.h b/src/util.h index 824ad51ac4..e1bdfb1988 100644 --- a/src/util.h +++ b/src/util.h @@ -215,7 +215,7 @@ public: * Return string argument or default value * * @param strArg Argument to get (e.g. "-foo") - * @param default (e.g. "1") + * @param strDefault (e.g. "1") * @return command-line argument or default value */ std::string GetArg(const std::string& strArg, const std::string& strDefault); @@ -224,7 +224,7 @@ public: * Return integer argument or default value * * @param strArg Argument to get (e.g. "-foo") - * @param default (e.g. 1) + * @param nDefault (e.g. 1) * @return command-line argument (0 if invalid number) or default value */ int64_t GetArg(const std::string& strArg, int64_t nDefault); @@ -233,7 +233,7 @@ public: * Return boolean argument or default value * * @param strArg Argument to get (e.g. "-foo") - * @param default (true or false) + * @param fDefault (true or false) * @return command-line argument or default value */ bool GetBoolArg(const std::string& strArg, bool fDefault); diff --git a/src/utilstrencodings.cpp b/src/utilstrencodings.cpp index 93abaec04b..9ee14070a2 100644 --- a/src/utilstrencodings.cpp +++ b/src/utilstrencodings.cpp @@ -91,6 +91,25 @@ std::vector<unsigned char> ParseHex(const std::string& str) return ParseHex(str.c_str()); } +void SplitHostPort(std::string in, int &portOut, std::string &hostOut) { + size_t colon = in.find_last_of(':'); + // if a : is found, and it either follows a [...], or no other : is in the string, treat it as port separator + bool fHaveColon = colon != in.npos; + bool fBracketed = fHaveColon && (in[0]=='[' && in[colon-1]==']'); // if there is a colon, and in[0]=='[', colon is not 0, so in[colon-1] is safe + bool fMultiColon = fHaveColon && (in.find_last_of(':',colon-1) != in.npos); + if (fHaveColon && (colon==0 || fBracketed || !fMultiColon)) { + int32_t n; + if (ParseInt32(in.substr(colon + 1), &n) && n > 0 && n < 0x10000) { + in = in.substr(0, colon); + portOut = n; + } + } + if (in.size()>0 && in[0] == '[' && in[in.size()-1] == ']') + hostOut = in.substr(1, in.size()-2); + else + hostOut = in; +} + std::string EncodeBase64(const unsigned char* pch, size_t len) { static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; @@ -228,7 +247,7 @@ std::vector<unsigned char> DecodeBase64(const char* p, bool* pfInvalid) std::string DecodeBase64(const std::string& str) { std::vector<unsigned char> vchRet = DecodeBase64(str.c_str()); - return (vchRet.size() == 0) ? std::string() : std::string((const char*)&vchRet[0], vchRet.size()); + return std::string((const char*)vchRet.data(), vchRet.size()); } std::string EncodeBase32(const unsigned char* pch, size_t len) @@ -415,7 +434,7 @@ std::vector<unsigned char> DecodeBase32(const char* p, bool* pfInvalid) std::string DecodeBase32(const std::string& str) { std::vector<unsigned char> vchRet = DecodeBase32(str.c_str()); - return (vchRet.size() == 0) ? std::string() : std::string((const char*)&vchRet[0], vchRet.size()); + return std::string((const char*)vchRet.data(), vchRet.size()); } static bool ParsePrechecks(const std::string& str) diff --git a/src/utilstrencodings.h b/src/utilstrencodings.h index 8b37fe12e0..707fdaad16 100644 --- a/src/utilstrencodings.h +++ b/src/utilstrencodings.h @@ -48,6 +48,7 @@ std::string DecodeBase32(const std::string& str); std::string EncodeBase32(const unsigned char* pch, size_t len); std::string EncodeBase32(const std::string& str); +void SplitHostPort(std::string in, int &portOut, std::string &hostOut); std::string i64tostr(int64_t n); std::string itostr(int n); int64_t atoi64(const char* psz); diff --git a/src/validation.cpp b/src/validation.cpp index 975cbcc79f..d7d880d24f 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -24,6 +24,7 @@ #include "primitives/block.h" #include "primitives/transaction.h" #include "random.h" +#include "reverse_iterator.h" #include "script/script.h" #include "script/sigcache.h" #include "script/standard.h" @@ -454,7 +455,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool // Reject transactions with witness before segregated witness activates (override with -prematurewitness) bool witnessEnabled = IsWitnessEnabled(chainActive.Tip(), chainparams.GetConsensus()); - if (!GetBoolArg("-prematurewitness",false) && tx.HasWitness() && !witnessEnabled) { + if (!GetBoolArg("-prematurewitness", false) && tx.HasWitness() && !witnessEnabled) { return state.DoS(0, false, REJECT_NONSTANDARD, "no-witness-yet", true); } @@ -531,24 +532,20 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool CCoinsViewMemPool viewMemPool(pcoinsTip, pool); view.SetBackend(viewMemPool); - // do we already have it? - for (size_t out = 0; out < tx.vout.size(); out++) { - COutPoint outpoint(hash, out); - bool had_coin_in_cache = pcoinsTip->HaveCoinInCache(outpoint); - if (view.HaveCoin(outpoint)) { - if (!had_coin_in_cache) { - coins_to_uncache.push_back(outpoint); - } - return state.Invalid(false, REJECT_DUPLICATE, "txn-already-known"); - } - } - // do all inputs exist? for (const CTxIn txin : tx.vin) { if (!pcoinsTip->HaveCoinInCache(txin.prevout)) { coins_to_uncache.push_back(txin.prevout); } if (!view.HaveCoin(txin.prevout)) { + // Are inputs missing because we already have the tx? + for (size_t out = 0; out < tx.vout.size(); out++) { + // Optimistically just do efficient check of cache for outputs + if (pcoinsTip->HaveCoinInCache(COutPoint(hash, out))) { + return state.Invalid(false, REJECT_DUPLICATE, "txn-already-known"); + } + } + // Otherwise assume this might be an orphan tx for which we just haven't seen parents yet if (pfMissingInputs) { *pfMissingInputs = true; } @@ -1140,7 +1137,7 @@ static void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) // or a chain that is entirely longer than ours and invalid (note that this should be detected by both) // We define it this way because it allows us to only store the highest fork tip (+ base) which meets // the 7-block condition and from this always have the most-likely-to-cause-warning fork - if (pfork && (!pindexBestForkTip || (pindexBestForkTip && pindexNewForkTip->nHeight > pindexBestForkTip->nHeight)) && + if (pfork && (!pindexBestForkTip || pindexNewForkTip->nHeight > pindexBestForkTip->nHeight) && pindexNewForkTip->nChainWork - pfork->nChainWork > (GetBlockProof(*pfork) * 7) && chainActive.Height() - pindexNewForkTip->nHeight < 72) { @@ -2340,7 +2337,7 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c nHeight = nTargetHeight; // Connect new blocks. - BOOST_REVERSE_FOREACH(CBlockIndex *pindexConnect, vpindexToConnect) { + for (CBlockIndex *pindexConnect : reverse_iterate(vpindexToConnect)) { if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace, disconnectpool)) { if (state.IsInvalid()) { // The block violates a consensus rule. @@ -2802,7 +2799,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P // checks that use witness data may be performed here. // Size limits - if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_BASE_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) > MAX_BLOCK_BASE_SIZE) + if (block.vtx.empty() || block.vtx.size() * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT) return state.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, "size limits failed"); // First transaction must be coinbase, the rest must not be @@ -2889,7 +2886,7 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc if (consensusParams.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout != 0) { if (commitpos == -1) { uint256 witnessroot = BlockWitnessMerkleRoot(block, NULL); - CHash256().Write(witnessroot.begin(), 32).Write(&ret[0], 32).Finalize(witnessroot.begin()); + CHash256().Write(witnessroot.begin(), 32).Write(ret.data(), 32).Finalize(witnessroot.begin()); CTxOut out; out.nValue = 0; out.scriptPubKey.resize(38); diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index 46d7c9b329..bf20d606f8 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -4,49 +4,123 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "validationinterface.h" +#include "init.h" +#include "scheduler.h" +#include "sync.h" +#include "util.h" + +#include <list> +#include <atomic> + +#include <boost/signals2/signal.hpp> + +struct MainSignalsInstance { + boost::signals2::signal<void (const CBlockIndex *, const CBlockIndex *, bool fInitialDownload)> UpdatedBlockTip; + boost::signals2::signal<void (const CTransactionRef &)> TransactionAddedToMempool; + boost::signals2::signal<void (const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::vector<CTransactionRef>&)> BlockConnected; + boost::signals2::signal<void (const std::shared_ptr<const CBlock> &)> BlockDisconnected; + boost::signals2::signal<void (const CBlockLocator &)> SetBestChain; + boost::signals2::signal<void (const uint256 &)> Inventory; + boost::signals2::signal<void (int64_t nBestBlockTime, CConnman* connman)> Broadcast; + boost::signals2::signal<void (const CBlock&, const CValidationState&)> BlockChecked; + boost::signals2::signal<void (const CBlockIndex *, const std::shared_ptr<const CBlock>&)> NewPoWValidBlock; + + // We are not allowed to assume the scheduler only runs in one thread, + // but must ensure all callbacks happen in-order, so we end up creating + // our own queue here :( + SingleThreadedSchedulerClient m_schedulerClient; + + MainSignalsInstance(CScheduler *pscheduler) : m_schedulerClient(pscheduler) {} +}; static CMainSignals g_signals; +void CMainSignals::RegisterBackgroundSignalScheduler(CScheduler& scheduler) { + assert(!m_internals); + m_internals.reset(new MainSignalsInstance(&scheduler)); +} + +void CMainSignals::UnregisterBackgroundSignalScheduler() { + m_internals.reset(nullptr); +} + +void CMainSignals::FlushBackgroundCallbacks() { + m_internals->m_schedulerClient.EmptyQueue(); +} + CMainSignals& GetMainSignals() { return g_signals; } void RegisterValidationInterface(CValidationInterface* pwalletIn) { - g_signals.UpdatedBlockTip.connect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1, _2, _3)); - g_signals.TransactionAddedToMempool.connect(boost::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, _1)); - g_signals.BlockConnected.connect(boost::bind(&CValidationInterface::BlockConnected, pwalletIn, _1, _2, _3)); - g_signals.BlockDisconnected.connect(boost::bind(&CValidationInterface::BlockDisconnected, pwalletIn, _1)); - g_signals.SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); - 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)); + g_signals.m_internals->UpdatedBlockTip.connect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1, _2, _3)); + g_signals.m_internals->TransactionAddedToMempool.connect(boost::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, _1)); + g_signals.m_internals->BlockConnected.connect(boost::bind(&CValidationInterface::BlockConnected, pwalletIn, _1, _2, _3)); + g_signals.m_internals->BlockDisconnected.connect(boost::bind(&CValidationInterface::BlockDisconnected, pwalletIn, _1)); + g_signals.m_internals->SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); + g_signals.m_internals->Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1)); + g_signals.m_internals->Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2)); + g_signals.m_internals->BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2)); + g_signals.m_internals->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)); - g_signals.SetBestChain.disconnect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); - g_signals.TransactionAddedToMempool.disconnect(boost::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, _1)); - g_signals.BlockConnected.disconnect(boost::bind(&CValidationInterface::BlockConnected, pwalletIn, _1, _2, _3)); - g_signals.BlockDisconnected.disconnect(boost::bind(&CValidationInterface::BlockDisconnected, pwalletIn, _1)); - g_signals.UpdatedBlockTip.disconnect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1, _2, _3)); - g_signals.NewPoWValidBlock.disconnect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2)); + g_signals.m_internals->BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2)); + g_signals.m_internals->Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2)); + g_signals.m_internals->Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1)); + g_signals.m_internals->SetBestChain.disconnect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); + g_signals.m_internals->TransactionAddedToMempool.disconnect(boost::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, _1)); + g_signals.m_internals->BlockConnected.disconnect(boost::bind(&CValidationInterface::BlockConnected, pwalletIn, _1, _2, _3)); + g_signals.m_internals->BlockDisconnected.disconnect(boost::bind(&CValidationInterface::BlockDisconnected, pwalletIn, _1)); + g_signals.m_internals->UpdatedBlockTip.disconnect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1, _2, _3)); + g_signals.m_internals->NewPoWValidBlock.disconnect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2)); } 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(); - g_signals.SetBestChain.disconnect_all_slots(); - g_signals.TransactionAddedToMempool.disconnect_all_slots(); - g_signals.BlockConnected.disconnect_all_slots(); - g_signals.BlockDisconnected.disconnect_all_slots(); - g_signals.UpdatedBlockTip.disconnect_all_slots(); - g_signals.NewPoWValidBlock.disconnect_all_slots(); + g_signals.m_internals->BlockChecked.disconnect_all_slots(); + g_signals.m_internals->Broadcast.disconnect_all_slots(); + g_signals.m_internals->Inventory.disconnect_all_slots(); + g_signals.m_internals->SetBestChain.disconnect_all_slots(); + g_signals.m_internals->TransactionAddedToMempool.disconnect_all_slots(); + g_signals.m_internals->BlockConnected.disconnect_all_slots(); + g_signals.m_internals->BlockDisconnected.disconnect_all_slots(); + g_signals.m_internals->UpdatedBlockTip.disconnect_all_slots(); + g_signals.m_internals->NewPoWValidBlock.disconnect_all_slots(); +} + +void CMainSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) { + m_internals->UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload); +} + +void CMainSignals::TransactionAddedToMempool(const CTransactionRef &ptx) { + m_internals->TransactionAddedToMempool(ptx); +} + +void CMainSignals::BlockConnected(const std::shared_ptr<const CBlock> &pblock, const CBlockIndex *pindex, const std::vector<CTransactionRef>& vtxConflicted) { + m_internals->BlockConnected(pblock, pindex, vtxConflicted); +} + +void CMainSignals::BlockDisconnected(const std::shared_ptr<const CBlock> &pblock) { + m_internals->BlockDisconnected(pblock); +} + +void CMainSignals::SetBestChain(const CBlockLocator &locator) { + m_internals->SetBestChain(locator); +} + +void CMainSignals::Inventory(const uint256 &hash) { + m_internals->Inventory(hash); +} + +void CMainSignals::Broadcast(int64_t nBestBlockTime, CConnman* connman) { + m_internals->Broadcast(nBestBlockTime, connman); +} + +void CMainSignals::BlockChecked(const CBlock& block, const CValidationState& state) { + m_internals->BlockChecked(block, state); +} + +void CMainSignals::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock> &block) { + m_internals->NewPoWValidBlock(pindex, block); } diff --git a/src/validationinterface.h b/src/validationinterface.h index 460aecf243..568da66df2 100644 --- a/src/validationinterface.h +++ b/src/validationinterface.h @@ -6,7 +6,6 @@ #ifndef BITCOIN_VALIDATIONINTERFACE_H #define BITCOIN_VALIDATIONINTERFACE_H -#include <boost/signals2/signal.hpp> #include <memory> #include "primitives/transaction.h" // CTransaction(Ref) @@ -20,6 +19,7 @@ class CReserveScript; class CValidationInterface; class CValidationState; class uint256; +class CScheduler; // These functions dispatch to one or all registered wallets @@ -32,52 +32,66 @@ void UnregisterAllValidationInterfaces(); class CValidationInterface { protected: - virtual void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {} - virtual void TransactionAddedToMempool(const CTransactionRef &ptxn) {} - virtual void BlockConnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex *pindex, const std::vector<CTransactionRef> &txnConflicted) {} - virtual void BlockDisconnected(const std::shared_ptr<const CBlock> &block) {} - virtual void SetBestChain(const CBlockLocator &locator) {} - 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*); - friend void ::UnregisterAllValidationInterfaces(); -}; - -struct CMainSignals { /** Notifies listeners of updated block chain tip */ - boost::signals2::signal<void (const CBlockIndex *, const CBlockIndex *, bool fInitialDownload)> UpdatedBlockTip; + virtual void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {} /** Notifies listeners of a transaction having been added to mempool. */ - boost::signals2::signal<void (const CTransactionRef &)> TransactionAddedToMempool; + virtual void TransactionAddedToMempool(const CTransactionRef &ptxn) {} /** * Notifies listeners of a block being connected. * Provides a vector of transactions evicted from the mempool as a result. */ - boost::signals2::signal<void (const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::vector<CTransactionRef> &)> BlockConnected; + virtual void BlockConnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex *pindex, const std::vector<CTransactionRef> &txnConflicted) {} /** Notifies listeners of a block being disconnected */ - boost::signals2::signal<void (const std::shared_ptr<const CBlock> &)> BlockDisconnected; - /** Notifies listeners of a new active block chain. */ - boost::signals2::signal<void (const CBlockLocator &)> SetBestChain; + virtual void BlockDisconnected(const std::shared_ptr<const CBlock> &block) {} + /** Notifies listeners of the new active block chain on-disk. */ + virtual void SetBestChain(const CBlockLocator &locator) {} /** Notifies listeners about an inventory item being seen on the network. */ - boost::signals2::signal<void (const uint256 &)> Inventory; + virtual void Inventory(const uint256 &hash) {} /** Tells listeners to broadcast their data. */ - boost::signals2::signal<void (int64_t nBestBlockTime, CConnman* connman)> Broadcast; + virtual void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) {} /** * Notifies listeners of a block validation result. * If the provided CValidationState IsValid, the provided block * is guaranteed to be the current best block at the time the * 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; + virtual void BlockChecked(const CBlock&, const CValidationState&) {} /** * 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 */ - boost::signals2::signal<void (const CBlockIndex *, const std::shared_ptr<const CBlock>&)> NewPoWValidBlock; + virtual void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& block) {}; + friend void ::RegisterValidationInterface(CValidationInterface*); + friend void ::UnregisterValidationInterface(CValidationInterface*); + friend void ::UnregisterAllValidationInterfaces(); +}; + +struct MainSignalsInstance; +class CMainSignals { +private: + std::unique_ptr<MainSignalsInstance> m_internals; + + friend void ::RegisterValidationInterface(CValidationInterface*); + friend void ::UnregisterValidationInterface(CValidationInterface*); + friend void ::UnregisterAllValidationInterfaces(); + +public: + /** Register a CScheduler to give callbacks which should run in the background (may only be called once) */ + void RegisterBackgroundSignalScheduler(CScheduler& scheduler); + /** Unregister a CScheduler to give callbacks which should run in the background - these callbacks will now be dropped! */ + void UnregisterBackgroundSignalScheduler(); + /** Call any remaining callbacks on the calling thread */ + void FlushBackgroundCallbacks(); + + void UpdatedBlockTip(const CBlockIndex *, const CBlockIndex *, bool fInitialDownload); + void TransactionAddedToMempool(const CTransactionRef &); + void BlockConnected(const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::vector<CTransactionRef> &); + void BlockDisconnected(const std::shared_ptr<const CBlock> &); + void UpdatedTransaction(const uint256 &); + void SetBestChain(const CBlockLocator &); + void Inventory(const uint256 &); + void Broadcast(int64_t nBestBlockTime, CConnman* connman); + void BlockChecked(const CBlock&, const CValidationState&); + void NewPoWValidBlock(const CBlockIndex *, const std::shared_ptr<const CBlock>&); }; CMainSignals& GetMainSignals(); diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h index cb4719ae90..fc0e7c519e 100644 --- a/src/wallet/coincontrol.h +++ b/src/wallet/coincontrol.h @@ -6,9 +6,12 @@ #define BITCOIN_WALLET_COINCONTROL_H #include "policy/feerate.h" +#include "policy/fees.h" #include "primitives/transaction.h" #include "wallet/wallet.h" +#include <boost/optional.hpp> + /** Coin Control Features. */ class CCoinControl { @@ -18,14 +21,16 @@ public: bool fAllowOtherInputs; //! Includes watch only addresses which match the ISMINE_WATCH_SOLVABLE criteria bool fAllowWatchOnly; - //! Override estimated feerate + //! Override automatic min/max checks on fee, m_feerate must be set if true bool fOverrideFeeRate; - //! Feerate to use if overrideFeeRate is true - CFeeRate nFeeRate; - //! Override the default confirmation target, 0 = use default - int nConfirmTarget; + //! Override the default payTxFee if set + boost::optional<CFeeRate> m_feerate; + //! Override the default confirmation target if set + boost::optional<unsigned int> m_confirm_target; //! Signal BIP-125 replace by fee. bool signalRbf; + //! Fee estimation mode to control arguments to estimateSmartFee + FeeEstimateMode m_fee_mode; CCoinControl() { @@ -38,10 +43,11 @@ public: fAllowOtherInputs = false; fAllowWatchOnly = false; setSelected.clear(); - nFeeRate = CFeeRate(0); + m_feerate.reset(); fOverrideFeeRate = false; - nConfirmTarget = 0; + m_confirm_target.reset(); signalRbf = fWalletRbf; + m_fee_mode = FeeEstimateMode::UNSET; } bool HasSelected() const diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp index 6fa685628f..dcce88cedc 100644 --- a/src/wallet/crypter.cpp +++ b/src/wallet/crypter.cpp @@ -12,7 +12,6 @@ #include <string> #include <vector> -#include <boost/foreach.hpp> int CCrypter::BytesToKeySHA512AES(const std::vector<unsigned char>& chSalt, const SecureString& strKeyData, int count, unsigned char *key,unsigned char *iv) const { diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index c8928a3b66..da2d180756 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -18,7 +18,6 @@ #include <sys/stat.h> #endif -#include <boost/foreach.hpp> #include <boost/thread.hpp> // diff --git a/src/wallet/db.h b/src/wallet/db.h index 7cccc65660..4f3ad0c42d 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -95,13 +95,13 @@ class CWalletDBWrapper friend class CDB; public: /** Create dummy DB handle */ - CWalletDBWrapper() : nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(nullptr) + CWalletDBWrapper() : nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(nullptr) { } /** Create DB handle to real database */ CWalletDBWrapper(CDBEnv *env_in, const std::string &strFile_in) : - nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(env_in), strFile(strFile_in) + nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(env_in), strFile(strFile_in) { } diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index 6a9e6cf9ff..4bfd8726a5 100644 --- a/src/wallet/feebumper.cpp +++ b/src/wallet/feebumper.cpp @@ -3,6 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "consensus/validation.h" +#include "wallet/coincontrol.h" #include "wallet/feebumper.h" #include "wallet/wallet.h" #include "policy/fees.h" @@ -66,7 +67,7 @@ bool CFeeBumper::preconditionChecks(const CWallet *pWallet, const CWalletTx& wtx return true; } -CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, int newConfirmTarget, bool ignoreGlobalPayTxFee, CAmount totalFee, bool newTxReplaceable) +CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, const CCoinControl& coin_control, CAmount totalFee) : txid(std::move(txidIn)), nOldFee(0), @@ -165,7 +166,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, nullptr, ignoreGlobalPayTxFee); + nNewFee = CWallet::GetMinimumFee(maxNewTxSize, coin_control, mempool, ::feeEstimator, nullptr /* FeeCalculation */); nNewFeeRate = CFeeRate(nNewFee, maxNewTxSize); // New fee rate must be at least old rate + minimum incremental relay rate @@ -220,7 +221,7 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, int newConf } // Mark new tx not replaceable, if requested. - if (!newTxReplaceable) { + if (!coin_control.signalRbf) { for (auto& input : mtx.vin) { if (input.nSequence < 0xfffffffe) input.nSequence = 0xfffffffe; } diff --git a/src/wallet/feebumper.h b/src/wallet/feebumper.h index fc32316704..3d64e53c15 100644 --- a/src/wallet/feebumper.h +++ b/src/wallet/feebumper.h @@ -10,6 +10,8 @@ class CWallet; class CWalletTx; class uint256; +class CCoinControl; +enum class FeeEstimateMode; enum class BumpFeeResult { @@ -24,7 +26,7 @@ enum class BumpFeeResult class CFeeBumper { public: - CFeeBumper(const CWallet *pWalletIn, const uint256 txidIn, int newConfirmTarget, bool ignoreGlobalPayTxFee, CAmount totalFee, bool newTxReplaceable); + CFeeBumper(const CWallet *pWalletIn, const uint256 txidIn, const CCoinControl& coin_control, CAmount totalFee); BumpFeeResult getResult() const { return currentResult; } const std::vector<std::string>& getErrors() const { return vErrors; } CAmount getOldFee() const { return nOldFee; } diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 0fe2412352..9f42b1f266 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -26,7 +26,6 @@ #include <univalue.h> -#include <boost/foreach.hpp> std::string static EncodeDumpTime(int64_t nTime) { return DateTimeStrFormat("%Y-%m-%dT%H:%M:%SZ", nTime); @@ -362,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); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 5bbb5088e2..ee8c7548fc 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" @@ -355,7 +356,7 @@ UniValue getaddressesbyaccount(const JSONRPCRequest& request) return ret; } -static void SendMoney(CWallet * const pwallet, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew) +static void SendMoney(CWallet * const pwallet, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew, const CCoinControl& coin_control) { CAmount curBalance = pwallet->GetBalance(); @@ -381,7 +382,7 @@ static void SendMoney(CWallet * const pwallet, const CTxDestination &address, CA int nChangePosRet = -1; CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount}; vecSend.push_back(recipient); - if (!pwallet->CreateTransaction(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) { + if (!pwallet->CreateTransaction(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError, coin_control)) { if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired)); throw JSONRPCError(RPC_WALLET_ERROR, strError); @@ -400,9 +401,9 @@ UniValue sendtoaddress(const JSONRPCRequest& request) return NullUniValue; } - if (request.fHelp || request.params.size() < 2 || request.params.size() > 5) + if (request.fHelp || request.params.size() < 2 || request.params.size() > 8) throw std::runtime_error( - "sendtoaddress \"address\" amount ( \"comment\" \"comment_to\" subtractfeefromamount )\n" + "sendtoaddress \"address\" amount ( \"comment\" \"comment_to\" subtractfeefromamount replaceable conf_target \"estimate_mode\")\n" "\nSend an amount to a given address.\n" + HelpRequiringPassphrase(pwallet) + "\nArguments:\n" @@ -415,6 +416,12 @@ UniValue sendtoaddress(const JSONRPCRequest& request) " transaction, just kept in your wallet.\n" "5. subtractfeefromamount (boolean, optional, default=false) The fee will be deducted from the amount being sent.\n" " The recipient will receive less bitcoins than you enter in the amount field.\n" + "6. replaceable (boolean, optional) Allow this transaction to be replaced by a transaction with higher fees via BIP 125\n" + "7. conf_target (numeric, optional) Confirmation target (in blocks)\n" + "8. \"estimate_mode\" (string, optional, default=UNSET) The fee estimate mode, must be one of:\n" + " \"UNSET\"\n" + " \"ECONOMICAL\"\n" + " \"CONSERVATIVE\"\n" "\nResult:\n" "\"txid\" (string) The transaction id.\n" "\nExamples:\n" @@ -443,12 +450,29 @@ UniValue sendtoaddress(const JSONRPCRequest& request) wtx.mapValue["to"] = request.params[3].get_str(); bool fSubtractFeeFromAmount = false; - if (request.params.size() > 4) + if (request.params.size() > 4 && !request.params[4].isNull()) { fSubtractFeeFromAmount = request.params[4].get_bool(); + } + + CCoinControl coin_control; + if (request.params.size() > 5 && !request.params[5].isNull()) { + coin_control.signalRbf = request.params[5].get_bool(); + } + + if (request.params.size() > 6 && !request.params[6].isNull()) { + coin_control.m_confirm_target = ParseConfirmTarget(request.params[6]); + } + + if (request.params.size() > 7 && !request.params[7].isNull()) { + if (!FeeModeFromString(request.params[7].get_str(), coin_control.m_fee_mode)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter"); + } + } + EnsureWalletIsUnlocked(pwallet); - SendMoney(pwallet, address.Get(), nAmount, fSubtractFeeFromAmount, wtx); + SendMoney(pwallet, address.Get(), nAmount, fSubtractFeeFromAmount, wtx, coin_control); return wtx.GetHash().GetHex(); } @@ -716,9 +740,9 @@ UniValue getbalance(const JSONRPCRequest& request) "\nResult:\n" "amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this account.\n" "\nExamples:\n" - "\nThe total amount in the wallet\n" + "\nThe total amount in the wallet with 1 or more confirmations\n" + HelpExampleCli("getbalance", "") + - "\nThe total amount in the wallet at least 5 blocks confirmed\n" + "\nThe total amount in the wallet at least 6 blocks confirmed\n" + HelpExampleCli("getbalance", "\"*\" 6") + "\nAs a json rpc call\n" + HelpExampleRpc("getbalance", "\"*\", 6") @@ -874,7 +898,8 @@ UniValue sendfrom(const JSONRPCRequest& request) if (nAmount > nBalance) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); - SendMoney(pwallet, address.Get(), nAmount, false, wtx); + CCoinControl no_coin_control; // This is a deprecated API + SendMoney(pwallet, address.Get(), nAmount, false, wtx, no_coin_control); return wtx.GetHash().GetHex(); } @@ -887,9 +912,9 @@ UniValue sendmany(const JSONRPCRequest& request) return NullUniValue; } - if (request.fHelp || request.params.size() < 2 || request.params.size() > 5) + if (request.fHelp || request.params.size() < 2 || request.params.size() > 8) throw std::runtime_error( - "sendmany \"fromaccount\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] )\n" + "sendmany \"fromaccount\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] replaceable conf_target \"estimate_mode\")\n" "\nSend multiple times. Amounts are double-precision floating point numbers." + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments:\n" @@ -909,7 +934,13 @@ UniValue sendmany(const JSONRPCRequest& request) " \"address\" (string) Subtract fee from this address\n" " ,...\n" " ]\n" - "\nResult:\n" + "6. replaceable (boolean, optional) Allow this transaction to be replaced by a transaction with higher fees via BIP 125\n" + "7. conf_target (numeric, optional) Confirmation target (in blocks)\n" + "8. \"estimate_mode\" (string, optional, default=UNSET) The fee estimate mode, must be one of:\n" + " \"UNSET\"\n" + " \"ECONOMICAL\"\n" + " \"CONSERVATIVE\"\n" + "\nResult:\n" "\"txid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n" " the number of addresses.\n" "\nExamples:\n" @@ -941,9 +972,24 @@ UniValue sendmany(const JSONRPCRequest& request) wtx.mapValue["comment"] = request.params[3].get_str(); UniValue subtractFeeFromAmount(UniValue::VARR); - if (request.params.size() > 4) + if (request.params.size() > 4 && !request.params[4].isNull()) subtractFeeFromAmount = request.params[4].get_array(); + CCoinControl coin_control; + if (request.params.size() > 5 && !request.params[5].isNull()) { + coin_control.signalRbf = request.params[5].get_bool(); + } + + if (request.params.size() > 6 && !request.params[6].isNull()) { + coin_control.m_confirm_target = ParseConfirmTarget(request.params[6]); + } + + if (request.params.size() > 7 && !request.params[7].isNull()) { + if (!FeeModeFromString(request.params[7].get_str(), coin_control.m_fee_mode)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter"); + } + } + std::set<CBitcoinAddress> setAddress; std::vector<CRecipient> vecSend; @@ -988,7 +1034,7 @@ UniValue sendmany(const JSONRPCRequest& request) CAmount nFeeRequired = 0; int nChangePosRet = -1; std::string strFailReason; - bool fCreated = pwallet->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, nChangePosRet, strFailReason); + bool fCreated = pwallet->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, nChangePosRet, strFailReason, coin_control); if (!fCreated) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason); CValidationState state; @@ -2655,7 +2701,13 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) " Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n" " If no outputs are specified here, the sender pays the fee.\n" " [vout_index,...]\n" - " \"optIntoRbf\" (boolean, optional) Allow this transaction to be replaced by a transaction with higher fees\n" + " \"replaceable\" (boolean, optional) Marks this transaction as BIP125 replaceable.\n" + " Allows this transaction to be replaced by a transaction with higher fees\n" + " \"conf_target\" (numeric, optional) Confirmation target (in blocks)\n" + " \"estimate_mode\" (string, optional, default=UNSET) The fee estimate mode, must be one of:\n" + " \"UNSET\"\n" + " \"ECONOMICAL\"\n" + " \"CONSERVATIVE\"\n" " }\n" " for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}\n" "\nResult:\n" @@ -2678,13 +2730,9 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) RPCTypeCheck(request.params, {UniValue::VSTR}); CCoinControl coinControl; - coinControl.destChange = CNoDestination(); int changePosition = -1; - coinControl.fAllowWatchOnly = false; // include watching bool lockUnspents = false; bool reserveChangeKey = true; - coinControl.nFeeRate = CFeeRate(0); - coinControl.fOverrideFeeRate = false; UniValue subtractFeeFromOutputs; std::set<int> setSubtractFeeFromOutputs; @@ -2707,7 +2755,9 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) {"reserveChangeKey", UniValueType(UniValue::VBOOL)}, {"feeRate", UniValueType()}, // will be checked below {"subtractFeeFromOutputs", UniValueType(UniValue::VARR)}, - {"optIntoRbf", UniValueType(UniValue::VBOOL)}, + {"replaceable", UniValueType(UniValue::VBOOL)}, + {"conf_target", UniValueType(UniValue::VNUM)}, + {"estimate_mode", UniValueType(UniValue::VSTR)}, }, true, true); @@ -2734,15 +2784,23 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) if (options.exists("feeRate")) { - coinControl.nFeeRate = CFeeRate(AmountFromValue(options["feeRate"])); + coinControl.m_feerate = CFeeRate(AmountFromValue(options["feeRate"])); coinControl.fOverrideFeeRate = true; } if (options.exists("subtractFeeFromOutputs")) subtractFeeFromOutputs = options["subtractFeeFromOutputs"].get_array(); - if (options.exists("optIntoRbf")) { - coinControl.signalRbf = options["optIntoRbf"].get_bool(); + if (options.exists("replaceable")) { + coinControl.signalRbf = options["replaceable"].get_bool(); + } + if (options.exists("conf_target")) { + coinControl.m_confirm_target = ParseConfirmTarget(options["conf_target"]); + } + if (options.exists("estimate_mode")) { + if (!FeeModeFromString(options["estimate_mode"].get_str(), coinControl.m_fee_mode)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter"); + } } } } @@ -2821,6 +2879,10 @@ UniValue bumpfee(const JSONRPCRequest& request) " so the new transaction will not be explicitly bip-125 replaceable (though it may\n" " still be replaceable in practice, for example if it has unconfirmed ancestors which\n" " are replaceable).\n" + " \"estimate_mode\" (string, optional, default=UNSET) The fee estimate mode, must be one of:\n" + " \"UNSET\"\n" + " \"ECONOMICAL\"\n" + " \"CONSERVATIVE\"\n" " }\n" "\nResult:\n" "{\n" @@ -2839,10 +2901,9 @@ UniValue bumpfee(const JSONRPCRequest& request) hash.SetHex(request.params[0].get_str()); // optional parameters - bool ignoreGlobalPayTxFee = false; - int newConfirmTarget = nTxConfirmTarget; CAmount totalFee = 0; - bool replaceable = true; + CCoinControl coin_control; + coin_control.signalRbf = true; if (request.params.size() > 1) { UniValue options = request.params[1]; RPCTypeCheckObj(options, @@ -2850,20 +2911,14 @@ UniValue bumpfee(const JSONRPCRequest& request) {"confTarget", UniValueType(UniValue::VNUM)}, {"totalFee", UniValueType(UniValue::VNUM)}, {"replaceable", UniValueType(UniValue::VBOOL)}, + {"estimate_mode", UniValueType(UniValue::VSTR)}, }, true, true); if (options.exists("confTarget") && options.exists("totalFee")) { throw JSONRPCError(RPC_INVALID_PARAMETER, "confTarget and totalFee options should not both be set. Please provide either a confirmation target for fee estimation or an explicit total fee for the transaction."); - } else if (options.exists("confTarget")) { - // If the user has explicitly set a confTarget in this rpc call, - // then override the default logic that uses the global payTxFee - // instead of the confirmation target. - ignoreGlobalPayTxFee = true; - newConfirmTarget = options["confTarget"].get_int(); - if (newConfirmTarget <= 0) { // upper-bound will be checked by estimatefee/smartfee - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid confTarget (cannot be <= 0)"); - } + } else if (options.exists("confTarget")) { // TODO: alias this to conf_target + coin_control.m_confirm_target = ParseConfirmTarget(options["confTarget"]); } else if (options.exists("totalFee")) { totalFee = options["totalFee"].get_int64(); if (totalFee <= 0) { @@ -2872,14 +2927,19 @@ UniValue bumpfee(const JSONRPCRequest& request) } if (options.exists("replaceable")) { - replaceable = options["replaceable"].get_bool(); + coin_control.signalRbf = options["replaceable"].get_bool(); + } + if (options.exists("estimate_mode")) { + if (!FeeModeFromString(options["estimate_mode"].get_str(), coin_control.m_fee_mode)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter"); + } } } LOCK2(cs_main, pwallet->cs_wallet); EnsureWalletIsUnlocked(pwallet); - CFeeBumper feeBump(pwallet, hash, newConfirmTarget, ignoreGlobalPayTxFee, totalFee, replaceable); + CFeeBumper feeBump(pwallet, hash, coin_control, totalFee); BumpFeeResult res = feeBump.getResult(); if (res != BumpFeeResult::OK) { @@ -2922,6 +2982,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); @@ -2976,8 +3081,8 @@ static const CRPCCommand commands[] = { "wallet", "lockunspent", &lockunspent, true, {"unlock","transactions"} }, { "wallet", "move", &movecmd, false, {"fromaccount","toaccount","amount","minconf","comment"} }, { "wallet", "sendfrom", &sendfrom, false, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} }, - { "wallet", "sendmany", &sendmany, false, {"fromaccount","amounts","minconf","comment","subtractfeefrom"} }, - { "wallet", "sendtoaddress", &sendtoaddress, false, {"address","amount","comment","comment_to","subtractfeefromamount"} }, + { "wallet", "sendmany", &sendmany, false, {"fromaccount","amounts","minconf","comment","subtractfeefrom","replaceable","conf_target","estimate_mode"} }, + { "wallet", "sendtoaddress", &sendtoaddress, false, {"address","amount","comment","comment_to","subtractfeefromamount","replaceable","conf_target","estimate_mode"} }, { "wallet", "setaccount", &setaccount, true, {"address","account"} }, { "wallet", "settxfee", &settxfee, true, {"amount"} }, { "wallet", "signmessage", &signmessage, true, {"address","message"} }, @@ -2985,6 +3090,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/rpcwallet.h b/src/wallet/rpcwallet.h index bd5dad18ca..31e2f6a699 100644 --- a/src/wallet/rpcwallet.h +++ b/src/wallet/rpcwallet.h @@ -16,7 +16,7 @@ void RegisterWalletRPCCommands(CRPCTable &t); * @param[in] request JSONRPCRequest that wishes to access a wallet * @return NULL if no wallet should be used, or a pointer to the CWallet */ -CWallet *GetWalletForJSONRPCRequest(const JSONRPCRequest&); +CWallet *GetWalletForJSONRPCRequest(const JSONRPCRequest& request); std::string HelpRequiringPassphrase(CWallet *); void EnsureWalletIsUnlocked(CWallet *); diff --git a/src/wallet/test/accounting_tests.cpp b/src/wallet/test/accounting_tests.cpp index 12d9f2e995..330878ceb5 100644 --- a/src/wallet/test/accounting_tests.cpp +++ b/src/wallet/test/accounting_tests.cpp @@ -8,7 +8,6 @@ #include <stdint.h> -#include <boost/foreach.hpp> #include <boost/test/unit_test.hpp> extern CWallet* pwalletMain; diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 96a1b14b60..8176a0017c 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -13,6 +13,7 @@ #include "rpc/server.h" #include "test/test_bitcoin.h" #include "validation.h" +#include "wallet/coincontrol.h" #include "wallet/test/wallet_test_fixture.h" #include <boost/test/unit_test.hpp> @@ -617,7 +618,8 @@ public: CAmount fee; int changePos = -1; std::string error; - BOOST_CHECK(wallet->CreateTransaction({recipient}, wtx, reservekey, fee, changePos, error)); + CCoinControl dummy; + BOOST_CHECK(wallet->CreateTransaction({recipient}, wtx, reservekey, fee, changePos, error, dummy)); CValidationState state; BOOST_CHECK(wallet->CommitTransaction(wtx, reservekey, nullptr, state)); auto it = wallet->mapWallet.find(wtx.GetHash()); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 0d1a86dd24..13da7bb2c2 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1718,7 +1718,7 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const const CTxOut &txout = tx->vout[i]; nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE); if (!MoneyRange(nCredit)) - throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); + throw std::runtime_error(std::string(__func__) + " : value out of range"); } } @@ -1761,7 +1761,7 @@ CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool& fUseCache) const const CTxOut &txout = tx->vout[i]; nCredit += pwallet->GetCredit(txout, ISMINE_WATCH_ONLY); if (!MoneyRange(nCredit)) - throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); + throw std::runtime_error(std::string(__func__) + ": value out of range"); } } @@ -2469,9 +2469,9 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC CReserveKey reservekey(this); CWalletTx wtx; - if (!CreateTransaction(vecSend, wtx, reservekey, nFeeRet, nChangePosInOut, strFailReason, &coinControl, false)) + if (!CreateTransaction(vecSend, wtx, reservekey, nFeeRet, nChangePosInOut, strFailReason, coinControl, false)) { return false; - + } if (nChangePosInOut != -1) tx.vout.insert(tx.vout.begin() + nChangePosInOut, wtx.tx->vout[nChangePosInOut]); @@ -2502,7 +2502,7 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC } bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, - int& nChangePosInOut, std::string& strFailReason, const CCoinControl* coinControl, bool sign) + int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign) { CAmount nValue = 0; int nChangePosRequest = nChangePosInOut; @@ -2567,9 +2567,42 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT LOCK2(cs_main, cs_wallet); { std::vector<COutput> vAvailableCoins; - AvailableCoins(vAvailableCoins, true, coinControl); + AvailableCoins(vAvailableCoins, true, &coin_control); + + // Create change script that will be used if we need change + // TODO: pass in scriptChange instead of reservekey so + // change transaction isn't always pay-to-bitcoin-address + CScript scriptChange; + + // coin control: send change to custom address + if (!boost::get<CNoDestination>(&coin_control.destChange)) { + scriptChange = GetScriptForDestination(coin_control.destChange); + } else { // no coin control: send change to newly generated address + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + CPubKey vchPubKey; + bool ret; + ret = reservekey.GetReservedKey(vchPubKey, true); + if (!ret) + { + strFailReason = _("Keypool ran out, please call keypoolrefill first"); + return false; + } + + scriptChange = GetScriptForDestination(vchPubKey.GetID()); + } + CTxOut change_prototype_txout(0, scriptChange); + size_t change_prototype_size = GetSerializeSize(change_prototype_txout, SER_DISK, 0); nFeeRet = 0; + bool pick_new_inputs = true; + CAmount nValueIn = 0; // Start with no fee and loop until there is enough fee while (true) { @@ -2615,49 +2648,21 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT } // Choose coins to use - CAmount nValueIn = 0; - setCoins.clear(); - if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl)) - { - strFailReason = _("Insufficient funds"); - return false; + if (pick_new_inputs) { + nValueIn = 0; + setCoins.clear(); + if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, &coin_control)) + { + strFailReason = _("Insufficient funds"); + return false; + } } const CAmount nChange = nValueIn - nValueToSelect; + if (nChange > 0) { // Fill a vout to ourself - // TODO: pass in scriptChange instead of reservekey so - // change transaction isn't always pay-to-bitcoin-address - CScript scriptChange; - - // coin control: send change to custom address - if (coinControl && !boost::get<CNoDestination>(&coinControl->destChange)) - scriptChange = GetScriptForDestination(coinControl->destChange); - - // no coin control: send change to newly generated address - else - { - // Note: We use a new key here to keep it from being obvious which side is the change. - // The drawback is that by not reusing a previous key, the change may be lost if a - // backup is restored, if the backup doesn't have the new private key for the change. - // If we reused the old key, it would be possible to add code to look for and - // rediscover unknown transactions that were written with keys of ours to recover - // post-backup change. - - // Reserve a new key pair from key pool - CPubKey vchPubKey; - bool ret; - ret = reservekey.GetReservedKey(vchPubKey, true); - if (!ret) - { - strFailReason = _("Keypool ran out, please call keypoolrefill first"); - return false; - } - - scriptChange = GetScriptForDestination(vchPubKey.GetID()); - } - CTxOut newTxOut(nChange, scriptChange); // Never create dust outputs; if we would, just @@ -2666,7 +2671,6 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT { nChangePosInOut = -1; nFeeRet += nChange; - reservekey.ReturnKey(); } else { @@ -2685,7 +2689,6 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT txNew.vout.insert(position, newTxOut); } } else { - reservekey.ReturnKey(); nChangePosInOut = -1; } @@ -2699,8 +2702,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT // to avoid conflicting with other possible uses of nSequence, // and in the spirit of "smallest possible change from prior // behavior." - bool rbf = coinControl ? coinControl->signalRbf : fWalletRbf; - const uint32_t nSequence = rbf ? MAX_BIP125_RBF_SEQUENCE : (std::numeric_limits<unsigned int>::max() - 1); + const uint32_t nSequence = coin_control.signalRbf ? MAX_BIP125_RBF_SEQUENCE : (std::numeric_limits<unsigned int>::max() - 1); for (const auto& coin : setCoins) txNew.vin.push_back(CTxIn(coin.outpoint,CScript(), nSequence)); @@ -2719,14 +2721,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT vin.scriptWitness.SetNull(); } - // Allow to override the default confirmation target over the CoinControl instance - int currentConfirmationTarget = nTxConfirmTarget; - if (coinControl && coinControl->nConfirmTarget > 0) - currentConfirmationTarget = coinControl->nConfirmTarget; - - CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, ::mempool, ::feeEstimator, &feeCalc); - if (coinControl && coinControl->fOverrideFeeRate) - nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes); + CAmount nFeeNeeded = GetMinimumFee(nBytes, coin_control, ::mempool, ::feeEstimator, &feeCalc); // If we made it here and we aren't even able to meet the relay fee on the next pass, give up // because we must be at the maximum allowed fee. @@ -2737,16 +2732,30 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT } if (nFeeRet >= nFeeNeeded) { - // Reduce fee to only the needed amount if we have change - // output to increase. This prevents potential overpayment - // in fees if the coins selected to meet nFeeNeeded result - // in a transaction that requires less fee than the prior - // iteration. + // Reduce fee to only the needed amount if possible. This + // prevents potential overpayment in fees if the coins + // selected to meet nFeeNeeded result in a transaction that + // requires less fee than the prior iteration. + // TODO: The case where nSubtractFeeFromAmount > 0 remains // to be addressed because it requires returning the fee to // the payees and not the change output. - // TODO: The case where there is no change output remains - // to be addressed so we avoid creating too small an output. + + // If we have no change and a big enough excess fee, then + // try to construct transaction again only without picking + // new inputs. We now know we only need the smaller fee + // (because of reduced tx size) and so we should add a + // change output. Only try this once. + CAmount fee_needed_for_change = GetMinimumFee(change_prototype_size, coin_control, ::mempool, ::feeEstimator, nullptr); + CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, ::dustRelayFee); + CAmount max_excess_fee = fee_needed_for_change + minimum_value_for_change; + if (nFeeRet > nFeeNeeded + max_excess_fee && nChangePosInOut == -1 && nSubtractFeeFromAmount == 0 && pick_new_inputs) { + pick_new_inputs = false; + nFeeRet = nFeeNeeded + fee_needed_for_change; + continue; + } + + // If we have change output already, just increase it if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { CAmount extraFeePaid = nFeeRet - nFeeNeeded; std::vector<CTxOut>::iterator change_position = txNew.vout.begin()+nChangePosInOut; @@ -2755,6 +2764,12 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT } break; // Done, enough fee included. } + else if (!pick_new_inputs) { + // This shouldn't happen, we should have had enough excess + // fee to pay for the new output and still meet nFeeNeeded + strFailReason = _("Transaction fee and change calculation failed"); + return false; + } // Try to reduce change to include necessary fee if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { @@ -2774,6 +2789,8 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT } } + if (nChangePosInOut == -1) reservekey.ReturnKey(); // Return any reserved key if we don't have change + if (sign) { CTransaction txNewConst(txNew); @@ -2905,33 +2922,61 @@ 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, FeeCalculation *feeCalc, bool ignoreGlobalPayTxFee) +CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc) { - // 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) { - 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) { - nFeeNeeded = fallbackFee.GetFee(nTxBytes); + /* User control of how to calculate fee uses the following parameter precedence: + 1. coin_control.m_feerate + 2. coin_control.m_confirm_target + 3. payTxFee (user-set global variable) + 4. nTxConfirmTarget (user-set global variable) + The first parameter that is set is used. + */ + CAmount fee_needed; + if (coin_control.m_feerate) { // 1. + fee_needed = coin_control.m_feerate->GetFee(nTxBytes); + if (feeCalc) feeCalc->reason = FeeReason::PAYTXFEE; + // Allow to override automatic min/max check over coin control instance + if (coin_control.fOverrideFeeRate) return fee_needed; + } + else if (!coin_control.m_confirm_target && ::payTxFee != CFeeRate(0)) { // 3. TODO: remove magic value of 0 for global payTxFee + fee_needed = ::payTxFee.GetFee(nTxBytes); + if (feeCalc) feeCalc->reason = FeeReason::PAYTXFEE; + } + else { // 2. or 4. + // We will use smart fee estimation + unsigned int target = coin_control.m_confirm_target ? *coin_control.m_confirm_target : ::nTxConfirmTarget; + // By default estimates are economical iff we are signaling opt-in-RBF + bool conservative_estimate = !coin_control.signalRbf; + // Allow to override the default fee estimate mode over the CoinControl instance + if (coin_control.m_fee_mode == FeeEstimateMode::CONSERVATIVE) conservative_estimate = true; + else if (coin_control.m_fee_mode == FeeEstimateMode::ECONOMICAL) conservative_estimate = false; + + fee_needed = estimator.estimateSmartFee(target, feeCalc, conservative_estimate).GetFee(nTxBytes); + if (fee_needed == 0) { + // if we don't have enough data for estimateSmartFee, then use fallbackFee + fee_needed = fallbackFee.GetFee(nTxBytes); if (feeCalc) feeCalc->reason = FeeReason::FALLBACK; } - } else { - if (feeCalc) feeCalc->reason = FeeReason::PAYTXFEE; + // Obey mempool min fee when using smart fee estimation + CAmount min_mempool_fee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nTxBytes); + if (fee_needed < min_mempool_fee) { + fee_needed = min_mempool_fee; + if (feeCalc) feeCalc->reason = FeeReason::MEMPOOL_MIN; + } } + // prevent user from paying a fee below minRelayTxFee or minTxFee - CAmount requiredFee = GetRequiredFee(nTxBytes); - if (requiredFee > nFeeNeeded) { - nFeeNeeded = requiredFee; + CAmount required_fee = GetRequiredFee(nTxBytes); + if (required_fee > fee_needed) { + fee_needed = required_fee; if (feeCalc) feeCalc->reason = FeeReason::REQUIRED; } // But always obey the maximum - if (nFeeNeeded > maxTxFee) { - nFeeNeeded = maxTxFee; + if (fee_needed > maxTxFee) { + fee_needed = maxTxFee; if (feeCalc) feeCalc->reason = FeeReason::MAXTXFEE; } - return nFeeNeeded; + return fee_needed; } @@ -2946,7 +2991,8 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet) if (dbw->Rewrite("\x04pool")) { LOCK(cs_wallet); - setKeyPool.clear(); + setInternalKeyPool.clear(); + setExternalKeyPool.clear(); // Note: can't top-up keypool here, because wallet is locked. // User will be prompted to unlock wallet the next operation // that requires a new key. @@ -2974,7 +3020,8 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256 { if (dbw->Rewrite("\x04pool")) { - setKeyPool.clear(); + setInternalKeyPool.clear(); + setExternalKeyPool.clear(); // Note: can't top-up keypool here, because wallet is locked. // User will be prompted to unlock wallet the next operation // that requires a new key. @@ -2999,7 +3046,8 @@ DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx) if (dbw->Rewrite("\x04pool")) { LOCK(cs_wallet); - setKeyPool.clear(); + setInternalKeyPool.clear(); + setExternalKeyPool.clear(); // Note: can't top-up keypool here, because wallet is locked. // User will be prompted to unlock wallet the next operation // that requires a new key. @@ -3083,9 +3131,16 @@ bool CWallet::NewKeyPool() { LOCK(cs_wallet); CWalletDB walletdb(*dbw); - for (int64_t nIndex : setKeyPool) + + for (int64_t nIndex : setInternalKeyPool) { walletdb.ErasePool(nIndex); - setKeyPool.clear(); + } + setInternalKeyPool.clear(); + + for (int64_t nIndex : setExternalKeyPool) { + walletdb.ErasePool(nIndex); + } + setExternalKeyPool.clear(); if (!TopUpKeyPool()) { return false; @@ -3097,25 +3152,8 @@ bool CWallet::NewKeyPool() size_t CWallet::KeypoolCountExternalKeys() { - AssertLockHeld(cs_wallet); // setKeyPool - - // immediately return setKeyPool's size if HD or HD_SPLIT is disabled or not supported - if (!IsHDEnabled() || !CanSupportFeature(FEATURE_HD_SPLIT)) - return setKeyPool.size(); - - CWalletDB walletdb(*dbw); - - // count amount of external keys - size_t amountE = 0; - for(const int64_t& id : setKeyPool) - { - CKeyPool tmpKeypool; - if (!walletdb.ReadPool(id, tmpKeypool)) - throw std::runtime_error(std::string(__func__) + ": read failed"); - amountE += !tmpKeypool.fInternal; - } - - return amountE; + AssertLockHeld(cs_wallet); // setExternalKeyPool + return setExternalKeyPool.size(); } bool CWallet::TopUpKeyPool(unsigned int kpSize) @@ -3135,10 +3173,8 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize) // count amount of available keys (internal, external) // make sure the keypool of external and internal keys fits the user selected target (-keypool) - int64_t amountExternal = KeypoolCountExternalKeys(); - int64_t amountInternal = setKeyPool.size() - amountExternal; - int64_t missingExternal = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - amountExternal, (int64_t) 0); - int64_t missingInternal = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - amountInternal, (int64_t) 0); + int64_t missingExternal = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - (int64_t)setExternalKeyPool.size(), (int64_t) 0); + int64_t missingInternal = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - (int64_t)setInternalKeyPool.size(), (int64_t) 0); if (!IsHDEnabled() || !CanSupportFeature(FEATURE_HD_SPLIT)) { @@ -3150,20 +3186,32 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize) for (int64_t i = missingInternal + missingExternal; i--;) { int64_t nEnd = 1; - if (i < missingInternal) + if (i < missingInternal) { internal = true; - if (!setKeyPool.empty()) - nEnd = *(--setKeyPool.end()) + 1; + } + + if (!setInternalKeyPool.empty()) { + nEnd = *(setInternalKeyPool.rbegin()) + 1; + } + if (!setExternalKeyPool.empty()) { + nEnd = std::max(nEnd, *(setExternalKeyPool.rbegin()) + 1); + } + if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey(internal), internal))) throw std::runtime_error(std::string(__func__) + ": writing generated key failed"); - setKeyPool.insert(nEnd); - LogPrintf("keypool added key %d, size=%u, internal=%d\n", nEnd, setKeyPool.size(), internal); + + if (internal) { + setInternalKeyPool.insert(nEnd); + } else { + setExternalKeyPool.insert(nEnd); + } + LogPrintf("keypool added key %d, size=%u (%u internal), new key is %s\n", nEnd, setInternalKeyPool.size() + setExternalKeyPool.size(), setInternalKeyPool.size(), internal ? "internal" : "external"); } } return true; } -void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool internal) +void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal) { nIndex = -1; keypool.vchPubKey = CPubKey(); @@ -3173,30 +3221,30 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool int if (!IsLocked()) TopUpKeyPool(); + bool fReturningInternal = IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT) && fRequestedInternal; + std::set<int64_t>& setKeyPool = fReturningInternal ? setInternalKeyPool : setExternalKeyPool; + // Get the oldest key if(setKeyPool.empty()) return; CWalletDB walletdb(*dbw); - // try to find a key that matches the internal/external filter - for(const int64_t& id : setKeyPool) - { - CKeyPool tmpKeypool; - if (!walletdb.ReadPool(id, tmpKeypool)) - throw std::runtime_error(std::string(__func__) + ": read failed"); - if (!HaveKey(tmpKeypool.vchPubKey.GetID())) - throw std::runtime_error(std::string(__func__) + ": unknown key in key pool"); - if (!IsHDEnabled() || !CanSupportFeature(FEATURE_HD_SPLIT) || tmpKeypool.fInternal == internal) - { - nIndex = id; - keypool = tmpKeypool; - setKeyPool.erase(id); - assert(keypool.vchPubKey.IsValid()); - LogPrintf("keypool reserve %d\n", nIndex); - return; - } + auto it = setKeyPool.begin(); + nIndex = *it; + setKeyPool.erase(it); + if (!walletdb.ReadPool(nIndex, keypool)) { + throw std::runtime_error(std::string(__func__) + ": read failed"); + } + if (!HaveKey(keypool.vchPubKey.GetID())) { + throw std::runtime_error(std::string(__func__) + ": unknown key in key pool"); + } + if (keypool.fInternal != fReturningInternal) { + throw std::runtime_error(std::string(__func__) + ": keypool entry misclassified"); } + + assert(keypool.vchPubKey.IsValid()); + LogPrintf("keypool reserve %d\n", nIndex); } } @@ -3208,12 +3256,16 @@ void CWallet::KeepKey(int64_t nIndex) LogPrintf("keypool keep %d\n", nIndex); } -void CWallet::ReturnKey(int64_t nIndex) +void CWallet::ReturnKey(int64_t nIndex, bool fInternal) { // Return to key pool { LOCK(cs_wallet); - setKeyPool.insert(nIndex); + if (fInternal) { + setInternalKeyPool.insert(nIndex); + } else { + setExternalKeyPool.insert(nIndex); + } } LogPrintf("keypool return %d\n", nIndex); } @@ -3237,48 +3289,35 @@ bool CWallet::GetKeyFromPool(CPubKey& result, bool internal) return true; } -int64_t CWallet::GetOldestKeyPoolTime() -{ - LOCK(cs_wallet); - - // if the keypool is empty, return <NOW> - if (setKeyPool.empty()) +static int64_t GetOldestKeyTimeInPool(const std::set<int64_t>& setKeyPool, CWalletDB& walletdb) { + if (setKeyPool.empty()) { return GetTime(); + } CKeyPool keypool; - CWalletDB walletdb(*dbw); - - if (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT)) - { - // if HD & HD Chain Split is enabled, response max(oldest-internal-key, oldest-external-key) - int64_t now = GetTime(); - int64_t oldest_external = now, oldest_internal = now; - - for(const int64_t& id : setKeyPool) - { - if (!walletdb.ReadPool(id, keypool)) { - throw std::runtime_error(std::string(__func__) + ": read failed"); - } - if (keypool.fInternal && keypool.nTime < oldest_internal) { - oldest_internal = keypool.nTime; - } - else if (!keypool.fInternal && keypool.nTime < oldest_external) { - oldest_external = keypool.nTime; - } - if (oldest_internal != now && oldest_external != now) { - break; - } - } - return std::max(oldest_internal, oldest_external); - } - // load oldest key from keypool, get time and return int64_t nIndex = *(setKeyPool.begin()); - if (!walletdb.ReadPool(nIndex, keypool)) + if (!walletdb.ReadPool(nIndex, keypool)) { throw std::runtime_error(std::string(__func__) + ": read oldest key in keypool failed"); + } assert(keypool.vchPubKey.IsValid()); return keypool.nTime; } +int64_t CWallet::GetOldestKeyPoolTime() +{ + LOCK(cs_wallet); + + CWalletDB walletdb(*dbw); + + // load oldest key from keypool, get time and return + int64_t oldestKey = GetOldestKeyTimeInPool(setExternalKeyPool, walletdb); + if (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT)) { + oldestKey = std::max(GetOldestKeyTimeInPool(setInternalKeyPool, walletdb), oldestKey); + } + + return oldestKey; +} + std::map<CTxDestination, CAmount> CWallet::GetAddressBalances() { std::map<CTxDestination, CAmount> balances; @@ -3437,6 +3476,7 @@ bool CReserveKey::GetReservedKey(CPubKey& pubkey, bool internal) else { return false; } + fInternal = keypool.fInternal; } assert(vchPubKey.IsValid()); pubkey = vchPubKey; @@ -3453,19 +3493,14 @@ void CReserveKey::KeepKey() void CReserveKey::ReturnKey() { - if (nIndex != -1) - pwallet->ReturnKey(nIndex); + if (nIndex != -1) { + pwallet->ReturnKey(nIndex, fInternal); + } nIndex = -1; vchPubKey = CPubKey(); } -void CWallet::GetAllReserveKeys(std::set<CKeyID>& setAddress) const -{ - setAddress.clear(); - - CWalletDB walletdb(*dbw); - - LOCK2(cs_main, cs_wallet); +static void LoadReserveKeysToSet(std::set<CKeyID>& setAddress, const std::set<int64_t>& setKeyPool, CWalletDB& walletdb) { for (const int64_t& id : setKeyPool) { CKeyPool keypool; @@ -3473,12 +3508,27 @@ void CWallet::GetAllReserveKeys(std::set<CKeyID>& setAddress) const throw std::runtime_error(std::string(__func__) + ": read failed"); assert(keypool.vchPubKey.IsValid()); CKeyID keyID = keypool.vchPubKey.GetID(); - if (!HaveKey(keyID)) - throw std::runtime_error(std::string(__func__) + ": unknown key in key pool"); setAddress.insert(keyID); } } +void CWallet::GetAllReserveKeys(std::set<CKeyID>& setAddress) const +{ + setAddress.clear(); + + CWalletDB walletdb(*dbw); + + LOCK2(cs_main, cs_wallet); + LoadReserveKeysToSet(setAddress, setInternalKeyPool, walletdb); + LoadReserveKeysToSet(setAddress, setExternalKeyPool, walletdb); + + for (const CKeyID& keyID : setAddress) { + if (!HaveKey(keyID)) { + throw std::runtime_error(std::string(__func__) + ": unknown key in key pool"); + } + } +} + void CWallet::GetScriptForMining(std::shared_ptr<CReserveScript> &script) { std::shared_ptr<CReserveKey> rKey = std::make_shared<CReserveKey>(this); @@ -4002,20 +4052,30 @@ bool CWallet::ParameterInteraction() LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\n", __func__); } - if (GetBoolArg("-salvagewallet", false) && SoftSetBoolArg("-rescan", true)) { + if (GetBoolArg("-salvagewallet", false)) { if (is_multiwallet) { return InitError(strprintf("%s is only allowed with a single wallet file", "-salvagewallet")); } // Rewrite just private keys: rescan to find transactions - LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting -rescan=1\n", __func__); + if (SoftSetBoolArg("-rescan", true)) { + LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting -rescan=1\n", __func__); + } + } + + int zapwallettxes = GetArg("-zapwallettxes", 0); + // -zapwallettxes implies dropping the mempool on startup + if (zapwallettxes != 0 && SoftSetBoolArg("-persistmempool", false)) { + LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting -persistmempool=0\n", __func__, zapwallettxes); } - // -zapwallettx implies a rescan - if (GetBoolArg("-zapwallettxes", false) && SoftSetBoolArg("-rescan", true)) { + // -zapwallettxes implies a rescan + if (zapwallettxes != 0) { if (is_multiwallet) { return InitError(strprintf("%s is only allowed with a single wallet file", "-zapwallettxes")); } - LogPrintf("%s: parameter interaction: -zapwallettxes=<mode> -> setting -rescan=1\n", __func__); + if (SoftSetBoolArg("-rescan", true)) { + LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting -rescan=1\n", __func__, zapwallettxes); + } } if (is_multiwallet) { diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index a3fd7408a0..b716e46a7d 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -80,6 +80,7 @@ class CTxMemPool; class CBlockPolicyEstimator; class CWalletTx; struct FeeCalculation; +enum class FeeEstimateMode; /** (client) version numbers for particular wallet features */ enum WalletFeature @@ -698,7 +699,8 @@ private: /* HD derive new child key (on internal or external chain) */ void DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret, bool internal = false); - std::set<int64_t> setKeyPool; + std::set<int64_t> setInternalKeyPool; + std::set<int64_t> setExternalKeyPool; int64_t nTimeFirstKey; @@ -743,7 +745,11 @@ public: void LoadKeyPool(int nIndex, const CKeyPool &keypool) { - setKeyPool.insert(nIndex); + if (keypool.fInternal) { + setInternalKeyPool.insert(nIndex); + } else { + setExternalKeyPool.insert(nIndex); + } // If no metadata exists yet, create a default with the pool key's // creation time. Note that this may be overwritten by actually @@ -948,7 +954,7 @@ public: * @note passing nChangePosInOut as -1 will result in setting a random position */ bool CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, - std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true); + std::string& strFailReason, const CCoinControl& coin_control, bool sign = true); bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CConnman* connman, CValidationState& state); void ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& entries); @@ -963,7 +969,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, FeeCalculation *feeCalc = nullptr, bool ignoreGlobalPayTxFee = false); + static CAmount GetMinimumFee(unsigned int nTxBytes, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc); /** * Return the minimum required fee taking into account the * floating relay fee and user set minimum transaction fee @@ -973,9 +979,9 @@ public: bool NewKeyPool(); size_t KeypoolCountExternalKeys(); bool TopUpKeyPool(unsigned int kpSize = 0); - void ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool internal); + void ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal); void KeepKey(int64_t nIndex); - void ReturnKey(int64_t nIndex); + void ReturnKey(int64_t nIndex, bool fInternal); bool GetKeyFromPool(CPubKey &key, bool internal = false); int64_t GetOldestKeyPoolTime(); void GetAllReserveKeys(std::set<CKeyID>& setAddress) const; @@ -1025,12 +1031,12 @@ public: } } - void GetScriptForMining(std::shared_ptr<CReserveScript> &script) override; + void GetScriptForMining(std::shared_ptr<CReserveScript> &script); unsigned int GetKeyPoolSize() { - AssertLockHeld(cs_wallet); // setKeyPool - return setKeyPool.size(); + AssertLockHeld(cs_wallet); // set{Ex,In}ternalKeyPool + return setInternalKeyPool.size() + setExternalKeyPool.size(); } bool SetDefaultKey(const CPubKey &vchPubKey); @@ -1134,11 +1140,13 @@ protected: CWallet* pwallet; int64_t nIndex; CPubKey vchPubKey; + bool fInternal; public: CReserveKey(CWallet* pwalletIn) { nIndex = -1; pwallet = pwalletIn; + fInternal = false; } CReserveKey() = default; @@ -1211,4 +1219,5 @@ bool CWallet::DummySignTx(CMutableTransaction &txNew, const ContainerType &coins } return true; } + #endif // BITCOIN_WALLET_WALLET_H diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 8321719b56..65a28af46d 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -18,7 +18,6 @@ #include <atomic> -#include <boost/foreach.hpp> #include <boost/thread.hpp> // @@ -95,23 +94,23 @@ bool CWalletDB::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey) bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript) { - return WriteIC(std::make_pair(std::string("cscript"), hash), *(const CScriptBase*)(&redeemScript), false); + return WriteIC(std::make_pair(std::string("cscript"), hash), redeemScript, false); } bool CWalletDB::WriteWatchOnly(const CScript &dest, const CKeyMetadata& keyMeta) { - if (!WriteIC(std::make_pair(std::string("watchmeta"), *(const CScriptBase*)(&dest)), keyMeta)) { + if (!WriteIC(std::make_pair(std::string("watchmeta"), dest), keyMeta)) { return false; } - return WriteIC(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)), '1'); + return WriteIC(std::make_pair(std::string("watchs"), dest), '1'); } bool CWalletDB::EraseWatchOnly(const CScript &dest) { - if (!EraseIC(std::make_pair(std::string("watchmeta"), *(const CScriptBase*)(&dest)))) { + if (!EraseIC(std::make_pair(std::string("watchmeta"), dest))) { return false; } - return EraseIC(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest))); + return EraseIC(std::make_pair(std::string("watchs"), dest)); } bool CWalletDB::WriteBestBlock(const CBlockLocator& locator) @@ -324,7 +323,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, { wss.nWatchKeys++; CScript script; - ssKey >> *(CScriptBase*)(&script); + ssKey >> script; char fYes; ssValue >> fYes; if (fYes == '1') @@ -441,7 +440,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, else if (strType == "watchmeta") { CScript script; - ssKey >> *(CScriptBase*)(&script); + ssKey >> script; keyID = CScriptID(script); } @@ -475,7 +474,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, uint160 hash; ssKey >> hash; CScript script; - ssValue >> *(CScriptBase*)(&script); + ssValue >> script; if (!pwallet->LoadCScript(script)) { strErr = "Error reading wallet database: LoadCScript failed"; @@ -748,7 +747,7 @@ DBErrors CWalletDB::ZapWalletTx(std::vector<CWalletTx>& vWtx) void MaybeCompactWalletDB() { - static std::atomic<bool> fOneThread; + static std::atomic<bool> fOneThread(false); if (fOneThread.exchange(true)) { return; } |