diff options
Diffstat (limited to 'src')
41 files changed, 286 insertions, 293 deletions
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/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 885b787b4d..92f6a21ebb 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -10,6 +10,7 @@ #include "chainparamsbase.h" #include "clientversion.h" #include "fs.h" +#include "utilstrencodings.h" #include "rpc/client.h" #include "rpc/protocol.h" #include "util.h" @@ -191,8 +192,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 c6d5db786e..9acb3fd30e 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -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]; 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/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/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 18d02fb913..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 <= MAX_OPCODE; 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 7f38e9e565..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,7 +16,6 @@ #include "utilmoneystr.h" #include "utilstrencodings.h" - std::string FormatScript(const CScript& script) { std::string ret; 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/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/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/fees.cpp b/src/policy/fees.cpp index 03fe11a0d8..b0a6a2e3d8 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -698,7 +698,7 @@ CFeeRate CBlockPolicyEstimator::estimateRawFee(int confTarget, double successThr break; } default: { - throw std::out_of_range("CBlockPoicyEstimator::estimateRawFee unknown FeeEstimateHorizon"); + throw std::out_of_range("CBlockPolicyEstimator::estimateRawFee unknown FeeEstimateHorizon"); } } @@ -730,7 +730,7 @@ unsigned int CBlockPolicyEstimator::HighestTargetTracked(FeeEstimateHorizon hori return longStats->GetMaxConfirms(); } default: { - throw std::out_of_range("CBlockPoicyEstimator::HighestTargetTracked unknown FeeEstimateHorizon"); + throw std::out_of_range("CBlockPolicyEstimator::HighestTargetTracked unknown FeeEstimateHorizon"); } } } diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 2f78d2f347..9f2d623e76 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -7,6 +7,7 @@ #include "policy/policy.h" +#include "consensus/validation.h" #include "validation.h" #include "coins.h" #include "tinyformat.h" diff --git a/src/prevector.h b/src/prevector.h index 02d860bb00..46640d6fff 100644 --- a/src/prevector.h +++ b/src/prevector.h @@ -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/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/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 27634eb179..86401d3bb4 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,9 +171,12 @@ 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(updateGlobalFeeVariables())); + 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())); @@ -179,10 +201,17 @@ void SendCoinsDialog::setModel(WalletModel *_model) // 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())); } } @@ -192,7 +221,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()); @@ -246,7 +275,7 @@ void SendCoinsDialog::on_sendButton_clicked() if (model->getOptionsModel()->getCoinControlFeatures()) ctrl = *CoinControlDialog::coinControl; if (ui->radioSmartFee->isChecked()) - ctrl.nConfirmTarget = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2; + ctrl.nConfirmTarget = getConfTargetForIndex(ui->confTargetSelector->currentIndex()); else ctrl.nConfirmTarget = 0; @@ -596,14 +625,11 @@ 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()); @@ -614,11 +640,7 @@ 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 { @@ -652,7 +674,7 @@ void SendCoinsDialog::updateSmartFeeLabel() if(!model || !model->getOptionsModel()) return; - int nBlocksToConfirm = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2; + int nBlocksToConfirm = getConfTargetForIndex(ui->confTargetSelector->currentIndex()); FeeCalculation feeCalc; bool conservative_estimate = CalculateEstimateType(FeeEstimateMode::UNSET, ui->optInRBF->isChecked()); CFeeRate feeRate = ::feeEstimator.estimateSmartFee(nBlocksToConfirm, &feeCalc, ::mempool, conservative_estimate); @@ -826,7 +848,7 @@ void SendCoinsDialog::coinControlUpdateLabels() CoinControlDialog::payAmounts.clear(); CoinControlDialog::fSubtractFeeFromAmount = false; if (ui->radioSmartFee->isChecked()) { - CoinControlDialog::coinControl->nConfirmTarget = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2; + CoinControlDialog::coinControl->nConfirmTarget = getConfTargetForIndex(ui->confTargetSelector->currentIndex()); } else { CoinControlDialog::coinControl->nConfirmTarget = model->getDefaultConfirmTarget(); } 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/mining.cpp b/src/rpc/mining.cpp index 046c85a76e..10bb341e54 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -645,15 +645,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())); 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/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/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp index be631ce7a6..093509e61c 100644 --- a/src/test/dbwrapper_tests.cpp +++ b/src/test/dbwrapper_tests.cpp @@ -95,7 +95,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,7 +210,7 @@ BOOST_AUTO_TEST_CASE(iterator_ordering) BOOST_CHECK(dbw.Write(key, value)); } - std::unique_ptr<CDBIterator> it(const_cast<CDBWrapper*>(&dbw)->NewIterator()); + std::unique_ptr<CDBIterator> it(const_cast<CDBWrapper&>(dbw).NewIterator()); for (int c=0; c<2; ++c) { int seek_start; if (c == 0) @@ -286,7 +286,7 @@ BOOST_AUTO_TEST_CASE(iterator_string_ordering) } } - std::unique_ptr<CDBIterator> it(const_cast<CDBWrapper*>(&dbw)->NewIterator()); + std::unique_ptr<CDBIterator> it(const_cast<CDBWrapper&>(dbw).NewIterator()); for (int c=0; c<2; ++c) { int seek_start; if (c == 0) 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/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/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/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/utilstrencodings.cpp b/src/utilstrencodings.cpp index 2fabb8cf7a..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+/"; 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 ddc8b1b964..d7d880d24f 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -532,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; } @@ -2803,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 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/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 5f72e3b6f5..873542a966 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -740,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") diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 5e9701c71c..5689cc7b0c 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2977,7 +2977,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. @@ -3005,7 +3006,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. @@ -3030,7 +3032,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. @@ -3114,9 +3117,16 @@ bool CWallet::NewKeyPool() { LOCK(cs_wallet); CWalletDB walletdb(*dbw); - for (int64_t nIndex : setKeyPool) + + for (int64_t nIndex : setInternalKeyPool) { + walletdb.ErasePool(nIndex); + } + setInternalKeyPool.clear(); + + for (int64_t nIndex : setExternalKeyPool) { walletdb.ErasePool(nIndex); - setKeyPool.clear(); + } + setExternalKeyPool.clear(); if (!TopUpKeyPool()) { return false; @@ -3128,25 +3138,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) @@ -3166,10 +3159,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)) { @@ -3181,20 +3172,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(); @@ -3204,30 +3207,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); } } @@ -3239,12 +3242,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); } @@ -3268,48 +3275,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; @@ -3468,6 +3462,7 @@ bool CReserveKey::GetReservedKey(CPubKey& pubkey, bool internal) else { return false; } + fInternal = keypool.fInternal; } assert(vchPubKey.IsValid()); pubkey = vchPubKey; @@ -3484,19 +3479,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; @@ -3504,12 +3494,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); @@ -4033,20 +4038,24 @@ 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__); + } } // -zapwallettx implies a rescan - if (GetBoolArg("-zapwallettxes", false) && SoftSetBoolArg("-rescan", true)) { + if (GetBoolArg("-zapwallettxes", false)) { 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=<mode> -> setting -rescan=1\n", __func__); + } } if (is_multiwallet) { diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index e3715cdf37..8848448682 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -699,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; @@ -744,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 @@ -974,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; @@ -1030,8 +1035,8 @@ public: 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); @@ -1135,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; diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index deb09a4771..65a28af46d 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -94,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) @@ -323,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') @@ -440,7 +440,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, else if (strType == "watchmeta") { CScript script; - ssKey >> *(CScriptBase*)(&script); + ssKey >> script; keyID = CScriptID(script); } @@ -474,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"; @@ -747,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; } |