diff options
Diffstat (limited to 'src')
181 files changed, 4250 insertions, 1549 deletions
diff --git a/src/.clang-format b/src/.clang-format index 129f062ef8..fc53509138 100644 --- a/src/.clang-format +++ b/src/.clang-format @@ -6,7 +6,7 @@ AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: false AllowShortBlocksOnASingleLine: false AllowShortFunctionsOnASingleLine: All -AllowShortIfStatementsOnASingleLine: false +AllowShortIfStatementsOnASingleLine: true AllowShortLoopsOnASingleLine: false AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: true diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index e58bd9dfbf..8c699c2f8c 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -15,6 +15,7 @@ bench_bench_bitcoin_SOURCES = \ bench/bench.cpp \ bench/bench.h \ bench/checkblock.cpp \ + bench/checkqueue.cpp \ bench/Examples.cpp \ bench/rollingbloom.cpp \ bench/crypto_hash.cpp \ diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 7ae42e83b1..edc3c4b292 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -355,7 +355,7 @@ endif RES_IMAGES = -RES_MOVIES = $(wildcard qt/res/movies/spinner-*.png) +RES_MOVIES = $(wildcard $(srcdir)/qt/res/movies/spinner-*.png) BITCOIN_RC = qt/res/bitcoin-qt-res.rc diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 27b456240e..4d44b35bb6 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -13,32 +13,54 @@ EXTRA_DIST += \ test/bctest.py \ test/bitcoin-util-test.py \ test/data/bitcoin-util-test.json \ + test/data/blanktxv1.hex \ test/data/blanktxv1.json \ + test/data/blanktxv2.hex \ test/data/blanktxv2.json \ + test/data/tt-delin1-out.hex \ test/data/tt-delin1-out.json \ + test/data/tt-delout1-out.hex \ test/data/tt-delout1-out.json \ + test/data/tt-locktime317000-out.hex \ test/data/tt-locktime317000-out.json \ + test/data/tx394b54bb.hex \ + test/data/txcreate1.hex \ test/data/txcreate1.json \ + test/data/txcreate2.hex \ test/data/txcreate2.json \ + test/data/txcreatedata1.hex \ test/data/txcreatedata1.json \ + test/data/txcreatedata2.hex \ test/data/txcreatedata2.json \ + test/data/txcreatedata_seq0.hex \ test/data/txcreatedata_seq0.json \ + test/data/txcreatedata_seq1.hex \ test/data/txcreatedata_seq1.json \ - test/data/txcreatesignv1.json \ - test/data/blanktxv1.hex \ - test/data/blanktxv2.hex \ - test/data/tt-delin1-out.hex \ - test/data/tt-delout1-out.hex \ - test/data/tt-locktime317000-out.hex \ - test/data/tx394b54bb.hex \ - test/data/txcreate1.hex \ - test/data/txcreate2.hex \ - test/data/txcreatedata1.hex \ - test/data/txcreatedata2.hex \ + test/data/txcreatemultisig1.hex \ + test/data/txcreatemultisig1.json \ + test/data/txcreatemultisig2.hex \ + test/data/txcreatemultisig2.json \ + test/data/txcreatemultisig3.hex \ + test/data/txcreatemultisig3.json \ + test/data/txcreatemultisig4.hex \ + test/data/txcreatemultisig4.json \ + test/data/txcreateoutpubkey1.hex \ + test/data/txcreateoutpubkey1.json \ + test/data/txcreateoutpubkey2.hex \ + test/data/txcreateoutpubkey2.json \ + test/data/txcreateoutpubkey3.hex \ + test/data/txcreateoutpubkey3.json \ + test/data/txcreatescript1.hex \ + test/data/txcreatescript1.json \ + test/data/txcreatescript2.hex \ + test/data/txcreatescript2.json \ + test/data/txcreatescript3.hex \ + test/data/txcreatescript3.json \ + test/data/txcreatescript4.hex \ + test/data/txcreatescript4.json \ test/data/txcreatesignv1.hex \ - test/data/txcreatesignv2.hex \ - test/data/txcreatedata_seq0.hex \ - test/data/txcreatedata_seq1.hex + test/data/txcreatesignv1.json \ + test/data/txcreatesignv2.hex JSON_TEST_FILES = \ test/data/script_tests.json \ diff --git a/src/addrman.cpp b/src/addrman.cpp index d72042510a..f3c238fbb2 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -252,7 +252,7 @@ bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimeP int nId; CAddrInfo* pinfo = Find(addr, &nId); - // Do not set a penality for a source's self-announcement + // Do not set a penalty for a source's self-announcement if (addr == source) { nTimePenalty = 0; } diff --git a/src/base58.h b/src/base58.h index cccebc9e0e..3998283bb1 100644 --- a/src/base58.h +++ b/src/base58.h @@ -147,7 +147,7 @@ public: K GetKey() { K ret; if (vchData.size() == Size) { - //if base58 encouded data not holds a ext key, return a !IsValid() key + // If base58 encoded data does not hold an ext key, return a !IsValid() key ret.Decode(&vchData[0]); } return ret; diff --git a/src/bench/checkqueue.cpp b/src/bench/checkqueue.cpp new file mode 100644 index 0000000000..6fa9fe4fe8 --- /dev/null +++ b/src/bench/checkqueue.cpp @@ -0,0 +1,103 @@ +// Copyright (c) 2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "bench.h" +#include "util.h" +#include "validation.h" +#include "checkqueue.h" +#include "prevector.h" +#include <vector> +#include <boost/thread/thread.hpp> +#include "random.h" + + +// This Benchmark tests the CheckQueue with the lightest +// weight Checks, so it should make any lock contention +// particularly visible +static const int MIN_CORES = 2; +static const size_t BATCHES = 101; +static const size_t BATCH_SIZE = 30; +static const int PREVECTOR_SIZE = 28; +static const int QUEUE_BATCH_SIZE = 128; +static void CCheckQueueSpeed(benchmark::State& state) +{ + struct FakeJobNoWork { + bool operator()() + { + return true; + } + void swap(FakeJobNoWork& x){}; + }; + CCheckQueue<FakeJobNoWork> queue {QUEUE_BATCH_SIZE}; + boost::thread_group tg; + for (auto x = 0; x < std::max(MIN_CORES, GetNumCores()); ++x) { + tg.create_thread([&]{queue.Thread();}); + } + while (state.KeepRunning()) { + CCheckQueueControl<FakeJobNoWork> control(&queue); + + // We call Add a number of times to simulate the behavior of adding + // a block of transactions at once. + + std::vector<std::vector<FakeJobNoWork>> vBatches(BATCHES); + for (auto& vChecks : vBatches) { + vChecks.resize(BATCH_SIZE); + } + for (auto& vChecks : vBatches) { + // We can't make vChecks in the inner loop because we want to measure + // the cost of getting the memory to each thread and we might get the same + // memory + control.Add(vChecks); + } + // control waits for completion by RAII, but + // it is done explicitly here for clarity + control.Wait(); + } + tg.interrupt_all(); + tg.join_all(); +} + +// This Benchmark tests the CheckQueue with a slightly realistic workload, +// where checks all contain a prevector that is indirect 50% of the time +// and there is a little bit of work done between calls to Add. +static void CCheckQueueSpeedPrevectorJob(benchmark::State& state) +{ + struct PrevectorJob { + prevector<PREVECTOR_SIZE, uint8_t> p; + PrevectorJob(){ + } + PrevectorJob(FastRandomContext& insecure_rand){ + p.resize(insecure_rand.rand32() % (PREVECTOR_SIZE*2)); + } + bool operator()() + { + return true; + } + void swap(PrevectorJob& x){p.swap(x.p);}; + }; + CCheckQueue<PrevectorJob> queue {QUEUE_BATCH_SIZE}; + boost::thread_group tg; + for (auto x = 0; x < std::max(MIN_CORES, GetNumCores()); ++x) { + tg.create_thread([&]{queue.Thread();}); + } + while (state.KeepRunning()) { + // Make insecure_rand here so that each iteration is identical. + FastRandomContext insecure_rand(true); + CCheckQueueControl<PrevectorJob> control(&queue); + std::vector<std::vector<PrevectorJob>> vBatches(BATCHES); + for (auto& vChecks : vBatches) { + vChecks.reserve(BATCH_SIZE); + for (size_t x = 0; x < BATCH_SIZE; ++x) + vChecks.emplace_back(insecure_rand); + control.Add(vChecks); + } + // control waits for completion by RAII, but + // it is done explicitly here for clarity + control.Wait(); + } + tg.interrupt_all(); + tg.join_all(); +} +BENCHMARK(CCheckQueueSpeed); +BENCHMARK(CCheckQueueSpeedPrevectorJob); diff --git a/src/bench/perf.cpp b/src/bench/perf.cpp index 1f43e5d3ac..a549ec29ea 100644 --- a/src/bench/perf.cpp +++ b/src/bench/perf.cpp @@ -6,7 +6,7 @@ #if defined(__i386__) || defined(__x86_64__) -/* These architectures support quering the cycle counter +/* These architectures support querying the cycle counter * from user space, no need for any syscall overhead. */ void perf_init(void) { } diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 24a53b65a0..3c3646523a 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -78,8 +78,16 @@ static int AppInitRawTx(int argc, char* argv[]) strUsage += HelpMessageOpt("locktime=N", _("Set TX lock time to N")); strUsage += HelpMessageOpt("nversion=N", _("Set TX version to N")); 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") + ". " + + _("Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.")); strUsage += HelpMessageOpt("outdata=[VALUE:]DATA", _("Add data-based output to TX")); - strUsage += HelpMessageOpt("outscript=VALUE:SCRIPT", _("Add raw script output to TX")); + strUsage += HelpMessageOpt("outscript=VALUE:SCRIPT[:FLAGS]", _("Add raw script output to TX") + ". " + + _("Optionally add the \"W\" flag to produce a pay-to-witness-script-hash output") + ". " + + _("Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.")); + strUsage += HelpMessageOpt("outmultisig=VALUE:REQUIRED:PUBKEYS:PUBKEY1:PUBKEY2:....[:FLAGS]", _("Add Pay To n-of-m Multi-sig output to TX. n = REQUIRED, m = PUBKEYS") + ". " + + _("Optionally add the \"W\" flag to produce a pay-to-witness-script-hash output") + ". " + + _("Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.")); strUsage += HelpMessageOpt("sign=SIGHASH-FLAGS", _("Add zero or more signatures to transaction") + ". " + _("This command requires JSON registers:") + _("prevtxs=JSON object") + ", " + @@ -168,6 +176,14 @@ static void RegisterLoad(const std::string& strInput) RegisterSetJson(key, valStr); } +static CAmount ExtractAndValidateValue(const std::string& strValue) +{ + CAmount value; + if (!ParseMoney(strValue, value)) + throw std::runtime_error("invalid TX output value"); + return value; +} + static void MutateTxVersion(CMutableTransaction& tx, const std::string& cmdVal) { int64_t newVersion = atoi64(cmdVal); @@ -222,25 +238,18 @@ static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInpu static void MutateTxAddOutAddr(CMutableTransaction& tx, const std::string& strInput) { - // separate VALUE:ADDRESS in string - size_t pos = strInput.find(':'); - if ((pos == std::string::npos) || - (pos == 0) || - (pos == (strInput.size() - 1))) - throw std::runtime_error("TX output missing separator"); + // Separate into VALUE:ADDRESS + std::vector<std::string> vStrInputParts; + boost::split(vStrInputParts, strInput, boost::is_any_of(":")); - // extract and validate VALUE - std::string strValue = strInput.substr(0, pos); - CAmount value; - if (!ParseMoney(strValue, value)) - throw std::runtime_error("invalid TX output value"); + // Extract and validate VALUE + CAmount value = ExtractAndValidateValue(vStrInputParts[0]); // extract and validate ADDRESS - std::string strAddr = strInput.substr(pos + 1, std::string::npos); + std::string strAddr = vStrInputParts[1]; CBitcoinAddress addr(strAddr); if (!addr.IsValid()) throw std::runtime_error("invalid TX output address"); - // build standard output script via GetScriptForDestination() CScript scriptPubKey = GetScriptForDestination(addr.Get()); @@ -249,6 +258,114 @@ static void MutateTxAddOutAddr(CMutableTransaction& tx, const std::string& strIn tx.vout.push_back(txout); } +static void MutateTxAddOutPubKey(CMutableTransaction& tx, const std::string& strInput) +{ + // Separate into VALUE:PUBKEY[:FLAGS] + std::vector<std::string> vStrInputParts; + boost::split(vStrInputParts, strInput, boost::is_any_of(":")); + + // Extract and validate VALUE + CAmount value = ExtractAndValidateValue(vStrInputParts[0]); + + // Extract and validate PUBKEY + CPubKey pubkey(ParseHex(vStrInputParts[1])); + if (!pubkey.IsFullyValid()) + throw std::runtime_error("invalid TX output pubkey"); + CScript scriptPubKey = GetScriptForRawPubKey(pubkey); + CBitcoinAddress addr(scriptPubKey); + + // Extract and validate FLAGS + bool bSegWit = false; + bool bScriptHash = false; + if (vStrInputParts.size() == 3) { + std::string flags = vStrInputParts[2]; + bSegWit = (flags.find("W") != std::string::npos); + bScriptHash = (flags.find("S") != std::string::npos); + } + + if (bSegWit) { + // Call GetScriptForWitness() to build a P2WSH scriptPubKey + scriptPubKey = GetScriptForWitness(scriptPubKey); + } + if (bScriptHash) { + // Get the address for the redeem script, then call + // GetScriptForDestination() to construct a P2SH scriptPubKey. + CBitcoinAddress redeemScriptAddr(scriptPubKey); + scriptPubKey = GetScriptForDestination(redeemScriptAddr.Get()); + } + + // construct TxOut, append to transaction output list + CTxOut txout(value, scriptPubKey); + tx.vout.push_back(txout); +} + +static void MutateTxAddOutMultiSig(CMutableTransaction& tx, const std::string& strInput) +{ + // Separate into VALUE:REQUIRED:NUMKEYS:PUBKEY1:PUBKEY2:....[:FLAGS] + std::vector<std::string> vStrInputParts; + boost::split(vStrInputParts, strInput, boost::is_any_of(":")); + + // Check that there are enough parameters + if (vStrInputParts.size()<3) + throw std::runtime_error("Not enough multisig parameters"); + + // Extract and validate VALUE + CAmount value = ExtractAndValidateValue(vStrInputParts[0]); + + // Extract REQUIRED + uint32_t required = stoul(vStrInputParts[1]); + + // Extract NUMKEYS + uint32_t numkeys = stoul(vStrInputParts[2]); + + // Validate there are the correct number of pubkeys + if (vStrInputParts.size() < numkeys + 3) + throw std::runtime_error("incorrect number of multisig pubkeys"); + + if (required < 1 || required > 20 || numkeys < 1 || numkeys > 20 || numkeys < required) + throw std::runtime_error("multisig parameter mismatch. Required " \ + + std::to_string(required) + " of " + std::to_string(numkeys) + "signatures."); + + // extract and validate PUBKEYs + std::vector<CPubKey> pubkeys; + for(int pos = 1; pos <= int(numkeys); pos++) { + CPubKey pubkey(ParseHex(vStrInputParts[pos + 2])); + if (!pubkey.IsFullyValid()) + throw std::runtime_error("invalid TX output pubkey"); + pubkeys.push_back(pubkey); + } + + // Extract FLAGS + bool bSegWit = false; + bool bScriptHash = false; + if (vStrInputParts.size() == numkeys + 4) { + std::string flags = vStrInputParts.back(); + bSegWit = (flags.find("W") != std::string::npos); + bScriptHash = (flags.find("S") != std::string::npos); + } + else if (vStrInputParts.size() > numkeys + 4) { + // Validate that there were no more parameters passed + throw std::runtime_error("Too many parameters"); + } + + CScript scriptPubKey = GetScriptForMultisig(required, pubkeys); + + if (bSegWit) { + // Call GetScriptForWitness() to build a P2WSH scriptPubKey + scriptPubKey = GetScriptForWitness(scriptPubKey); + } + if (bScriptHash) { + // Get the address for the redeem script, then call + // GetScriptForDestination() to construct a P2SH scriptPubKey. + CBitcoinAddress addr(scriptPubKey); + scriptPubKey = GetScriptForDestination(addr.Get()); + } + + // construct TxOut, append to transaction output list + CTxOut txout(value, scriptPubKey); + tx.vout.push_back(txout); +} + static void MutateTxAddOutData(CMutableTransaction& tx, const std::string& strInput) { CAmount value = 0; @@ -260,10 +377,8 @@ static void MutateTxAddOutData(CMutableTransaction& tx, const std::string& strIn throw std::runtime_error("TX output value not specified"); if (pos != std::string::npos) { - // extract and validate VALUE - std::string strValue = strInput.substr(0, pos); - if (!ParseMoney(strValue, value)) - throw std::runtime_error("invalid TX output value"); + // Extract and validate VALUE + value = ExtractAndValidateValue(strInput.substr(0, pos)); } // extract and validate DATA @@ -280,21 +395,35 @@ static void MutateTxAddOutData(CMutableTransaction& tx, const std::string& strIn static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& strInput) { - // separate VALUE:SCRIPT in string - size_t pos = strInput.find(':'); - if ((pos == std::string::npos) || - (pos == 0)) + // separate VALUE:SCRIPT[:FLAGS] + std::vector<std::string> vStrInputParts; + boost::split(vStrInputParts, strInput, boost::is_any_of(":")); + if (vStrInputParts.size() < 2) throw std::runtime_error("TX output missing separator"); - // extract and validate VALUE - std::string strValue = strInput.substr(0, pos); - CAmount value; - if (!ParseMoney(strValue, value)) - throw std::runtime_error("invalid TX output value"); + // Extract and validate VALUE + CAmount value = ExtractAndValidateValue(vStrInputParts[0]); // extract and validate script - std::string strScript = strInput.substr(pos + 1, std::string::npos); - CScript scriptPubKey = ParseScript(strScript); // throws on err + std::string strScript = vStrInputParts[1]; + CScript scriptPubKey = ParseScript(strScript); + + // Extract FLAGS + bool bSegWit = false; + bool bScriptHash = false; + if (vStrInputParts.size() == 3) { + std::string flags = vStrInputParts.back(); + bSegWit = (flags.find("W") != std::string::npos); + bScriptHash = (flags.find("S") != std::string::npos); + } + + if (bSegWit) { + scriptPubKey = GetScriptForWitness(scriptPubKey); + } + if (bScriptHash) { + CBitcoinAddress addr(scriptPubKey); + scriptPubKey = GetScriptForDestination(addr.Get()); + } // construct TxOut, append to transaction output list CTxOut txout(value, scriptPubKey); @@ -538,10 +667,14 @@ static void MutateTx(CMutableTransaction& tx, const std::string& command, MutateTxDelOutput(tx, commandVal); else if (command == "outaddr") MutateTxAddOutAddr(tx, commandVal); - else if (command == "outdata") - MutateTxAddOutData(tx, commandVal); + else if (command == "outpubkey") + MutateTxAddOutPubKey(tx, commandVal); + else if (command == "outmultisig") + MutateTxAddOutMultiSig(tx, commandVal); else if (command == "outscript") MutateTxAddOutScript(tx, commandVal); + else if (command == "outdata") + MutateTxAddOutData(tx, commandVal); else if (command == "sign") { if (!ecc) { ecc.reset(new Secp256k1Init()); } diff --git a/src/blockencodings.cpp b/src/blockencodings.cpp index 72fe17bdc7..4a311cbba2 100644 --- a/src/blockencodings.cpp +++ b/src/blockencodings.cpp @@ -47,7 +47,7 @@ uint64_t CBlockHeaderAndShortTxIDs::GetShortID(const uint256& txhash) const { -ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& cmpctblock) { +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) @@ -104,6 +104,7 @@ ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& c return READ_STATUS_FAILED; // Short ID collision std::vector<bool> have_txn(txn_available.size()); + { LOCK(pool->cs); const std::vector<std::pair<uint256, CTxMemPool::txiter> >& vTxHashes = pool->vTxHashes; for (size_t i = 0; i < vTxHashes.size(); i++) { @@ -130,6 +131,38 @@ ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& c if (mempool_count == shorttxids.size()) break; } + } + + for (size_t i = 0; i < extra_txn.size(); i++) { + uint64_t shortid = cmpctblock.GetShortID(extra_txn[i].first); + std::unordered_map<uint64_t, uint16_t>::iterator idit = shorttxids.find(shortid); + if (idit != shorttxids.end()) { + if (!have_txn[idit->second]) { + txn_available[idit->second] = extra_txn[i].second; + have_txn[idit->second] = true; + mempool_count++; + extra_count++; + } else { + // If we find two mempool/extra txn that match the short id, just + // request it. + // This should be rare enough that the extra bandwidth doesn't matter, + // but eating a round-trip due to FillBlock failure would be annoying + // Note that we dont want duplication between extra_txn and mempool to + // trigger this case, so we compare witness hashes first + if (txn_available[idit->second] && + txn_available[idit->second]->GetWitnessHash() != extra_txn[i].second->GetWitnessHash()) { + txn_available[idit->second].reset(); + mempool_count--; + extra_count--; + } + } + } + // Though ideally we'd continue scanning for the two-txn-match-shortid case, + // the performance win of an early exit here is too good to pass up and worth + // the extra risk. + if (mempool_count == shorttxids.size()) + break; + } LogPrint("cmpctblock", "Initialized PartiallyDownloadedBlock for block %s using a cmpctblock of size %lu\n", cmpctblock.header.GetHash().ToString(), GetSerializeSize(cmpctblock, SER_NETWORK, PROTOCOL_VERSION)); @@ -176,7 +209,7 @@ ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector< return READ_STATUS_CHECKBLOCK_FAILED; } - LogPrint("cmpctblock", "Successfully reconstructed block %s with %lu txn prefilled, %lu txn from mempool and %lu txn requested\n", hash.ToString(), prefilled_count, mempool_count, vtx_missing.size()); + LogPrint("cmpctblock", "Successfully reconstructed block %s with %lu txn prefilled, %lu txn from mempool (incl at least %lu from extra pool) and %lu txn requested\n", hash.ToString(), prefilled_count, mempool_count, extra_count, vtx_missing.size()); if (vtx_missing.size() < 5) { for (const auto& tx : vtx_missing) LogPrint("cmpctblock", "Reconstructed block %s required tx %s\n", hash.ToString(), tx->GetHash().ToString()); diff --git a/src/blockencodings.h b/src/blockencodings.h index 809ccbf936..5a1d80d421 100644 --- a/src/blockencodings.h +++ b/src/blockencodings.h @@ -99,7 +99,7 @@ public: } }; -// Dumb serialization/storage-helper for CBlockHeaderAndShortTxIDs and PartiallyDownlaodedBlock +// Dumb serialization/storage-helper for CBlockHeaderAndShortTxIDs and PartiallyDownloadedBlock struct PrefilledTransaction { // Used as an offset since last prefilled tx in CBlockHeaderAndShortTxIDs, // as a proper transaction-in-block-index in PartiallyDownloadedBlock @@ -194,13 +194,14 @@ public: class PartiallyDownloadedBlock { protected: std::vector<CTransactionRef> txn_available; - size_t prefilled_count = 0, mempool_count = 0; + size_t prefilled_count = 0, mempool_count = 0, extra_count = 0; CTxMemPool* pool; public: CBlockHeader header; PartiallyDownloadedBlock(CTxMemPool* poolIn) : pool(poolIn) {} - ReadStatus InitData(const CBlockHeaderAndShortTxIDs& cmpctblock); + // extra_txn is a list of extra transactions to look at, in <witness hash, reference> form + ReadStatus InitData(const CBlockHeaderAndShortTxIDs& cmpctblock, const std::vector<std::pair<uint256, CTransactionRef>>& extra_txn); bool IsTxAvailable(size_t index) const; ReadStatus FillBlock(CBlock& block, const std::vector<CTransactionRef>& vtx_missing); }; diff --git a/src/bloom.cpp b/src/bloom.cpp index 520e10cdc8..8d47cb76e8 100644 --- a/src/bloom.cpp +++ b/src/bloom.cpp @@ -19,15 +19,13 @@ #define LN2SQUARED 0.4804530139182014246671025263266649717305529515945455 #define LN2 0.6931471805599453094172321214581765680755001343602552 -using namespace std; - CBloomFilter::CBloomFilter(unsigned int nElements, double nFPRate, unsigned int nTweakIn, unsigned char nFlagsIn) : /** * The ideal size for a bloom filter with a given number of elements and false positive rate is: * - nElements * log(fp rate) / ln(2)^2 * We ignore filter parameters which will create a bloom filter larger than the protocol limits */ - vData(min((unsigned int)(-1 / LN2SQUARED * nElements * log(nFPRate)), MAX_BLOOM_FILTER_SIZE * 8) / 8), + vData(std::min((unsigned int)(-1 / LN2SQUARED * nElements * log(nFPRate)), MAX_BLOOM_FILTER_SIZE * 8) / 8), /** * The ideal number of hash functions is filter size * ln(2) / number of elements * Again, we ignore filter parameters which will create a bloom filter with more hash functions than the protocol limits @@ -35,7 +33,7 @@ CBloomFilter::CBloomFilter(unsigned int nElements, double nFPRate, unsigned int */ isFull(false), isEmpty(true), - nHashFuncs(min((unsigned int)(vData.size() * 8 / nElements * LN2), MAX_HASH_FUNCS)), + nHashFuncs(std::min((unsigned int)(vData.size() * 8 / nElements * LN2), MAX_HASH_FUNCS)), nTweak(nTweakIn), nFlags(nFlagsIn) { @@ -58,7 +56,7 @@ inline unsigned int CBloomFilter::Hash(unsigned int nHashNum, const std::vector< return MurmurHash3(nHashNum * 0xFBA4C795 + nTweak, vDataToHash) % (vData.size() * 8); } -void CBloomFilter::insert(const vector<unsigned char>& vKey) +void CBloomFilter::insert(const std::vector<unsigned char>& vKey) { if (isFull) return; @@ -75,17 +73,17 @@ void CBloomFilter::insert(const COutPoint& outpoint) { CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); stream << outpoint; - vector<unsigned char> data(stream.begin(), stream.end()); + std::vector<unsigned char> data(stream.begin(), stream.end()); insert(data); } void CBloomFilter::insert(const uint256& hash) { - vector<unsigned char> data(hash.begin(), hash.end()); + std::vector<unsigned char> data(hash.begin(), hash.end()); insert(data); } -bool CBloomFilter::contains(const vector<unsigned char>& vKey) const +bool CBloomFilter::contains(const std::vector<unsigned char>& vKey) const { if (isFull) return true; @@ -105,13 +103,13 @@ bool CBloomFilter::contains(const COutPoint& outpoint) const { CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); stream << outpoint; - vector<unsigned char> data(stream.begin(), stream.end()); + std::vector<unsigned char> data(stream.begin(), stream.end()); return contains(data); } bool CBloomFilter::contains(const uint256& hash) const { - vector<unsigned char> data(hash.begin(), hash.end()); + std::vector<unsigned char> data(hash.begin(), hash.end()); return contains(data); } @@ -154,7 +152,7 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx) // This means clients don't have to update the filter themselves when a new relevant tx // is discovered in order to find spending transactions, which avoids round-tripping and race conditions. CScript::const_iterator pc = txout.scriptPubKey.begin(); - vector<unsigned char> data; + std::vector<unsigned char> data; while (pc < txout.scriptPubKey.end()) { opcodetype opcode; @@ -168,7 +166,7 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx) else if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_P2PUBKEY_ONLY) { txnouttype type; - vector<vector<unsigned char> > vSolutions; + std::vector<std::vector<unsigned char> > vSolutions; if (Solver(txout.scriptPubKey, type, vSolutions) && (type == TX_PUBKEY || type == TX_MULTISIG)) insert(COutPoint(hash, i)); @@ -189,7 +187,7 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx) // Match if the filter contains any arbitrary script data element in any scriptSig in tx CScript::const_iterator pc = txin.scriptSig.begin(); - vector<unsigned char> data; + std::vector<unsigned char> data; while (pc < txin.scriptSig.end()) { opcodetype opcode; @@ -280,7 +278,7 @@ void CRollingBloomFilter::insert(const std::vector<unsigned char>& vKey) void CRollingBloomFilter::insert(const uint256& hash) { - vector<unsigned char> vData(hash.begin(), hash.end()); + std::vector<unsigned char> vData(hash.begin(), hash.end()); insert(vData); } @@ -300,7 +298,7 @@ bool CRollingBloomFilter::contains(const std::vector<unsigned char>& vKey) const bool CRollingBloomFilter::contains(const uint256& hash) const { - vector<unsigned char> vData(hash.begin(), hash.end()); + std::vector<unsigned char> vData(hash.begin(), hash.end()); return contains(vData); } diff --git a/src/chain.cpp b/src/chain.cpp index 3cd7b33920..a5b369c4fc 100644 --- a/src/chain.cpp +++ b/src/chain.cpp @@ -5,8 +5,6 @@ #include "chain.h" -using namespace std; - /** * CChain implementation */ @@ -61,10 +59,10 @@ const CBlockIndex *CChain::FindFork(const CBlockIndex *pindex) const { return pindex; } -CBlockIndex* CChain::FindLatestBefore(int64_t nTime) const +CBlockIndex* CChain::FindEarliestAtLeast(int64_t nTime) const { std::vector<CBlockIndex*>::const_iterator lower = std::lower_bound(vChain.begin(), vChain.end(), nTime, - [](CBlockIndex* pBlock, const int64_t& time) -> bool { return pBlock->GetBlockTime() < time; }); + [](CBlockIndex* pBlock, const int64_t& time) -> bool { return pBlock->GetBlockTimeMax() < time; }); return (lower == vChain.end() ? NULL : *lower); } diff --git a/src/chain.h b/src/chain.h index 989a71958c..acb29b667b 100644 --- a/src/chain.h +++ b/src/chain.h @@ -202,6 +202,9 @@ public: //! (memory only) Sequential id assigned to distinguish order in which blocks are received. int32_t nSequenceId; + //! (memory only) Maximum nTime in the chain upto and including this block. + unsigned int nTimeMax; + void SetNull() { phashBlock = NULL; @@ -216,6 +219,7 @@ public: nChainTx = 0; nStatus = 0; nSequenceId = 0; + nTimeMax = 0; nVersion = 0; hashMerkleRoot = uint256(); @@ -281,6 +285,11 @@ public: return (int64_t)nTime; } + int64_t GetBlockTimeMax() const + { + return (int64_t)nTimeMax; + } + enum { nMedianTimeSpan=11 }; int64_t GetMedianTimePast() const @@ -461,8 +470,8 @@ public: /** Find the last common block between this chain and a block index entry. */ const CBlockIndex *FindFork(const CBlockIndex *pindex) const; - /** Find the most recent block with timestamp lower than the given. */ - CBlockIndex* FindLatestBefore(int64_t nTime) const; + /** Find the earliest block with timestamp equal or greater than the given. */ + CBlockIndex* FindEarliestAtLeast(int64_t nTime) const; }; #endif // BITCOIN_CHAIN_H diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 1e294da9f4..d99f800f0a 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -99,6 +99,9 @@ public: // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000002cb971dd56d1c583c20f90"); + // By default assume that the signatures in ancestors of this block are valid. + consensus.defaultAssumeValid = uint256S("0x0000000000000000030abc968e1bd635736e880b946085c93152969b9a81a6e2"); //447235 + /** * The message start string is designed to be unlikely to occur in normal data. * The characters are rarely used upper ASCII, not valid as UTF-8, and produce @@ -151,11 +154,15 @@ public: (225430, uint256S("0x00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932")) (250000, uint256S("0x000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214")) (279000, uint256S("0x0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40")) - (295000, uint256S("0x00000000000000004d9b4ef50f0f9d686fd69db2e03af35a100370c64632a983")), - 1397080064, // * UNIX timestamp of last checkpoint block - 36544669, // * total number of transactions between genesis and last checkpoint + (295000, uint256S("0x00000000000000004d9b4ef50f0f9d686fd69db2e03af35a100370c64632a983")) + }; + + chainTxData = ChainTxData{ + // Data as of block 00000000000000000166d612d5595e2b1cd88d71d695fc580af64d8da8658c23 (height 446482). + 1483472411, // * UNIX timestamp of last known number of transactions + 184495391, // * total number of transactions between genesis and that timestamp // (the tx=... number in the SetBestChain debug.log lines) - 60000.0 // * estimated number of transactions per day after checkpoint + 3.2 // * estimated number of transactions per second after that timestamp }; } }; @@ -197,6 +204,9 @@ public: // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000000198b4def2baa9338d6"); + // By default assume that the signatures in ancestors of this block are valid. + consensus.defaultAssumeValid = uint256S("0x000000000871ee6842d3648317ccc8a435eb8cc3c2429aee94faff9ba26b05a0"); //1043841 + pchMessageStart[0] = 0x0b; pchMessageStart[1] = 0x11; pchMessageStart[2] = 0x09; @@ -234,9 +244,13 @@ public: checkpointData = (CCheckpointData) { boost::assign::map_list_of ( 546, uint256S("000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70")), - 1337966069, - 1488, - 300 + }; + + chainTxData = ChainTxData{ + // Data as of block 00000000c2872f8f8a8935c8e3c5862be9038c97d4de2cf37ed496991166928a (height 1063660) + 1483546230, + 12834668, + 0.15 }; } @@ -275,6 +289,9 @@ public: // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x00"); + // By default assume that the signatures in ancestors of this block are valid. + consensus.defaultAssumeValid = uint256S("0x00"); + pchMessageStart[0] = 0xfa; pchMessageStart[1] = 0xbf; pchMessageStart[2] = 0xb5; @@ -297,11 +314,15 @@ public: checkpointData = (CCheckpointData){ boost::assign::map_list_of - ( 0, uint256S("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")), + ( 0, uint256S("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")) + }; + + chainTxData = ChainTxData{ 0, 0, 0 }; + base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,111); base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,196); base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,239); diff --git a/src/chainparams.h b/src/chainparams.h index 381bac12f9..db524e8f8e 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -28,9 +28,12 @@ typedef std::map<int, uint256> MapCheckpoints; struct CCheckpointData { MapCheckpoints mapCheckpoints; - int64_t nTimeLastCheckpoint; - int64_t nTransactionsLastCheckpoint; - double fTransactionsPerDay; +}; + +struct ChainTxData { + int64_t nTime; + int64_t nTxCount; + double dTxRate; }; /** @@ -73,6 +76,7 @@ public: const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; } const std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; } const CCheckpointData& Checkpoints() const { return checkpointData; } + const ChainTxData& TxData() const { return chainTxData; } protected: CChainParams() {} @@ -90,6 +94,7 @@ protected: bool fRequireStandard; bool fMineBlocksOnDemand; CCheckpointData checkpointData; + ChainTxData chainTxData; }; /** diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index 9a20747049..13b5876530 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -15,46 +15,6 @@ namespace Checkpoints { - /** - * How many times slower we expect checking transactions after the last - * checkpoint to be (from checking signatures, which is skipped up to the - * last checkpoint). This number is a compromise, as it can't be accurate - * for every system. When reindexing from a fast disk with a slow CPU, it - * can be up to 20, while when downloading from a slow network with a - * fast multicore CPU, it won't be much higher than 1. - */ - static const double SIGCHECK_VERIFICATION_FACTOR = 5.0; - - //! Guess how far we are in the verification process at the given block index - double GuessVerificationProgress(const CCheckpointData& data, CBlockIndex *pindex, bool fSigchecks) { - if (pindex==NULL) - return 0.0; - - int64_t nNow = time(NULL); - - double fSigcheckVerificationFactor = fSigchecks ? SIGCHECK_VERIFICATION_FACTOR : 1.0; - double fWorkBefore = 0.0; // Amount of work done before pindex - double fWorkAfter = 0.0; // Amount of work left after pindex (estimated) - // Work is defined as: 1.0 per transaction before the last checkpoint, and - // fSigcheckVerificationFactor per transaction after. - - if (pindex->nChainTx <= data.nTransactionsLastCheckpoint) { - double nCheapBefore = pindex->nChainTx; - double nCheapAfter = data.nTransactionsLastCheckpoint - pindex->nChainTx; - double nExpensiveAfter = (nNow - data.nTimeLastCheckpoint)/86400.0*data.fTransactionsPerDay; - fWorkBefore = nCheapBefore; - fWorkAfter = nCheapAfter + nExpensiveAfter*fSigcheckVerificationFactor; - } else { - double nCheapBefore = data.nTransactionsLastCheckpoint; - double nExpensiveBefore = pindex->nChainTx - data.nTransactionsLastCheckpoint; - double nExpensiveAfter = (nNow - pindex->GetBlockTime())/86400.0*data.fTransactionsPerDay; - fWorkBefore = nCheapBefore + nExpensiveBefore*fSigcheckVerificationFactor; - fWorkAfter = nExpensiveAfter*fSigcheckVerificationFactor; - } - - return fWorkBefore / (fWorkBefore + fWorkAfter); - } - CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) { const MapCheckpoints& checkpoints = data.mapCheckpoints; diff --git a/src/checkpoints.h b/src/checkpoints.h index f826de2319..7449d1063b 100644 --- a/src/checkpoints.h +++ b/src/checkpoints.h @@ -22,8 +22,6 @@ namespace Checkpoints //! Returns last CBlockIndex* in mapBlockIndex that is a checkpoint CBlockIndex* GetLastCheckpoint(const CCheckpointData& data); -double GuessVerificationProgress(const CCheckpointData& data, CBlockIndex* pindex, bool fSigchecks = true); - } //namespace Checkpoints #endif // BITCOIN_CHECKPOINTS_H diff --git a/src/clientversion.h b/src/clientversion.h index 0cd7e517ee..0b27bb1bdf 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -26,7 +26,7 @@ * Copyright year (2009-this) * Todo: update this when changing our copyright comments in the source */ -#define COPYRIGHT_YEAR 2016 +#define COPYRIGHT_YEAR 2017 #endif //HAVE_CONFIG_H diff --git a/src/consensus/params.h b/src/consensus/params.h index edf445e1c7..6240e82857 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -47,7 +47,7 @@ struct Params { /** Block height at which BIP66 becomes active */ int BIP66Height; /** - * Minimum blocks including miner confirmation of the total of 2016 blocks in a retargetting period, + * Minimum blocks including miner confirmation of the total of 2016 blocks in a retargeting period, * (nPowTargetTimespan / nPowTargetSpacing) which is also used for BIP9 deployments. * Examples: 1916 for 95%, 1512 for testchains. */ @@ -62,6 +62,7 @@ struct Params { int64_t nPowTargetTimespan; int64_t DifficultyAdjustmentInterval() const { return nPowTargetTimespan / nPowTargetSpacing; } uint256 nMinimumChainWork; + uint256 defaultAssumeValid; }; } // namespace Consensus diff --git a/src/core_read.cpp b/src/core_read.cpp index 85bb62c176..a8d667e3bc 100644 --- a/src/core_read.cpp +++ b/src/core_read.cpp @@ -20,13 +20,11 @@ #include <boost/algorithm/string/split.hpp> #include <boost/assign/list_of.hpp> -using namespace std; - CScript ParseScript(const std::string& s) { CScript result; - static map<string, opcodetype> mapOpNames; + static std::map<std::string, opcodetype> mapOpNames; if (mapOpNames.empty()) { @@ -39,7 +37,7 @@ CScript ParseScript(const std::string& s) const char* name = GetOpName((opcodetype)op); if (strcmp(name, "OP_UNKNOWN") == 0) continue; - string strName(name); + std::string strName(name); mapOpNames[strName] = (opcodetype)op; // Convenience: OP_ADD and just ADD are both recognized: boost::algorithm::replace_first(strName, "OP_", ""); @@ -47,7 +45,7 @@ CScript ParseScript(const std::string& s) } } - vector<string> words; + std::vector<std::string> words; boost::algorithm::split(words, s, boost::algorithm::is_any_of(" \t\n"), boost::algorithm::token_compress_on); for (std::vector<std::string>::const_iterator w = words.begin(); w != words.end(); ++w) @@ -57,16 +55,16 @@ CScript ParseScript(const std::string& s) // Empty string, ignore. (boost::split given '' will return one word) } else if (all(*w, boost::algorithm::is_digit()) || - (boost::algorithm::starts_with(*w, "-") && all(string(w->begin()+1, w->end()), boost::algorithm::is_digit()))) + (boost::algorithm::starts_with(*w, "-") && all(std::string(w->begin()+1, w->end()), boost::algorithm::is_digit()))) { // Number int64_t n = atoi64(*w); result << n; } - else if (boost::algorithm::starts_with(*w, "0x") && (w->begin()+2 != w->end()) && IsHex(string(w->begin()+2, w->end()))) + else if (boost::algorithm::starts_with(*w, "0x") && (w->begin()+2 != w->end()) && IsHex(std::string(w->begin()+2, w->end()))) { // Raw hex data, inserted NOT pushed onto stack: - std::vector<unsigned char> raw = ParseHex(string(w->begin()+2, w->end())); + std::vector<unsigned char> raw = ParseHex(std::string(w->begin()+2, w->end())); result.insert(result.end(), raw.begin(), raw.end()); } else if (w->size() >= 2 && boost::algorithm::starts_with(*w, "'") && boost::algorithm::ends_with(*w, "'")) @@ -83,7 +81,7 @@ CScript ParseScript(const std::string& s) } else { - throw runtime_error("script parse error"); + throw std::runtime_error("script parse error"); } } @@ -95,7 +93,7 @@ bool DecodeHexTx(CMutableTransaction& tx, const std::string& strHexTx, bool fTry if (!IsHex(strHexTx)) return false; - vector<unsigned char> txData(ParseHex(strHexTx)); + std::vector<unsigned char> txData(ParseHex(strHexTx)); if (fTryNoWitness) { CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS); @@ -113,6 +111,8 @@ bool DecodeHexTx(CMutableTransaction& tx, const std::string& strHexTx, bool fTry CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); try { ssData >> tx; + if (!ssData.empty()) + return false; } catch (const std::exception&) { return false; @@ -138,9 +138,9 @@ bool DecodeHexBlk(CBlock& block, const std::string& strHexBlk) return true; } -uint256 ParseHashUV(const UniValue& v, const string& strName) +uint256 ParseHashUV(const UniValue& v, const std::string& strName) { - string strHex; + std::string strHex; if (v.isStr()) strHex = v.getValStr(); return ParseHashStr(strHex, strName); // Note: ParseHashStr("") throws a runtime_error @@ -149,19 +149,19 @@ uint256 ParseHashUV(const UniValue& v, const string& strName) uint256 ParseHashStr(const std::string& strHex, const std::string& strName) { if (!IsHex(strHex)) // Note: IsHex("") is false - throw runtime_error(strName+" must be hexadecimal string (not '"+strHex+"')"); + throw std::runtime_error(strName + " must be hexadecimal string (not '" + strHex + "')"); uint256 result; result.SetHex(strHex); return result; } -vector<unsigned char> ParseHexUV(const UniValue& v, const string& strName) +std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::string& strName) { - string strHex; + std::string strHex; if (v.isStr()) strHex = v.getValStr(); if (!IsHex(strHex)) - throw runtime_error(strName+" must be hexadecimal string (not '"+strHex+"')"); + throw std::runtime_error(strName + " must be hexadecimal string (not '" + strHex + "')"); return ParseHex(strHex); } diff --git a/src/core_write.cpp b/src/core_write.cpp index ee8a897ca4..b0993a131f 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -18,16 +18,14 @@ #include <boost/assign/list_of.hpp> #include <boost/foreach.hpp> -using namespace std; - -string FormatScript(const CScript& script) +std::string FormatScript(const CScript& script) { - string ret; + std::string ret; CScript::const_iterator it = script.begin(); opcodetype op; while (it != script.end()) { CScript::const_iterator it2 = it; - vector<unsigned char> vch; + std::vector<unsigned char> vch; if (script.GetOp2(it, op, &vch)) { if (op == OP_0) { ret += "0 "; @@ -36,9 +34,9 @@ string FormatScript(const CScript& script) ret += strprintf("%i ", op - OP_1NEGATE - 1); continue; } else if (op >= OP_NOP && op <= OP_NOP10) { - string str(GetOpName(op)); - if (str.substr(0, 3) == string("OP_")) { - ret += str.substr(3, string::npos) + " "; + std::string str(GetOpName(op)); + if (str.substr(0, 3) == std::string("OP_")) { + ret += str.substr(3, std::string::npos) + " "; continue; } } @@ -55,14 +53,14 @@ string FormatScript(const CScript& script) return ret.substr(0, ret.size() - 1); } -const map<unsigned char, string> mapSigHashTypes = +const std::map<unsigned char, std::string> mapSigHashTypes = boost::assign::map_list_of - (static_cast<unsigned char>(SIGHASH_ALL), string("ALL")) - (static_cast<unsigned char>(SIGHASH_ALL|SIGHASH_ANYONECANPAY), string("ALL|ANYONECANPAY")) - (static_cast<unsigned char>(SIGHASH_NONE), string("NONE")) - (static_cast<unsigned char>(SIGHASH_NONE|SIGHASH_ANYONECANPAY), string("NONE|ANYONECANPAY")) - (static_cast<unsigned char>(SIGHASH_SINGLE), string("SINGLE")) - (static_cast<unsigned char>(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY), string("SINGLE|ANYONECANPAY")) + (static_cast<unsigned char>(SIGHASH_ALL), std::string("ALL")) + (static_cast<unsigned char>(SIGHASH_ALL|SIGHASH_ANYONECANPAY), std::string("ALL|ANYONECANPAY")) + (static_cast<unsigned char>(SIGHASH_NONE), std::string("NONE")) + (static_cast<unsigned char>(SIGHASH_NONE|SIGHASH_ANYONECANPAY), std::string("NONE|ANYONECANPAY")) + (static_cast<unsigned char>(SIGHASH_SINGLE), std::string("SINGLE")) + (static_cast<unsigned char>(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY), std::string("SINGLE|ANYONECANPAY")) ; /** @@ -72,11 +70,11 @@ const map<unsigned char, string> mapSigHashTypes = * of a signature. Only pass true for scripts you believe could contain signatures. For example, * pass false, or omit the this argument (defaults to false), for scriptPubKeys. */ -string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode) +std::string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode) { - string str; + std::string str; opcodetype opcode; - vector<unsigned char> vch; + std::vector<unsigned char> vch; CScript::const_iterator pc = script.begin(); while (pc < script.end()) { if (!str.empty()) { @@ -87,12 +85,12 @@ string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode) return str; } if (0 <= opcode && opcode <= OP_PUSHDATA4) { - if (vch.size() <= static_cast<vector<unsigned char>::size_type>(4)) { + if (vch.size() <= static_cast<std::vector<unsigned char>::size_type>(4)) { str += strprintf("%d", CScriptNum(vch, false).getint()); } else { // the IsUnspendable check makes sure not to try to decode OP_RETURN data that may match the format of a signature if (fAttemptSighashDecode && !script.IsUnspendable()) { - string strSigHashDecode; + std::string strSigHashDecode; // goal: only attempt to decode a defined sighash type from data that looks like a signature within a scriptSig. // this won't decode correctly formatted public keys in Pubkey or Multisig scripts due to // the restrictions on the pubkey formats (see IsCompressedOrUncompressedPubKey) being incongruous with the @@ -116,7 +114,7 @@ string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode) return str; } -string EncodeHexTx(const CTransaction& tx, const int serialFlags) +std::string EncodeHexTx(const CTransaction& tx, const int serialFlags) { CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION | serialFlags); ssTx << tx; @@ -127,7 +125,7 @@ void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex) { txnouttype type; - vector<CTxDestination> addresses; + std::vector<CTxDestination> addresses; int nRequired; out.pushKV("asm", ScriptToAsmStr(scriptPubKey)); diff --git a/src/crypto/common.h b/src/crypto/common.h index 580c72f5a6..4a9d1150b6 100644 --- a/src/crypto/common.h +++ b/src/crypto/common.h @@ -10,57 +10,73 @@ #endif #include <stdint.h> +#include <string.h> #include "compat/endian.h" uint16_t static inline ReadLE16(const unsigned char* ptr) { - return le16toh(*((uint16_t*)ptr)); + uint16_t x; + memcpy((char*)&x, ptr, 2); + return le16toh(x); } uint32_t static inline ReadLE32(const unsigned char* ptr) { - return le32toh(*((uint32_t*)ptr)); + uint32_t x; + memcpy((char*)&x, ptr, 4); + return le32toh(x); } uint64_t static inline ReadLE64(const unsigned char* ptr) { - return le64toh(*((uint64_t*)ptr)); + uint64_t x; + memcpy((char*)&x, ptr, 8); + return le64toh(x); } void static inline WriteLE16(unsigned char* ptr, uint16_t x) { - *((uint16_t*)ptr) = htole16(x); + uint16_t v = htole16(x); + memcpy(ptr, (char*)&v, 2); } void static inline WriteLE32(unsigned char* ptr, uint32_t x) { - *((uint32_t*)ptr) = htole32(x); + uint32_t v = htole32(x); + memcpy(ptr, (char*)&v, 4); } void static inline WriteLE64(unsigned char* ptr, uint64_t x) { - *((uint64_t*)ptr) = htole64(x); + uint64_t v = htole64(x); + memcpy(ptr, (char*)&v, 8); } uint32_t static inline ReadBE32(const unsigned char* ptr) { - return be32toh(*((uint32_t*)ptr)); + uint32_t x; + memcpy((char*)&x, ptr, 4); + return be32toh(x); } uint64_t static inline ReadBE64(const unsigned char* ptr) { - return be64toh(*((uint64_t*)ptr)); + uint64_t x; + memcpy((char*)&x, ptr, 8); + return be64toh(x); } void static inline WriteBE32(unsigned char* ptr, uint32_t x) { - *((uint32_t*)ptr) = htobe32(x); + uint32_t v = htobe32(x); + memcpy(ptr, (char*)&v, 4); } void static inline WriteBE64(unsigned char* ptr, uint64_t x) { - *((uint64_t*)ptr) = htobe64(x); + uint64_t v = htobe64(x); + memcpy(ptr, (char*)&v, 8); } #endif // BITCOIN_CRYPTO_COMMON_H diff --git a/src/cuckoocache.h b/src/cuckoocache.h index efd6a820b5..ff47e9776b 100644 --- a/src/cuckoocache.h +++ b/src/cuckoocache.h @@ -257,7 +257,7 @@ private: * * First, epoch_check decrements and checks the cheap heuristic, and then does * a more expensive scan if the cheap heuristic runs out. If the expensive - * scan suceeds, the epochs are aged and old elements are allow_erased. The + * scan succeeds, the epochs are aged and old elements are allow_erased. The * cheap heuristic is reset to retrigger after the worst case growth of the * current epoch's elements would exceed the epoch_size. */ @@ -395,7 +395,7 @@ public: * 1) On first iteration, last_loc == invalid(), find returns last, so * last_loc defaults to locs[0]. * 2) On further iterations, where last_loc == locs[k], last_loc will - * go to locs[k+1 % 8], i.e., next of the 8 indicies wrapping around + * go to locs[k+1 % 8], i.e., next of the 8 indices wrapping around * to 0 if needed. * * This prevents moving the element we just put in. diff --git a/src/hash.cpp b/src/hash.cpp index a399ca9252..a14a2386a2 100644 --- a/src/hash.cpp +++ b/src/hash.cpp @@ -57,7 +57,7 @@ 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 742ff2b8fb..daac7a0f1a 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -25,7 +25,7 @@ static const char* WWW_AUTH_HEADER_DATA = "Basic realm=\"jsonrpc\""; /** Simple one-shot callback timer to be used by the RPC mechanism to e.g. - * re-lock the wellet. + * re-lock the wallet. */ class HTTPRPCTimer : public RPCTimerBase { @@ -113,8 +113,8 @@ static bool multiUserAuthorized(std::string strUserPass) std::string strHash = vFields[2]; unsigned int KEY_SIZE = 32; - unsigned char *out = new unsigned char[KEY_SIZE]; - + unsigned char out[KEY_SIZE]; + CHMAC_SHA256(reinterpret_cast<const unsigned char*>(strSalt.c_str()), strSalt.size()).Write(reinterpret_cast<const unsigned char*>(strPass.c_str()), strPass.size()).Finalize(out); std::vector<unsigned char> hexvec(out, out+KEY_SIZE); std::string strHashFromPass = HexStr(hexvec); diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 1692b43f28..e1763c6ad2 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -118,7 +118,7 @@ public: void Run() { ThreadCounter count(*this); - while (running) { + while (true) { std::unique_ptr<WorkItem> i; { std::unique_lock<std::mutex> lock(cs); diff --git a/src/init.cpp b/src/init.cpp index 992ce8ebdc..7c108ac4a6 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -65,8 +65,6 @@ #include "zmq/zmqnotificationinterface.h" #endif -using namespace std; - bool fFeeEstimatesInitialized = false; static const bool DEFAULT_PROXYRANDOMIZE = true; static const bool DEFAULT_REST_ENABLE = false; @@ -310,10 +308,10 @@ void OnRPCStopped() void OnRPCPreCommand(const CRPCCommand& cmd) { // Observe safe mode - string strWarning = GetWarnings("rpc"); + std::string strWarning = GetWarnings("rpc"); if (strWarning != "" && !GetBoolArg("-disablesafemode", DEFAULT_DISABLE_SAFEMODE) && !cmd.okSafeMode) - throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, string("Safe mode: ") + strWarning); + throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, std::string("Safe mode: ") + strWarning); } std::string HelpMessage(HelpMessageMode mode) @@ -322,15 +320,14 @@ std::string HelpMessage(HelpMessageMode mode) // When adding new options to the categories, please keep and ensure alphabetical ordering. // Do not translate _(...) -help-debug options, Many technical terms, and only a very small audience, so is unnecessary stress to translators. - string strUsage = HelpMessageGroup(_("Options:")); + std::string strUsage = HelpMessageGroup(_("Options:")); strUsage += HelpMessageOpt("-?", _("Print this help message and exit")); strUsage += HelpMessageOpt("-version", _("Print version and exit")); strUsage += HelpMessageOpt("-alertnotify=<cmd>", _("Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)")); strUsage += HelpMessageOpt("-blocknotify=<cmd>", _("Execute command when the best block changes (%s in cmd is replaced by block hash)")); if (showDebug) strUsage += HelpMessageOpt("-blocksonly", strprintf(_("Whether to operate in a blocks only mode (default: %u)"), DEFAULT_BLOCKSONLY)); - strUsage += HelpMessageOpt("-checkblocks=<n>", strprintf(_("How many blocks to check at startup (default: %u, 0 = all)"), DEFAULT_CHECKBLOCKS)); - strUsage += HelpMessageOpt("-checklevel=<n>", strprintf(_("How thorough the block verification of -checkblocks is (0-4, default: %u)"), DEFAULT_CHECKLEVEL)); + strUsage +=HelpMessageOpt("-assumevalid=<hex>", strprintf(_("If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: %s, testnet: %s)"), Params(CBaseChainParams::MAIN).GetConsensus().defaultAssumeValid.GetHex(), Params(CBaseChainParams::TESTNET).GetConsensus().defaultAssumeValid.GetHex())); strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), BITCOIN_CONF_FILENAME)); if (mode == HMM_BITCOIND) { @@ -346,14 +343,15 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-maxorphantx=<n>", strprintf(_("Keep at most <n> unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS)); strUsage += HelpMessageOpt("-maxmempool=<n>", strprintf(_("Keep the transaction memory pool below <n> megabytes (default: %u)"), DEFAULT_MAX_MEMPOOL_SIZE)); strUsage += HelpMessageOpt("-mempoolexpiry=<n>", strprintf(_("Do not keep transactions in the mempool longer than <n> hours (default: %u)"), DEFAULT_MEMPOOL_EXPIRY)); + strUsage += HelpMessageOpt("-blockreconstructionextratxn=<n>", strprintf(_("Extra transactions to keep in memory for compact block reconstructions (default: %u)"), DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN)); strUsage += HelpMessageOpt("-par=<n>", strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"), -GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS)); #ifndef WIN32 strUsage += HelpMessageOpt("-pid=<file>", strprintf(_("Specify pid file (default: %s)"), BITCOIN_PID_FILENAME)); #endif - strUsage += HelpMessageOpt("-prune=<n>", strprintf(_("Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. " + strUsage += HelpMessageOpt("-prune=<n>", strprintf(_("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, >%u = target size in MiB to use for block files)"), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024)); + "(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)"), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024)); strUsage += HelpMessageOpt("-reindex-chainstate", _("Rebuild chain state from the currently indexed blocks")); strUsage += HelpMessageOpt("-reindex", _("Rebuild chain state and block index from the blk*.dat files on disk")); #ifndef WIN32 @@ -420,6 +418,8 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-uacomment=<cmt>", _("Append comment to the user agent string")); if (showDebug) { + strUsage += HelpMessageOpt("-checkblocks=<n>", strprintf(_("How many blocks to check at startup (default: %u, 0 = all)"), DEFAULT_CHECKBLOCKS)); + strUsage += HelpMessageOpt("-checklevel=<n>", strprintf(_("How thorough the block verification of -checkblocks is (0-4, default: %u)"), DEFAULT_CHECKLEVEL)); strUsage += HelpMessageOpt("-checkblockindex", strprintf("Do a full consistency check for mapBlockIndex, setBlockIndexCandidates, chainActive and mapBlocksUnlinked occasionally. Also sets -checkmempool (default: %u)", Params(CBaseChainParams::MAIN).DefaultConsistencyChecks())); strUsage += HelpMessageOpt("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u)", Params(CBaseChainParams::MAIN).DefaultConsistencyChecks())); strUsage += HelpMessageOpt("-checkpoints", strprintf("Disable expensive verification for known chain history (default: %u)", DEFAULT_CHECKPOINTS_ENABLED)); @@ -434,7 +434,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-limitdescendantsize=<n>", strprintf("Do not accept transactions if any ancestor would have more than <n> kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT)); strUsage += HelpMessageOpt("-bip9params=deployment:start:end", "Use given start/end times for specified BIP9 deployment (regtest-only)"); } - string debugCategories = "addrman, alert, bench, cmpctblock, coindb, db, http, libevent, lock, mempool, mempoolrej, net, proxy, prune, rand, reindex, rpc, selectcoins, tor, zmq"; // Don't translate these and qt below + std::string debugCategories = "addrman, alert, bench, cmpctblock, coindb, db, http, libevent, lock, mempool, mempoolrej, net, proxy, prune, rand, reindex, rpc, selectcoins, tor, zmq"; // Don't translate these and qt below if (mode == HMM_BITCOIN_QT) debugCategories += ", qt"; strUsage += HelpMessageOpt("-debug=<category>", strprintf(_("Output debugging information (default: %u, supplying <category> is optional)"), 0) + ". " + @@ -467,8 +467,11 @@ std::string HelpMessage(HelpMessageMode mode) AppendParamsHelpMessages(strUsage, showDebug); strUsage += HelpMessageGroup(_("Node relay options:")); - if (showDebug) + if (showDebug) { strUsage += HelpMessageOpt("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", !Params(CBaseChainParams::TESTNET).RequireStandard())); + strUsage += HelpMessageOpt("-incrementalrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to define cost of relay, used for mempool limiting and BIP 125 replacement. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_INCREMENTAL_RELAY_FEE))); + strUsage += HelpMessageOpt("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to defined dust, the value of an output such that it will cost about 1/3 of its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE))); + } strUsage += HelpMessageOpt("-bytespersigop", strprintf(_("Equivalent bytes per sigop in transactions for relay and mining (default: %u)"), DEFAULT_BYTES_PER_SIGOP)); strUsage += HelpMessageOpt("-datacarrier", strprintf(_("Relay and mine data carrier transactions (default: %u)"), DEFAULT_ACCEPT_DATACARRIER)); strUsage += HelpMessageOpt("-datacarriersize", strprintf(_("Maximum size of data in data carrier transactions we relay and mine (default: %u)"), MAX_OP_RETURN_RELAY)); @@ -478,6 +481,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-blockmaxweight=<n>", strprintf(_("Set maximum BIP141 block weight (default: %d)"), DEFAULT_BLOCK_MAX_WEIGHT)); strUsage += HelpMessageOpt("-blockmaxsize=<n>", strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE)); strUsage += HelpMessageOpt("-blockprioritysize=<n>", strprintf(_("Set maximum size of high-priority/low-fee transactions in bytes (default: %d)"), DEFAULT_BLOCK_PRIORITY_SIZE)); + strUsage += HelpMessageOpt("-blockmintxfee=<amt>", strprintf(_("Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE))); if (showDebug) strUsage += HelpMessageOpt("-blockversion=<n>", "Override block version to test forking scenarios"); @@ -570,15 +574,14 @@ struct CImportingNow // works correctly. void CleanupBlockRevFiles() { - using namespace boost::filesystem; - map<string, path> mapBlockFiles; + std::map<std::string, boost::filesystem::path> mapBlockFiles; // Glob all blk?????.dat and rev?????.dat files from the blocks directory. // Remove the rev files immediately and insert the blk file paths into an // ordered map keyed by block file index. LogPrintf("Removing unusable blk?????.dat and rev?????.dat files for -reindex with -prune\n"); - path blocksdir = GetDataDir() / "blocks"; - for (directory_iterator it(blocksdir); it != directory_iterator(); it++) { + boost::filesystem::path blocksdir = GetDataDir() / "blocks"; + for (boost::filesystem::directory_iterator it(blocksdir); it != boost::filesystem::directory_iterator(); it++) { if (is_regular_file(*it) && it->path().filename().string().length() == 12 && it->path().filename().string().substr(8,4) == ".dat") @@ -595,7 +598,7 @@ void CleanupBlockRevFiles() // keeping a separate counter. Once we hit a gap (or if 0 doesn't exist) // start removing block files. int nContigCounter = 0; - BOOST_FOREACH(const PAIRTYPE(string, path)& item, mapBlockFiles) { + BOOST_FOREACH(const PAIRTYPE(std::string, boost::filesystem::path)& item, mapBlockFiles) { if (atoi(item.first) == nContigCounter) { nContigCounter++; continue; @@ -888,8 +891,8 @@ bool AppInitParameterInteraction() fDebug = mapMultiArgs.count("-debug"); // Special-case: if -debug=0/-nodebug is set, turn off debugging messages if (fDebug) { - const vector<string>& categories = mapMultiArgs.at("-debug"); - if (GetBoolArg("-nodebug", false) || find(categories.begin(), categories.end(), string("0")) != categories.end()) + const std::vector<std::string>& categories = mapMultiArgs.at("-debug"); + if (GetBoolArg("-nodebug", false) || find(categories.begin(), categories.end(), std::string("0")) != categories.end()) fDebug = false; } @@ -920,11 +923,26 @@ bool AppInitParameterInteraction() fCheckBlockIndex = GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks()); fCheckpointsEnabled = GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_ENABLED); + hashAssumeValid = uint256S(GetArg("-assumevalid", chainparams.GetConsensus().defaultAssumeValid.GetHex())); + if (!hashAssumeValid.IsNull()) + LogPrintf("Assuming ancestors of block %s have valid signatures.\n", hashAssumeValid.GetHex()); + else + LogPrintf("Validating signatures for all blocks.\n"); + // mempool limits int64_t nMempoolSizeMax = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; int64_t nMempoolSizeMin = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000 * 40; if (nMempoolSizeMax < 0 || nMempoolSizeMax < nMempoolSizeMin) return InitError(strprintf(_("-maxmempool must be at least %d MB"), std::ceil(nMempoolSizeMin / 1000000.0))); + // incremental relay fee sets the minimimum feerate increase necessary for BIP 125 replacement in the mempool + // and the amount the mempool min fee increases above the feerate of txs evicted due to mempool limiting. + if (IsArgSet("-incrementalrelayfee")) + { + CAmount n = 0; + if (!ParseMoney(GetArg("-incrementalrelayfee", ""), n)) + return InitError(AmountErrMsg("incrementalrelayfee", GetArg("-incrementalrelayfee", ""))); + incrementalRelayFee = CFeeRate(n); + } // -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency nScriptCheckThreads = GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS); @@ -936,12 +954,16 @@ bool AppInitParameterInteraction() nScriptCheckThreads = MAX_SCRIPTCHECK_THREADS; // block pruning; get the amount of disk space (in MiB) to allot for block & undo files - int64_t nSignedPruneTarget = GetArg("-prune", 0) * 1024 * 1024; - if (nSignedPruneTarget < 0) { + int64_t nPruneArg = GetArg("-prune", 0); + if (nPruneArg < 0) { return InitError(_("Prune cannot be configured with a negative value.")); } - nPruneTarget = (uint64_t) nSignedPruneTarget; - if (nPruneTarget) { + nPruneTarget = (uint64_t) nPruneArg * 1024 * 1024; + if (nPruneArg == 1) { // manual pruning: -prune=1 + LogPrintf("Block pruning enabled. Use RPC call pruneblockchain(height) to manually prune block and undo files.\n"); + nPruneTarget = std::numeric_limits<uint64_t>::max(); + fPruneMode = true; + } else if (nPruneTarget) { if (nPruneTarget < MIN_DISK_SPACE_FOR_BLOCK_FILES) { return InitError(strprintf(_("Prune configured below the minimum of %d MiB. Please use a higher number."), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024)); } @@ -971,6 +993,29 @@ bool AppInitParameterInteraction() return InitError(AmountErrMsg("minrelaytxfee", GetArg("-minrelaytxfee", ""))); // High fee check is done afterward in CWallet::ParameterInteraction() ::minRelayTxFee = CFeeRate(n); + } else if (incrementalRelayFee > ::minRelayTxFee) { + // Allow only setting incrementalRelayFee to control both + ::minRelayTxFee = incrementalRelayFee; + LogPrintf("Increasing minrelaytxfee to %s to match incrementalrelayfee\n",::minRelayTxFee.ToString()); + } + + // Sanity check argument for min fee for including tx in block + // TODO: Harmonize which arguments need sanity checking and where that happens + if (IsArgSet("-blockmintxfee")) + { + CAmount n = 0; + if (!ParseMoney(GetArg("-blockmintxfee", ""), n)) + return InitError(AmountErrMsg("blockmintxfee", GetArg("-blockmintxfee", ""))); + } + + // Feerate used to define dust. Shouldn't be changed lightly as old + // implementations may inadvertently create non-standard transactions + if (IsArgSet("-dustrelayfee")) + { + CAmount n = 0; + if (!ParseMoney(GetArg("-dustrelayfee", ""), n) || 0 == n) + return InitError(AmountErrMsg("dustrelayfee", GetArg("-dustrelayfee", ""))); + dustRelayFee = CFeeRate(n); } fRequireStandard = !GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard()); @@ -1015,7 +1060,7 @@ bool AppInitParameterInteraction() if (!chainparams.MineBlocksOnDemand()) { return InitError("BIP9 parameters may only be overridden on regtest."); } - const vector<string>& deployments = mapMultiArgs.at("-bip9params"); + const std::vector<std::string>& deployments = mapMultiArgs.at("-bip9params"); for (auto i : deployments) { std::vector<std::string> vDeploymentParams; boost::split(vDeploymentParams, i, boost::is_any_of(":")); @@ -1101,8 +1146,11 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) #ifndef WIN32 CreatePidFile(GetPidFile(), getpid()); #endif - if (GetBoolArg("-shrinkdebugfile", !fDebug)) + if (GetBoolArg("-shrinkdebugfile", !fDebug)) { + // Do this first since it both loads a bunch of debug.log into memory, + // and because this needs to happen before any other debug.log printing ShrinkDebugFile(); + } if (fPrintToDebugLog) OpenDebugLog(); @@ -1160,9 +1208,9 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) RegisterNodeSignals(GetNodeSignals()); // sanitize comments per BIP-0014, format user agent and check total size - std::vector<string> uacomments; + std::vector<std::string> uacomments; if (mapMultiArgs.count("-uacomment")) { - BOOST_FOREACH(string cmt, mapMultiArgs.at("-uacomment")) + BOOST_FOREACH(std::string cmt, mapMultiArgs.at("-uacomment")) { if (cmt != SanitizeString(cmt, SAFE_CHARS_UA_COMMENT)) return InitError(strprintf(_("User Agent comment (%s) contains unsafe characters."), cmt)); @@ -1505,7 +1553,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) } if (chainparams.GetConsensus().vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout != 0) { - // Only advertize witness capabilities if they have a reasonable start time. + // Only advertise witness capabilities if they have a reasonable start time. // This allows us to have the code merged without a defined softfork, by setting its // end time to 0. // Note that setting NODE_WITNESS is never required: the only downside from not diff --git a/src/merkleblock.cpp b/src/merkleblock.cpp index 86c8172a31..e3f3e4621a 100644 --- a/src/merkleblock.cpp +++ b/src/merkleblock.cpp @@ -9,14 +9,12 @@ #include "consensus/consensus.h" #include "utilstrencodings.h" -using namespace std; - CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter& filter) { header = block.GetBlockHeader(); - vector<bool> vMatch; - vector<uint256> vHashes; + std::vector<bool> vMatch; + std::vector<uint256> vHashes; vMatch.reserve(block.vtx.size()); vHashes.reserve(block.vtx.size()); @@ -27,7 +25,7 @@ CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter& filter) if (filter.IsRelevantAndUpdate(*block.vtx[i])) { vMatch.push_back(true); - vMatchedTxn.push_back(make_pair(i, hash)); + vMatchedTxn.push_back(std::make_pair(i, hash)); } else vMatch.push_back(false); @@ -41,8 +39,8 @@ CMerkleBlock::CMerkleBlock(const CBlock& block, const std::set<uint256>& txids) { header = block.GetBlockHeader(); - vector<bool> vMatch; - vector<uint256> vHashes; + std::vector<bool> vMatch; + std::vector<uint256> vHashes; vMatch.reserve(block.vtx.size()); vHashes.reserve(block.vtx.size()); diff --git a/src/miner.cpp b/src/miner.cpp index 856e9edc14..d01edd93b5 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -31,8 +31,6 @@ #include <queue> #include <utility> -using namespace std; - ////////////////////////////////////////////////////////////////////////////// // // BitcoinMiner @@ -95,12 +93,18 @@ BlockAssembler::BlockAssembler(const CChainParams& _chainparams) nBlockMaxWeight = nBlockMaxSize * WITNESS_SCALE_FACTOR; } } + if (IsArgSet("-blockmintxfee")) { + CAmount n = 0; + ParseMoney(GetArg("-blockmintxfee", ""), n); + blockMinFeeRate = CFeeRate(n); + } else { + blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE); + } // Limit weight to between 4K and MAX_BLOCK_WEIGHT-4K for sanity: nBlockMaxWeight = std::max((unsigned int)4000, std::min((unsigned int)(MAX_BLOCK_WEIGHT-4000), nBlockMaxWeight)); // Limit size to between 1K and MAX_BLOCK_SERIALIZED_SIZE-1K for sanity: nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SERIALIZED_SIZE-1000), nBlockMaxSize)); - // Whether we need to account for byte usage (in addition to weight usage) fNeedSizeAccounting = (nBlockMaxSize < MAX_BLOCK_SERIALIZED_SIZE-1000); } @@ -460,7 +464,7 @@ void BlockAssembler::addPackageTxs() packageSigOpsCost = modit->nSigOpCostWithAncestors; } - if (packageFees < ::minRelayTxFee.GetFee(packageSize)) { + if (packageFees < blockMinFeeRate.GetFee(packageSize)) { // Everything else we might consider has a lower fee rate return; } @@ -494,7 +498,7 @@ void BlockAssembler::addPackageTxs() } // Package can be added. Sort the entries in a valid order. - vector<CTxMemPool::txiter> sortedEntries; + std::vector<CTxMemPool::txiter> sortedEntries; SortForBlock(ancestors, iter, sortedEntries); for (size_t i=0; i<sortedEntries.size(); ++i) { @@ -523,7 +527,7 @@ void BlockAssembler::addPriorityTxs() fNeedSizeAccounting = true; // This vector will be sorted into a priority queue: - vector<TxCoinAgePriority> vecPriority; + std::vector<TxCoinAgePriority> vecPriority; TxCoinAgePriorityCompare pricomparer; std::map<CTxMemPool::txiter, double, CTxMemPool::CompareIteratorByHash> waitPriMap; typedef std::map<CTxMemPool::txiter, double, CTxMemPool::CompareIteratorByHash>::iterator waitPriIter; diff --git a/src/miner.h b/src/miner.h index 3dd9bf4cab..3ba92b16b8 100644 --- a/src/miner.h +++ b/src/miner.h @@ -143,6 +143,7 @@ private: bool fIncludeWitness; unsigned int nBlockMaxWeight, nBlockMaxSize; bool fNeedSizeAccounting; + CFeeRate blockMinFeeRate; // Information on the current status of the block uint64_t nBlockWeight; diff --git a/src/net.cpp b/src/net.cpp index 37e7dfed4c..4d0d781d6d 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -342,8 +342,8 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo CNode* pnode = FindNode((CService)addrConnect); if (pnode) { - pnode->AddRef(); - return pnode; + LogPrintf("Failed to open new connection, already connected\n"); + return NULL; } } @@ -369,18 +369,16 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo // In that case, drop the connection that was just created, and return the existing CNode instead. // Also store the name we used to connect in that CNode, so that future FindNode() calls to that // name catch this early. + LOCK(cs_vNodes); CNode* pnode = FindNode((CService)addrConnect); if (pnode) { - pnode->AddRef(); - { - LOCK(cs_vNodes); - if (pnode->addrName.empty()) { - pnode->addrName = std::string(pszDest); - } + if (pnode->addrName.empty()) { + pnode->addrName = std::string(pszDest); } CloseSocket(hSocket); - return pnode; + LogPrintf("Failed to open new connection, already connected\n"); + return NULL; } } @@ -391,13 +389,8 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize(); CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, pszDest ? pszDest : "", false); pnode->nServicesExpected = ServiceFlags(addrConnect.nServices & nRelevantServices); - pnode->nTimeConnected = GetTime(); + pnode->nTimeConnected = GetSystemTimeInSeconds(); pnode->AddRef(); - GetNodeSignals().InitializeNode(pnode, *this); - { - LOCK(cs_vNodes); - vNodes.push_back(pnode); - } return pnode; } else if (!proxyConnectionFailed) { @@ -437,11 +430,6 @@ void CNode::CloseSocketDisconnect() LogPrint("net", "disconnecting peer=%d\n", id); CloseSocket(hSocket); } - - // in case this fails, we'll empty the recv buffer when the CNode is deleted - TRY_LOCK(cs_vRecvMsg, lockRecv); - if (lockRecv) - vRecvMsg.clear(); } void CConnman::ClearBanned() @@ -650,16 +638,18 @@ void CNode::copyStats(CNodeStats &stats) } #undef X -// requires LOCK(cs_vRecvMsg) bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete) { complete = false; + int64_t nTimeMicros = GetTimeMicros(); + nLastRecv = nTimeMicros / 1000000; + nRecvBytes += nBytes; while (nBytes > 0) { // get current incomplete message, or create a new one if (vRecvMsg.empty() || vRecvMsg.back().complete()) - vRecvMsg.push_back(CNetMessage(Params().MessageStart(), SER_NETWORK, nRecvVersion)); + vRecvMsg.push_back(CNetMessage(Params().MessageStart(), SER_NETWORK, INIT_PROTO_VERSION)); CNetMessage& msg = vRecvMsg.back(); @@ -691,7 +681,7 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete assert(i != mapRecvBytesPerMsgCmd.end()); i->second += msg.hdr.nMessageSize + CMessageHeader::HEADER_SIZE; - msg.nTime = GetTimeMicros(); + msg.nTime = nTimeMicros; complete = true; } } @@ -699,6 +689,33 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete return true; } +void CNode::SetSendVersion(int nVersionIn) +{ + // Send version may only be changed in the version message, and + // only one version message is allowed per session. We can therefore + // treat this value as const and even atomic as long as it's only used + // once a version message has been successfully processed. Any attempt to + // set this twice is an error. + if (nSendVersion != 0) { + error("Send version already set for node: %i. Refusing to change from %i to %i", id, nSendVersion, nVersionIn); + } else { + nSendVersion = nVersionIn; + } +} + +int CNode::GetSendVersion() const +{ + // The send version should always be explicitly set to + // INIT_PROTO_VERSION rather than using this value until SetSendVersion + // has been called. + if (nSendVersion == 0) { + error("Requesting unset send version for node: %i. Using %i", id, INIT_PROTO_VERSION); + return INIT_PROTO_VERSION; + } + return nSendVersion; +} + + int CNetMessage::readHeader(const char *pch, unsigned int nBytes) { // copy data to temporary parsing buffer @@ -764,7 +781,7 @@ const uint256& CNetMessage::GetMessageHash() const // requires LOCK(cs_vSend) -size_t SocketSendData(CNode *pnode) +size_t CConnman::SocketSendData(CNode *pnode) const { auto it = pnode->vSendMsg.begin(); size_t nSentSize = 0; @@ -774,13 +791,14 @@ size_t SocketSendData(CNode *pnode) assert(data.size() > pnode->nSendOffset); int nBytes = send(pnode->hSocket, reinterpret_cast<const char*>(data.data()) + pnode->nSendOffset, data.size() - pnode->nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT); if (nBytes > 0) { - pnode->nLastSend = GetTime(); + pnode->nLastSend = GetSystemTimeInSeconds(); pnode->nSendBytes += nBytes; pnode->nSendOffset += nBytes; nSentSize += nBytes; if (pnode->nSendOffset == data.size()) { pnode->nSendOffset = 0; pnode->nSendSize -= data.size(); + pnode->fPauseSend = pnode->nSendSize > nSendBufferMaxSize; it++; } else { // could not send full message; stop sending more @@ -1052,8 +1070,7 @@ void CConnman::ThreadSocketHandler() std::vector<CNode*> vNodesCopy = vNodes; BOOST_FOREACH(CNode* pnode, vNodesCopy) { - if (pnode->fDisconnect || - (pnode->GetRefCount() <= 0 && pnode->vRecvMsg.empty() && pnode->nSendSize == 0)) + if (pnode->fDisconnect) { // remove from vNodes vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end()); @@ -1083,13 +1100,9 @@ void CConnman::ThreadSocketHandler() TRY_LOCK(pnode->cs_vSend, lockSend); if (lockSend) { - TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); - if (lockRecv) - { TRY_LOCK(pnode->cs_inventory, lockInv); if (lockInv) fDelete = true; - } } } if (fDelete) @@ -1149,29 +1162,19 @@ void CConnman::ThreadSocketHandler() // write buffer in this case before receiving more. This avoids // needlessly queueing received data, if the remote peer is not themselves // receiving data. This means properly utilizing TCP flow control signalling. - // * Otherwise, if there is no (complete) message in the receive buffer, - // or there is space left in the buffer, select() for receiving data. - // * (if neither of the above applies, there is certainly one message - // in the receiver buffer ready to be processed). - // Together, that means that at least one of the following is always possible, - // so we don't deadlock: - // * We send some data. - // * We wait for data to be received (and disconnect after timeout). - // * We process a message in the buffer (message handler thread). + // * Otherwise, if there is space left in the receive buffer, select() for + // receiving data. + // * Hand off all complete messages to the processor, to be handled without + // blocking here. { - TRY_LOCK(pnode->cs_vSend, lockSend); - if (lockSend) { - if (!pnode->vSendMsg.empty()) { - FD_SET(pnode->hSocket, &fdsetSend); - continue; - } + LOCK(pnode->cs_vSend); + if (!pnode->vSendMsg.empty()) { + FD_SET(pnode->hSocket, &fdsetSend); + continue; } } { - TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); - if (lockRecv && ( - pnode->vRecvMsg.empty() || !pnode->vRecvMsg.front().complete() || - pnode->GetTotalRecvSize() <= GetReceiveFloodSize())) + if (!pnode->fPauseRecv) FD_SET(pnode->hSocket, &fdsetRecv); } } @@ -1230,8 +1233,6 @@ void CConnman::ThreadSocketHandler() continue; if (FD_ISSET(pnode->hSocket, &fdsetRecv) || FD_ISSET(pnode->hSocket, &fdsetError)) { - TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); - if (lockRecv) { { // typical socket buffer is 8K-64K @@ -1242,11 +1243,23 @@ void CConnman::ThreadSocketHandler() bool notify = false; if (!pnode->ReceiveMsgBytes(pchBuf, nBytes, notify)) pnode->CloseSocketDisconnect(); - if(notify) - condMsgProc.notify_one(); - pnode->nLastRecv = GetTime(); - pnode->nRecvBytes += nBytes; RecordBytesRecv(nBytes); + if (notify) { + size_t nSizeAdded = 0; + auto it(pnode->vRecvMsg.begin()); + for (; it != pnode->vRecvMsg.end(); ++it) { + if (!it->complete()) + break; + nSizeAdded += it->vRecv.size() + CMessageHeader::HEADER_SIZE; + } + { + LOCK(pnode->cs_vProcessMsg); + pnode->vProcessMsg.splice(pnode->vProcessMsg.end(), pnode->vRecvMsg, pnode->vRecvMsg.begin(), it); + pnode->nProcessQueueSize += nSizeAdded; + pnode->fPauseRecv = pnode->nProcessQueueSize > nReceiveFloodSize; + } + WakeMessageHandler(); + } } else if (nBytes == 0) { @@ -1277,18 +1290,17 @@ void CConnman::ThreadSocketHandler() continue; if (FD_ISSET(pnode->hSocket, &fdsetSend)) { - TRY_LOCK(pnode->cs_vSend, lockSend); - if (lockSend) { - size_t nBytes = SocketSendData(pnode); - if (nBytes) - RecordBytesSent(nBytes); + LOCK(pnode->cs_vSend); + size_t nBytes = SocketSendData(pnode); + if (nBytes) { + RecordBytesSent(nBytes); } } // // Inactivity checking // - int64_t nTime = GetTime(); + int64_t nTime = GetSystemTimeInSeconds(); if (nTime - pnode->nTimeConnected > 60) { if (pnode->nLastRecv == 0 || pnode->nLastSend == 0) @@ -1321,8 +1333,14 @@ void CConnman::ThreadSocketHandler() } } - - +void CConnman::WakeMessageHandler() +{ + { + std::lock_guard<std::mutex> lock(mutexMsgProc); + fMsgProcWake = true; + } + condMsgProc.notify_one(); +} @@ -1842,6 +1860,12 @@ bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai if (fAddnode) pnode->fAddnode = true; + GetNodeSignals().InitializeNode(pnode, *this); + { + LOCK(cs_vNodes); + vNodes.push_back(pnode); + } + return true; } @@ -1858,7 +1882,7 @@ void CConnman::ThreadMessageHandler() } } - bool fSleep = true; + bool fMoreWork = false; BOOST_FOREACH(CNode* pnode, vNodesCopy) { @@ -1866,30 +1890,15 @@ void CConnman::ThreadMessageHandler() continue; // Receive messages - { - TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); - if (lockRecv) - { - if (!GetNodeSignals().ProcessMessages(pnode, *this, flagInterruptMsgProc)) - pnode->CloseSocketDisconnect(); - - if (pnode->nSendSize < GetSendBufferSize()) - { - if (!pnode->vRecvGetData.empty() || (!pnode->vRecvMsg.empty() && pnode->vRecvMsg[0].complete())) - { - fSleep = false; - } - } - } - } + bool fMoreNodeWork = GetNodeSignals().ProcessMessages(pnode, *this, flagInterruptMsgProc); + fMoreWork |= (fMoreNodeWork && !pnode->fPauseSend); if (flagInterruptMsgProc) return; // Send messages { - TRY_LOCK(pnode->cs_vSend, lockSend); - if (lockSend) - GetNodeSignals().SendMessages(pnode, *this, flagInterruptMsgProc); + LOCK(pnode->cs_sendProcessing); + GetNodeSignals().SendMessages(pnode, *this, flagInterruptMsgProc); } if (flagInterruptMsgProc) return; @@ -1901,10 +1910,11 @@ void CConnman::ThreadMessageHandler() pnode->Release(); } - if (fSleep) { - std::unique_lock<std::mutex> lock(mutexMsgProc); - condMsgProc.wait_until(lock, std::chrono::steady_clock::now() + std::chrono::milliseconds(100)); + std::unique_lock<std::mutex> lock(mutexMsgProc); + if (!fMoreWork) { + condMsgProc.wait_until(lock, std::chrono::steady_clock::now() + std::chrono::milliseconds(100), [this] { return fMsgProcWake; }); } + fMsgProcWake = false; } } @@ -2121,7 +2131,7 @@ bool CConnman::Start(CScheduler& scheduler, std::string& strNodeError, Options c nMaxFeeler = connOptions.nMaxFeeler; nSendBufferMaxSize = connOptions.nSendBufferMaxSize; - nReceiveFloodSize = connOptions.nSendBufferMaxSize; + nReceiveFloodSize = connOptions.nReceiveFloodSize; nMaxOutboundLimit = connOptions.nMaxOutboundLimit; nMaxOutboundTimeframe = connOptions.nMaxOutboundTimeframe; @@ -2182,6 +2192,11 @@ bool CConnman::Start(CScheduler& scheduler, std::string& strNodeError, Options c interruptNet.reset(); flagInterruptMsgProc = false; + { + std::unique_lock<std::mutex> lock(mutexMsgProc); + fMsgProcWake = false; + } + // Send and receive from sockets, accept connections threadSocketHandler = std::thread(&TraceThread<std::function<void()> >, "net", std::function<void()>(std::bind(&CConnman::ThreadSocketHandler, this))); @@ -2382,26 +2397,9 @@ void CConnman::GetNodeStats(std::vector<CNodeStats>& vstats) } } -bool CConnman::DisconnectAddress(const CNetAddr& netAddr) -{ - if (CNode* pnode = FindNode(netAddr)) { - pnode->fDisconnect = true; - return true; - } - return false; -} - -bool CConnman::DisconnectSubnet(const CSubNet& subNet) -{ - if (CNode* pnode = FindNode(subNet)) { - pnode->fDisconnect = true; - return true; - } - return false; -} - bool CConnman::DisconnectNode(const std::string& strNode) { + LOCK(cs_vNodes); if (CNode* pnode = FindNode(strNode)) { pnode->fDisconnect = true; return true; @@ -2420,16 +2418,6 @@ bool CConnman::DisconnectNode(NodeId id) return false; } -void CConnman::RelayTransaction(const CTransaction& tx) -{ - CInv inv(MSG_TX, tx.GetHash()); - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - { - pnode->PushInventory(inv); - } -} - void CConnman::RecordBytesRecv(uint64_t bytes) { LOCK(cs_totalBytesRecv); @@ -2576,7 +2564,7 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn nLastRecv = 0; nSendBytes = 0; nRecvBytes = 0; - nTimeConnected = GetTime(); + nTimeConnected = GetSystemTimeInSeconds(); nTimeOffset = 0; addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn; nVersion = 0; @@ -2613,6 +2601,9 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn minFeeFilter = 0; lastSentFeeFilter = 0; nextSendTimeFeeFilter = 0; + fPauseRecv = false; + fPauseSend = false; + nProcessQueueSize = 0; BOOST_FOREACH(const std::string &msg, getAllNetMessageTypes()) mapRecvBytesPerMsgCmd[msg] = 0; @@ -2666,6 +2657,11 @@ void CNode::AskFor(const CInv& inv) mapAskFor.insert(std::make_pair(nRequestTime, inv)); } +bool CConnman::NodeFullyConnected(const CNode* pnode) +{ + return pnode && pnode->fSuccessfullyConnected && !pnode->fDisconnect; +} + void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg) { size_t nMessageSize = msg.data.size(); @@ -2692,6 +2688,8 @@ void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg) pnode->mapSendBytesPerMsgCmd[msg.command] += nTotalSize; pnode->nSendSize += nTotalSize; + if (pnode->nSendSize > nSendBufferMaxSize) + pnode->fPauseSend = true; pnode->vSendMsg.push_back(std::move(serializedHeader)); if (nMessageSize) pnode->vSendMsg.push_back(std::move(msg.data)); @@ -2714,19 +2712,19 @@ bool CConnman::ForNode(NodeId id, std::function<bool(CNode* pnode)> func) break; } } - return found != nullptr && func(found); + return found != nullptr && NodeFullyConnected(found) && func(found); } int64_t PoissonNextSend(int64_t nNow, int average_interval_seconds) { return nNow + (int64_t)(log1p(GetRand(1ULL << 48) * -0.0000000000000035527136788 /* -1/2^48 */) * average_interval_seconds * -1000000.0 + 0.5); } -CSipHasher CConnman::GetDeterministicRandomizer(uint64_t id) +CSipHasher CConnman::GetDeterministicRandomizer(uint64_t id) const { return CSipHasher(nSeed0, nSeed1).Write(id); } -uint64_t CConnman::CalculateKeyedNetGroup(const CAddress& ad) +uint64_t CConnman::CalculateKeyedNetGroup(const CAddress& ad) const { std::vector<unsigned char> vchNetGroup(ad.GetGroup()); @@ -162,75 +162,33 @@ public: void PushMessage(CNode* pnode, CSerializedNetMsg&& msg); template<typename Callable> - bool ForEachNodeContinueIf(Callable&& func) - { - LOCK(cs_vNodes); - for (auto&& node : vNodes) - if(!func(node)) - return false; - return true; - }; - - template<typename Callable> - bool ForEachNodeContinueIf(Callable&& func) const - { - LOCK(cs_vNodes); - for (const auto& node : vNodes) - if(!func(node)) - return false; - return true; - }; - - template<typename Callable, typename CallableAfter> - bool ForEachNodeContinueIfThen(Callable&& pre, CallableAfter&& post) - { - bool ret = true; - LOCK(cs_vNodes); - for (auto&& node : vNodes) - if(!pre(node)) { - ret = false; - break; - } - post(); - return ret; - }; - - template<typename Callable, typename CallableAfter> - bool ForEachNodeContinueIfThen(Callable&& pre, CallableAfter&& post) const - { - bool ret = true; - LOCK(cs_vNodes); - for (const auto& node : vNodes) - if(!pre(node)) { - ret = false; - break; - } - post(); - return ret; - }; - - template<typename Callable> void ForEachNode(Callable&& func) { LOCK(cs_vNodes); - for (auto&& node : vNodes) - func(node); + for (auto&& node : vNodes) { + if (NodeFullyConnected(node)) + func(node); + } }; template<typename Callable> void ForEachNode(Callable&& func) const { LOCK(cs_vNodes); - for (const auto& node : vNodes) - func(node); + for (auto&& node : vNodes) { + if (NodeFullyConnected(node)) + func(node); + } }; template<typename Callable, typename CallableAfter> void ForEachNodeThen(Callable&& pre, CallableAfter&& post) { LOCK(cs_vNodes); - for (auto&& node : vNodes) - pre(node); + for (auto&& node : vNodes) { + if (NodeFullyConnected(node)) + pre(node); + } post(); }; @@ -238,13 +196,13 @@ public: void ForEachNodeThen(Callable&& pre, CallableAfter&& post) const { LOCK(cs_vNodes); - for (const auto& node : vNodes) - pre(node); + for (auto&& node : vNodes) { + if (NodeFullyConnected(node)) + pre(node); + } post(); }; - void RelayTransaction(const CTransaction& tx); - // Addrman functions size_t GetAddressCount() const; void SetServices(const CService &addr, ServiceFlags nServices); @@ -286,10 +244,8 @@ public: size_t GetNodeCount(NumConnections num); void GetNodeStats(std::vector<CNodeStats>& vstats); - bool DisconnectAddress(const CNetAddr& addr); bool DisconnectNode(const std::string& node); bool DisconnectNode(NodeId id); - bool DisconnectSubnet(const CSubNet& subnet); unsigned int GetSendBufferSize() const; @@ -325,8 +281,11 @@ public: int GetBestHeight() const; /** Get a unique deterministic randomizer. */ - CSipHasher GetDeterministicRandomizer(uint64_t id); + CSipHasher GetDeterministicRandomizer(uint64_t id) const; + + unsigned int GetReceiveFloodSize() const; + void WakeMessageHandler(); private: struct ListenSocket { SOCKET socket; @@ -343,7 +302,7 @@ private: void ThreadSocketHandler(); void ThreadDNSAddressSeed(); - uint64_t CalculateKeyedNetGroup(const CAddress& ad); + uint64_t CalculateKeyedNetGroup(const CAddress& ad) const; CNode* FindNode(const CNetAddr& ip); CNode* FindNode(const CSubNet& subNet); @@ -358,6 +317,7 @@ private: NodeId GetNewNodeId(); + size_t SocketSendData(CNode *pnode) const; //!check is the banlist has unwritten changes bool BannedSetIsDirty(); //!set the "dirty" flag for the banlist @@ -368,12 +328,13 @@ private: void DumpData(); void DumpBanlist(); - unsigned int GetReceiveFloodSize() const; - // Network stats void RecordBytesRecv(uint64_t bytes); void RecordBytesSent(uint64_t bytes); + // Whether the node should be passed out in ForEach* callbacks + static bool NodeFullyConnected(const CNode* pnode); + // Network usage totals CCriticalSection cs_totalBytesRecv; CCriticalSection cs_totalBytesSent; @@ -428,6 +389,9 @@ private: /** SipHasher seeds for deterministic randomness */ const uint64_t nSeed0, nSeed1; + /** flag for waking the message processor. */ + bool fMsgProcWake; + std::condition_variable condMsgProc; std::mutex mutexMsgProc; std::atomic<bool> flagInterruptMsgProc; @@ -445,7 +409,6 @@ void Discover(boost::thread_group& threadGroup); void MapPort(bool fUseUPnP); unsigned short GetListenPort(); bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false); -size_t SocketSendData(CNode *pnode); struct CombinerAll { @@ -610,11 +573,15 @@ public: std::deque<std::vector<unsigned char>> vSendMsg; CCriticalSection cs_vSend; + CCriticalSection cs_vProcessMsg; + std::list<CNetMessage> vProcessMsg; + size_t nProcessQueueSize; + + CCriticalSection cs_sendProcessing; + std::deque<CInv> vRecvGetData; - std::deque<CNetMessage> vRecvMsg; - CCriticalSection cs_vRecvMsg; uint64_t nRecvBytes; - int nRecvVersion; + std::atomic<int> nRecvVersion; int64_t nLastSend; int64_t nLastRecv; @@ -623,7 +590,7 @@ public: const CAddress addr; std::string addrName; CService addrLocal; - int nVersion; + std::atomic<int> nVersion; // strSubVer is whatever byte array we read from the wire. However, this field is intended // to be printed out, displayed to humans in various forms and so on. So we sanitize it and // store the sanitized version in cleanSubVer. The original should be used when dealing with @@ -635,7 +602,7 @@ public: bool fAddnode; bool fClient; const bool fInbound; - bool fSuccessfullyConnected; + std::atomic_bool fSuccessfullyConnected; std::atomic_bool fDisconnect; // We use fRelayTxes for two purposes - // a) it allows us to not relay tx invs before receiving the peer's version message @@ -650,6 +617,8 @@ public: const NodeId id; const uint64_t nKeyedNetGroup; + std::atomic_bool fPauseRecv; + std::atomic_bool fPauseSend; protected: mapMsgCmdSize mapSendBytesPerMsgCmd; @@ -723,6 +692,7 @@ private: const ServiceFlags nLocalServices; const int nMyStartingHeight; int nSendVersion; + std::list<CNetMessage> vRecvMsg; // Used only by SocketHandler thread public: NodeId GetId() const { @@ -743,44 +713,18 @@ public: return nRefCount; } - // requires LOCK(cs_vRecvMsg) - unsigned int GetTotalRecvSize() - { - unsigned int total = 0; - BOOST_FOREACH(const CNetMessage &msg, vRecvMsg) - total += msg.vRecv.size() + 24; - return total; - } - - // requires LOCK(cs_vRecvMsg) bool ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete); - // requires LOCK(cs_vRecvMsg) void SetRecvVersion(int nVersionIn) { nRecvVersion = nVersionIn; - BOOST_FOREACH(CNetMessage &msg, vRecvMsg) - msg.SetVersion(nVersionIn); } - void SetSendVersion(int nVersionIn) - { - // Send version may only be changed in the version message, and - // only one version message is allowed per session. We can therefore - // treat this value as const and even atomic as long as it's only used - // once the handshake is complete. Any attempt to set this twice is an - // error. - assert(nSendVersion == 0); - nSendVersion = nVersionIn; - } - - int GetSendVersion() const + int GetRecvVersion() { - // The send version should always be explicitly set to - // INIT_PROTO_VERSION rather than using this value until the handshake - // is complete. - assert(nSendVersion != 0); - return nSendVersion; + return nRecvVersion; } + void SetSendVersion(int nVersionIn); + int GetSendVersion() const; CNode* AddRef() { diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 3a956e89e7..bb14e69d83 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -32,8 +32,6 @@ #include <boost/thread.hpp> -using namespace std; - #if defined(NDEBUG) # error "Bitcoin cannot be compiled without assertions." #endif @@ -55,10 +53,13 @@ struct COrphanTx { NodeId fromPeer; int64_t nTimeExpire; }; -map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(cs_main); -map<COutPoint, set<map<uint256, COrphanTx>::iterator, IteratorComparator>> mapOrphanTransactionsByPrev GUARDED_BY(cs_main); +std::map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(cs_main); +std::map<COutPoint, std::set<std::map<uint256, COrphanTx>::iterator, IteratorComparator>> mapOrphanTransactionsByPrev GUARDED_BY(cs_main); void EraseOrphansFor(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main); +static size_t vExtraTxnForCompactIt = 0; +static std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUARDED_BY(cs_main); + static const uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL; // SHA256("main address relay")[0:8] // Internal stuff @@ -73,7 +74,7 @@ namespace { * Set mapBlockSource[hash].second to false if the node should not be * punished if the block is invalid. */ - map<uint256, std::pair<NodeId, bool>> mapBlockSource; + std::map<uint256, std::pair<NodeId, bool>> mapBlockSource; /** * Filter for transactions that were recently rejected by @@ -101,14 +102,14 @@ namespace { /** Blocks that are in flight, and that are in the queue to be downloaded. Protected by cs_main. */ struct QueuedBlock { uint256 hash; - CBlockIndex* pindex; //!< Optional. + const CBlockIndex* pindex; //!< Optional. bool fValidatedHeaders; //!< Whether this block has validated headers at the time of request. std::unique_ptr<PartiallyDownloadedBlock> partialBlock; //!< Optional, used for CMPCTBLOCK downloads }; - map<uint256, pair<NodeId, list<QueuedBlock>::iterator> > mapBlocksInFlight; + std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> > mapBlocksInFlight; /** Stack of nodes which we have set to announce using compact blocks */ - list<NodeId> lNodesAnnouncingHeaderAndIDs; + std::list<NodeId> lNodesAnnouncingHeaderAndIDs; /** Number of preferable block download peers. */ int nPreferredDownload = 0; @@ -132,7 +133,7 @@ namespace { struct CBlockReject { unsigned char chRejectCode; - string strRejectReason; + std::string strRejectReason; uint256 hashBlock; }; @@ -156,20 +157,20 @@ struct CNodeState { //! List of asynchronously-determined block rejections to notify this peer about. std::vector<CBlockReject> rejects; //! The best known block we know this peer has announced. - CBlockIndex *pindexBestKnownBlock; + const CBlockIndex *pindexBestKnownBlock; //! The hash of the last unknown block this peer has announced. uint256 hashLastUnknownBlock; //! The last full block we both have. - CBlockIndex *pindexLastCommonBlock; + const CBlockIndex *pindexLastCommonBlock; //! The best header we have sent our peer. - CBlockIndex *pindexBestHeaderSent; + const CBlockIndex *pindexBestHeaderSent; //! Length of current-streak of unconnecting headers announcements int nUnconnectingHeaders; //! Whether we've started headers synchronization with this peer. bool fSyncStarted; //! Since when we're stalling block download progress (in microseconds), or 0. int64_t nStallingSince; - list<QueuedBlock> vBlocksInFlight; + std::list<QueuedBlock> vBlocksInFlight; //! When the first entry in vBlocksInFlight started downloading. Don't care when vBlocksInFlight is empty. int64_t nDownloadingSince; int nBlocksInFlight; @@ -221,11 +222,11 @@ struct CNodeState { }; /** Map maintaining per-node state. Requires cs_main. */ -map<NodeId, CNodeState> mapNodeState; +std::map<NodeId, CNodeState> mapNodeState; // Requires cs_main. CNodeState *State(NodeId pnode) { - map<NodeId, CNodeState>::iterator it = mapNodeState.find(pnode); + std::map<NodeId, CNodeState>::iterator it = mapNodeState.find(pnode); if (it == mapNodeState.end()) return NULL; return &it->second; @@ -307,7 +308,7 @@ void FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) { // Returns a bool indicating whether we requested this block. // Also used if a block was /not/ received and timed out or started with another peer bool MarkBlockAsReceived(const uint256& hash) { - map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash); + std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash); if (itInFlight != mapBlocksInFlight.end()) { CNodeState *state = State(itInFlight->second.first); state->nBlocksInFlightValidHeaders -= itInFlight->second.second->fValidatedHeaders; @@ -331,12 +332,12 @@ bool MarkBlockAsReceived(const uint256& hash) { // Requires cs_main. // returns false, still setting pit, if the block was already in flight from the same peer // pit will only be valid as long as the same cs_main lock is being held -bool MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Params& consensusParams, CBlockIndex *pindex = NULL, list<QueuedBlock>::iterator **pit = NULL) { +bool MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Params& consensusParams, const CBlockIndex* pindex = NULL, std::list<QueuedBlock>::iterator** pit = NULL) { CNodeState *state = State(nodeid); assert(state != NULL); // Short-circuit most stuff in case its from the same node - map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash); + std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash); if (itInFlight != mapBlocksInFlight.end() && itInFlight->second.first == nodeid) { *pit = &itInFlight->second.second; return false; @@ -345,7 +346,7 @@ bool MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Pa // Make sure it's not listed somewhere already. MarkBlockAsReceived(hash); - list<QueuedBlock>::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), + std::list<QueuedBlock>::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), {hash, pindex, pindex != NULL, std::unique_ptr<PartiallyDownloadedBlock>(pit ? new PartiallyDownloadedBlock(&mempool) : NULL)}); state->nBlocksInFlight++; state->nBlocksInFlightValidHeaders += it->fValidatedHeaders; @@ -395,33 +396,38 @@ void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) { } } -void MaybeSetPeerAsAnnouncingHeaderAndIDs(const CNodeState* nodestate, CNode* pfrom, CConnman& connman) { - if (!nodestate->fSupportsDesiredCmpctVersion) { +void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman& connman) { + AssertLockHeld(cs_main); + CNodeState* nodestate = State(nodeid); + if (!nodestate || !nodestate->fSupportsDesiredCmpctVersion) { // Never ask from peers who can't provide witnesses. return; } if (nodestate->fProvidesHeaderAndIDs) { for (std::list<NodeId>::iterator it = lNodesAnnouncingHeaderAndIDs.begin(); it != lNodesAnnouncingHeaderAndIDs.end(); it++) { - if (*it == pfrom->GetId()) { + if (*it == nodeid) { lNodesAnnouncingHeaderAndIDs.erase(it); - lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId()); + lNodesAnnouncingHeaderAndIDs.push_back(nodeid); return; } } - bool fAnnounceUsingCMPCTBLOCK = false; - uint64_t nCMPCTBLOCKVersion = (pfrom->GetLocalServices() & NODE_WITNESS) ? 2 : 1; - if (lNodesAnnouncingHeaderAndIDs.size() >= 3) { - // As per BIP152, we only get 3 of our peers to announce - // blocks using compact encodings. - connman.ForNode(lNodesAnnouncingHeaderAndIDs.front(), [&connman, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion](CNode* pnodeStop){ - connman.PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetSendVersion()).Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); - return true; - }); - lNodesAnnouncingHeaderAndIDs.pop_front(); - } - fAnnounceUsingCMPCTBLOCK = true; - connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); - lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId()); + connman.ForNode(nodeid, [&connman](CNode* pfrom){ + bool fAnnounceUsingCMPCTBLOCK = false; + uint64_t nCMPCTBLOCKVersion = (pfrom->GetLocalServices() & NODE_WITNESS) ? 2 : 1; + if (lNodesAnnouncingHeaderAndIDs.size() >= 3) { + // As per BIP152, we only get 3 of our peers to announce + // blocks using compact encodings. + connman.ForNode(lNodesAnnouncingHeaderAndIDs.front(), [&connman, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion](CNode* pnodeStop){ + connman.PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetSendVersion()).Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); + return true; + }); + lNodesAnnouncingHeaderAndIDs.pop_front(); + } + fAnnounceUsingCMPCTBLOCK = true; + connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); + lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId()); + return true; + }); } } @@ -432,7 +438,7 @@ bool CanDirectFetch(const Consensus::Params &consensusParams) } // Requires cs_main -bool PeerHasHeader(CNodeState *state, CBlockIndex *pindex) +bool PeerHasHeader(CNodeState *state, const CBlockIndex *pindex) { if (state->pindexBestKnownBlock && pindex == state->pindexBestKnownBlock->GetAncestor(pindex->nHeight)) return true; @@ -443,7 +449,7 @@ bool PeerHasHeader(CNodeState *state, CBlockIndex *pindex) /** Find the last common ancestor two blocks have. * Both pa and pb must be non-NULL. */ -CBlockIndex* LastCommonAncestor(CBlockIndex* pa, CBlockIndex* pb) { +const CBlockIndex* LastCommonAncestor(const CBlockIndex* pa, const CBlockIndex* pb) { if (pa->nHeight > pb->nHeight) { pa = pa->GetAncestor(pb->nHeight); } else if (pb->nHeight > pa->nHeight) { @@ -462,7 +468,7 @@ CBlockIndex* LastCommonAncestor(CBlockIndex* pa, CBlockIndex* pb) { /** Update pindexLastCommonBlock and add not-in-flight missing successors to vBlocks, until it has * at most count entries. */ -void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<CBlockIndex*>& vBlocks, NodeId& nodeStaller, const Consensus::Params& consensusParams) { +void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<const CBlockIndex*>& vBlocks, NodeId& nodeStaller, const Consensus::Params& consensusParams) { if (count == 0) return; @@ -490,8 +496,8 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<CBl if (state->pindexLastCommonBlock == state->pindexBestKnownBlock) return; - std::vector<CBlockIndex*> vToFetch; - CBlockIndex *pindexWalk = state->pindexLastCommonBlock; + std::vector<const CBlockIndex*> vToFetch; + const CBlockIndex *pindexWalk = state->pindexLastCommonBlock; // Never fetch further than the best block we know the peer has, or more than BLOCK_DOWNLOAD_WINDOW + 1 beyond the last // linked block we have in common with this peer. The +1 is so we can detect stalling, namely if we would be able to // download that next block if the window were 1 larger. @@ -514,7 +520,7 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<CBl // are not yet downloaded and not in flight to vBlocks. In the mean time, update // pindexLastCommonBlock as long as all ancestors are already downloaded, or if it's // already part of our chain (and therefore don't need it even if pruned). - BOOST_FOREACH(CBlockIndex* pindex, vToFetch) { + BOOST_FOREACH(const CBlockIndex* pindex, vToFetch) { if (!pindex->IsValid(BLOCK_VALID_TREE)) { // We consider the chain that this peer is on invalid. return; @@ -586,6 +592,17 @@ void UnregisterNodeSignals(CNodeSignals& nodeSignals) // mapOrphanTransactions // +void AddToCompactExtraTransactions(const CTransactionRef& tx) +{ + size_t max_extra_txn = GetArg("-blockreconstructionextratxn", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN); + if (max_extra_txn <= 0) + return; + if (!vExtraTxnForCompact.size()) + vExtraTxnForCompact.resize(max_extra_txn); + vExtraTxnForCompact[vExtraTxnForCompactIt] = std::make_pair(tx->GetWitnessHash(), tx); + vExtraTxnForCompactIt = (vExtraTxnForCompactIt + 1) % max_extra_txn; +} + bool AddOrphanTx(const CTransactionRef& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { const uint256& hash = tx->GetHash(); @@ -612,6 +629,8 @@ bool AddOrphanTx(const CTransactionRef& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRE mapOrphanTransactionsByPrev[txin.prevout].insert(ret.first); } + AddToCompactExtraTransactions(tx); + LogPrint("mempool", "stored orphan tx %s (mapsz %u outsz %u)\n", hash.ToString(), mapOrphanTransactions.size(), mapOrphanTransactionsByPrev.size()); return true; @@ -619,7 +638,7 @@ bool AddOrphanTx(const CTransactionRef& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRE int static EraseOrphanTx(uint256 hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { - map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.find(hash); + std::map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.find(hash); if (it == mapOrphanTransactions.end()) return 0; BOOST_FOREACH(const CTxIn& txin, it->second.tx->vin) @@ -638,16 +657,16 @@ int static EraseOrphanTx(uint256 hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) void EraseOrphansFor(NodeId peer) { int nErased = 0; - map<uint256, COrphanTx>::iterator iter = mapOrphanTransactions.begin(); + std::map<uint256, COrphanTx>::iterator iter = mapOrphanTransactions.begin(); while (iter != mapOrphanTransactions.end()) { - map<uint256, COrphanTx>::iterator maybeErase = iter++; // increment to avoid iterator becoming invalid + std::map<uint256, COrphanTx>::iterator maybeErase = iter++; // increment to avoid iterator becoming invalid if (maybeErase->second.fromPeer == peer) { nErased += EraseOrphanTx(maybeErase->second.tx->GetHash()); } } - if (nErased > 0) LogPrint("mempool", "Erased %d orphan tx from peer %d\n", nErased, peer); + if (nErased > 0) LogPrint("mempool", "Erased %d orphan tx from peer=%d\n", nErased, peer); } @@ -660,10 +679,10 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) EXCLUSIVE_LOCKS_REQUIRE // Sweep out expired orphan pool entries: int nErased = 0; int64_t nMinExpTime = nNow + ORPHAN_TX_EXPIRE_TIME - ORPHAN_TX_EXPIRE_INTERVAL; - map<uint256, COrphanTx>::iterator iter = mapOrphanTransactions.begin(); + std::map<uint256, COrphanTx>::iterator iter = mapOrphanTransactions.begin(); while (iter != mapOrphanTransactions.end()) { - map<uint256, COrphanTx>::iterator maybeErase = iter++; + std::map<uint256, COrphanTx>::iterator maybeErase = iter++; if (maybeErase->second.nTimeExpire <= nNow) { nErased += EraseOrphanTx(maybeErase->second.tx->GetHash()); } else { @@ -678,7 +697,7 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) EXCLUSIVE_LOCKS_REQUIRE { // Evict a random orphan: uint256 randomhash = GetRandHash(); - map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.lower_bound(randomhash); + std::map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.lower_bound(randomhash); if (it == mapOrphanTransactions.end()) it = mapOrphanTransactions.begin(); EraseOrphanTx(it->first); @@ -752,6 +771,51 @@ void PeerLogicValidation::SyncTransaction(const CTransaction& tx, const CBlockIn } } +static CCriticalSection cs_most_recent_block; +static std::shared_ptr<const CBlock> most_recent_block; +static std::shared_ptr<const CBlockHeaderAndShortTxIDs> most_recent_compact_block; +static uint256 most_recent_block_hash; + +void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) { + std::shared_ptr<const CBlockHeaderAndShortTxIDs> pcmpctblock = std::make_shared<const CBlockHeaderAndShortTxIDs> (*pblock, true); + const CNetMsgMaker msgMaker(PROTOCOL_VERSION); + + LOCK(cs_main); + + static int nHighestFastAnnounce = 0; + if (pindex->nHeight <= nHighestFastAnnounce) + return; + nHighestFastAnnounce = pindex->nHeight; + + bool fWitnessEnabled = IsWitnessEnabled(pindex->pprev, Params().GetConsensus()); + uint256 hashBlock(pblock->GetHash()); + + { + LOCK(cs_most_recent_block); + most_recent_block_hash = hashBlock; + most_recent_block = pblock; + most_recent_compact_block = pcmpctblock; + } + + connman->ForEachNode([this, &pcmpctblock, pindex, &msgMaker, fWitnessEnabled, &hashBlock](CNode* pnode) { + // TODO: Avoid the repeated-serialization here + if (pnode->nVersion < INVALID_CB_NO_BAN_VERSION || pnode->fDisconnect) + return; + ProcessBlockAvailability(pnode->GetId()); + CNodeState &state = *State(pnode->GetId()); + // If the peer has, or we announced to them the previous block already, + // but we don't think they have this one, go ahead and announce it + if (state.fPreferHeaderAndIDs && (!fWitnessEnabled || state.fWantsCmpctWitness) && + !PeerHasHeader(&state, pindex) && PeerHasHeader(&state, pindex->pprev)) { + + LogPrint("net", "%s sending header-and-ids %s to peer=%d\n", "PeerLogicValidation::NewPoWValidBlock", + hashBlock.ToString(), pnode->id); + connman->PushMessage(pnode, msgMaker.Make(NetMsgType::CMPCTBLOCK, *pcmpctblock)); + state.pindexBestHeaderSent = pindex; + } + }); +} + void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) { const int nNewHeight = pindexNew->nHeight; connman->SetBestHeight(nNewHeight); @@ -777,6 +841,7 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB } } }); + connman->WakeMessageHandler(); } nTimeBestReceived = GetTime(); @@ -798,6 +863,19 @@ void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationSta Misbehaving(it->second.first, nDoS); } } + // Check that: + // 1. The block is valid + // 2. We're not in initial block download + // 3. This is currently the best block we're aware of. We haven't updated + // the tip yet so we have no way to check this directly here. Instead we + // just check that there are currently no other blocks in flight. + else if (state.IsValid() && + !IsInitialBlockDownload() && + mapBlocksInFlight.count(hash) == mapBlocksInFlight.size()) { + if (it != mapBlockSource.end()) { + MaybeSetPeerAsAnnouncingHeaderAndIDs(it->second.first, *connman); + } + } if (it != mapBlockSource.end()) mapBlockSource.erase(it); } @@ -886,17 +964,16 @@ static void RelayAddress(const CAddress& addr, bool fReachable, CConnman& connma connman.ForEachNodeThen(std::move(sortfunc), std::move(pushfunc)); } -void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParams, CConnman& connman, std::atomic<bool>& interruptMsgProc) +void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParams, CConnman& connman, const std::atomic<bool>& interruptMsgProc) { std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin(); - unsigned int nMaxSendBufferSize = connman.GetSendBufferSize(); - vector<CInv> vNotFound; - CNetMsgMaker msgMaker(pfrom->GetSendVersion()); + std::vector<CInv> vNotFound; + const CNetMsgMaker msgMaker(pfrom->GetSendVersion()); LOCK(cs_main); while (it != pfrom->vRecvGetData.end()) { // Don't bother if send buffer is too full to respond anyway - if (pfrom->nSendSize >= nMaxSendBufferSize) + if (pfrom->fPauseSend) break; const CInv &inv = *it; @@ -912,6 +989,21 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam BlockMap::iterator mi = mapBlockIndex.find(inv.hash); if (mi != mapBlockIndex.end()) { + if (mi->second->nChainTx && !mi->second->IsValid(BLOCK_VALID_SCRIPTS) && + mi->second->IsValid(BLOCK_VALID_TREE)) { + // If we have the block and all of its parents, but have not yet validated it, + // we might be in the middle of connecting it (ie in the unlock of cs_main + // before ActivateBestChain but after AcceptBlock). + // In this case, we need to run ActivateBestChain prior to checking the relay + // conditions below. + std::shared_ptr<const CBlock> a_recent_block; + { + LOCK(cs_most_recent_block); + a_recent_block = most_recent_block; + } + CValidationState dummy; + ActivateBestChain(dummy, Params(), a_recent_block); + } if (chainActive.Contains(mi->second)) { send = true; } else { @@ -997,7 +1089,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam // Bypass PushInventory, this must send even if redundant, // and we want it right after the last block so they don't // wait for other stuff first. - vector<CInv> vInv; + std::vector<CInv> vInv; vInv.push_back(CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash())); connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::INV, vInv)); pfrom->hashContinue.SetNull(); @@ -1049,7 +1141,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam } } -uint32_t GetFetchFlags(CNode* pfrom, CBlockIndex* pprev, const Consensus::Params& chainparams) { +uint32_t GetFetchFlags(CNode* pfrom, const CBlockIndex* pprev, const Consensus::Params& chainparams) { uint32_t nFetchFlags = 0; if ((pfrom->GetLocalServices() & NODE_WITNESS) && State(pfrom->GetId())->fHaveWitness) { nFetchFlags |= MSG_WITNESS_FLAG; @@ -1057,10 +1149,25 @@ uint32_t GetFetchFlags(CNode* pfrom, CBlockIndex* pprev, const Consensus::Params return nFetchFlags; } -bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman& connman, std::atomic<bool>& interruptMsgProc) -{ - unsigned int nMaxSendBufferSize = connman.GetSendBufferSize(); +inline void static SendBlockTransactions(const CBlock& block, const BlockTransactionsRequest& req, CNode* pfrom, CConnman& connman) { + BlockTransactions resp(req); + for (size_t i = 0; i < req.indexes.size(); i++) { + if (req.indexes[i] >= block.vtx.size()) { + LOCK(cs_main); + Misbehaving(pfrom->GetId(), 100); + LogPrintf("Peer %d sent us a getblocktxn with out-of-bounds tx indices", pfrom->id); + return; + } + resp.txn[i] = block.vtx[req.indexes[i]]; + } + LOCK(cs_main); + const CNetMsgMaker msgMaker(pfrom->GetSendVersion()); + int nSendFlags = State(pfrom->GetId())->fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS; + connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp)); +} +bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman& connman, const std::atomic<bool>& interruptMsgProc) +{ LogPrint("net", "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->id); if (IsArgSet("-dropmessagestest") && GetRand(GetArg("-dropmessagestest", 0)) == 0) { @@ -1089,7 +1196,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Each connection can only send one version message if (pfrom->nVersion != 0) { - connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_DUPLICATE, string("Duplicate version message"))); + connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_DUPLICATE, std::string("Duplicate version message"))); LOCK(cs_main); Misbehaving(pfrom->GetId(), 1); return false; @@ -1100,50 +1207,51 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, CAddress addrFrom; uint64_t nNonce = 1; uint64_t nServiceInt; - vRecv >> pfrom->nVersion >> nServiceInt >> nTime >> addrMe; - pfrom->nServices = ServiceFlags(nServiceInt); + ServiceFlags nServices; + int nVersion; + int nSendVersion; + std::string strSubVer; + int nStartingHeight = -1; + bool fRelay = true; + + vRecv >> nVersion >> nServiceInt >> nTime >> addrMe; + nSendVersion = std::min(nVersion, PROTOCOL_VERSION); + nServices = ServiceFlags(nServiceInt); if (!pfrom->fInbound) { - connman.SetServices(pfrom->addr, pfrom->nServices); + connman.SetServices(pfrom->addr, nServices); } - if (pfrom->nServicesExpected & ~pfrom->nServices) + if (pfrom->nServicesExpected & ~nServices) { - LogPrint("net", "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->id, pfrom->nServices, pfrom->nServicesExpected); + LogPrint("net", "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->id, nServices, pfrom->nServicesExpected); connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD, strprintf("Expected to offer services %08x", pfrom->nServicesExpected))); pfrom->fDisconnect = true; return false; } - if (pfrom->nVersion < MIN_PEER_PROTO_VERSION) + if (nVersion < MIN_PEER_PROTO_VERSION) { // disconnect from peers older than this proto version - LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion); + LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, nVersion); connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION))); pfrom->fDisconnect = true; return false; } - if (pfrom->nVersion == 10300) - pfrom->nVersion = 300; + if (nVersion == 10300) + nVersion = 300; if (!vRecv.empty()) vRecv >> addrFrom >> nNonce; if (!vRecv.empty()) { - vRecv >> LIMITED_STRING(pfrom->strSubVer, MAX_SUBVERSION_LENGTH); - pfrom->cleanSubVer = SanitizeString(pfrom->strSubVer); + vRecv >> LIMITED_STRING(strSubVer, MAX_SUBVERSION_LENGTH); } if (!vRecv.empty()) { - vRecv >> pfrom->nStartingHeight; + vRecv >> nStartingHeight; } - { - LOCK(pfrom->cs_filter); - if (!vRecv.empty()) - vRecv >> pfrom->fRelayTxes; // set to true after we get the first filter* message - else - pfrom->fRelayTxes = true; - } - + if (!vRecv.empty()) + vRecv >> fRelay; // Disconnect if we connected to ourself if (pfrom->fInbound && !connman.CheckIncomingNonce(nNonce)) { @@ -1152,7 +1260,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return true; } - pfrom->addrLocal = addrMe; if (pfrom->fInbound && addrMe.IsRoutable()) { SeenLocal(addrMe); @@ -1162,9 +1269,24 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (pfrom->fInbound) PushNodeVersion(pfrom, connman, GetAdjustedTime()); - pfrom->fClient = !(pfrom->nServices & NODE_NETWORK); + connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERACK)); + + pfrom->nServices = nServices; + pfrom->addrLocal = addrMe; + pfrom->strSubVer = strSubVer; + pfrom->cleanSubVer = SanitizeString(strSubVer); + pfrom->nStartingHeight = nStartingHeight; + pfrom->fClient = !(nServices & NODE_NETWORK); + { + LOCK(pfrom->cs_filter); + pfrom->fRelayTxes = fRelay; // set to true after we get the first filter* message + } + + // Change version + pfrom->SetSendVersion(nSendVersion); + pfrom->nVersion = nVersion; - if((pfrom->nServices & NODE_WITNESS)) + if((nServices & NODE_WITNESS)) { LOCK(cs_main); State(pfrom->GetId())->fHaveWitness = true; @@ -1176,11 +1298,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, UpdatePreferredDownload(pfrom, State(pfrom->GetId())); } - // Change version - connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERACK)); - int nSendVersion = std::min(pfrom->nVersion, PROTOCOL_VERSION); - pfrom->SetSendVersion(nSendVersion); - if (!pfrom->fInbound) { // Advertise our address @@ -1208,9 +1325,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, connman.MarkAddressGood(pfrom->addr); } - pfrom->fSuccessfullyConnected = true; - - string remoteAddr; + std::string remoteAddr; if (fLogIPs) remoteAddr = ", peeraddr=" + pfrom->addr.ToString(); @@ -1223,6 +1338,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->nTimeOffset = nTimeOffset; AddTimeData(pfrom->addr, nTimeOffset); + // If the peer is old enough to have the old alert system, send it the final alert. + if (pfrom->nVersion <= 70012) { + CDataStream finalAlert(ParseHex("60010000000000000000000000ffffff7f00000000ffffff7ffeffff7f01ffffff7f00000000ffffff7f00ffffff7f002f555247454e543a20416c657274206b657920636f6d70726f6d697365642c2075706772616465207265717569726564004630440220653febd6410f470f6bae11cad19c48413becb1ac2c17f908fd0fd53bdc3abd5202206d0e9c96fe88d4a0f01ed9dedae2b6f9e00da94cad0fecaae66ecf689bf71b50"), SER_NETWORK, PROTOCOL_VERSION); + connman.PushMessage(pfrom, CNetMsgMaker(nSendVersion).Make("alert", finalAlert)); + } + // Feeler connections exist only to verify if address is online. if (pfrom->fFeeler) { assert(pfrom->fInbound == false); @@ -1241,11 +1362,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } // At this point, the outgoing message serialization version can't change. - CNetMsgMaker msgMaker(pfrom->GetSendVersion()); + const CNetMsgMaker msgMaker(pfrom->GetSendVersion()); if (strCommand == NetMsgType::VERACK) { - pfrom->SetRecvVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); + pfrom->SetRecvVersion(std::min(pfrom->nVersion.load(), PROTOCOL_VERSION)); if (!pfrom->fInbound) { // Mark this node as currently connected, so we update its timestamp later. @@ -1273,12 +1394,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, nCMPCTBLOCKVersion = 1; connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); } + pfrom->fSuccessfullyConnected = true; } else if (strCommand == NetMsgType::ADDR) { - vector<CAddress> vAddr; + std::vector<CAddress> vAddr; vRecv >> vAddr; // Don't want addr from older versions unless seeding @@ -1292,7 +1414,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } // Store the new addresses - vector<CAddress> vAddrOk; + std::vector<CAddress> vAddrOk; int64_t nNow = GetAdjustedTime(); int64_t nSince = nNow - 10 * 60; BOOST_FOREACH(CAddress& addr, vAddr) @@ -1355,7 +1477,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, else if (strCommand == NetMsgType::INV) { - vector<CInv> vInv; + std::vector<CInv> vInv; vRecv >> vInv; if (vInv.size() > MAX_INV_SZ) { @@ -1413,11 +1535,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Track requests for our stuff GetMainSignals().Inventory(inv.hash); - - if (pfrom->nSendSize > (nMaxSendBufferSize * 2)) { - Misbehaving(pfrom->GetId(), 50); - return error("send buffer size() = %u", pfrom->nSendSize); - } } if (!vToFetch.empty()) @@ -1427,7 +1544,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, else if (strCommand == NetMsgType::GETDATA) { - vector<CInv> vInv; + std::vector<CInv> vInv; vRecv >> vInv; if (vInv.size() > MAX_INV_SZ) { @@ -1453,10 +1570,27 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, uint256 hashStop; vRecv >> locator >> hashStop; + // We might have announced the currently-being-connected tip using a + // compact block, which resulted in the peer sending a getblocks + // request, which we would otherwise respond to without the new block. + // To avoid this situation we simply verify that we are on our best + // known chain now. This is super overkill, but we handle it better + // for getheaders requests, and there are no known nodes which support + // compact blocks but still use getblocks to request blocks. + { + std::shared_ptr<const CBlock> a_recent_block; + { + LOCK(cs_most_recent_block); + a_recent_block = most_recent_block; + } + CValidationState dummy; + ActivateBestChain(dummy, Params(), a_recent_block); + } + LOCK(cs_main); // Find the last block the caller has in the main chain - CBlockIndex* pindex = FindForkInGlobalIndex(chainActive, locator); + const CBlockIndex* pindex = FindForkInGlobalIndex(chainActive, locator); // Send the rest of the chain if (pindex) @@ -1496,6 +1630,18 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, BlockTransactionsRequest req; vRecv >> req; + std::shared_ptr<const CBlock> recent_block; + { + LOCK(cs_most_recent_block); + if (most_recent_block_hash == req.blockhash) + recent_block = most_recent_block; + // Unlock cs_most_recent_block to avoid cs_main lock inversion + } + if (recent_block) { + SendBlockTransactions(*recent_block, req, pfrom, connman); + return true; + } + LOCK(cs_main); BlockMap::iterator it = mapBlockIndex.find(req.blockhash); @@ -1525,17 +1671,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, bool ret = ReadBlockFromDisk(block, it->second, chainparams.GetConsensus()); assert(ret); - BlockTransactions resp(req); - for (size_t i = 0; i < req.indexes.size(); i++) { - if (req.indexes[i] >= block.vtx.size()) { - Misbehaving(pfrom->GetId(), 100); - LogPrintf("Peer %d sent us a getblocktxn with out-of-bounds tx indices", pfrom->id); - return true; - } - resp.txn[i] = block.vtx[req.indexes[i]]; - } - int nSendFlags = State(pfrom->GetId())->fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS; - connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp)); + SendBlockTransactions(block, req, pfrom, connman); } @@ -1552,7 +1688,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } CNodeState *nodestate = State(pfrom->GetId()); - CBlockIndex* pindex = NULL; + const CBlockIndex* pindex = NULL; if (locator.IsNull()) { // If locator is null, return the hashStop block @@ -1570,7 +1706,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } // we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end - vector<CBlock> vHeaders; + std::vector<CBlock> vHeaders; int nLimit = MAX_HEADERS_RESULTS; LogPrint("net", "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), pfrom->id); for (; pindex; pindex = chainActive.Next(pindex)) @@ -1583,6 +1719,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // if our peer has chainActive.Tip() (and thus we are sending an empty // headers message). In both cases it's safe to update // pindexBestHeaderSent to be our tip. + // + // It is important that we simply reset the BestHeaderSent value here, + // and not max(BestHeaderSent, newHeaderSent). We might have announced + // the currently-being-connected tip using a compact block, which + // resulted in the peer sending a headers request, which we respond to + // without the new block. By resetting the BestHeaderSent, we ensure we + // will re-announce the new block via headers (or compact blocks again) + // in the SendMessages logic. nodestate->pindexBestHeaderSent = pindex ? pindex : chainActive.Tip(); connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::HEADERS, vHeaders)); } @@ -1598,8 +1742,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return true; } - deque<COutPoint> vWorkQueue; - vector<uint256> vEraseQueue; + std::deque<COutPoint> vWorkQueue; + std::vector<uint256> vEraseQueue; CTransactionRef ptx; vRecv >> ptx; const CTransaction& tx = *ptx; @@ -1615,7 +1759,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->setAskFor.erase(inv.hash); mapAlreadyAskedFor.erase(inv.hash); - if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, ptx, true, &fMissingInputs)) { + std::list<CTransactionRef> lRemovedTxn; + + if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, ptx, true, &fMissingInputs, &lRemovedTxn)) { mempool.check(pcoinsTip); RelayTransaction(tx, connman); for (unsigned int i = 0; i < tx.vout.size(); i++) { @@ -1630,7 +1776,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, mempool.size(), mempool.DynamicMemoryUsage() / 1000); // Recursively process any orphan transactions that depended on this one - set<NodeId> setMisbehaving; + std::set<NodeId> setMisbehaving; while (!vWorkQueue.empty()) { auto itByPrev = mapOrphanTransactionsByPrev.find(vWorkQueue.front()); vWorkQueue.pop_front(); @@ -1653,7 +1799,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (setMisbehaving.count(fromPeer)) continue; - if (AcceptToMemoryPool(mempool, stateDummy, porphanTx, true, &fMissingInputs2)) { + if (AcceptToMemoryPool(mempool, stateDummy, porphanTx, true, &fMissingInputs2, &lRemovedTxn)) { LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString()); RelayTransaction(orphanTx, connman); for (unsigned int i = 0; i < orphanTx.vout.size(); i++) { @@ -1715,6 +1861,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted); } else { LogPrint("mempool", "not keeping orphan with rejected parents %s\n",tx.GetHash().ToString()); + // We will continue to reject this tx since it has rejected + // parents so avoid re-requesting it from other peers. + recentRejects->insert(tx.GetHash()); } } else { if (!tx.HasWitness() && !state.CorruptionPossible()) { @@ -1723,6 +1872,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // See https://github.com/bitcoin/bitcoin/issues/8279 for details. assert(recentRejects); recentRejects->insert(tx.GetHash()); + if (RecursiveDynamicUsage(*ptx) < 100000) { + AddToCompactExtraTransactions(ptx); + } + } else if (tx.HasWitness() && RecursiveDynamicUsage(*ptx) < 100000) { + AddToCompactExtraTransactions(ptx); } if (pfrom->fWhitelisted && GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) { @@ -1743,6 +1897,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } } } + + for (const CTransactionRef& removedTx : lRemovedTxn) + AddToCompactExtraTransactions(removedTx); + int nDoS = 0; if (state.IsInvalid(nDoS)) { @@ -1775,7 +1933,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } } - CBlockIndex *pindex = NULL; + const CBlockIndex *pindex = NULL; CValidationState state; if (!ProcessNewBlockHeaders({cmpctblock.header}, state, chainparams, &pindex)) { int nDoS; @@ -1812,7 +1970,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, assert(pindex); UpdateBlockAvailability(pfrom->GetId(), pindex->GetBlockHash()); - std::map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator blockInFlightIt = mapBlocksInFlight.find(pindex->GetBlockHash()); + std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> >::iterator blockInFlightIt = mapBlocksInFlight.find(pindex->GetBlockHash()); bool fAlreadyInFlight = blockInFlightIt != mapBlocksInFlight.end(); if (pindex->nStatus & BLOCK_HAVE_DATA) // Nothing to do here @@ -1847,7 +2005,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (pindex->nHeight <= chainActive.Height() + 2) { if ((!fAlreadyInFlight && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) || (fAlreadyInFlight && blockInFlightIt->second.first == pfrom->GetId())) { - list<QueuedBlock>::iterator *queuedBlockIt = NULL; + std::list<QueuedBlock>::iterator* queuedBlockIt = NULL; if (!MarkBlockAsInFlight(pfrom->GetId(), pindex->GetBlockHash(), chainparams.GetConsensus(), pindex, &queuedBlockIt)) { if (!(*queuedBlockIt)->partialBlock) (*queuedBlockIt)->partialBlock.reset(new PartiallyDownloadedBlock(&mempool)); @@ -1859,7 +2017,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } PartiallyDownloadedBlock& partialBlock = *(*queuedBlockIt)->partialBlock; - ReadStatus status = partialBlock.InitData(cmpctblock); + ReadStatus status = partialBlock.InitData(cmpctblock, vExtraTxnForCompact); if (status == READ_STATUS_INVALID) { MarkBlockAsReceived(pindex->GetBlockHash()); // Reset in-flight state in case of whitelist Misbehaving(pfrom->GetId(), 100); @@ -1873,12 +2031,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return true; } - if (!fAlreadyInFlight && mapBlocksInFlight.size() == 1 && pindex->pprev->IsValid(BLOCK_VALID_CHAIN)) { - // We seem to be rather well-synced, so it appears pfrom was the first to provide us - // with this block! Let's get them to announce using compact blocks in the future. - MaybeSetPeerAsAnnouncingHeaderAndIDs(nodestate, pfrom, connman); - } - BlockTransactionsRequest req; for (size_t i = 0; i < cmpctblock.BlockTxCount(); i++) { if (!partialBlock.IsTxAvailable(i)) @@ -1901,7 +2053,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Optimistically try to reconstruct anyway since we might be // able to without any round trips. PartiallyDownloadedBlock tempBlock(&mempool); - ReadStatus status = tempBlock.InitData(cmpctblock); + ReadStatus status = tempBlock.InitData(cmpctblock, vExtraTxnForCompact); if (status != READ_STATUS_OK) { // TODO: don't ignore failures return true; @@ -1971,7 +2123,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, { LOCK(cs_main); - map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator it = mapBlocksInFlight.find(resp.blockhash); + std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> >::iterator it = mapBlocksInFlight.find(resp.blockhash); if (it == mapBlocksInFlight.end() || !it->second.second->partialBlock || it->second.first != pfrom->GetId()) { LogPrint("net", "Peer %d sent us block transactions for block we weren't expecting\n", pfrom->id); @@ -2051,7 +2203,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return true; } - CBlockIndex *pindexLast = NULL; + const CBlockIndex *pindexLast = NULL; { LOCK(cs_main); CNodeState *nodestate = State(pfrom->GetId()); @@ -2128,8 +2280,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // If this set of headers is valid and ends in a block with at least as // much work as our tip, download as much as possible. if (fCanDirectFetch && pindexLast->IsValid(BLOCK_VALID_TREE) && chainActive.Tip()->nChainWork <= pindexLast->nChainWork) { - vector<CBlockIndex *> vToFetch; - CBlockIndex *pindexWalk = pindexLast; + std::vector<const CBlockIndex*> vToFetch; + const CBlockIndex *pindexWalk = pindexLast; // Calculate all the blocks we'd need to switch to pindexLast, up to a limit. while (pindexWalk && !chainActive.Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) { if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) && @@ -2149,9 +2301,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pindexLast->GetBlockHash().ToString(), pindexLast->nHeight); } else { - vector<CInv> vGetData; + std::vector<CInv> vGetData; // Download as much as possible, from earliest to latest. - BOOST_REVERSE_FOREACH(CBlockIndex *pindex, vToFetch) { + BOOST_REVERSE_FOREACH(const CBlockIndex *pindex, vToFetch) { if (nodestate->nBlocksInFlight >= MAX_BLOCKS_IN_TRANSIT_PER_PEER) { // Can't download any more from this peer break; @@ -2168,9 +2320,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } if (vGetData.size() > 0) { if (nodestate->fSupportsDesiredCmpctVersion && vGetData.size() == 1 && mapBlocksInFlight.size() == 1 && pindexLast->pprev->IsValid(BLOCK_VALID_CHAIN)) { - // We seem to be rather well-synced, so it appears pfrom was the first to provide us - // with this block! Let's get them to announce using compact blocks in the future. - MaybeSetPeerAsAnnouncingHeaderAndIDs(nodestate, pfrom, connman); // In any case, we want to download using a compact block, not a regular one vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash); } @@ -2231,7 +2380,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->fSentAddr = true; pfrom->vAddrToSend.clear(); - vector<CAddress> vAddr = connman.GetAddresses(); + std::vector<CAddress> vAddr = connman.GetAddresses(); FastRandomContext insecure_rand; BOOST_FOREACH(const CAddress &addr, vAddr) pfrom->PushAddress(addr, insecure_rand); @@ -2362,7 +2511,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, else if (strCommand == NetMsgType::FILTERADD) { - vector<unsigned char> vData; + std::vector<unsigned char> vData; vRecv >> vData; // Nodes must NEVER send a data item > 520 bytes (the max size for a script data object, @@ -2400,10 +2549,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, { if (fDebug) { try { - string strMsg; unsigned char ccode; string strReason; + std::string strMsg; unsigned char ccode; std::string strReason; vRecv >> LIMITED_STRING(strMsg, CMessageHeader::COMMAND_SIZE) >> ccode >> LIMITED_STRING(strReason, MAX_REJECT_MESSAGE_LENGTH); - ostringstream ss; + std::ostringstream ss; ss << strMsg << " code " << itostr(ccode) << ": " << strReason; if (strMsg == NetMsgType::BLOCK || strMsg == NetMsgType::TX) @@ -2447,14 +2596,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return true; } -// requires LOCK(cs_vRecvMsg) -bool ProcessMessages(CNode* pfrom, CConnman& connman, std::atomic<bool>& interruptMsgProc) +bool ProcessMessages(CNode* pfrom, CConnman& connman, const std::atomic<bool>& interruptMsgProc) { const CChainParams& chainparams = Params(); - unsigned int nMaxSendBufferSize = connman.GetSendBufferSize(); - //if (fDebug) - // LogPrintf("%s(%u messages)\n", __func__, pfrom->vRecvMsg.size()); - // // Message format // (4) message start @@ -2463,40 +2607,40 @@ bool ProcessMessages(CNode* pfrom, CConnman& connman, std::atomic<bool>& interru // (4) checksum // (x) data // - bool fOk = true; + bool fMoreWork = false; if (!pfrom->vRecvGetData.empty()) ProcessGetData(pfrom, chainparams.GetConsensus(), connman, interruptMsgProc); + if (pfrom->fDisconnect) + return false; + // this maintains the order of responses - if (!pfrom->vRecvGetData.empty()) return fOk; + if (!pfrom->vRecvGetData.empty()) return true; - std::deque<CNetMessage>::iterator it = pfrom->vRecvMsg.begin(); - while (!pfrom->fDisconnect && it != pfrom->vRecvMsg.end()) { // Don't bother if send buffer is too full to respond anyway - if (pfrom->nSendSize >= nMaxSendBufferSize) - break; - - // get next message - CNetMessage& msg = *it; - - //if (fDebug) - // LogPrintf("%s(message %u msgsz, %u bytes, complete:%s)\n", __func__, - // msg.hdr.nMessageSize, msg.vRecv.size(), - // msg.complete() ? "Y" : "N"); - - // end, if an incomplete message is found - if (!msg.complete()) - break; - - // at this point, any failure means we can delete the current message - it++; + if (pfrom->fPauseSend) + return false; + std::list<CNetMessage> msgs; + { + LOCK(pfrom->cs_vProcessMsg); + if (pfrom->vProcessMsg.empty()) + return false; + // Just take one message + msgs.splice(msgs.begin(), pfrom->vProcessMsg, pfrom->vProcessMsg.begin()); + pfrom->nProcessQueueSize -= msgs.front().vRecv.size() + CMessageHeader::HEADER_SIZE; + pfrom->fPauseRecv = pfrom->nProcessQueueSize > connman.GetReceiveFloodSize(); + fMoreWork = !pfrom->vProcessMsg.empty(); + } + CNetMessage& msg(msgs.front()); + + msg.SetVersion(pfrom->GetRecvVersion()); // Scan for message start if (memcmp(msg.hdr.pchMessageStart, chainparams.MessageStart(), CMessageHeader::MESSAGE_START_SIZE) != 0) { LogPrintf("PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", SanitizeString(msg.hdr.GetCommand()), pfrom->id); - fOk = false; - break; + pfrom->fDisconnect = true; + return false; } // Read header @@ -2504,9 +2648,9 @@ bool ProcessMessages(CNode* pfrom, CConnman& connman, std::atomic<bool>& interru if (!hdr.IsValid(chainparams.MessageStart())) { LogPrintf("PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", SanitizeString(hdr.GetCommand()), pfrom->id); - continue; + return fMoreWork; } - string strCommand = hdr.GetCommand(); + std::string strCommand = hdr.GetCommand(); // Message size unsigned int nMessageSize = hdr.nMessageSize; @@ -2520,7 +2664,7 @@ bool ProcessMessages(CNode* pfrom, CConnman& connman, std::atomic<bool>& interru SanitizeString(strCommand), nMessageSize, HexStr(hash.begin(), hash.begin()+CMessageHeader::CHECKSUM_SIZE), HexStr(hdr.pchChecksum, hdr.pchChecksum+CMessageHeader::CHECKSUM_SIZE)); - continue; + return fMoreWork; } // Process message @@ -2529,11 +2673,13 @@ bool ProcessMessages(CNode* pfrom, CConnman& connman, std::atomic<bool>& interru { fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime, chainparams, connman, interruptMsgProc); if (interruptMsgProc) - return true; + return false; + if (!pfrom->vRecvGetData.empty()) + fMoreWork = true; } catch (const std::ios_base::failure& e) { - connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, string("error parsing message"))); + connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, std::string("error parsing message"))); if (strstr(e.what(), "end of data")) { // Allow exceptions from under-length message on vRecv @@ -2563,14 +2709,7 @@ bool ProcessMessages(CNode* pfrom, CConnman& connman, std::atomic<bool>& interru if (!fRet) LogPrintf("%s(%s, %u bytes) FAILED peer=%d\n", __func__, SanitizeString(strCommand), nMessageSize, pfrom->id); - break; - } - - // In case the connection got shut down, its receive buffer was wiped - if (!pfrom->fDisconnect) - pfrom->vRecvMsg.erase(pfrom->vRecvMsg.begin(), it); - - return fOk; + return fMoreWork; } class CompareInvMempoolOrder @@ -2590,16 +2729,16 @@ public: } }; -bool SendMessages(CNode* pto, CConnman& connman, std::atomic<bool>& interruptMsgProc) +bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interruptMsgProc) { const Consensus::Params& consensusParams = Params().GetConsensus(); { - // Don't send anything until we get its version message - if (pto->nVersion == 0 || pto->fDisconnect) + // Don't send anything until the version handshake is complete + if (!pto->fSuccessfullyConnected || pto->fDisconnect) return true; // If we get here, the outgoing message serialization version is set and can't change. - CNetMsgMaker msgMaker(pto->GetSendVersion()); + const CNetMsgMaker msgMaker(pto->GetSendVersion()); // // Message: ping @@ -2637,7 +2776,7 @@ bool SendMessages(CNode* pto, CConnman& connman, std::atomic<bool>& interruptMsg CNodeState &state = *State(pto->GetId()); BOOST_FOREACH(const CBlockReject& reject, state.rejects) - connman.PushMessage(pto, msgMaker.Make(NetMsgType::REJECT, (string)NetMsgType::BLOCK, reject.chRejectCode, reject.strRejectReason, reject.hashBlock)); + connman.PushMessage(pto, msgMaker.Make(NetMsgType::REJECT, (std::string)NetMsgType::BLOCK, reject.chRejectCode, reject.strRejectReason, reject.hashBlock)); state.rejects.clear(); if (state.fShouldBan) { @@ -2670,7 +2809,7 @@ bool SendMessages(CNode* pto, CConnman& connman, std::atomic<bool>& interruptMsg // if (pto->nNextAddrSend < nNow) { pto->nNextAddrSend = PoissonNextSend(nNow, AVG_ADDRESS_BROADCAST_INTERVAL); - vector<CAddress> vAddr; + std::vector<CAddress> vAddr; vAddr.reserve(pto->vAddrToSend.size()); BOOST_FOREACH(const CAddress& addr, pto->vAddrToSend) { @@ -2738,11 +2877,11 @@ bool SendMessages(CNode* pto, CConnman& connman, std::atomic<bool>& interruptMsg // blocks, or if the peer doesn't want headers, just // add all to the inv queue. LOCK(pto->cs_inventory); - vector<CBlock> vHeaders; + std::vector<CBlock> vHeaders; bool fRevertToInv = ((!state.fPreferHeaders && (!state.fPreferHeaderAndIDs || pto->vBlockHashesToAnnounce.size() > 1)) || pto->vBlockHashesToAnnounce.size() > MAX_BLOCKS_TO_ANNOUNCE); - CBlockIndex *pBestIndex = NULL; // last header queued for delivery + const CBlockIndex *pBestIndex = NULL; // last header queued for delivery ProcessBlockAvailability(pto->id); // ensure pindexBestKnownBlock is up-to-date if (!fRevertToInv) { @@ -2753,7 +2892,7 @@ bool SendMessages(CNode* pto, CConnman& connman, std::atomic<bool>& interruptMsg BOOST_FOREACH(const uint256 &hash, pto->vBlockHashesToAnnounce) { BlockMap::iterator mi = mapBlockIndex.find(hash); assert(mi != mapBlockIndex.end()); - CBlockIndex *pindex = mi->second; + const CBlockIndex *pindex = mi->second; if (chainActive[pindex->nHeight] != pindex) { // Bail out if we reorged away from this block fRevertToInv = true; @@ -2797,15 +2936,31 @@ bool SendMessages(CNode* pto, CConnman& connman, std::atomic<bool>& interruptMsg if (vHeaders.size() == 1 && state.fPreferHeaderAndIDs) { // We only send up to 1 block as header-and-ids, as otherwise // probably means we're doing an initial-ish-sync or they're slow - LogPrint("net", "%s sending header-and-ids %s to peer %d\n", __func__, + LogPrint("net", "%s sending header-and-ids %s to peer=%d\n", __func__, vHeaders.front().GetHash().ToString(), pto->id); - //TODO: Shouldn't need to reload block from disk, but requires refactor - CBlock block; - bool ret = ReadBlockFromDisk(block, pBestIndex, consensusParams); - assert(ret); - CBlockHeaderAndShortTxIDs cmpctblock(block, state.fWantsCmpctWitness); + int nSendFlags = state.fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS; - connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock)); + + bool fGotBlockFromCache = false; + { + LOCK(cs_most_recent_block); + if (most_recent_block_hash == pBestIndex->GetBlockHash()) { + if (state.fWantsCmpctWitness) + connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *most_recent_compact_block)); + else { + CBlockHeaderAndShortTxIDs cmpctblock(*most_recent_block, state.fWantsCmpctWitness); + connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock)); + } + fGotBlockFromCache = true; + } + } + if (!fGotBlockFromCache) { + CBlock block; + bool ret = ReadBlockFromDisk(block, pBestIndex, consensusParams); + assert(ret); + CBlockHeaderAndShortTxIDs cmpctblock(block, state.fWantsCmpctWitness); + connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock)); + } state.pindexBestHeaderSent = pBestIndex; } else if (state.fPreferHeaders) { if (vHeaders.size() > 1) { @@ -2830,7 +2985,7 @@ bool SendMessages(CNode* pto, CConnman& connman, std::atomic<bool>& interruptMsg const uint256 &hashToAnnounce = pto->vBlockHashesToAnnounce.back(); BlockMap::iterator mi = mapBlockIndex.find(hashToAnnounce); assert(mi != mapBlockIndex.end()); - CBlockIndex *pindex = mi->second; + const CBlockIndex *pindex = mi->second; // Warn if we're announcing a block that is not on the main chain. // This should be very rare and could be optimized out. @@ -2854,7 +3009,7 @@ bool SendMessages(CNode* pto, CConnman& connman, std::atomic<bool>& interruptMsg // // Message: inventory // - vector<CInv> vInv; + std::vector<CInv> vInv; { LOCK(pto->cs_inventory); vInv.reserve(std::max<size_t>(pto->vInventoryBlockToSend.size(), INVENTORY_BROADCAST_MAX)); @@ -2919,7 +3074,7 @@ bool SendMessages(CNode* pto, CConnman& connman, std::atomic<bool>& interruptMsg // Determine transactions to relay if (fSendTrickle) { // Produce a vector with all candidates for sending - vector<std::set<uint256>::iterator> vInvTx; + std::vector<std::set<uint256>::iterator> vInvTx; vInvTx.reserve(pto->setInventoryTxToSend.size()); for (std::set<uint256>::iterator it = pto->setInventoryTxToSend.begin(); it != pto->setInventoryTxToSend.end(); it++) { vInvTx.push_back(it); @@ -3013,12 +3168,12 @@ bool SendMessages(CNode* pto, CConnman& connman, std::atomic<bool>& interruptMsg // // Message: getdata (blocks) // - vector<CInv> vGetData; + std::vector<CInv> vGetData; if (!pto->fClient && (fFetch || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { - vector<CBlockIndex*> vToDownload; + std::vector<const CBlockIndex*> vToDownload; NodeId staller = -1; FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller, consensusParams); - BOOST_FOREACH(CBlockIndex *pindex, vToDownload) { + BOOST_FOREACH(const CBlockIndex *pindex, vToDownload) { uint32_t nFetchFlags = GetFetchFlags(pto, pindex->pprev, consensusParams); vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash())); MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), consensusParams, pindex); diff --git a/src/net_processing.h b/src/net_processing.h index 230d805bd4..9e3f1b7156 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -9,6 +9,15 @@ #include "net.h" #include "validationinterface.h" +/** Default for -maxorphantx, maximum number of orphan transactions kept in memory */ +static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100; +/** Expiration time for orphan transactions in seconds */ +static const int64_t ORPHAN_TX_EXPIRE_TIME = 20 * 60; +/** Minimum time between orphan transactions expire time checks in seconds */ +static const int64_t ORPHAN_TX_EXPIRE_INTERVAL = 5 * 60; +/** Default number of orphan+recently-replaced txn to keep around for block reconstruction */ +static const unsigned int DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN = 100; + /** Register with a network node to receive its signals */ void RegisterNodeSignals(CNodeSignals& nodeSignals); /** Unregister a network node */ @@ -24,6 +33,7 @@ public: virtual void SyncTransaction(const CTransaction& tx, const CBlockIndex* pindex, int nPosInBlock); virtual void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload); virtual void BlockChecked(const CBlock& block, const CValidationState& state); + virtual void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock); }; struct CNodeStateStats { @@ -39,14 +49,15 @@ bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats); void Misbehaving(NodeId nodeid, int howmuch); /** Process protocol messages received from a given node */ -bool ProcessMessages(CNode* pfrom, CConnman& connman, std::atomic<bool>& interrupt); +bool ProcessMessages(CNode* pfrom, CConnman& connman, const std::atomic<bool>& interrupt); /** * Send queued protocol messages to be sent to a give node. * * @param[in] pto The node which we are sending messages to. * @param[in] connman The connection manager for that node. * @param[in] interrupt Interrupt condition for processing threads + * @return True if there is more work to be done */ -bool SendMessages(CNode* pto, CConnman& connman, std::atomic<bool>& interrupt); +bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interrupt); #endif // BITCOIN_NET_PROCESSING_H diff --git a/src/netaddress.h b/src/netaddress.h index bc430dd823..a85c2b7452 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -49,7 +49,7 @@ class CNetAddr bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0) bool IsIPv6() const; // IPv6 address (not mapped IPv4, not Tor) bool IsRFC1918() const; // IPv4 private networks (10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12) - bool IsRFC2544() const; // IPv4 inter-network communcations (192.18.0.0/15) + bool IsRFC2544() const; // IPv4 inter-network communications (192.18.0.0/15) bool IsRFC6598() const; // IPv4 ISP-level NAT (100.64.0.0/10) bool IsRFC5737() const; // IPv4 documentation addresses (192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24) bool IsRFC3849() const; // IPv6 documentation address (2001:0DB8::/32) diff --git a/src/netmessagemaker.h b/src/netmessagemaker.h index 7167434a19..8e8a6e4a02 100644 --- a/src/netmessagemaker.h +++ b/src/netmessagemaker.h @@ -15,7 +15,7 @@ public: CNetMsgMaker(int nVersionIn) : nVersion(nVersionIn){} template <typename... Args> - CSerializedNetMsg Make(int nFlags, std::string sCommand, Args&&... args) + CSerializedNetMsg Make(int nFlags, std::string sCommand, Args&&... args) const { CSerializedNetMsg msg; msg.command = std::move(sCommand); @@ -24,7 +24,7 @@ public: } template <typename... Args> - CSerializedNetMsg Make(std::string sCommand, Args&&... args) + CSerializedNetMsg Make(std::string sCommand, Args&&... args) const { return Make(0, std::move(sCommand), std::forward<Args>(args)...); } diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index d318a0997c..ec398f6627 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -105,7 +105,7 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnes else if ((whichType == TX_MULTISIG) && (!fIsBareMultisigStd)) { reason = "bare-multisig"; return false; - } else if (txout.IsDust(::minRelayTxFee)) { + } else if (txout.IsDust(dustRelayFee)) { reason = "dust"; return false; } @@ -206,6 +206,8 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) return true; } +CFeeRate incrementalRelayFee = CFeeRate(DEFAULT_INCREMENTAL_RELAY_FEE); +CFeeRate dustRelayFee = CFeeRate(DUST_RELAY_TX_FEE); unsigned int nBytesPerSigOp = DEFAULT_BYTES_PER_SIGOP; int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost) diff --git a/src/policy/policy.h b/src/policy/policy.h index 764ee27806..9b1323ac26 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -20,6 +20,8 @@ static const unsigned int DEFAULT_BLOCK_MAX_SIZE = 750000; static const unsigned int DEFAULT_BLOCK_PRIORITY_SIZE = 0; /** Default for -blockmaxweight, which controls the range of block weights the mining code will create **/ static const unsigned int DEFAULT_BLOCK_MAX_WEIGHT = 3000000; +/** Default for -blockmintxfee, which sets the minimum feerate for a transaction in blocks created by mining code **/ +static const unsigned int DEFAULT_BLOCK_MIN_TX_FEE = 1000; /** The maximum weight for transactions we're willing to relay/mine */ static const unsigned int MAX_STANDARD_TX_WEIGHT = 400000; /** Maximum number of signature check operations in an IsStandard() P2SH script */ @@ -28,6 +30,8 @@ static const unsigned int MAX_P2SH_SIGOPS = 15; static const unsigned int MAX_STANDARD_TX_SIGOPS_COST = MAX_BLOCK_SIGOPS_COST/5; /** Default for -maxmempool, maximum megabytes of mempool memory usage */ static const unsigned int DEFAULT_MAX_MEMPOOL_SIZE = 300; +/** Default for -incrementalrelayfee, which sets the minimum feerate increase for mempool limiting or BIP 125 replacement **/ +static const unsigned int DEFAULT_INCREMENTAL_RELAY_FEE = 1000; /** Default for -bytespersigop */ static const unsigned int DEFAULT_BYTES_PER_SIGOP = 20; /** The maximum number of witness stack items in a standard P2WSH script */ @@ -36,6 +40,12 @@ static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEMS = 100; static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEM_SIZE = 80; /** The maximum size of a standard witnessScript */ static const unsigned int MAX_STANDARD_P2WSH_SCRIPT_SIZE = 3600; +/** Min feerate for defining dust. Historically this has been the same as the + * minRelayTxFee, however changing the dust limit changes which transactions are + * standard and should be done with care and ideally rarely. It makes sense to + * only increase the dust limit after prior releases were already not creating + * outputs below the new threshold */ +static const unsigned int DUST_RELAY_TX_FEE = 1000; /** * Standard script verification flags that standard transactions will comply * with. However scripts violating these flags may still be present in valid @@ -83,6 +93,8 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) */ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs); +extern CFeeRate incrementalRelayFee; +extern CFeeRate dustRelayFee; extern unsigned int nBytesPerSigOp; /** Compute the virtual transaction size (weight reinterpreted as bytes). */ diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp index 00a915dd8d..4b34e73eb7 100644 --- a/src/qt/bantablemodel.cpp +++ b/src/qt/bantablemodel.cpp @@ -64,7 +64,7 @@ public: } if (sortColumn >= 0) - // sort cachedBanlist (use stable sort to prevent rows jumping around unneceesarily) + // sort cachedBanlist (use stable sort to prevent rows jumping around unnecessarily) qStableSort(cachedBanlist.begin(), cachedBanlist.end(), BannedNodeLessThan(sortColumn, sortOrder)); } diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 9d5ff31fb4..1c1acb6b10 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -31,6 +31,7 @@ #include "macdockiconhandler.h" #endif +#include "chainparams.h" #include "init.h" #include "ui_interface.h" #include "util.h" @@ -748,6 +749,15 @@ void BitcoinGUI::setNetworkActive(bool networkActive) updateNetworkState(); } +void BitcoinGUI::updateHeadersSyncProgressLabel() +{ + int64_t headersTipTime = clientModel->getHeaderTipTime(); + int headersTipHeight = clientModel->getHeaderTipHeight(); + int estHeadersLeft = (GetTime() - headersTipTime) / Params().GetConsensus().nPowTargetSpacing; + if (estHeadersLeft > HEADER_HEIGHT_DELTA_SYNC) + progressBarLabel->setText(tr("Syncing Headers (%1%)...").arg(QString::number(100.0 / (headersTipHeight+estHeadersLeft)*headersTipHeight, 'f', 1))); +} + void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool header) { if (modalOverlay) @@ -760,7 +770,7 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer if (!clientModel) return; - // Prevent orphan statusbar messages (e.g. hover Quit in main menu, wait until chain-sync starts -> garbelled text) + // Prevent orphan statusbar messages (e.g. hover Quit in main menu, wait until chain-sync starts -> garbled text) statusBar()->clearMessage(); // Acquire current block source @@ -768,9 +778,11 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer switch (blockSource) { case BLOCK_SOURCE_NETWORK: if (header) { + updateHeadersSyncProgressLabel(); return; } progressBarLabel->setText(tr("Synchronizing with network...")); + updateHeadersSyncProgressLabel(); break; case BLOCK_SOURCE_DISK: if (header) { @@ -786,8 +798,7 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer if (header) { return; } - // Case: not Importing, not Reindexing and no network connection - progressBarLabel->setText(tr("No block source available...")); + progressBarLabel->setText(tr("Connecting to peers...")); break; } @@ -817,7 +828,7 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer } else { - QString timeBehindText = GUIUtil::formateNiceTimeOffset(secs); + QString timeBehindText = GUIUtil::formatNiceTimeOffset(secs); progressBarLabel->setVisible(true); progressBar->setFormat(tr("%1 behind").arg(timeBehindText)); diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index c8b6ce5474..62d419d3ef 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -149,6 +149,8 @@ private: /** Update UI with latest network info from model. */ void updateNetworkState(); + void updateHeadersSyncProgressLabel(); + Q_SIGNALS: /** Signal raised when a URI was entered or dragged to the GUI */ void receivedURI(const QString &uri); diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp index 348e7378ab..a1e5cccc0b 100644 --- a/src/qt/bitcoinstrings.cpp +++ b/src/qt/bitcoinstrings.cpp @@ -78,6 +78,9 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "Execute command when the best block changes (%s in cmd is replaced by block " "hash)"), QT_TRANSLATE_NOOP("bitcoin-core", "" +"Extra transactions to keep in memory for compact block reconstructions " +"(default: %u)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" "Fees (in %s/kB) smaller than this are considered zero fee for relaying, " "mining and transaction creation (default: %s)"), QT_TRANSLATE_NOOP("bitcoin-core", "" @@ -95,6 +98,10 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "If paytxfee is not set, include enough fee so transactions begin " "confirmation on average within n blocks (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "" +"If this block is in the chain assume that it and its ancestors are valid and " +"potentially skip their script verification (0 to verify all, default: %s, " +"testnet: %s)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" "Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay " "fee of %s to prevent stuck transactions)"), QT_TRANSLATE_NOOP("bitcoin-core", "" @@ -132,14 +139,21 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "Randomize credentials for every proxy connection. This enables Tor stream " "isolation (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Reduce storage requirements by pruning (deleting) old blocks. This mode is " -"incompatible with -txindex and -rescan. Warning: Reverting this setting " -"requires re-downloading the entire blockchain. (default: 0 = disable pruning " -"blocks, >%u = target size in MiB to use for block files)"), +"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)"), QT_TRANSLATE_NOOP("bitcoin-core", "" "Rescans are not possible in pruned mode. You will need to use -reindex which " "will download the whole blockchain again."), 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 = " diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 20a04dc8a8..bb10e49422 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -130,7 +130,7 @@ double ClientModel::getVerificationProgress(const CBlockIndex *tipIn) const LOCK(cs_main); tip = chainActive.Tip(); } - return Checkpoints::GuessVerificationProgress(Params().Checkpoints(), tip); + return GuessVerificationProgress(Params().TxData(), tip); } void ClientModel::updateTimer() diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index c01c1a84cf..2c10e633b8 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -66,7 +66,7 @@ public: //! Return true if core is doing initial block download bool inInitialBlockDownload() const; - //! Return true if core is importing blocks + //! Returns enum BlockSource of the current importing/syncing state enum BlockSource getBlockSource() const; //! Return true if network activity in core is enabled bool getNetworkActive() const; diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index a0c2813477..f8aba70d92 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -15,7 +15,8 @@ #include "wallet/coincontrol.h" #include "init.h" -#include "validation.h" // For minRelayTxFee +#include "policy/policy.h" +#include "validation.h" // For mempool #include "wallet/wallet.h" #include <boost/assign/list_of.hpp> // for 'map_list_of()' @@ -432,7 +433,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) { CTxOut txout(amount, (CScript)std::vector<unsigned char>(24, 0)); txDummy.vout.push_back(txout); - if (txout.IsDust(::minRelayTxFee)) + if (txout.IsDust(dustRelayFee)) fDust = true; } } @@ -545,10 +546,10 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) if (nChange > 0 && nChange < MIN_CHANGE) { CTxOut txout(nChange, (CScript)std::vector<unsigned char>(24, 0)); - if (txout.IsDust(::minRelayTxFee)) + if (txout.IsDust(dustRelayFee)) { if (CoinControlDialog::fSubtractFeeFromAmount) // dust-change will be raised until no dust - nChange = txout.GetDustThreshold(::minRelayTxFee); + nChange = txout.GetDustThreshold(dustRelayFee); else { nPayFee += nChange; diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 4463cfdaef..5d6c0e2e31 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -11,7 +11,7 @@ #include "primitives/transaction.h" #include "init.h" -#include "validation.h" // For minRelayTxFee +#include "policy/policy.h" #include "protocol.h" #include "script/script.h" #include "script/standard.h" @@ -257,7 +257,7 @@ bool isDust(const QString& address, const CAmount& amount) CTxDestination dest = CBitcoinAddress(address.toStdString()).Get(); CScript script = GetScriptForDestination(dest); CTxOut txOut(amount, script); - return txOut.IsDust(::minRelayTxFee); + return txOut.IsDust(dustRelayFee); } QString HtmlEscape(const QString& str, bool fMultiLine) @@ -536,7 +536,7 @@ int TableViewLastColumnResizingFixer::getAvailableWidthForColumn(int column) return nResult; } -// Make sure we don't make the columns wider than the tables viewport width. +// Make sure we don't make the columns wider than the table's viewport width. void TableViewLastColumnResizingFixer::adjustTableColumnsWidth() { disconnectViewHeadersSignals(); @@ -570,7 +570,7 @@ void TableViewLastColumnResizingFixer::on_sectionResized(int logicalIndex, int o } } -// When the tabless geometry is ready, we manually perform the stretch of the "Message" column, +// When the table's geometry is ready, we manually perform the stretch of the "Message" column, // as the "Stretch" resize mode does not allow for interactive resizing. void TableViewLastColumnResizingFixer::on_geometriesChanged() { @@ -951,7 +951,7 @@ QString formatTimeOffset(int64_t nTimeOffset) return QString(QObject::tr("%1 s")).arg(QString::number((int)nTimeOffset, 10)); } -QString formateNiceTimeOffset(qint64 secs) +QString formatNiceTimeOffset(qint64 secs) { // Represent time from last generated block in human readable text QString timeBehindText; diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index b2c9338b56..913aa5e24b 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -140,7 +140,7 @@ namespace GUIUtil * Also makes sure the column widths are never larger than the table's viewport. * In Qt, all columns are resizable from the right, but it's not intuitive resizing the last column from the right. * Usually our second to last columns behave as if stretched, and when on strech mode, columns aren't resizable - * interactively or programatically. + * interactively or programmatically. * * This helper object takes care of this issue. * @@ -200,7 +200,7 @@ namespace GUIUtil /* Format a CNodeCombinedStats.nTimeOffset into a user-readable string. */ QString formatTimeOffset(int64_t nTimeOffset); - QString formateNiceTimeOffset(qint64 secs); + QString formatNiceTimeOffset(qint64 secs); class ClickableLabel : public QLabel { diff --git a/src/qt/locale/bitcoin_af.ts b/src/qt/locale/bitcoin_af.ts index 8cbf5aadc5..9726987b63 100644 --- a/src/qt/locale/bitcoin_af.ts +++ b/src/qt/locale/bitcoin_af.ts @@ -382,10 +382,6 @@ <translation>Blokke op skyf word geprosesseer...</translation> </message> <message> - <source>No block source available...</source> - <translation>Geen blokbron beskikbaar...</translation> - </message> - <message> <source>%1 behind</source> <translation>%1 agter</translation> </message> diff --git a/src/qt/locale/bitcoin_af_ZA.ts b/src/qt/locale/bitcoin_af_ZA.ts index 1db5008384..1679482c77 100644 --- a/src/qt/locale/bitcoin_af_ZA.ts +++ b/src/qt/locale/bitcoin_af_ZA.ts @@ -10,9 +10,21 @@ <translation>Maak 'n kopie van die huidige adres na die stelsel klipbord</translation> </message> <message> + <source>&Copy</source> + <translation>&Kopie</translation> + </message> + <message> + <source>Delete the currently selected address from the list</source> + <translation>Verwyder die huidiglik gekieste address van die lys</translation> + </message> + <message> <source>&Delete</source> <translation>&Verwyder</translation> </message> + <message> + <source>Choose the address to send coins to</source> + <translation>Kies die address na wie die muntstukke gestuur moet word</translation> + </message> </context> <context> <name>AddressTableModel</name> @@ -411,6 +423,14 @@ <translation>Kopieer bedrag</translation> </message> <message> + <source>%1 to %2</source> + <translation>%1 tot %2</translation> + </message> + <message> + <source>or</source> + <translation>of</translation> + </message> + <message> <source>(no label)</source> <translation>(geen etiket)</translation> </message> diff --git a/src/qt/locale/bitcoin_ar.ts b/src/qt/locale/bitcoin_ar.ts index 15c5158f21..9b865f29bf 100644 --- a/src/qt/locale/bitcoin_ar.ts +++ b/src/qt/locale/bitcoin_ar.ts @@ -422,10 +422,6 @@ <translation>معالجة الكتل على القرص...</translation> </message> <message> - <source>No block source available...</source> - <translation>لا يوجد أي مصدر الكتلة</translation> - </message> - <message> <source>%1 behind</source> <translation>خلف %1</translation> </message> diff --git a/src/qt/locale/bitcoin_be_BY.ts b/src/qt/locale/bitcoin_be_BY.ts index d2c52f2c16..62f2ffc9e6 100644 --- a/src/qt/locale/bitcoin_be_BY.ts +++ b/src/qt/locale/bitcoin_be_BY.ts @@ -390,10 +390,6 @@ <translation>Опцыі каманднага радка</translation> </message> <message> - <source>No block source available...</source> - <translation>Крыніца блокаў недасяжная...</translation> - </message> - <message> <source>%1 behind</source> <translation>%1 таму</translation> </message> diff --git a/src/qt/locale/bitcoin_bg.ts b/src/qt/locale/bitcoin_bg.ts index 5a0fd2aec2..c571698304 100644 --- a/src/qt/locale/bitcoin_bg.ts +++ b/src/qt/locale/bitcoin_bg.ts @@ -422,10 +422,6 @@ <translation>Обработване на блокове на диска...</translation> </message> <message> - <source>No block source available...</source> - <translation>Липсва източник на блоковете...</translation> - </message> - <message> <source>%1 behind</source> <translation>%1 зад</translation> </message> diff --git a/src/qt/locale/bitcoin_ca.ts b/src/qt/locale/bitcoin_ca.ts index c09d857632..84f51d18a8 100644 --- a/src/qt/locale/bitcoin_ca.ts +++ b/src/qt/locale/bitcoin_ca.ts @@ -441,10 +441,6 @@ <source>Processing blocks on disk...</source> <translation>S'estan processant els blocs al disc...</translation> </message> - <message> - <source>No block source available...</source> - <translation>No hi ha cap font de bloc disponible...</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>S'ha processat %n bloc de l'historial de transacció.</numerusform><numerusform>S'han processat %n blocs de l'historial de transacció.</numerusform></translation> @@ -2822,10 +2818,6 @@ <translation>Poda: la darrera sincronització del moneder va més enllà de les dades podades. Cal que activeu -reindex (baixeu tota la cadena de blocs de nou en cas de node podat)</translation> </message> <message> - <source>Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> - <translation>Reduïu els requisits d'emmagatzematge podant (suprimint) els blocs antics. Aquest mode és incompatible amb -txindex i -rescan. Avís: la reversió d'aquest paràmetre implica haver de tornar a baixar la cadena de blocs sencera. (per defecte: 0 = inhabilita la poda de blocs, >%u = mida objectiu en MiB per utilitzar en els fitxers de blocs)</translation> - </message> - <message> <source>Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.</source> <translation>Els rescanejos no són possible en el mode de poda. Caldrà que utilitzeu -reindex, que tornarà a baixar la cadena de blocs sencera.</translation> </message> diff --git a/src/qt/locale/bitcoin_ca@valencia.ts b/src/qt/locale/bitcoin_ca@valencia.ts index c4748c0afa..0123f8faab 100644 --- a/src/qt/locale/bitcoin_ca@valencia.ts +++ b/src/qt/locale/bitcoin_ca@valencia.ts @@ -394,10 +394,6 @@ <translation>Opcions de la &línia d'ordes</translation> </message> <message> - <source>No block source available...</source> - <translation>No hi ha cap font de bloc disponible...</translation> - </message> - <message> <source>%1 behind</source> <translation>%1 darrere</translation> </message> diff --git a/src/qt/locale/bitcoin_ca_ES.ts b/src/qt/locale/bitcoin_ca_ES.ts index 838190b10e..8cbb57bd35 100644 --- a/src/qt/locale/bitcoin_ca_ES.ts +++ b/src/qt/locale/bitcoin_ca_ES.ts @@ -441,10 +441,6 @@ <source>Processing blocks on disk...</source> <translation>S'estan processant els blocs al disc...</translation> </message> - <message> - <source>No block source available...</source> - <translation>No hi ha cap font de bloc disponible...</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>S'ha processat %n bloc de l'historial de transacció.</numerusform><numerusform>S'han processat %n blocs de l'historial de transacció.</numerusform></translation> @@ -2822,10 +2818,6 @@ <translation>Poda: la darrera sincronització del moneder va més enllà de les dades podades. Cal que activeu -reindex (baixeu tota la cadena de blocs de nou en cas de node podat)</translation> </message> <message> - <source>Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> - <translation>Reduïu els requisits d'emmagatzematge podant (suprimint) els blocs antics. Aquest mode és incompatible amb -txindex i -rescan. Avís: la reversió d'aquest paràmetre implica haver de tornar a baixar la cadena de blocs sencera. (per defecte: 0 = inhabilita la poda de blocs, >%u = mida objectiu en MiB per utilitzar en els fitxers de blocs)</translation> - </message> - <message> <source>Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.</source> <translation>Els rescanejos no són possible en el mode de poda. Caldrà que utilitzeu -reindex, que tornarà a baixar la cadena de blocs sencera.</translation> </message> diff --git a/src/qt/locale/bitcoin_cs.ts b/src/qt/locale/bitcoin_cs.ts index e0f4a18994..67c595ab34 100644 --- a/src/qt/locale/bitcoin_cs.ts +++ b/src/qt/locale/bitcoin_cs.ts @@ -441,10 +441,6 @@ <source>Processing blocks on disk...</source> <translation>Zpracovávám bloky na disku...</translation> </message> - <message> - <source>No block source available...</source> - <translation>Není dostupný žádný zdroj bloků...</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>Zpracován %n blok transakční historie.</numerusform><numerusform>Zpracovány %n bloky transakční historie.</numerusform><numerusform>Zpracováno %n bloků transakční historie.</numerusform></translation> @@ -2205,6 +2201,14 @@ <translation>Upozornění: Neznámá adresa pro drobné</translation> </message> <message> + <source>Confirm custom change address</source> + <translation>Potvrď vlastní adresu pro drobné</translation> + </message> + <message> + <source>The address you selected for change is not part of this wallet. Any or all funds in your wallet may be sent to this address. Are you sure?</source> + <translation>Adresa, kterou jsi zvolil pro drobné, není součástí této peněženky. Potenciálně všechny prostředky z tvé peněženky mohou být na tuto adresu odeslány. Souhlasíš, aby se tak stalo?</translation> + </message> + <message> <source>(no label)</source> <translation>(bez označení)</translation> </message> @@ -3022,10 +3026,6 @@ <translation>Prořezávání: poslední synchronizace peněženky proběhla před už prořezanými daty. Je třeba provést -reindex (tedy v případě prořezávacího režimu stáhnout znovu celý řetězec bloků)</translation> </message> <message> - <source>Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> - <translation>Omezit nároky na úložný prostor prořezáváním (mazáním) starých bloků. Tento režim není slučitelný s -txindex ani -rescan. Upozornění: opětovná změna tohoto nastavení bude vyžadovat nové stažení celého řetězce bloků. (výchozí: 0 = bloky neprořezávat, >%u = cílová velikost souborů s bloky, v MiB)</translation> - </message> - <message> <source>Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.</source> <translation>V prořezávacím režimu není možné přeskenovávat řetězec bloků. Musíš provést -reindex, což znovu stáhne celý řetězec bloků.</translation> </message> @@ -3126,6 +3126,10 @@ <translation>Použít UPnP k namapování naslouchacího portu (výchozí: 1, pokud naslouchá a nepoužívá -proxy)</translation> </message> <message> + <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times</source> + <translation>Uživatelské jméno a zahašované heslo pro JSON-RPC spojení. Pole <userpw> má formát: <UŽIVATELSKÉ_JMÉNO>:<SŮL>$<HAŠ>. Pomocný pythonní skript je přiložen v share/rpcuser. Klient se pak už připojuje normálně pomocí páru argumentů rpcuser=<UŽIVATELSKÉ_JMÉNO>/rpcpassword=<HESLO>. Tuto volbu lze použít i vícekrát</translation> + </message> + <message> <source>Wallet will not create transactions that violate mempool chain limits (default: %u)</source> <translation>Peněženka nebude vytvářet transakce, které by porušovaly limity transakčního zásobníku na řetězce (výchozí: %u)</translation> </message> @@ -3706,10 +3710,6 @@ <translation>Použít samostatnou SOCKS5 proxy ke spojení s protějšky přes skryté služby v Toru (výchozí: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Uživatelské jméno a zahašované heslo pro JSON-RPC spojení. Pole <userpw> má formát: <UŽIVATELSKÉ_JMÉNO>:<SŮL>$<HAŠ>. Pomocný pythonní skript je přiložen v share/rpcuser. Tuto volbu lze použít i vícekrát</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>Upozornění: Síť těží neznámé verze bloků! Je možné, že jsou v platnosti neznámá pravidla</translation> </message> diff --git a/src/qt/locale/bitcoin_da.ts b/src/qt/locale/bitcoin_da.ts index 9fc52efe57..54ef4a2bdf 100644 --- a/src/qt/locale/bitcoin_da.ts +++ b/src/qt/locale/bitcoin_da.ts @@ -330,6 +330,10 @@ <translation>Klik for a aktivere netværksaktivitet igen.</translation> </message> <message> + <source>Syncing Headers (%1%)...</source> + <translation>Synkroniserer hoveder (%1%)…</translation> + </message> + <message> <source>Reindexing blocks on disk...</source> <translation>Genindekserer blokke på disken…</translation> </message> @@ -441,10 +445,6 @@ <source>Processing blocks on disk...</source> <translation>Bearbejder blokke på disken…</translation> </message> - <message> - <source>No block source available...</source> - <translation>Ingen blokkilde tilgængelig…</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>Bearbejdede %n blok med transaktionshistorik.</numerusform><numerusform>Bearbejdede %n blokke med transaktionshistorik.</numerusform></translation> @@ -486,6 +486,10 @@ <translation>%1-klient</translation> </message> <message> + <source>Connecting to peers...</source> + <translation>Forbinder til knuder…</translation> + </message> + <message> <source>Catching up...</source> <translation>Indhenter…</translation> </message> @@ -2205,6 +2209,14 @@ <translation>Advarsel: Ukendt byttepengeadresse</translation> </message> <message> + <source>Confirm custom change address</source> + <translation>Bekræft tilpasset byttepengeadresse</translation> + </message> + <message> + <source>The address you selected for change is not part of this wallet. Any or all funds in your wallet may be sent to this address. Are you sure?</source> + <translation>Den adresse, du har valgt til byttepenge, er ikke en del af denne tegnebog. Nogle af eller alle penge i din tegnebog kan blive sendt til denne adresse. Er du sikker?</translation> + </message> + <message> <source>(no label)</source> <translation>(ingen mærkat)</translation> </message> @@ -3022,10 +3034,6 @@ <translation>Beskæring: Seneste synkronisering rækker udover beskårne data. Du er nødt til at bruge -reindex (downloade hele blokkæden igen i fald af beskåret knude)</translation> </message> <message> - <source>Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> - <translation>Reducér lagringskravene ved at beskære (slette) gamle blokke. Denne tilstand er ikke kompatibel med -txindex og -rescan. Advarsel: Fortrydelse af denne indstilling kræver gendownload af hele blokkæden. (standard: 0 = deaktivér beskæring af blokke, >%u = målstørrelse i MiB der skal bruges på blokfiler)</translation> - </message> - <message> <source>Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.</source> <translation>Genindlæsninger er ikke mulige i beskåret tilstand. Du er nødt til at bruge -reindex, hvilket vil downloade hele blokkæden igen.</translation> </message> @@ -3090,6 +3098,14 @@ <translation>Udfør kommando, når en transaktion i tegnebogen ændres (%s i kommandoen erstattes med TxID)</translation> </message> <message> + <source>Extra transactions to keep in memory for compact block reconstructions (default: %u)</source> + <translation>Ekstra transaktioner, der skal beholdes i hukommelsen til kompakte blokgenopbygninger (standard: %u)</translation> + </message> + <message> + <source>If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: %s, testnet: %s)</source> + <translation>Hvis denne blok er i kæden, så antag at den og dens forgængere er gyldige, og spring potentielt deres scriptverificering over (0 for at verificere alle, standard: %s, testnet: %s)</translation> + </message> + <message> <source>Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds)</source> <translation>Justering af maksimalt tilladt gennemsnitlig afvigelse fra peer-tid. Den lokale opfattelse af tid kan blive påvirket frem eller tilbage af peers med denne mængde tid. (standard: %u sekunder)</translation> </message> @@ -3106,6 +3122,14 @@ <translation>Overvej venligst at bidrage til udviklingen, hvis du finder %s brugbar. Besøg %s for yderligere information om softwaren.</translation> </message> <message> + <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>Reducér pladskravene ved at beskære (slette, "prune") gamle blokke. Dette tillader pruneblockchain-RPC'en at blive kaldt for at slette specifikke blokke, og det aktiverer automatisk beskæring af gamle blokke, hvis en målstørrelse i MiB er angivet. Denne tilstand er ikke kompatibel med -txindex og -rescan. Advarsel: Fortrydelse af denne indstilling kræver download af hele blokkæden igen. (standard: 0 = slå beskæring af blokke fra, 1 = tillad manuel beskæring via RPC, >%u = beskær automatisk blokfiler for at bliver under den angivne målstørrelse i MiB)</translation> + </message> + <message> + <source>Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s)</source> + <translation>Sæt den laveste gebyrrate (i %s/kB) for transaktioner, der skal inkluderes i blokoprettelse. (standard: %s)</translation> + </message> + <message> <source>Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)</source> <translation>Sæt antallet af scriptverificeringstråde (%u til %d, 0 = auto, <0 = efterlad det antal kernet fri, standard: %d)</translation> </message> @@ -3126,6 +3150,10 @@ <translation>Brug UPnP for at konfigurere den lyttende port (standard: 1 under lytning og ingen -proxy)</translation> </message> <message> + <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times</source> + <translation>Brugernavn og hashet adgangskode for JSON-RPC-forbindelser. Feltet <userpw> er i formatet: <BRUGERNAVN>:<SALT>$<HASH>. Et kanonisk Python-skript er inkluderet i share/rpcuser. Klienten forbinder så normalt ved hjælp af argumentparret rpcuser=<BRUGERNAVN>/rpcpassword=<ADGANGSKODE>. Dette tilvalg kan angives flere gange</translation> + </message> + <message> <source>Wallet will not create transactions that violate mempool chain limits (default: %u)</source> <translation>Tegnebogen vil ikke oprette transaktioner, som overtræder begrænsningen for hukommelsespuljekæden (standard: %u)</translation> </message> @@ -3706,10 +3734,6 @@ <translation>Brug separat SOCS5-proxy for at nå knuder via skjulte Tor-tjenester (standard: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Brugernavn og hashet adgangskode for JSON-RPC-forbindelser. Feltet <userpw> er i formatet: <BRUGERNAVN>:<SALT>$<HASH>. Et kanonisk Python-skript inkluderes i share/rpcuser. Dette tilvalg kan angives flere gange</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>Advarsel: Ukendte blokversioner bliver minet! Det er muligt, at ukendte regler er i brug</translation> </message> diff --git a/src/qt/locale/bitcoin_de.ts b/src/qt/locale/bitcoin_de.ts index 25d3f45163..af79d47736 100644 --- a/src/qt/locale/bitcoin_de.ts +++ b/src/qt/locale/bitcoin_de.ts @@ -330,6 +330,10 @@ <translation>Klicken zum Aktivieren der Netzwerkaktivität.</translation> </message> <message> + <source>Syncing Headers (%1%)...</source> + <translation>Kopfdaten werden synchronisiert (%1%)...</translation> + </message> + <message> <source>Reindexing blocks on disk...</source> <translation>Reindiziere Blöcke auf Datenträger...</translation> </message> @@ -441,10 +445,6 @@ <source>Processing blocks on disk...</source> <translation>Verarbeite Blöcke auf Datenträger...</translation> </message> - <message> - <source>No block source available...</source> - <translation>Keine Blockquelle verfügbar...</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>%n Block des Transaktionsverlaufs verarbeitet.</numerusform><numerusform>%n Blöcke des Transaktionsverlaufs verarbeitet.</numerusform></translation> @@ -486,6 +486,10 @@ <translation>%1 Client</translation> </message> <message> + <source>Connecting to peers...</source> + <translation>Verbinde mit Netzwerk...</translation> + </message> + <message> <source>Catching up...</source> <translation>Hole auf...</translation> </message> @@ -2193,6 +2197,10 @@ <translation>Warnung: Unbekannte Wechselgeld-Adresse</translation> </message> <message> + <source>Confirm custom change address</source> + <translation>Bestätige benutzerdefinierte Wechselgeld-Adresse</translation> + </message> + <message> <source>(no label)</source> <translation>(keine Bezeichnung)</translation> </message> @@ -2590,6 +2598,10 @@ <translation>Gesamte Transaktionsgröße</translation> </message> <message> + <source>Output index</source> + <translation>Ausgabeindex</translation> + </message> + <message> <source>Merchant</source> <translation>Händler</translation> </message> @@ -3002,10 +3014,6 @@ <translation>Prune (Kürzung): Die letzte Syncronisation der Wallet liegt vor gekürzten (gelöschten) Blöcken. Es ist ein -reindex (download der gesamten Blockkette) notwendig.</translation> </message> <message> - <source>Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> - <translation>Speicherplatzanforderung durch kürzen (löschen) alter Blöcke reduzieren. Dieser Modus ist nicht mit -txindex und -rescan kompatibel. Warnung: Die Umkehr dieser Einstellung erfordert das erneute Herunterladen der gesamten Blockkette. (Standard: 0 = deaktiviert das Kürzen von Blöcken, >%u = Zielgröße in MiB, die für Blockdateien verwendet werden darf)</translation> - </message> - <message> <source>Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.</source> <translation>Rescans sind im pruned mode nicht möglich. Ein -reindex ist notwendig, welcher die gesmate Blockkette erneut herunterlädt.</translation> </message> @@ -3102,6 +3110,10 @@ <translation>UPnP verwenden, um eine Portweiterleitung einzurichten (Standard: 1, wenn abgehört wird und -proxy nicht gesetzt ist)</translation> </message> <message> + <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times</source> + <translation>Benutzername und gehashtes Passwort für JSON-RPC Verbindungen. Das Feld <userpw> kommt im Format: <USERNAME>:<SALT>$<HASH>. Ein kanonisches Pythonskript ist in share/rpcuser inbegriffen. Der client benutzt wie gehabt, die rpcuser/rpcpassword Parameter. Diese Option kann mehrere Male spezifiziert werden</translation> + </message> + <message> <source>Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.</source> <translation>Warnung: Das Netzwerk scheint nicht vollständig übereinzustimmen! Einige Miner scheinen Probleme zu haben.</translation> </message> @@ -3662,10 +3674,6 @@ <translation>Separaten SOCKS5-Proxy verwenden, um Gegenstellen über versteckte Tor-Dienste zu erreichen (Standard: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Benutzername und gehashtes Passwort für JSON-RPC Verbindungen. Das Feld <userpw> kommt im Format: <USERNAME>:<SALT>$<HASH>. Ein entsprechendes Pythonskript ist in share/rpcuser inbegriffen. Diese Option kann mehrere Male spezifiziert werden</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>Warnung: Unbekannte Blockversion wird durch Mining erzeugt! Es ist möglich, dass unbekannte Regeln in Kraft sind.</translation> </message> diff --git a/src/qt/locale/bitcoin_el_GR.ts b/src/qt/locale/bitcoin_el_GR.ts index b1caab06b1..0390a378e7 100644 --- a/src/qt/locale/bitcoin_el_GR.ts +++ b/src/qt/locale/bitcoin_el_GR.ts @@ -246,10 +246,6 @@ <translation>&Επιλογές γραμμής εντολών</translation> </message> <message> - <source>No block source available...</source> - <translation>Η πηγή του μπλοκ δεν ειναι διαθέσιμη... </translation> - </message> - <message> <source>%1 behind</source> <translation>%1 πίσω</translation> </message> diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts index 02a6888998..cc834a8b96 100644 --- a/src/qt/locale/bitcoin_en.ts +++ b/src/qt/locale/bitcoin_en.ts @@ -299,17 +299,17 @@ <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+356"/> + <location filename="../bitcoingui.cpp" line="+357"/> <source>Sign &message...</source> <translation>Sign &message...</translation> </message> <message> - <location line="+417"/> + <location line="+427"/> <source>Synchronizing with network...</source> <translation>Synchronizing with network...</translation> </message> <message> - <location line="-495"/> + <location line="-505"/> <source>&Overview</source> <translation>&Overview</translation> </message> @@ -419,12 +419,17 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+53"/> + <location line="+27"/> + <source>Syncing Headers (%1%)...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+37"/> <source>Reindexing blocks on disk...</source> <translation>Reindexing blocks on disk...</translation> </message> <message> - <location line="-497"/> + <location line="-508"/> <source>Send coins to a Bitcoin address</source> <translation>Send coins to a Bitcoin address</translation> </message> @@ -454,12 +459,12 @@ <translation>&Verify message...</translation> </message> <message> - <location line="+504"/> + <location line="+514"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> <message> - <location line="-729"/> + <location line="-739"/> <source>Wallet</source> <translation>Wallet</translation> </message> @@ -552,7 +557,7 @@ </translation> </message> <message> - <location line="+49"/> + <location line="+60"/> <source>Indexing blocks on disk...</source> <translation type="unfinished"></translation> </message> @@ -561,13 +566,8 @@ <source>Processing blocks on disk...</source> <translation type="unfinished"></translation> </message> - <message> - <location line="+11"/> - <source>No block source available...</source> - <translation>No block source available...</translation> - </message> <message numerus="yes"> - <location line="+9"/> + <location line="+19"/> <source>Processed %n block(s) of transaction history.</source> <translation> <numerusform>Processed %n block of transaction history.</numerusform> @@ -610,7 +610,7 @@ <translation>Up to date</translation> </message> <message> - <location line="-428"/> + <location line="-438"/> <source>Show the %1 help message to get a list with possible Bitcoin command-line options</source> <translation type="unfinished"></translation> </message> @@ -620,7 +620,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+255"/> + <location line="+227"/> + <source>Connecting to peers...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+38"/> <source>Catching up...</source> <translation>Catching up...</translation> </message> @@ -778,7 +783,7 @@ <translation type="unfinished">Confirmed</translation> </message> <message> - <location filename="../coincontroldialog.cpp" line="+54"/> + <location filename="../coincontroldialog.cpp" line="+55"/> <source>Copy address</source> <translation type="unfinished"></translation> </message> @@ -1129,7 +1134,7 @@ <message> <location line="+7"/> <location line="+26"/> - <location filename="../modaloverlay.cpp" line="+136"/> + <location filename="../modaloverlay.cpp" line="+138"/> <source>Unknown...</source> <translation type="unfinished"></translation> </message> @@ -2528,7 +2533,7 @@ <name>SendCoinsDialog</name> <message> <location filename="../forms/sendcoinsdialog.ui" line="+14"/> - <location filename="../sendcoinsdialog.cpp" line="+553"/> + <location filename="../sendcoinsdialog.cpp" line="+554"/> <source>Send Coins</source> <translation>Send Coins</translation> </message> @@ -3862,7 +3867,7 @@ <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+304"/> + <location filename="../bitcoinstrings.cpp" line="+318"/> <source>Options:</source> <translation>Options:</translation> </message> @@ -3887,7 +3892,7 @@ <translation>Accept command line and JSON-RPC commands</translation> </message> <message> - <location line="-207"/> + <location line="-221"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect/-noconnect)</source> <translation type="unfinished"></translation> </message> @@ -3902,12 +3907,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+34"/> + <location line="+37"/> <source>If <category> is not supplied or if <category> = 1, output all debugging information.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+32"/> + <location line="+36"/> <source>Prune configured below the minimum of %d MiB. Please use a higher number.</source> <translation type="unfinished"></translation> </message> @@ -3917,17 +3922,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+9"/> - <source>Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+5"/> + <location line="+18"/> <source>Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+129"/> + <location line="+132"/> <source>Error: A fatal internal error occurred, see debug.log for details</source> <translation type="unfinished"></translation> </message> @@ -3952,7 +3952,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="-346"/> + <location line="-360"/> <source>Bitcoin Core</source> <translation type="unfinished">Bitcoin Core</translation> </message> @@ -4002,7 +4002,17 @@ <translation>Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)</translation> </message> <message> - <location line="+29"/> + <location line="+6"/> + <source>Extra transactions to keep in memory for compact block reconstructions (default: %u)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+20"/> + <source>If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: %s, testnet: %s)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+10"/> <source>Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds)</source> <translation type="unfinished"></translation> </message> @@ -4022,7 +4032,17 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+24"/> + <location line="+14"/> + <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> + <message> + <location line="+12"/> + <source>Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+5"/> <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> @@ -4397,7 +4417,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="-344"/> + <location line="-358"/> <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> @@ -4432,7 +4452,7 @@ <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> <message> - <location line="+9"/> + <location line="+12"/> <source>Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)</source> <translation type="unfinished"></translation> </message> @@ -4442,7 +4462,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+3"/> + <location line="+7"/> <source>Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)</source> <translation type="unfinished"></translation> </message> @@ -4457,7 +4477,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+11"/> + <location line="+18"/> <source>Set maximum size of high-priority/low-fee transactions in bytes (default: %d)</source> <translation type="unfinished"></translation> </message> @@ -4662,12 +4682,12 @@ <translation>Password for JSON-RPC connections</translation> </message> <message> - <location line="-228"/> + <location line="-242"/> <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="+156"/> + <location line="+170"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>Allow DNS lookups for -addnode, -seednode and -connect</translation> </message> @@ -4677,7 +4697,7 @@ <translation>Loading addresses...</translation> </message> <message> - <location line="-277"/> + <location line="-291"/> <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> @@ -4697,7 +4717,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+21"/> + <location line="+24"/> <source>Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)</source> <translation type="unfinished"></translation> </message> @@ -4712,7 +4732,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+11"/> + <location line="+15"/> <source>Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)</source> <translation type="unfinished"></translation> </message> @@ -4732,7 +4752,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+19"/> + <location line="+26"/> <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> diff --git a/src/qt/locale/bitcoin_en_GB.ts b/src/qt/locale/bitcoin_en_GB.ts index e2944030c7..8af5db3e64 100644 --- a/src/qt/locale/bitcoin_en_GB.ts +++ b/src/qt/locale/bitcoin_en_GB.ts @@ -333,10 +333,6 @@ <source>Processing blocks on disk...</source> <translation>Processing blocks on disk...</translation> </message> - <message> - <source>No block source available...</source> - <translation>No block source available...</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>Processed %n block of transaction history.</numerusform><numerusform>Processed %n blocks of transaction history.</numerusform></translation> @@ -1874,10 +1870,6 @@ <translation>Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)</translation> </message> <message> - <source>Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> - <translation>Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</translation> - </message> - <message> <source>Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.</source> <translation>Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.</translation> </message> @@ -2514,10 +2506,6 @@ <translation>Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</translation> </message> diff --git a/src/qt/locale/bitcoin_eo.ts b/src/qt/locale/bitcoin_eo.ts index 2337c2bd29..b4ed5e7fd5 100644 --- a/src/qt/locale/bitcoin_eo.ts +++ b/src/qt/locale/bitcoin_eo.ts @@ -242,10 +242,6 @@ <translation>&Komandliniaj agordaĵoj</translation> </message> <message> - <source>No block source available...</source> - <translation>Neniu fonto de blokoj trovebla...</translation> - </message> - <message> <source>%1 behind</source> <translation>mankas %1</translation> </message> diff --git a/src/qt/locale/bitcoin_es.ts b/src/qt/locale/bitcoin_es.ts index 435ea110cf..fc71bf841b 100644 --- a/src/qt/locale/bitcoin_es.ts +++ b/src/qt/locale/bitcoin_es.ts @@ -330,6 +330,10 @@ <translation>Pulsar para volver a habilitar la actividad de red.</translation> </message> <message> + <source>Syncing Headers (%1%)...</source> + <translation>Sincronizando cabeceras (%1%)</translation> + </message> + <message> <source>Reindexing blocks on disk...</source> <translation>Reindexando bloques en disco...</translation> </message> @@ -441,10 +445,6 @@ <source>Processing blocks on disk...</source> <translation>Procesando bloques en disco...</translation> </message> - <message> - <source>No block source available...</source> - <translation>Ninguna fuente de bloques disponible...</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>%n bloque procesado del historial de transacciones.</numerusform><numerusform>%n bloques procesados del historial de transacciones.</numerusform></translation> @@ -1360,7 +1360,15 @@ <source>Node/Service</source> <translation>Nodo/Servicio</translation> </message> - </context> + <message> + <source>NodeId</source> + <translation>ID de nodo</translation> + </message> + <message> + <source>Ping</source> + <translation>Sonido</translation> + </message> +</context> <context> <name>QObject</name> <message> @@ -1399,14 +1407,54 @@ <source>%1 ms</source> <translation>%1 ms</translation> </message> + <message numerus="yes"> + <source>%n second(s)</source> + <translation><numerusform>%n segundo</numerusform><numerusform>%n segundos</numerusform></translation> + </message> + <message numerus="yes"> + <source>%n minute(s)</source> + <translation><numerusform>%n minuto</numerusform><numerusform>%n minutos</numerusform></translation> + </message> + <message numerus="yes"> + <source>%n hour(s)</source> + <translation><numerusform>%n hora</numerusform><numerusform>%n horas</numerusform></translation> + </message> + <message numerus="yes"> + <source>%n day(s)</source> + <translation><numerusform>%n dia</numerusform><numerusform>%n dias</numerusform></translation> + </message> + <message numerus="yes"> + <source>%n week(s)</source> + <translation><numerusform>%n semana</numerusform><numerusform>%n semanas</numerusform></translation> + </message> <message> <source>%1 and %2</source> <translation>%1 y %2</translation> </message> - </context> + <message numerus="yes"> + <source>%n year(s)</source> + <translation><numerusform>%n año</numerusform><numerusform>%n años</numerusform></translation> + </message> + <message> + <source>%1 didn't yet exit safely...</source> + <translation>%1 no se ha cerrado de forma segura todavía...</translation> + </message> +</context> <context> <name>QObject::QObject</name> - </context> + <message> + <source>Error: Specified data directory "%1" does not exist.</source> + <translation>Error: El directorio de datos «%1» especificado no existe.</translation> + </message> + <message> + <source>Error: Cannot parse configuration file: %1. Only use key=value syntax.</source> + <translation>Error: No se puede analizar el archivo de configuración: %1. Utilice únicamente la sintaxis clave=valor.</translation> + </message> + <message> + <source>Error: %1</source> + <translation>Error: %1</translation> + </message> +</context> <context> <name>QRImageWidget</name> <message> @@ -1585,6 +1633,10 @@ <translation>Espera de Ping</translation> </message> <message> + <source>Min Ping</source> + <translation>Sonido Mínimo</translation> + </message> + <message> <source>Time Offset</source> <translation>Desplazamiento de tiempo</translation> </message> @@ -1645,6 +1697,18 @@ <translation>1 &año</translation> </message> <message> + <source>&Disconnect</source> + <translation>&Desconectar</translation> + </message> + <message> + <source>Ban for</source> + <translation>Prohibir para</translation> + </message> + <message> + <source>&Unban</source> + <translation>&Unbano</translation> + </message> + <message> <source>Welcome to the %1 RPC console.</source> <translation>Bienvenido a la consola RPC %1.</translation> </message> @@ -1657,6 +1721,14 @@ <translation>Escriba <b>help</b> para ver un resumen de los comandos disponibles.</translation> </message> <message> + <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> + <translation>ADVERTENCIA: Los estafadores han sido activados, diciéndoles a los usuarios que escriban comandos aquí, robando el contenido de sus monederos. No utilice esta consola sin entender completamente la repercusión de un comando.</translation> + </message> + <message> + <source>Network activity disabled</source> + <translation>Actividad de red deshabilitada</translation> + </message> + <message> <source>%1 B</source> <translation>%1 B</translation> </message> @@ -1776,6 +1848,10 @@ <translation>Eliminar</translation> </message> <message> + <source>Copy URI</source> + <translation>Copiar URI</translation> + </message> + <message> <source>Copy label</source> <translation>Copiar capa</translation> </message> @@ -2001,6 +2077,10 @@ <translation>Polvo:</translation> </message> <message> + <source>Confirmation time target:</source> + <translation>Objetivo de tiempo de confirmación</translation> + </message> + <message> <source>Clear &All</source> <translation>Vaciar &todo</translation> </message> @@ -2093,6 +2173,10 @@ <translation>¡Falló la creación de transacción!</translation> </message> <message> + <source>The transaction was rejected with the following reason: %1</source> + <translation>Se ha rechazado la transacción por la siguiente razón: %1</translation> + </message> + <message> <source>A fee higher than %1 is considered an absurdly high fee.</source> <translation>Una comisión mayor que %1 se considera una cuota irracionalmente alta.</translation> </message> @@ -2100,10 +2184,18 @@ <source>Payment request expired.</source> <translation>Solicitud de pago caducada.</translation> </message> + <message numerus="yes"> + <source>%n block(s)</source> + <translation><numerusform>%n bloque</numerusform><numerusform>%n bloques</numerusform></translation> + </message> <message> <source>Pay only the required fee of %1</source> <translation>Pagar únicamente la comisión solicitada de %1</translation> </message> + <message numerus="yes"> + <source>Estimated to begin confirmation within %n block(s).</source> + <translation><numerusform>Estimado para empezar la confirmación dentro de %n bloque.</numerusform><numerusform>Estimado para empezar la confirmación dentro de %n bloques.</numerusform></translation> + </message> <message> <source>Warning: Invalid Bitcoin address</source> <translation>Alerta: dirección Bitcoin inválida</translation> @@ -2113,6 +2205,14 @@ <translation>Alerta: dirección cambiada desconocida</translation> </message> <message> + <source>Confirm custom change address</source> + <translation>Confirmar dirección de cambio personalizada</translation> + </message> + <message> + <source>The address you selected for change is not part of this wallet. Any or all funds in your wallet may be sent to this address. Are you sure?</source> + <translation>La dirección que ha seleccionado para cambiar no es parte de este monedero. ninguno o todos los fondos de su monedero pueden ser enviados a esta dirección. ¿Está seguro?</translation> + </message> + <message> <source>(no label)</source> <translation>(sin etiqueta)</translation> </message> @@ -2900,10 +3000,6 @@ <translation>Poda: la ultima sincronizacion del monedero sobrepasa los datos podados. Necesitas reindexar con -reindex (o descargar la cadena de bloques de nuevo en el caso de un nodo podado)</translation> </message> <message> - <source>Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> - <translation>Reduce los requisitos de almacenaje podando (eliminando) los bloques viejos. Este modo es incompatible con -txindex y -rescan. Advertencia: Revertir este ajuste requiere volver a descargar la cadena de bloques al completo. (predeterminado: 0 = deshabilitar la poda de bloques, >%u = objetivo de tamaño en MiB para usar para los archivos de bloques)</translation> - </message> - <message> <source>Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.</source> <translation>Nos es posible re-escanear en modo podado.Necesitas utilizar -reindex el cual descargara la cadena de bloques al completo de nuevo.</translation> </message> @@ -3523,6 +3619,11 @@ <translation>Mostrar depuración (por defecto: %u, proporcionar <category> es opcional)</translation> </message> <message> + <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>https://www.transifex.com/joyful-world/breaking-english/ +Establecer la serialización de las transacciones sin procesar o el bloque hex devuelto en non-verbose mode, non-segwit(O) o segwit(1) (default: %d)</translation> + </message> + <message> <source>Support filtering of blocks and transaction with bloom filters (default: %u)</source> <translation>Admite filtrado de bloques, y transacciones con filtros Bloom. Reduce la carga de red. ( por defecto :%u)</translation> </message> @@ -3547,10 +3648,6 @@ <translation>Usar distintos proxys SOCKS5 para comunicarse vía Tor de forma anónima (Por defecto: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Nombre de usuario y hash de la contraseña para las conexiones JSON-RPC. El campo <userpw> tiene el formato: <USERNAME>:<SALT>$<HASH>. Se incluye un script python convencional en share/rpcuser. Esta opción puede ser especificada multiples veces</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>Advertencia: Se están minando versiones de bloques desconocidas! Es posible que normas desconocidas estén activas</translation> </message> diff --git a/src/qt/locale/bitcoin_es_DO.ts b/src/qt/locale/bitcoin_es_DO.ts index eb6583083b..77e6ef16f3 100644 --- a/src/qt/locale/bitcoin_es_DO.ts +++ b/src/qt/locale/bitcoin_es_DO.ts @@ -238,10 +238,6 @@ <translation>&Opciones de linea de comando</translation> </message> <message> - <source>No block source available...</source> - <translation>Ninguna fuente de bloques disponible ...</translation> - </message> - <message> <source>%1 behind</source> <translation>%1 atrás</translation> </message> diff --git a/src/qt/locale/bitcoin_es_ES.ts b/src/qt/locale/bitcoin_es_ES.ts index 5ac57a5c25..7865483183 100644 --- a/src/qt/locale/bitcoin_es_ES.ts +++ b/src/qt/locale/bitcoin_es_ES.ts @@ -429,10 +429,6 @@ <source>Processing blocks on disk...</source> <translation>Procesando bloques en disco...</translation> </message> - <message> - <source>No block source available...</source> - <translation>Ninguna fuente de bloques disponible ...</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>%n bloque procesado del historial de transacciones.</numerusform><numerusform>%n bloques procesados del historial de transacciones.</numerusform></translation> @@ -2840,10 +2836,6 @@ <translation>Poda: la ultima sincronizacion de la cartera sobrepasa los datos podados. Necesitas reindexar con -reindex (o descargar la cadena de bloques de nuevo en el caso de un nodo podado)</translation> </message> <message> - <source>Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> - <translation>Reduce los requisitos de almacenaje podando (eliminando) los bloques viejos. Este modo es incompatible con -txindex y -rescan. Advertencia: Revertir este ajuste requiere volver a descargar la cadena de bloques al completo. (predeterminado: 0 = deshabilitar la poda de bloques, >%u = objetivo de tamaño en MiB para usar para los archivos de bloques)</translation> - </message> - <message> <source>Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.</source> <translation>Nos es posible re-escanear en modo podado.Necesitas utilizar -reindex el cual descargara la cadena de bloques al completo de nuevo.</translation> </message> @@ -3483,10 +3475,6 @@ <translation>Usar distintos proxys SOCKS5 para comunicarse vía Tor de forma anónima (Por defecto: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Nombre de usuario y hash de la contraseña para las conexiones JSON-RPC. El campo <userpw> tiene el formato: <USERNAME>:<SALT>$<HASH>. Se incluye un script python convencional en share/rpcuser. Esta opción puede ser especificada multiples veces</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>Advertencia: Se están minando versiones de bloques desconocidas! Es posible que normas desconocidas estén activas</translation> </message> diff --git a/src/qt/locale/bitcoin_et.ts b/src/qt/locale/bitcoin_et.ts index 0512b9f117..f95b01d33b 100644 --- a/src/qt/locale/bitcoin_et.ts +++ b/src/qt/locale/bitcoin_et.ts @@ -385,10 +385,6 @@ <source>Processing blocks on disk...</source> <translation>Kõvakettal olevate plokkide töötlemine...</translation> </message> - <message> - <source>No block source available...</source> - <translation>Plokkide allikas pole saadaval...</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>Töödeldud %n plokk transaktsioonide ajaloost.</numerusform><numerusform>Töödeldud %n plokki transaktsioonide ajaloost.</numerusform></translation> diff --git a/src/qt/locale/bitcoin_fa.ts b/src/qt/locale/bitcoin_fa.ts index c1061822ca..c9cfad0f2a 100644 --- a/src/qt/locale/bitcoin_fa.ts +++ b/src/qt/locale/bitcoin_fa.ts @@ -357,10 +357,6 @@ <source>Processing blocks on disk...</source> <translation>پردازش بلوکها روی دیسک...</translation> </message> - <message> - <source>No block source available...</source> - <translation>منبعی برای دریافت بلاک در دسترس نیست...</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>پردازش %n بلاک از تاریخچه ی تراکنش ها </numerusform></translation> diff --git a/src/qt/locale/bitcoin_fi.ts b/src/qt/locale/bitcoin_fi.ts index db643e73d2..ef76abc098 100644 --- a/src/qt/locale/bitcoin_fi.ts +++ b/src/qt/locale/bitcoin_fi.ts @@ -273,10 +273,6 @@ <source>Processing blocks on disk...</source> <translation>Käsitellään lohkoja levyllä...</translation> </message> - <message> - <source>No block source available...</source> - <translation>Lohkojen lähdettä ei saatavilla...</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>Prosessoitu %n lohko rahansiirtohistoriasta.</numerusform><numerusform>Prosessoitu %n lohkoa rahansiirtohistoriasta.</numerusform></translation> @@ -1758,10 +1754,6 @@ <translation>Karsinta: viime lompakon synkronisointi menee karsitun datan taakse. Sinun tarvitsee ajaa -reindex (lataa koko lohkoketju uudelleen tapauksessa jossa karsiva noodi)</translation> </message> <message> - <source>Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> - <translation>Vähennä levytilan tarvetta karsimalla (poistamalla) vanhoja lohkoja. Tämä tila ei ole yhteensopiva -txindex ja -rescan -parametrien kanssa. Varoitus: Tämän asetuksen peruutus vaatii koko lohkoketjun uudelleenlataamisen. (oletus: 0 = poista karsinta käytöstä, >%u = kohdekoko muodossa MiB jota käytetään lohkotiedostoille) </translation> - </message> - <message> <source>Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.</source> <translation>Uudelleenskannaukset eivät ole mahdollisia karsivassa tilassa. Sinun täytyy käyttää -reindex joka lataa koko lohkoketjun uudelleen.</translation> </message> diff --git a/src/qt/locale/bitcoin_fr.ts b/src/qt/locale/bitcoin_fr.ts index bdcfed9584..92682ff297 100644 --- a/src/qt/locale/bitcoin_fr.ts +++ b/src/qt/locale/bitcoin_fr.ts @@ -441,10 +441,6 @@ <source>Processing blocks on disk...</source> <translation>Traitement des blocs sur le disque...</translation> </message> - <message> - <source>No block source available...</source> - <translation>Aucune source de blocs disponible...</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>%n bloc d'historique transactionnel a été traité</numerusform><numerusform>%n blocs d'historique transactionnel ont été traités</numerusform></translation> @@ -486,6 +482,10 @@ <translation>Client %1</translation> </message> <message> + <source>Connecting to peers...</source> + <translation>Connexion aux pairs...</translation> + </message> + <message> <source>Catching up...</source> <translation>Rattrapage…</translation> </message> @@ -1998,11 +1998,11 @@ </message> <message> <source>If this is activated, but the change address is empty or invalid, change will be sent to a newly generated address.</source> - <translation>Si cette option est activée, et l'adresse de monnaie rendue est vide ou invalide, la monnaie sera envoyée vers une adresse nouvellement générée.</translation> + <translation>Si cette option est activée et l'adresse de monnaie est vide ou invalide, la monnaie sera envoyée vers une adresse nouvellement générée.</translation> </message> <message> <source>Custom change address</source> - <translation>Adresse personnalisée de monnaie rendue</translation> + <translation>Adresse personnalisée de monnaie</translation> </message> <message> <source>Transaction Fee:</source> @@ -2202,7 +2202,15 @@ </message> <message> <source>Warning: Unknown change address</source> - <translation>Avertissement : adresse de monnaie rendue inconnue</translation> + <translation>Avertissement : adresse de monnaie inconnue</translation> + </message> + <message> + <source>Confirm custom change address</source> + <translation>Confimer l'adresse personnalisée de monnaie</translation> + </message> + <message> + <source>The address you selected for change is not part of this wallet. Any or all funds in your wallet may be sent to this address. Are you sure?</source> + <translation>L'adresse que vous avez sélectionnée pour la monnaie ne fait pas partie de ce porte-monnaie. Les fonds de ce porte-monnaie peuvent en partie ou en totalité être envoyés vers cette adresse. Êtes-vous certain ?</translation> </message> <message> <source>(no label)</source> @@ -2307,7 +2315,7 @@ </message> <message> <source>Do not shut down the computer until this window disappears.</source> - <translation>Ne pas fermer l'ordinateur jusqu'à la disparition de cette fenêtre.</translation> + <translation>Ne pas éteindre l'ordinateur jusqu'à la disparition de cette fenêtre.</translation> </message> </context> <context> @@ -3022,10 +3030,6 @@ <translation>Élagage : la dernière synchronisation de porte-monnaie va par-delà les données élaguées. Vous devez -reindex (réindexer, télécharger de nouveau toute la chaîne de blocs en cas de nœud élagué)</translation> </message> <message> - <source>Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> - <translation>Réduire les exigences de stockage en élaguant (supprimant) les anciens blocs. Ce mode est incompatible avec -txindex et -rescan. Avertissement : ramener ce paramètre à sa valeur antérieure exige un nouveau téléchargement de la chaîne de blocs en entier (par défaut : 0 = désactiver l'élagage des blocs, >%u = taille cible en Mio à utiliser pour les fichiers de blocs).</translation> - </message> - <message> <source>Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.</source> <translation>Les rebalayages sont impossibles en mode élagage. Vous devrez utiliser -reindex, ce qui téléchargera de nouveau la chaîne de blocs en entier.</translation> </message> @@ -3090,6 +3094,10 @@ <translation>Exécuter la commande lorsqu'une transaction de porte-monnaie change (%s dans la commande est remplacée par TxID)</translation> </message> <message> + <source>If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: %s, testnet: %s)</source> + <translation>Si ce bloc est dans la chaîne, supposer qu'il est valide, ainsi que ces ancêtres, et ignorer potentiellement la vérification de leur script (0 pour tout vérifier, valeur par défaut : %s, réseau de test : %s)</translation> + </message> + <message> <source>Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds)</source> <translation>Réglage moyen maximal autorisé de décalage de l'heure d'un pair. La perspective locale du temps peut être influencée par les pairs, en avance ou en retard, de cette valeur. (Par défaut : %u secondes)</translation> </message> @@ -3106,6 +3114,14 @@ <translation>Si vous trouvez %s utile, vous pouvez y contribuer. Vous trouverez davantage d'informations à propos du logiciel sur %s.</translation> </message> <message> + <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>Réduire les exigences de stockage en activant l'élagage (suppression) des anciens blocs. Cela permet d'appeler le RPC « pruneblockchain » pour supprimer des blocs précis et active l'élagage automatique des anciens blocs si une taille cible en Mio est fournie. Ce mode n'est pas compatible avec -txindex et -rescan. Avertissement : ramener ce paramètre à sa valeur antérieure exige de retélécharger l'intégralité de la chaîne de blocs (par défaut : 0 = désactiver l'élagage des blocs, 1 = permettre l'élagage manuel par RPC, >%u = élaguer automatiquement les fichiers de blocs pour rester en deçà de la taille cible précisée en Mio).</translation> + </message> + <message> + <source>Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s)</source> + <translation>Définir le taux minimal de frais (en %s/ko) pour les transactions à inclure dans la création de blocs (par défaut : %s)</translation> + </message> + <message> <source>Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)</source> <translation>Définir le nombre de fils de vérification des scripts (%u à %d, 0 = auto, < 0 = laisser ce nombre de cœurs inutilisés, par défaut : %d)</translation> </message> @@ -3126,6 +3142,10 @@ <translation>Utiliser l'UPnP pour mapper le port d'écoute (par défaut : 1 en écoute et sans -proxy)</translation> </message> <message> + <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times</source> + <translation>Nom d'utilisateur et mot de passe haché pour les connexions JSON-RPC. Le champ <userpw> est au format : <USERNAME>:<SALT>$<HASH>. Un script python canonique est inclus dans share/rpcuser. Le client se connecte ensuite normalement en utilisant la paire d'arguments rpcuser=<USERNAME>/rpcpassword=<PASSWORD>. Cette option peut être spécifiée plusieurs fois.</translation> + </message> + <message> <source>Wallet will not create transactions that violate mempool chain limits (default: %u)</source> <translation>Un porte-monnaie ne créera aucune transaction qui enfreint les limites de chaîne de la réserve de mémoire (par défaut : %u)</translation> </message> @@ -3706,10 +3726,6 @@ <translation>Utiliser un serveur mandataire SOCKS5 séparé pour atteindre les pairs par les services cachés de Tor (par défaut : %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Nom d'utilisateur et mot de passe haché pour les connexions JSON-RPC. Le champ <userpw> vient au format : <USERNAME>:<SALT>$<HASH>. Un script python canonique est inclus dans share/rpcuser. Cette option peut être spécifiée plusieurs fois.</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>Avertissement : des versions de blocs inconnues sont minées ! Il est possible que des règles inconnues soient en vigeur</translation> </message> diff --git a/src/qt/locale/bitcoin_fr_FR.ts b/src/qt/locale/bitcoin_fr_FR.ts index d53baed462..4d02aa5114 100644 --- a/src/qt/locale/bitcoin_fr_FR.ts +++ b/src/qt/locale/bitcoin_fr_FR.ts @@ -262,10 +262,6 @@ <translation>Indexation des blocs sur le disque...</translation> </message> <message> - <source>No block source available...</source> - <translation>Aucun bloc source disponible</translation> - </message> - <message> <source>%1 behind</source> <translation>en retard de %1</translation> </message> diff --git a/src/qt/locale/bitcoin_gl.ts b/src/qt/locale/bitcoin_gl.ts index 5d31c632ac..ff0804d79b 100644 --- a/src/qt/locale/bitcoin_gl.ts +++ b/src/qt/locale/bitcoin_gl.ts @@ -226,10 +226,6 @@ <translation>Opcións da liña de comandos</translation> </message> <message> - <source>No block source available...</source> - <translation>Non hai orixe de bloques dispoñible...</translation> - </message> - <message> <source>%1 behind</source> <translation>%1 detrás</translation> </message> diff --git a/src/qt/locale/bitcoin_he.ts b/src/qt/locale/bitcoin_he.ts index 9293b794ad..78ef446ff1 100644 --- a/src/qt/locale/bitcoin_he.ts +++ b/src/qt/locale/bitcoin_he.ts @@ -318,10 +318,6 @@ <translation>מעבד בלוקים על הדיסק...</translation> </message> <message> - <source>No block source available...</source> - <translation>אין מקור מקטעים זמין…</translation> - </message> - <message> <source>%1 behind</source> <translation>%1 מאחור</translation> </message> diff --git a/src/qt/locale/bitcoin_hu.ts b/src/qt/locale/bitcoin_hu.ts index 49b9dc7d1c..28270e8c2e 100644 --- a/src/qt/locale/bitcoin_hu.ts +++ b/src/qt/locale/bitcoin_hu.ts @@ -257,10 +257,6 @@ <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>%n aktív kapcsolat a Bitcoin hálózathoz</numerusform><numerusform>%n aktív kapcsolat a Bitcoin hálózathoz</numerusform></translation> </message> - <message> - <source>No block source available...</source> - <translation>Blokk forrása ismeretlen...</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>%n blokk feldolgozva a tranzakció előzményből.</numerusform><numerusform>%n blokk feldolgozva a tranzakció előzményből.</numerusform></translation> diff --git a/src/qt/locale/bitcoin_id_ID.ts b/src/qt/locale/bitcoin_id_ID.ts index 5cc5d71c6d..fd77f07cd8 100644 --- a/src/qt/locale/bitcoin_id_ID.ts +++ b/src/qt/locale/bitcoin_id_ID.ts @@ -253,10 +253,6 @@ <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>%n koneksi aktif ke jaringan Bitcoin</numerusform></translation> </message> - <message> - <source>No block source available...</source> - <translation>Sumber blok tidak tersedia...</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>%n blok dari riwayat transaksi diproses.</numerusform></translation> diff --git a/src/qt/locale/bitcoin_it.ts b/src/qt/locale/bitcoin_it.ts index 265120b19f..484e7f86bd 100644 --- a/src/qt/locale/bitcoin_it.ts +++ b/src/qt/locale/bitcoin_it.ts @@ -401,10 +401,6 @@ <source>Processing blocks on disk...</source> <translation>Processando i blocchi su disco...</translation> </message> - <message> - <source>No block source available...</source> - <translation>Nessuna fonte di blocchi disponibile...</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>Elaborato %n blocco dello storico transazioni.</numerusform><numerusform>Elaborati %n blocchi dello storico transazioni.</numerusform></translation> @@ -1943,10 +1939,6 @@ Per specificare più URL separarli con una barra verticale "|".</translation> <translation>Prune: l'ultima sincronizzazione del wallet risulta essere oltre la riduzione dei dati. È necessario eseguire un -reindex (scaricare nuovamente la blockchain in caso di nodo pruned)</translation> </message> <message> - <source>Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> - <translation>Riduce i requisiti di spazio di archiviazione attraverso la rimozione dei vecchi blocchi (pruning). Questa modalità è incompatibile con l'opzione -txindex e -rescan. Attenzione: ripristinando questa opzione l'intera blockchain dovrà essere riscaricata. (default: 0 = disabilita il pruning, >%u = dimensione desiderata in MiB per i file dei blocchi)</translation> - </message> - <message> <source>Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.</source> <translation>Non è possibile un Rescan in modalità pruned. Sarà necessario utilizzare -reindex che farà scaricare nuovamente tutta la blockchain.</translation> </message> @@ -2583,10 +2575,6 @@ Per specificare più URL separarli con una barra verticale "|".</translation> <translation>Usa un proxy SOCKS5 a parte per raggiungere i peer attraverso gli hidden services di Tor (predefinito: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Username e hash password per connessioni JSON-RPC. Il campo <userpw> utilizza il formato: <USERNAME>:<SALT>$<HASH>. Uno script python standard è incluso in share/rpcuser. Questa opzione può essere specificata più volte</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>Attenzione: si stanno minando versioni sconocsiute di blocchi! E' possibile che siano attive regole sconosciute</translation> </message> diff --git a/src/qt/locale/bitcoin_ja.ts b/src/qt/locale/bitcoin_ja.ts index 548fa26cc1..f81818896f 100644 --- a/src/qt/locale/bitcoin_ja.ts +++ b/src/qt/locale/bitcoin_ja.ts @@ -330,6 +330,10 @@ <translation>クリックするとネットワーク活動を再び有効化します。</translation> </message> <message> + <source>Syncing Headers (%1%)...</source> + <translation>未知。ヘッダを同期しています (%1%)...</translation> + </message> + <message> <source>Reindexing blocks on disk...</source> <translation>ディスク上のブロックのインデックスを再作成中...</translation> </message> @@ -441,10 +445,6 @@ <source>Processing blocks on disk...</source> <translation>ディスク上のブロックを処理しています...</translation> </message> - <message> - <source>No block source available...</source> - <translation>利用可能なブロックがありません...</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>トランザクション履歴の %n ブロックを処理しました。</numerusform></translation> @@ -486,6 +486,10 @@ <translation>%1 クライアント</translation> </message> <message> + <source>Connecting to peers...</source> + <translation>ピアに接続しています...</translation> + </message> + <message> <source>Catching up...</source> <translation>追跡中...</translation> </message> @@ -2205,6 +2209,14 @@ <translation>警告:未知のおつりアドレスです</translation> </message> <message> + <source>Confirm custom change address</source> + <translation>カスタムおつりアドレスを確認</translation> + </message> + <message> + <source>The address you selected for change is not part of this wallet. Any or all funds in your wallet may be sent to this address. Are you sure?</source> + <translation>おつりとして指定されたアドレスはこのウォレットに属さないもののようです。このウォレットの一部またはすべての資産がこのアドレスへ送金されます。よろしいですか?</translation> + </message> + <message> <source>(no label)</source> <translation>(ラベル無し)</translation> </message> @@ -3022,10 +3034,6 @@ <translation>剪定: 最後のウォレット同期ポイントは、選定されたデータよりも過去のものとなっています。-reindexをする必要があります (剪定されたノードの場合、ブロックチェイン全体をダウンロードします)</translation> </message> <message> - <source>Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> - <translation>古いブロックを剪定する (削除する) ことで記憶容量の必要量を削減する。このモードを有効にすると-txindexや-rescanと互換性がなくなります。警告: この設定の再有効化には全ブロックチェインの再ダウンロードが必要となります。(規定値: 0 = ブロックの剪定無効、>%u = ブロックファイルに使用するMiB単位の目標サイズ)</translation> - </message> - <message> <source>Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.</source> <translation>剪定モードでは再スキャンを行うことはできません。-reindexを指定し、ブロックチェイン全体を再ダウンロードする必要があります。</translation> </message> @@ -3090,6 +3098,14 @@ <translation>ウォレットの取引を変更する際にコマンドを実行 (cmd の %s は TxID に置換される)</translation> </message> <message> + <source>Extra transactions to keep in memory for compact block reconstructions (default: %u)</source> + <translation>コンパクトブロック再構成のために追加のトランザクションをメモリ内に保管しておく (デフォルト: %u)</translation> + </message> + <message> + <source>If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: %s, testnet: %s)</source> + <translation>このブロックがブロックチェーン内に含まれていた場合には、このブロックおよびそれ以前のすべてのブロックを有効であるとみなし、スクリプトの検証を省略する (0ならすべてを検証、デフォルト: %s、テストネット: %s)</translation> + </message> + <message> <source>Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds)</source> <translation>時間オフセット調整値のピア中央値に対する最大の許容値。ローカル時間の見込み値は、接続するピアにより前方ないし後方へ影響されます。(初期値: %u 秒)</translation> </message> @@ -3106,6 +3122,14 @@ <translation>%s が有用だと感じられた方はぜひプロジェクトへの貢献をお願いします。ソフトウェアのより詳細な情報については %s をご覧ください。</translation> </message> <message> + <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>古いブロックの剪定 (削除) を有効にすることでストレージの必要量を削減する。これにより pruneblockchain RPC を呼び出すことで指定されたブロックを削除することができます。またターゲットサイズが MiB 単位で指定された場合には古いブロックの自動剪定が有効となります。このモードは -txindex および -rescan オプションと互換性がありません。警告: この設定を最有効化するにはすべてのブロックチェーンの再ダウンロードが必要となります。(デフォルト: 0 = ブロックの剪定を無効化する, 1 = RPC 経由での手動剪定を許可する, >%u = MiB 単位で指定されたターゲットサイズを常に下回るようにブロックファイルを自動的に剪定する)</translation> + </message> + <message> + <source>Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s)</source> + <translation>ブロック生成時に取り込まれるトランザクションの最低手数料率 (%s/kB 単位)。(デフォルト: %s)</translation> + </message> + <message> <source>Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)</source> <translation>スクリプト検証スレッドを設定 (%uから%dの間, 0 = 自動, <0 = たくさんのコアを自由にしておく, 初期値: %d)</translation> </message> @@ -3126,6 +3150,10 @@ <translation>リスン ポートの割当に UPnP を使用 (初期値: リスン中および-proxyが指定されていない場合は1)</translation> </message> <message> + <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times</source> + <translation>JSON-RPC 接続時のユーザ名とハッシュ化されたパスワード。<userpw> フィールドのフォーマットは <USERNAME>:<SALT>$<HASH>。標準的な Python スクリプトが share/rpcuser 内に含まれています。クライアントは通常の場合には rpcuser=<USERNAME>/rpcpassword=<PASSWORD> を利用して接続を行います。このオプションは複数回指定できます。</translation> + </message> + <message> <source>Wallet will not create transactions that violate mempool chain limits (default: %u)</source> <translation>ウォレットがmempoolチェーン制限数を超えてトランザクションを作らないようにする (初期値: %u)</translation> </message> @@ -3707,10 +3735,6 @@ <translation>Tor 秘匿サービスを通し、別々の SOCKS5 プロキシを用いることでピアに到達する (初期値: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>JSON-RPC接続時のユーザ名とハッシュ化されたパスワード。<userpw> フィールドのフォーマットは <USERNAME>:<SALT>$<HASH>。標準的な Python スクリプトが share/rpcuser 内に含まれています。このオプションは複数回指定できます。</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>警告: 未知のバージョンのブロックが採掘されました。未知のルールが導入された可能性があります</translation> </message> diff --git a/src/qt/locale/bitcoin_ka.ts b/src/qt/locale/bitcoin_ka.ts index 920f44758a..14378ebea1 100644 --- a/src/qt/locale/bitcoin_ka.ts +++ b/src/qt/locale/bitcoin_ka.ts @@ -238,10 +238,6 @@ <translation>საკომანდო სტრიქონის ოპ&ციები</translation> </message> <message> - <source>No block source available...</source> - <translation>ბლოკების წყარო მიუწვდომელია...</translation> - </message> - <message> <source>%1 behind</source> <translation>%1 გავლილია</translation> </message> diff --git a/src/qt/locale/bitcoin_ko_KR.ts b/src/qt/locale/bitcoin_ko_KR.ts index 4198b7ea56..9b5c1c077e 100644 --- a/src/qt/locale/bitcoin_ko_KR.ts +++ b/src/qt/locale/bitcoin_ko_KR.ts @@ -429,10 +429,6 @@ <source>Processing blocks on disk...</source> <translation>디스크에서 블록 처리중...</translation> </message> - <message> - <source>No block source available...</source> - <translation>사용 가능한 블록이 없습니다...</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>%n 블럭 만큼의 거래 기록이 처리됨.</numerusform></translation> @@ -2278,10 +2274,6 @@ <translation>블록 축소: 마지막 지갑 동기화 지점이 축소된 데이터보다 과거의 것 입니다. -reindex가 필요합니다 (정지된 노드의 경우 모든 블록체인을 재다운로드합니다)</translation> </message> <message> - <source>Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> - <translation>오래된 블록을 제거(축소)하여 디스크 용량을 줄입니다. 이 모드는 -txindex 와 -rescan 과 호환되지 않습니다. 경고: 이 모드를 취소하면 모든 블록체인을 다시 다운로드 받아야 합니다. (기본값:0 = 블록 축소 비활성화, >%u = 블록파일에 사용할 용량을 MiB단위로 지정)</translation> - </message> - <message> <source>Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.</source> <translation>블록 축소 모드에서는 재검색이 불가능 합니다. -reindex 명령을 사용해서 모든 블록체인을 다시 다운로드 해야 합니다.</translation> </message> @@ -2898,10 +2890,6 @@ <translation>Tor 서비스를 이용하여 피어에게 연결하기 위해 분리된 SOCKS5 프록시를 사용 (기본값: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>JSON-RPC 연결시 사용자 이름과 해시화된 암호문. <userpw> 필드는 <USERNAME>:<SALT>$<HASH> 포멧으로 구성되어 있습니다. 전형적 파이썬 스크립트에선 share/rpcuser가 포함되어 있습니다. 이 옵션은 여러번 지정할 수 있습니다.</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>경고: 알려지지 않은 버전의 블록이 채굴되었습니다. 알려지지 않은 규칙이 적용되었을 가능성이 있습니다.</translation> </message> diff --git a/src/qt/locale/bitcoin_la.ts b/src/qt/locale/bitcoin_la.ts index 3b67fa1e74..d84dd7e4e2 100644 --- a/src/qt/locale/bitcoin_la.ts +++ b/src/qt/locale/bitcoin_la.ts @@ -194,10 +194,6 @@ <translation>Optiones mandati initiantis</translation> </message> <message> - <source>No block source available...</source> - <translation>Nulla fons frustorum absens...</translation> - </message> - <message> <source>%1 behind</source> <translation>%1 post</translation> </message> diff --git a/src/qt/locale/bitcoin_lv_LV.ts b/src/qt/locale/bitcoin_lv_LV.ts index 750e0ff28d..2953da443a 100644 --- a/src/qt/locale/bitcoin_lv_LV.ts +++ b/src/qt/locale/bitcoin_lv_LV.ts @@ -230,10 +230,6 @@ <translation>&Komandrindas iespējas</translation> </message> <message> - <source>No block source available...</source> - <translation>Nav pieejams neviens bloku avots...</translation> - </message> - <message> <source>%1 behind</source> <translation>%1 aizmugurē</translation> </message> diff --git a/src/qt/locale/bitcoin_nb.ts b/src/qt/locale/bitcoin_nb.ts index 9749db2b1f..183cbac80a 100644 --- a/src/qt/locale/bitcoin_nb.ts +++ b/src/qt/locale/bitcoin_nb.ts @@ -265,10 +265,6 @@ <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>%n aktiv forbindelse til Bitcoin-nettverket</numerusform><numerusform>%n aktive forbindelser til Bitcoin-nettverket</numerusform></translation> </message> - <message> - <source>No block source available...</source> - <translation>Ingen kilde for blokker tilgjengelig...</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>Lastet %n blokk med transaksjonshistorikk.</numerusform><numerusform>Lastet %n blokker med transaksjonshistorikk.</numerusform></translation> @@ -1710,10 +1706,6 @@ <translation>Beskjæring: siste lommeboksynkronisering går utenfor beskjærte data. Du må bruke -reindex (laster ned hele blokkjeden igjen for beskjærte noder)</translation> </message> <message> - <source>Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> - <translation>Reduser lagringsbehovet ved beskjæring (sletting) av gamle blokker. Denne modusen er ikke kompatibel med -txindex og -rescan. Advarsel: Tilbakestilling av denne innstillingen krever at hele blokkjeden må lastes ned på nytt. (Standardverdi: 0 = deaktiver beskjæring av blokker, >%u = mål for størrelse i MiB å bruke for blokkfiler)</translation> - </message> - <message> <source>Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.</source> <translation>Omsøk er ikke mulig i beskjært modus. Du vil måtte bruke -reindex som vil laste nede hele blokkjeden på nytt.</translation> </message> @@ -2202,10 +2194,6 @@ <translation>Bruk separate SOCKS5 proxyer for å nå noder via Tor skjulte tjenester (standardverdi: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Brukernavn og hashet passord for JSON-RPC tilkoblinger. Feltet <userpw> kommer i formatet: <USERNAME>:<SALT>$<HASH>. Et Python-skript er inkludert i share/rpcuser. Dette alternativet kan angis flere ganger</translation> - </message> - <message> <source>(default: %s)</source> <translation>(standardverdi: %s)</translation> </message> diff --git a/src/qt/locale/bitcoin_nl.ts b/src/qt/locale/bitcoin_nl.ts index 3f5b903336..2b625b5a07 100644 --- a/src/qt/locale/bitcoin_nl.ts +++ b/src/qt/locale/bitcoin_nl.ts @@ -429,10 +429,6 @@ <source>Processing blocks on disk...</source> <translation>Bezig met verwerken van blokken op harde schijf...</translation> </message> - <message> - <source>No block source available...</source> - <translation>Geen bron voor blokken beschikbaar...</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>%n blok aan transactiegeschiedenis verwerkt.</numerusform><numerusform>%n blokken aan transactiegeschiedenis verwerkt.</numerusform></translation> @@ -2838,10 +2834,6 @@ <translation>Snoei: laatste portemoneesynchronisatie gaat verder dan de gesnoeide data. U moet -reindex gebruiken (download opnieuw de gehele blokketen voor een weggesnoeide node)</translation> </message> <message> - <source>Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> - <translation>Beperk benodigde opslag door snoeien (verwijderen) van oude blokken. Deze modus is niet-compatibele met -txindex en -rescan. Waarschuwing: Terugzetten van deze instellingen vereist opnieuw downloaden van gehele de blokketen. (standaard:0 = uitzetten snoeimodus, >%u = doelgrootte in MiB voor blokbestanden)</translation> - </message> - <message> <source>Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.</source> <translation>Herscannen is niet mogelijk in de snoeimodus. U moet -reindex gebruiken dat de hele blokketen opnieuw zal downloaden.</translation> </message> @@ -3482,10 +3474,6 @@ <translation>Gebruik een aparte SOCKS5 proxy om verborgen diensten van Tor te bereiken (standaard: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Gebruikersnaam en gehasht wachtwoord voor JSON-RPC-verbindingen. De velden <userpw> is in het formaat: <GEBRUIKERSNAAM>:<SALT>$<HASH>. Een kanoniek Pythonscript is inbegrepen in de share/rpcuser. Deze optie kan meerdere keren worden meegegeven</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>Waarschuwing: Onbekende blok versies worden gemined! Er zijn mogelijk onbekende regels in werking getreden</translation> </message> diff --git a/src/qt/locale/bitcoin_pl.ts b/src/qt/locale/bitcoin_pl.ts index b7369c5093..4634814070 100644 --- a/src/qt/locale/bitcoin_pl.ts +++ b/src/qt/locale/bitcoin_pl.ts @@ -168,14 +168,34 @@ <translation>Potwierdź szyfrowanie portfela</translation> </message> <message> + <source>Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR BITCOINS</b>!</source> + <translation>Uwaga: jeśli zaszyfrujesz swój portfel i zgubisz hasło <b>STRACISZ WSZYSTKIE SWOJE BITCOINY</b>!</translation> + </message> + <message> + <source>Are you sure you wish to encrypt your wallet?</source> + <translation>Jesteś pewien, że chcesz zaszyfrować swój portfel?</translation> + </message> + <message> <source>Wallet encrypted</source> <translation>Portfel zaszyfrowany</translation> </message> <message> + <source>%1 will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> + <translation>%1 zamknie się aby dokończyć proces szyfrowania. Pamiętaj, że szyfrowanie portfela nie zabezpiecza w pełni Twoich bitcoinów przed kradzieżą przez wirusy lub trojany mogące zainfekować Twój komputer.</translation> + </message> + <message> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation>WAŻNE: Wszystkie wykonane wcześniej kopie pliku portfela powinny być zamienione na nowe, szyfrowane pliki. Z powodów bezpieczeństwa, poprzednie kopie nieszyfrowanych plików portfela staną się bezużyteczne jak tylko zaczniesz korzystać z nowego, szyfrowanego portfela.</translation> + </message> + <message> <source>Wallet encryption failed</source> <translation>Szyfrowanie portfela nie powiodło się</translation> </message> <message> + <source>Wallet encryption failed due to an internal error. Your wallet was not encrypted.</source> + <translation>Szyfrowanie portfela nie powiodło się z powodu wewnętrznego błędu. Twój portfel nie został zaszyfrowany.</translation> + </message> + <message> <source>The supplied passphrases do not match.</source> <translation>Podane hasła nie są takie same.</translation> </message> @@ -409,10 +429,6 @@ <source>Processing blocks on disk...</source> <translation>Przetwarzanie blocks on disk...</translation> </message> - <message> - <source>No block source available...</source> - <translation>Brak dostępnych źródeł bloków...</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>Przetworzono %n bloków historii transakcji.</numerusform><numerusform>Przetworzono %n bloków historii transakcji.</numerusform><numerusform>Przetworzono %n bloków historii transakcji.</numerusform></translation> @@ -1209,7 +1225,47 @@ <source>Cannot start bitcoin: click-to-pay handler</source> <translation>Nie można uruchomić protokołu bitcoin: kliknij-by-zapłacić</translation> </message> - </context> + <message> + <source>URI handling</source> + <translation>Obsługa URI</translation> + </message> + <message> + <source>Payment request expired.</source> + <translation>Żądanie płatności upłynęło.</translation> + </message> + <message> + <source>Payment request is not initialized.</source> + <translation>Żądanie płatności nie jest zainicjowane.</translation> + </message> + <message> + <source>Unverified payment requests to custom payment scripts are unsupported.</source> + <translation>Niezweryfikowane żądania płatności do własnych skryptów płatności są niewspierane.</translation> + </message> + <message> + <source>Invalid payment request.</source> + <translation>Nieprawidłowe żądanie płatności</translation> + </message> + <message> + <source>Refund from %1</source> + <translation>Zwrot z %1</translation> + </message> + <message> + <source>Payment request cannot be parsed!</source> + <translation>Żądanie płatności nie może zostać przetworzone.</translation> + </message> + <message> + <source>Bad response from server %1</source> + <translation>Błędna odpowiedź z serwera %1</translation> + </message> + <message> + <source>Network request error</source> + <translation>Błąd żądania sieci</translation> + </message> + <message> + <source>Payment acknowledged</source> + <translation>Płatność potwierdzona</translation> + </message> +</context> <context> <name>PeerTableModel</name> <message> @@ -1220,7 +1276,15 @@ <source>Node/Service</source> <translation>Węzeł/Usługi</translation> </message> - </context> + <message> + <source>NodeId</source> + <translation>Identyfikator węzła</translation> + </message> + <message> + <source>Ping</source> + <translation>Ping</translation> + </message> +</context> <context> <name>QObject</name> <message> @@ -1259,6 +1323,14 @@ <source>%1 ms</source> <translation>%1 ms</translation> </message> + <message numerus="yes"> + <source>%n second(s)</source> + <translation><numerusform>%n sekunda</numerusform><numerusform>%n sekund</numerusform><numerusform>%n sekund</numerusform></translation> + </message> + <message numerus="yes"> + <source>%n minute(s)</source> + <translation><numerusform>%n minuta</numerusform><numerusform>%n minut</numerusform><numerusform>%n minut</numerusform></translation> + </message> <message> <source>%1 and %2</source> <translation>%1 i %2</translation> @@ -1266,10 +1338,30 @@ </context> <context> <name>QObject::QObject</name> - </context> + <message> + <source>Error: %1</source> + <translation>Błąd: %1</translation> + </message> +</context> <context> <name>QRImageWidget</name> - </context> + <message> + <source>&Save Image...</source> + <translation>&Zapisz obraz...</translation> + </message> + <message> + <source>&Copy Image</source> + <translation>&Kopiuj obraz</translation> + </message> + <message> + <source>Save QR Code</source> + <translation>Zapisz Kod QR</translation> + </message> + <message> + <source>PNG Image (*.png)</source> + <translation>Obraz PNG (*.png)</translation> + </message> +</context> <context> <name>RPCConsole</name> <message> @@ -1429,6 +1521,10 @@ <translation>Czas odpowiedzi</translation> </message> <message> + <source>Min Ping</source> + <translation>Minimalny czas odpowiedzi</translation> + </message> + <message> <source>Time Offset</source> <translation>Przesunięcie czasu</translation> </message> @@ -1489,6 +1585,18 @@ <translation>1 &rok</translation> </message> <message> + <source>&Disconnect</source> + <translation>&Rozłącz</translation> + </message> + <message> + <source>Ban for</source> + <translation>Zbanuj na</translation> + </message> + <message> + <source>&Unban</source> + <translation>&Odblokuj</translation> + </message> + <message> <source>Welcome to the %1 RPC console.</source> <translation>Witaj w konsoli %1 RPC.</translation> </message> @@ -1620,10 +1728,18 @@ <translation>Usuń</translation> </message> <message> + <source>Copy URI</source> + <translation>Kopiuj URI:</translation> + </message> + <message> <source>Copy label</source> <translation>Kopiuj etykietę</translation> </message> <message> + <source>Copy message</source> + <translation>Kopiuj wiadomość</translation> + </message> + <message> <source>Copy amount</source> <translation>Kopiuj kwotę</translation> </message> @@ -1647,13 +1763,33 @@ <translation>&Zapisz obraz...</translation> </message> <message> + <source>Request payment to %1</source> + <translation>Zażądaj płatności do %1</translation> + </message> + <message> + <source>Payment information</source> + <translation>Informacje o płatności</translation> + </message> + <message> + <source>URI</source> + <translation>URI</translation> + </message> + <message> <source>Address</source> <translation>Adres</translation> </message> <message> + <source>Amount</source> + <translation>Kwota</translation> + </message> + <message> <source>Label</source> <translation>Etykieta</translation> </message> + <message> + <source>Message</source> + <translation>Wiadomość</translation> + </message> </context> <context> <name>RecentRequestsTableModel</name> @@ -1666,10 +1802,22 @@ <translation>Etykieta</translation> </message> <message> + <source>Message</source> + <translation>Wiadomość</translation> + </message> + <message> <source>(no label)</source> <translation>(brak etykiety)</translation> </message> - </context> + <message> + <source>(no message)</source> + <translation>(brak wiadomości)</translation> + </message> + <message> + <source>Requested</source> + <translation>Zażądano</translation> + </message> +</context> <context> <name>SendCoinsDialog</name> <message> @@ -1797,6 +1945,10 @@ <translation>Pył:</translation> </message> <message> + <source>Confirmation time target:</source> + <translation>Docelowy czas potwierdzenia:</translation> + </message> + <message> <source>Clear &All</source> <translation>Wyczyść &wszystko</translation> </message> @@ -1841,6 +1993,38 @@ <translation>Skopiuj resztę</translation> </message> <message> + <source>%1 to %2</source> + <translation>%1 do %2</translation> + </message> + <message> + <source>or</source> + <translation>lub</translation> + </message> + <message> + <source>The amount to pay must be larger than 0.</source> + <translation>Kwota do zapłacenia musi być większa od 0.</translation> + </message> + <message> + <source>Transaction creation failed!</source> + <translation>Utworzenie transakcji nie powiodło się!</translation> + </message> + <message> + <source>Payment request expired.</source> + <translation>Żądanie płatności upłynęło.</translation> + </message> + <message> + <source>Pay only the required fee of %1</source> + <translation>Zapłać tylko wymaganą opłatę w wysokości %1</translation> + </message> + <message> + <source>Warning: Invalid Bitcoin address</source> + <translation>Ostrzeżenie: nieprawidłowy adres Bitcoin</translation> + </message> + <message> + <source>Warning: Unknown change address</source> + <translation>Ostrzeżenie: Nieznany adres reszty</translation> + </message> + <message> <source>(no label)</source> <translation>(brak etykiety)</translation> </message> @@ -1926,7 +2110,11 @@ </context> <context> <name>SendConfirmationDialog</name> - </context> + <message> + <source>Yes</source> + <translation>Tak</translation> + </message> +</context> <context> <name>ShutdownWindow</name> <message> @@ -2025,6 +2213,22 @@ Zwróć uwagę, że poprawnie zweryfikowana wiadomość potwierdza to, że nadaw <source>Reset all verify message fields</source> <translation>Resetuje wszystkie pola weryfikacji wiadomości</translation> </message> + <message> + <source>Wallet unlock was cancelled.</source> + <translation>Odblokowanie portfela zostało anulowane.</translation> + </message> + <message> + <source>Private key for the entered address is not available.</source> + <translation>Klucz prywatny dla podanego adresu nie jest dostępny.</translation> + </message> + <message> + <source>Message signing failed.</source> + <translation>Podpisanie wiadomości nie powiodło się.</translation> + </message> + <message> + <source>Message signed.</source> + <translation>Wiadomość podpisana.</translation> + </message> </context> <context> <name>SplashScreen</name> @@ -2078,6 +2282,14 @@ Zwróć uwagę, że poprawnie zweryfikowana wiadomość potwierdza to, że nadaw <source>label</source> <translation>etykieta</translation> </message> + <message> + <source>Message</source> + <translation>Wiadomość</translation> + </message> + <message> + <source>Amount</source> + <translation>Kwota</translation> + </message> </context> <context> <name>TransactionDescDialog</name> @@ -2251,10 +2463,6 @@ Zwróć uwagę, że poprawnie zweryfikowana wiadomość potwierdza to, że nadaw <translation>Prune: ostatnia synchronizacja portfela jest za danymi. Muszisz -reindexować (pobrać cały ciąg bloków ponownie w przypadku przyciętego węzła)</translation> </message> <message> - <source>Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> - <translation>Zredukuj wymaganą ilość miejsca na dysku poprzez usuwanie starych bloków. Ten tryb jest niekompatybilny z -txindex oraz -rescan. Ostrzeżenie: Wycofanie tego ustawienia wymaga ponownego pobrania całego łańcucha bloków. (domyślnie: 0 = wyłącz usuwanie bloków, >%u = docelowy rozmiar w MiB jaki wykorzystać na pliki z blokami)</translation> - </message> - <message> <source>Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.</source> <translation>Ponowne skanowanie nie jest możliwe w trybie przycinania. Będzie trzeba użyć -reindex, co pobierze ponownie cały łańcuch bloków.</translation> </message> @@ -2583,6 +2791,10 @@ Zwróć uwagę, że poprawnie zweryfikowana wiadomość potwierdza to, że nadaw <translation>Uruchom polecenie przy otrzymaniu odpowiedniego powiadomienia lub gdy zobaczymy naprawdę długie rozgałęzienie (%s w poleceniu jest podstawiane za komunikat)</translation> </message> <message> + <source>Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)</source> + <translation>Opłaty (w %s/Kb) mniejsze niż ta, będą traktowane jako zerowe przy tworzeniu, przesyłaniu i zatwierdzaniu transakcji (domyślnie: %s)</translation> + </message> + <message> <source>If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)</source> <translation>Jeżeli nie ustawiono paytxfee, dołącz wystarczająca opłatę, aby transakcja mogła zostać zatwierdzona w ciągu średniej ilości n bloków (domyślnie: %u)</translation> </message> @@ -2603,6 +2815,10 @@ Zwróć uwagę, że poprawnie zweryfikowana wiadomość potwierdza to, że nadaw <translation>Zbyt niska kwota transakcji do wysłania po odjęciu opłaty</translation> </message> <message> + <source>Use hierarchical deterministic key generation (HD) after BIP32. Only has effect during wallet creation/first start</source> + <translation>Użyj hierarchicznej deterministycznej metody generowania kluczy (HD) zgodnie z BIP32. Ma znaczenie tylko podczas tworzenia portfela/pierwszego startu.</translation> + </message> + <message> <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>Węzły z białej listy nie mogą zostać zbanowane za ataki DoS, a ich transakcje będą zawsze przekazywane, nawet jeżeli będą znajdywać się już w pamięci, przydatne np. dla bramek płatniczych</translation> </message> diff --git a/src/qt/locale/bitcoin_pt_BR.ts b/src/qt/locale/bitcoin_pt_BR.ts index 3589cdbc1b..c637035303 100644 --- a/src/qt/locale/bitcoin_pt_BR.ts +++ b/src/qt/locale/bitcoin_pt_BR.ts @@ -326,6 +326,10 @@ <translation>Clique para ativar a atividade de rede.</translation> </message> <message> + <source>Syncing Headers (%1%)...</source> + <translation>Sincronizando cabeçahos (%1%)...</translation> + </message> + <message> <source>Reindexing blocks on disk...</source> <translation>Reindexando blocos no disco...</translation> </message> @@ -437,10 +441,6 @@ <source>Processing blocks on disk...</source> <translation>Processando blocos no disco...</translation> </message> - <message> - <source>No block source available...</source> - <translation>Nenhum servidor disponível...</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>%n bloco processado do histórico de transações.</numerusform><numerusform>%n blocos processados do histórico de transações.</numerusform></translation> @@ -482,6 +482,10 @@ <translation>%1</translation> </message> <message> + <source>Connecting to peers...</source> + <translation>Conectando...</translation> + </message> + <message> <source>Catching up...</source> <translation>Recuperando o atraso ...</translation> </message> @@ -2197,6 +2201,14 @@ <translation>Aviso: Endereço de troco inválido</translation> </message> <message> + <source>Confirm custom change address</source> + <translation>Confirmar endereço de troco personalizado</translation> + </message> + <message> + <source>The address you selected for change is not part of this wallet. Any or all funds in your wallet may be sent to this address. Are you sure?</source> + <translation>O endereço selecionado para o troco não pertence a esta carteira. Alguns ou todos os fundos da sua carteira modem ser mandados para esse endereço. Tem certeza?</translation> + </message> + <message> <source>(no label)</source> <translation>(sem rótuo)</translation> </message> @@ -3014,10 +3026,6 @@ <translation>Prune: A ultima sincronização da carteira foi além do dado comprimido. Você precisa reindexar (fazer o download de toda a blockchain novamente)</translation> </message> <message> - <source>Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> - <translation>Reduza o armazenamento de dados apagando os blocos mais antigos. Esse modo é incompatível com -txindex e -rescan. Cuidado: Reverter essa configuração requer um novo download de toda a blockchain. (Padrão: 0 = desabilitado, >%u = tamanho em MiB para o uso de blocos cortados)</translation> - </message> - <message> <source>Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.</source> <translation>Rescans não são possíveis no modo prune. Você precisa usar -reindex, que irá fazer o download de toda a blockchain novamente.</translation> </message> @@ -3082,6 +3090,14 @@ <translation>Executa um comando quando uma transação da carteira mudar (%s no comando será substituído por TxID)</translation> </message> <message> + <source>Extra transactions to keep in memory for compact block reconstructions (default: %u)</source> + <translation>Transações extras para manter na memória para reconstruções de blocos compactos (padrão: %u)</translation> + </message> + <message> + <source>If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: %s, testnet: %s)</source> + <translation>Se este bloco está no blockchain, assume-se que ele e seus ancestrais são válidos e podem ignorar a verificação de scripts (0 para verificar todos, padrão: %s, testnet: %s)</translation> + </message> + <message> <source>Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds)</source> <translation>A media máxima permitida de peer time compensa o ajuste. Perspectiva local de horário pode ser influenciada por pares à frente ou atrás neste montante. (padrão: %u segundos)</translation> </message> @@ -3098,6 +3114,14 @@ <translation>Por favor contribua se você entender que %s é útil. Visite %s para mais informações sobre o software.</translation> </message> <message> + <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>Reduz o requerimente de espaço habiitando o pruning (apagando) blocos antigos. Isso permite o chamar o comando pruneblockchain via RPC para apagar blocos específicos, e habiita o pruning automático de blocos antigos se o tamanho em MiB for atingido. Esse modo é incompatíve com -txindex e -rescan. Aviso: Reverter essa configuração requer re-baixar o blockchain inteiro. (padrão: 0 = disabilitado, 1 = permite o pruning manua via RPC, >%u = pruna os blocos para ficar abaixo do expecificado, em MiB)</translation> + </message> + <message> + <source>Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s)</source> + <translation>Taxa (em %s/KiB) a ser adicionada às transações que você mandar (padrão: %s)</translation> + </message> + <message> <source>Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)</source> <translation>Define o número de threads de verificação de script (%u a %d, 0 = automático, <0 = número de cores deixados livres, padrão: %d)</translation> </message> @@ -3118,6 +3142,10 @@ <translation>Use UPnP para mapear a porta escutada (padrão: 1 quando escutando e sem -proxy)</translation> </message> <message> + <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times</source> + <translation>Nome de usuário e hash da senha para conexões JSON-RPC. O campo <userpw> vem com o formato: <USERNAME>:<SALT>$<HASH>. Um script python canônico é incluído em share/rpcuser. O cliente pode conectar normalmente usando o rpcuser=<USERNAME>/rpcpassword=<PASSWORD>. Esta opção pode ser especificado multiplas vezes</translation> + </message> + <message> <source>Wallet will not create transactions that violate mempool chain limits (default: %u)</source> <translation>A carteira não irá criar transações que vioem o imite de memória (padrão: %u)</translation> </message> @@ -3694,10 +3722,6 @@ <translation>Use um proxy SOCKS5 separado para alcançar participantes da rede via serviços ocultos Tor (padrão: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Nome de usuário e senha hash para conexões JSON-RPC. O campo <userpw> vem com o formato: <USERNAME>:<SALT>$<HASH>. Um script python canônico é incluído em share/rpcuser. Essa opção pode ser especificada múltiplas vezes.</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>Aviso: Versões de bloco desconhecidas sendo mineradas! É possível que regras estranhas estejam ativas</translation> </message> diff --git a/src/qt/locale/bitcoin_pt_PT.ts b/src/qt/locale/bitcoin_pt_PT.ts index a2a52efd27..c97272d4b7 100644 --- a/src/qt/locale/bitcoin_pt_PT.ts +++ b/src/qt/locale/bitcoin_pt_PT.ts @@ -43,35 +43,159 @@ </message> <message> <source>Choose the address to send coins to</source> - <translation>Escolhe qual o endereço para o qual enviar moedas</translation> + <translation>Escolha o endereço para enviar as moedas</translation> </message> <message> <source>Choose the address to receive coins with</source> - <translation>Escolhe qual o endereço para receber moedas</translation> + <translation>Escolha o endereço para receber as moedas</translation> </message> - </context> + <message> + <source>C&hoose</source> + <translation>Escol&her</translation> + </message> + <message> + <source>Sending addresses</source> + <translation>A enviar endereços</translation> + </message> + <message> + <source>Receiving addresses</source> + <translation>A receber endereços</translation> + </message> + <message> + <source>&Copy Address</source> + <translation>&Copiar Endereço</translation> + </message> + <message> + <source>Copy &Label</source> + <translation>Copiar &Etiqueta</translation> + </message> + <message> + <source>&Edit</source> + <translation>&Editar</translation> + </message> + <message> + <source>Export Address List</source> + <translation>Exportar Lista de Endereços</translation> + </message> + <message> + <source>Comma separated file (*.csv)</source> + <translation>Ficheiro separado por vírgulas (*.csv)</translation> + </message> + <message> + <source>Exporting Failed</source> + <translation>Exportação Falhou</translation> + </message> + <message> + <source>There was an error trying to save the address list to %1. Please try again.</source> + <translation>Ocorreu um erro ao tentar guardar a lista de endereços para %1. Por favor, tente novamente.</translation> + </message> +</context> <context> <name>AddressTableModel</name> - </context> + <message> + <source>Label</source> + <translation>Etiqueta</translation> + </message> + <message> + <source>Address</source> + <translation>Endereço</translation> + </message> + <message> + <source>(no label)</source> + <translation>(sem etiqueta)</translation> + </message> +</context> <context> <name>AskPassphraseDialog</name> <message> <source>Passphrase Dialog</source> - <translation>Janela da frase de palavra-passe</translation> + <translation>Janela da Frase de Segurança</translation> </message> <message> <source>Enter passphrase</source> - <translation>Insira a frase de palavra-passe</translation> + <translation>Insira a frase de segurança</translation> </message> <message> <source>New passphrase</source> - <translation>Nova frase de palavra-passe</translation> + <translation>Nova frase de frase de segurança</translation> </message> <message> <source>Repeat new passphrase</source> - <translation>Repita a nova frase de palavra-passe</translation> + <translation>Repita a nova frase de frase de segurança</translation> </message> - </context> + <message> + <source>Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>ten or more random characters</b>, or <b>eight or more words</b>.</source> + <translation>Insira a nova frase de segurança para a carteira. <br/> Por favor, utilize uma frase de segurança de <b>10 ou mais carateres aleatórios,</b> ou <b>oito ou mais palavras</b>.</translation> + </message> + <message> + <source>Encrypt wallet</source> + <translation>Encriptar carteira</translation> + </message> + <message> + <source>This operation needs your wallet passphrase to unlock the wallet.</source> + <translation>Esta operação precisa da sua frase de segurança da carteira para desbloquear a mesma.</translation> + </message> + <message> + <source>Unlock wallet</source> + <translation>Desbloquear carteira</translation> + </message> + <message> + <source>This operation needs your wallet passphrase to decrypt the wallet.</source> + <translation>Esta operação precisa da sua frase de segurança da carteira para desencriptar a mesma.</translation> + </message> + <message> + <source>Decrypt wallet</source> + <translation>Desencriptar carteira</translation> + </message> + <message> + <source>Change passphrase</source> + <translation>Alterar frase de segurança</translation> + </message> + <message> + <source>Enter the old passphrase and new passphrase to the wallet.</source> + <translation>Insira a frase de segurança antiga e a nova frase de segurança para a carteira.</translation> + </message> + <message> + <source>Confirm wallet encryption</source> + <translation>Confirmar encriptação da carteira</translation> + </message> + <message> + <source>Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR BITCOINS</b>!</source> + <translation>Aviso: se encriptar a sua carteira e perder a sua frase de segurnça, <b>PERDERÁ TODOS OS SEUS BITCOINS</b>!</translation> + </message> + <message> + <source>Are you sure you wish to encrypt your wallet?</source> + <translation>Tem a certeza que deseja encriptar a sua carteira?</translation> + </message> + <message> + <source>Wallet encrypted</source> + <translation>Carteira encriptada</translation> + </message> + <message> + <source>Wallet encryption failed</source> + <translation>Encriptação da carteira falhou</translation> + </message> + <message> + <source>The supplied passphrases do not match.</source> + <translation>As frases de segurança fornecidas não coincidem.</translation> + </message> + <message> + <source>Wallet unlock failed</source> + <translation>Desbloqueio da carteira falhou</translation> + </message> + <message> + <source>Wallet decryption failed</source> + <translation>Desencriptação da carteira falhou</translation> + </message> + <message> + <source>Wallet passphrase was successfully changed.</source> + <translation>A frase de segurança da carteira foi alterada com sucesso.</translation> + </message> + <message> + <source>Warning: The Caps Lock key is on!</source> + <translation>Aviso: a tecla Caps Lock está ligada!</translation> + </message> +</context> <context> <name>BanTableModel</name> <message> @@ -95,7 +219,7 @@ </message> <message> <source>&Overview</source> - <translation>&Sinopse</translation> + <translation>&Resumo</translation> </message> <message> <source>Node</source> @@ -103,7 +227,7 @@ </message> <message> <source>Show general overview of wallet</source> - <translation>Mostrar sinopse geral da carteira</translation> + <translation>Mostrar resumo geral da carteira</translation> </message> <message> <source>&Transactions</source> @@ -123,19 +247,19 @@ </message> <message> <source>&About %1</source> - <translation>&Acerca de %1</translation> + <translation>&Sobre o %1</translation> </message> <message> <source>Show information about %1</source> - <translation>Mostrar informação sobre %1</translation> + <translation>Mostrar informação sobre o %1</translation> </message> <message> <source>About &Qt</source> - <translation>Sobre &Qt</translation> + <translation>Sobre o &Qt</translation> </message> <message> <source>Show information about Qt</source> - <translation>Mostrar informação sobre Qt</translation> + <translation>Mostrar informação sobre o Qt</translation> </message> <message> <source>&Options...</source> @@ -151,11 +275,11 @@ </message> <message> <source>&Backup Wallet...</source> - <translation>&Guardar Carteira...</translation> + <translation>Efetuar &Cópia de Segurança da Carteira...</translation> </message> <message> <source>&Change Passphrase...</source> - <translation>Alterar &Frase de Palavra-passe...</translation> + <translation>Alterar &Frase de Segurança...</translation> </message> <message> <source>&Sending addresses...</source> @@ -170,12 +294,24 @@ <translation>Abrir &URI...</translation> </message> <message> + <source>Click to disable network activity.</source> + <translation>Clique para desativar a atividade de rede.</translation> + </message> + <message> + <source>Network activity disabled.</source> + <translation>Atividade de rede desativada.</translation> + </message> + <message> + <source>Click to enable network activity again.</source> + <translation>Clique para ativar novamente a atividade de rede.</translation> + </message> + <message> <source>Reindexing blocks on disk...</source> <translation>A reindexar os blocos no disco...</translation> </message> <message> <source>Send coins to a Bitcoin address</source> - <translation>Enviar moedas para um endereço bitcoin</translation> + <translation>Enviar moedas para um endereço Bitcoin</translation> </message> <message> <source>Backup wallet to another location</source> @@ -183,11 +319,11 @@ </message> <message> <source>Change the passphrase used for wallet encryption</source> - <translation>Alterar a frase de palavra-passe utilizada na encriptação da carteira</translation> + <translation>Alterar a frase de segurança utilizada na encriptação da carteira</translation> </message> <message> <source>&Debug window</source> - <translation>Janela de &depuração</translation> + <translation>Janela de &Depuração</translation> </message> <message> <source>Open debugging and diagnostic console</source> @@ -227,7 +363,7 @@ </message> <message> <source>Sign messages with your Bitcoin addresses to prove you own them</source> - <translation>Assine mensagens com os seus endereços Bitcoin para provar que os controla</translation> + <translation>Assine as mensagens com os seus endereços Bitcoin para provar que é o proprietário dos mesmos</translation> </message> <message> <source>Verify messages to ensure they were signed with specified Bitcoin addresses</source> @@ -281,10 +417,6 @@ <source>Processing blocks on disk...</source> <translation>A processar blocos no disco...</translation> </message> - <message> - <source>No block source available...</source> - <translation>Nenhuma fonte de blocos disponível...</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>Processado %n bloco do histórico de transações.</numerusform><numerusform>Processados %n blocos do histórico de transações.</numerusform></translation> @@ -318,6 +450,10 @@ <translation>Atualizado</translation> </message> <message> + <source>%1 client</source> + <translation>Cliente %1</translation> + </message> + <message> <source>Catching up...</source> <translation>Recuperando o atraso...</translation> </message> @@ -360,6 +496,14 @@ <translation>Transação recebida</translation> </message> <message> + <source>HD key generation is <b>enabled</b></source> + <translation>Criação de chave HD está <b>ativada</b></translation> + </message> + <message> + <source>HD key generation is <b>disabled</b></source> + <translation>Criação de chave HD está <b>desativada</b></translation> + </message> + <message> <source>Wallet is <b>encrypted</b> and currently <b>unlocked</b></source> <translation>A carteira está <b>encriptada</b> e atualmente <b>desbloqueada</b></translation> </message> @@ -438,7 +582,75 @@ <source>Confirmed</source> <translation>Confirmada</translation> </message> - </context> + <message> + <source>Copy address</source> + <translation>Copiar endereço</translation> + </message> + <message> + <source>Copy label</source> + <translation>Copiar etiqueta</translation> + </message> + <message> + <source>Copy amount</source> + <translation>Copiar valor</translation> + </message> + <message> + <source>Copy transaction ID</source> + <translation>Copiar Id. da transação</translation> + </message> + <message> + <source>Lock unspent</source> + <translation>Bloquear não gasto</translation> + </message> + <message> + <source>Unlock unspent</source> + <translation>Desbloquear não gasto</translation> + </message> + <message> + <source>Copy quantity</source> + <translation>Copiar quantidade</translation> + </message> + <message> + <source>Copy fee</source> + <translation>Copiar taxa</translation> + </message> + <message> + <source>Copy after fee</source> + <translation>Copiar depois da taxa</translation> + </message> + <message> + <source>Copy bytes</source> + <translation>Copiar bytes</translation> + </message> + <message> + <source>Copy change</source> + <translation>Copiar troco</translation> + </message> + <message> + <source>(%1 locked)</source> + <translation>(%1 bloqueado)</translation> + </message> + <message> + <source>yes</source> + <translation>sim</translation> + </message> + <message> + <source>no</source> + <translation>não</translation> + </message> + <message> + <source>(no label)</source> + <translation>(sem etiqueta)</translation> + </message> + <message> + <source>change from %1 (%2)</source> + <translation>troco de %1 (%2)</translation> + </message> + <message> + <source>(change)</source> + <translation>(troco)</translation> + </message> +</context> <context> <name>EditAddressDialog</name> <message> @@ -461,12 +673,20 @@ <source>&Address</source> <translation>E&ndereço</translation> </message> - </context> + <message> + <source>Could not unlock wallet.</source> + <translation>Não foi possível desbloquear a carteira.</translation> + </message> + <message> + <source>New key generation failed.</source> + <translation>A criação da nova chave falhou.</translation> + </message> +</context> <context> <name>FreespaceChecker</name> <message> <source>A new data directory will be created.</source> - <translation>Irá ser criada uma nova diretoria de dados.</translation> + <translation>Será criada uma nova diretoria de dados.</translation> </message> <message> <source>name</source> @@ -478,7 +698,7 @@ </message> <message> <source>Path already exists, and is not a directory.</source> - <translation>Caminho já existe, e não é uma pasta.</translation> + <translation>O caminho já existe, e este não é uma pasta.</translation> </message> <message> <source>Cannot create data directory here.</source> @@ -497,7 +717,7 @@ </message> <message> <source>About %1</source> - <translation>Sobre %1</translation> + <translation>Sobre o %1</translation> </message> <message> <source>Command-line options</source> @@ -517,11 +737,11 @@ </message> <message> <source>Choose data directory on startup (default: %u)</source> - <translation>Escolha a diretoria dos dados no arranque (predefinição: %u)</translation> + <translation>Escolher a pasta de dados no arranque (predefinição: %u)</translation> </message> <message> <source>Set language, for example "de_DE" (default: system locale)</source> - <translation>Definir linguagem, por exemplo "pt_PT" (por defeito: linguagem do sistema)</translation> + <translation>Definir idioma, por exemplo "pt_PT" (predefinição: idioma do sistema)</translation> </message> <message> <source>Start minimized</source> @@ -529,13 +749,17 @@ </message> <message> <source>Set SSL root certificates for payment request (default: -system-)</source> - <translation>Configurar certificados SSL root para pedido de pagamento (default: -system-)</translation> + <translation>Definir certificados de raiz SSL para pedidos de pagamento (predefinição: -system-)</translation> </message> <message> <source>Show splash screen on startup (default: %u)</source> <translation>Mostrar o ecrã de abertura no arranque (predefinição: %u)</translation> </message> - </context> + <message> + <source>Reset all settings changed in the GUI</source> + <translation>Redefinir todas as definições alteradas na GUI</translation> + </message> +</context> <context> <name>Intro</name> <message> @@ -544,19 +768,19 @@ </message> <message> <source>Welcome to %1.</source> - <translation>Bem-vindo a %1.</translation> + <translation>Bem-vindo ao %1.</translation> </message> <message> <source>Use the default data directory</source> - <translation>Utilizar a diretoria de dados predefinida</translation> + <translation>Utilizar a pasta de dados predefinida</translation> </message> <message> <source>Use a custom data directory:</source> - <translation>Utilizar uma diretoria de dados personalizada:</translation> + <translation>Utilizar uma pasta de dados personalizada:</translation> </message> <message> <source>Error: Specified data directory "%1" cannot be created.</source> - <translation>Erro: não pode ser criada a diretoria de dados especificada como "%1.</translation> + <translation>Erro: não pode ser criada a pasta de dados especificada como "%1.</translation> </message> <message> <source>Error</source> @@ -578,14 +802,38 @@ <translation>Formulário</translation> </message> <message> + <source>Number of blocks left</source> + <translation>Número de blocos restantes</translation> + </message> + <message> + <source>Unknown...</source> + <translation>Desconhecido...</translation> + </message> + <message> <source>Last block time</source> <translation>Data do último bloco</translation> </message> <message> + <source>Progress</source> + <translation>Progresso</translation> + </message> + <message> + <source>calculating...</source> + <translation>a calcular...</translation> + </message> + <message> + <source>Estimated time left until synced</source> + <translation>tempo restante estimado até à sincronização</translation> + </message> + <message> <source>Hide</source> - <translation>Esconder</translation> + <translation>Ocultar</translation> </message> - </context> + <message> + <source>Unknown. Syncing Headers (%1)...</source> + <translation>Desconhecido. Sincronização de Cabeçalhos (%1)...</translation> + </message> +</context> <context> <name>OpenURIDialog</name> <message> @@ -602,9 +850,13 @@ </message> <message> <source>Select payment request file</source> - <translation>Seleccione o ficheiro de pedido de pagamento</translation> + <translation>Selecione o ficheiro de pedido de pagamento</translation> </message> - </context> + <message> + <source>Select payment request file to open</source> + <translation>Selecione o ficheiro de pedido de pagamento para abrir</translation> + </message> +</context> <context> <name>OptionsDialog</name> <message> @@ -617,7 +869,7 @@ </message> <message> <source>Automatically start %1 after logging in to the system.</source> - <translation>Começar o %1 automaticamente ao iniciar a sessão no sistema.</translation> + <translation>Iniciar automaticamente o %1 depois de iniciar a sessão no sistema.</translation> </message> <message> <source>&Start %1 on system login</source> @@ -645,7 +897,7 @@ </message> <message> <source>IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1)</source> - <translation>Endereço IP do proxy (p.ex. IPv4: 127.0.0.1 / IPv6: ::1)</translation> + <translation>Endereço de IP do proxy (exemplo, IPv4: 127.0.0.1 / IPv6: ::1)</translation> </message> <message> <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> @@ -686,15 +938,15 @@ </message> <message> <source>Expert</source> - <translation> Especialistas </translation> + <translation> Técnicos</translation> </message> <message> <source>Enable coin &control features</source> - <translation>Ativar as funcionalidades de controlo de moedas</translation> + <translation>Ativar as funcionalidades de &controlo de moedas</translation> </message> <message> <source>If you disable the spending of unconfirmed change, the change from a transaction cannot be used until that transaction has at least one confirmation. This also affects how your balance is computed.</source> - <translation>No caso de desativar o gasto de troco não confirmado, o troco de uma transação não poderá ser utilizado até que essa transação tenha pelo menos uma confirmação. Isto também afeta o cálculo do seu saldo.</translation> + <translation>Se desativar o gasto de troco não confirmado, o troco de uma transação não pode ser utilizado até que essa transação tenha pelo menos uma confirmação. Isto também afeta o cálculo do seu saldo.</translation> </message> <message> <source>&Spend unconfirmed change</source> @@ -761,6 +1013,10 @@ <translation>&Janela</translation> </message> <message> + <source>&Hide the icon from the system tray.</source> + <translation>&Ocultar o ícone da bandeja do sistema.</translation> + </message> + <message> <source>Hide tray icon</source> <translation>Ocultar ícone da bandeja</translation> </message> @@ -826,7 +1082,7 @@ </message> <message> <source>This change would require a client restart.</source> - <translation>Esta alteração requer um reinício do cliente.</translation> + <translation>Esta alteração obrigará a um reinício do cliente.</translation> </message> <message> <source>The supplied proxy address is invalid.</source> @@ -910,6 +1166,34 @@ </context> <context> <name>PaymentServer</name> + <message> + <source>Payment request error</source> + <translation>Erro do pedido de pagamento</translation> + </message> + <message> + <source>Payment request rejected</source> + <translation>Pedido de pagamento rejeitado</translation> + </message> + <message> + <source>Payment request expired.</source> + <translation>Pedido de pagamento expirado.</translation> + </message> + <message> + <source>Payment request is not initialized.</source> + <translation>O pedido de pagamento não foi inicializado.</translation> + </message> + <message> + <source>Invalid payment request.</source> + <translation>Pedido de pagamento inválido.</translation> + </message> + <message> + <source>Refund from %1</source> + <translation>Reembolso de %1</translation> + </message> + <message> + <source>Error communicating with %1: %2</source> + <translation>Erro ao comunicar com %1: %2</translation> + </message> </context> <context> <name>PeerTableModel</name> @@ -1312,7 +1596,15 @@ <source>Remove</source> <translation>Remover</translation> </message> - </context> + <message> + <source>Copy label</source> + <translation>Copiar etiqueta</translation> + </message> + <message> + <source>Copy amount</source> + <translation>Copiar valor</translation> + </message> +</context> <context> <name>ReceiveRequestDialog</name> <message> @@ -1332,12 +1624,28 @@ <translation>&Salvar Imagem...</translation> </message> <message> + <source>Address</source> + <translation>Endereço</translation> + </message> + <message> <source>Amount</source> <translation>Valor</translation> </message> + <message> + <source>Label</source> + <translation>Etiqueta</translation> + </message> </context> <context> <name>RecentRequestsTableModel</name> + <message> + <source>Label</source> + <translation>Etiqueta</translation> + </message> + <message> + <source>(no label)</source> + <translation>(sem etiqueta)</translation> + </message> </context> <context> <name>SendCoinsDialog</name> @@ -1387,7 +1695,7 @@ </message> <message> <source>If this is activated, but the change address is empty or invalid, change will be sent to a newly generated address.</source> - <translation>Se isto estiver ativo, mas o endereço de troco estiver vazio ou for inválido, o troco irá ser enviado para um novo endereço.</translation> + <translation>Se isto estiver ativo, mas o endereço de troco estiver vazio ou for inválido, o troco será enviado para um novo endereço gerado.</translation> </message> <message> <source>Custom change address</source> @@ -1395,7 +1703,7 @@ </message> <message> <source>Transaction Fee:</source> - <translation>Custo da Transação:</translation> + <translation>Taxa da transação:</translation> </message> <message> <source>Choose...</source> @@ -1403,7 +1711,7 @@ </message> <message> <source>collapse fee-settings</source> - <translation>fechar definições-de custos</translation> + <translation>ocultar definições de taxa</translation> </message> <message> <source>per kilobyte</source> @@ -1411,7 +1719,7 @@ </message> <message> <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>Se a taxa fixa for 1000 satoshis e a transação for somente 250 bytes, pagará somente 250 satoshis "por kilobyte" em custos se trasacionar "pelo menos" 1000 satoshis. Transações superiores a um kilobyte são cobradas por kilobyte.</translation> + <translation>Se a taxa personalizada estiver definida para 1.000 satoshis e a transação é de apenas 250 bytes, então paga apenas 250 satoshis "por kilobyte" na taxa, enquanto em "total pelo menos" paga 1.000 satoshis. Para transações superiores a um kilobyte ambos pagam por kilobyte.</translation> </message> <message> <source>Hide</source> @@ -1439,7 +1747,7 @@ </message> <message> <source>(Smart fee not initialized yet. This usually takes a few blocks...)</source> - <translation>(Taxa inteligente ainda não foi acionada. Normalmente demora alguns blocos...)</translation> + <translation>(A taxa inteligente ainda não foi inicializada. Isto normalmente demora alguns blocos...)</translation> </message> <message> <source>normal</source> @@ -1481,7 +1789,63 @@ <source>S&end</source> <translation>E&nviar</translation> </message> - </context> + <message> + <source>Copy quantity</source> + <translation>Copiar quantidade</translation> + </message> + <message> + <source>Copy amount</source> + <translation>Copiar valor</translation> + </message> + <message> + <source>Copy fee</source> + <translation>Copiar taxa</translation> + </message> + <message> + <source>Copy after fee</source> + <translation>Copiar depois da taxa</translation> + </message> + <message> + <source>Copy bytes</source> + <translation>Copiar bytes</translation> + </message> + <message> + <source>Copy change</source> + <translation>Copiar troco</translation> + </message> + <message> + <source>added as transaction fee</source> + <translation>adicionado como taxa de transação</translation> + </message> + <message> + <source>The total exceeds your balance when the %1 transaction fee is included.</source> + <translation>O total excede o seu saldo quando a taxa de transação %1 está incluída.</translation> + </message> + <message> + <source>A fee higher than %1 is considered an absurdly high fee.</source> + <translation>Uma taxa superior a %1 é considerada uma taxa altamente absurda.</translation> + </message> + <message> + <source>Payment request expired.</source> + <translation>Pedido de pagamento expirado.</translation> + </message> + <message> + <source>Pay only the required fee of %1</source> + <translation>Pague apenas a taxa obrigatória de %1</translation> + </message> + <message> + <source>Warning: Unknown change address</source> + <translation>Aviso: endereço de troco desconhecido</translation> + </message> + <message> + <source>Confirm custom change address</source> + <translation>Confirmar endereço de troco personalizado</translation> + </message> + <message> + <source>(no label)</source> + <translation>(sem etiqueta)</translation> + </message> +</context> <context> <name>SendCoinsEntry</name> <message> @@ -1530,7 +1894,7 @@ </message> <message> <source>S&ubtract fee from amount</source> - <translation>S&ubtrair taxa ao montante</translation> + <translation>S&ubtrair a taxa ao montante</translation> </message> <message> <source>Message:</source> @@ -1679,6 +2043,10 @@ <context> <name>TransactionDesc</name> <message> + <source>Transaction fee</source> + <translation>Taxa de transação</translation> + </message> + <message> <source>Amount</source> <translation>Valor</translation> </message> @@ -1692,9 +2060,49 @@ </context> <context> <name>TransactionTableModel</name> + <message> + <source>Label</source> + <translation>Etiqueta</translation> + </message> + <message> + <source>(no label)</source> + <translation>(sem etiqueta)</translation> + </message> </context> <context> <name>TransactionView</name> + <message> + <source>Copy address</source> + <translation>Copiar endereço</translation> + </message> + <message> + <source>Copy label</source> + <translation>Copiar etiqueta</translation> + </message> + <message> + <source>Copy amount</source> + <translation>Copiar valor</translation> + </message> + <message> + <source>Copy transaction ID</source> + <translation>Copiar Id. da transação</translation> + </message> + <message> + <source>Comma separated file (*.csv)</source> + <translation>Ficheiro separado por vírgulas (*.csv)</translation> + </message> + <message> + <source>Label</source> + <translation>Etiqueta</translation> + </message> + <message> + <source>Address</source> + <translation>Endereço</translation> + </message> + <message> + <source>Exporting Failed</source> + <translation>Exportação Falhou</translation> + </message> </context> <context> <name>UnitDisplayStatusBarControl</name> @@ -1751,10 +2159,6 @@ <translation>Suprimir: a última sincronização da carteira vai além dos dados suprimidos. O que precisa para -reindex (transferir novamente toda a cadeia de blocos, no caso de nó suprimido)</translation> </message> <message> - <source>Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> - <translation>Reduza os requisitos de armazenamento podando (eliminando) blocos antigos. Este modo é incompatível com -txindex e -rescan. Aviso: Reverter esta opção requer um novo descarregamento da cadeia de blocos completa. (padrão: 0 = desactivar poda de blocos, >%u = tamanho desejado em MiB para utilizar em ficheiros de blocos)</translation> - </message> - <message> <source>Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.</source> <translation>Reanálises não são possíveis no modo de suprimir. Para isso terá de utilizar -reindex que irá transferir novamente toda a cadeia de blocos.</translation> </message> @@ -1764,7 +2168,7 @@ </message> <message> <source>Fee (in %s/kB) to add to transactions you send (default: %s)</source> - <translation>Taxa (em %s/kB) a adicionar às transacções que envia (padrão: %s)</translation> + <translation>Taxa (em %s/kB) para adicionar às transações que envia (predefinição: %s)</translation> </message> <message> <source>Pruning blockstore...</source> @@ -1800,7 +2204,7 @@ </message> <message> <source>Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)</source> - <translation>Executar comando quando uma das transações na carteira mudar (no comando, %s é substituído pelo ID da Transação)</translation> + <translation>Executar o comando quando uma transação da carteira muda (no comando, %s é substituído pela Id. da Transação)</translation> </message> <message> <source>Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)</source> @@ -2040,7 +2444,7 @@ </message> <message> <source>Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)</source> - <translation>Taxas (em %s/kB) abaixo deste valor são consideradas nulas para propagação, mineração e criação de transacções (padrão: %s)</translation> + <translation>Taxas (em %s/kB) inferiores a este valor são consideradas nulas para propagação, mineração e criação de transações (predefinição: %s)</translation> </message> <message> <source>If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)</source> @@ -2220,7 +2624,7 @@ </message> <message> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> - <translation>Executar comando quando o melhor bloco mudar (no comando, %s é substituído pela hash do bloco)</translation> + <translation>Executar o comando quando o melhor bloco muda (no comando, %s é substituído pela hash do bloco)</translation> </message> <message> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> @@ -2287,10 +2691,6 @@ <translation>Use um proxy SOCKS5 separado para alcançar pares via serviços ocultos do Tor (padrão: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Username e hash da password para ligações JSON-RPC. O campo <userpw> está no formato: <USERNAME>:<SALT>$<HASH>. Um script python está incluido em share/rpcuser. Esta opção pode ser especificada múltiplas vezes.</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>Atenção: Versões desconhecidas de blocos estão a ser mineradas! É possível que regras desconhecias estão a ser efetuadas</translation> </message> @@ -2372,7 +2772,7 @@ </message> <message> <source>Spend unconfirmed change when sending transactions (default: %u)</source> - <translation>Gastar troco não confirmado ao enviar transacções (padrão: %u)</translation> + <translation>Gastar o troco não confirmado quando enviar transações (predefinição: %u)</translation> </message> <message> <source>Threshold for disconnecting misbehaving peers (default: %u)</source> diff --git a/src/qt/locale/bitcoin_ro_RO.ts b/src/qt/locale/bitcoin_ro_RO.ts index 92fc87f4f2..3d3a4b0431 100644 --- a/src/qt/locale/bitcoin_ro_RO.ts +++ b/src/qt/locale/bitcoin_ro_RO.ts @@ -253,10 +253,6 @@ <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>%n conexiune activă către reţeaua Bitcoin</numerusform><numerusform>%n conexiuni active către reţeaua Bitcoin</numerusform><numerusform>%n de conexiuni active către reţeaua Bitcoin</numerusform></translation> </message> - <message> - <source>No block source available...</source> - <translation>Nici o sursă de bloc disponibilă...</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>S-a procesat %n bloc din istoricul tranzacţiilor.</numerusform><numerusform>S-au procesat %n blocuri din istoricul tranzacţiilor.</numerusform><numerusform>S-au procesat %n de blocuri din istoricul tranzacţiilor.</numerusform></translation> diff --git a/src/qt/locale/bitcoin_ru.ts b/src/qt/locale/bitcoin_ru.ts index aa30270415..b5f40fc058 100644 --- a/src/qt/locale/bitcoin_ru.ts +++ b/src/qt/locale/bitcoin_ru.ts @@ -63,7 +63,7 @@ </message> <message> <source>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</source> - <translation>Это ваши адреса Bitcoin для отправки платежей. Всегда проверяйте количество и адрес получателя перед отправкой перевода.</translation> + <translation>Это ваши адреса Bitcoin для отправки платежей. Всегда проверяйте сумму и адрес получателя перед отправкой перевода.</translation> </message> <message> <source>These are your Bitcoin addresses for receiving payments. It is recommended to use a new receiving address for each transaction.</source> @@ -441,10 +441,6 @@ <source>Processing blocks on disk...</source> <translation>Обработка блоков на диске...</translation> </message> - <message> - <source>No block source available...</source> - <translation>Источник блоков недоступен...</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>Обработан %n блок истории транзакций.</numerusform><numerusform>Обработано %n блока истории транзакций.</numerusform><numerusform>Обработано %n блоков истории транзакций.</numerusform><numerusform>Обработано %n блоков истории транзакций.</numerusform></translation> @@ -486,6 +482,10 @@ <translation>%1 клиент</translation> </message> <message> + <source>Connecting to peers...</source> + <translation>Подключение к пирам...</translation> + </message> + <message> <source>Catching up...</source> <translation>Синхронизируется...</translation> </message> @@ -543,7 +543,11 @@ <source>Wallet is <b>encrypted</b> and currently <b>locked</b></source> <translation>Бумажник <b>зашифрован</b> и в настоящее время <b>заблокирован</b></translation> </message> - </context> + <message> + <source>A fatal error occurred. Bitcoin can no longer continue safely and will quit.</source> + <translation>Произошла неисправимая ошибка. Bitcoin не может безопасно продолжать работу и будет закрыт.</translation> + </message> +</context> <context> <name>CoinControlDialog</name> <message> @@ -1431,10 +1435,26 @@ <source>%n year(s)</source> <translation><numerusform>%n год</numerusform><numerusform>%n года</numerusform><numerusform>%n лет</numerusform><numerusform>%n лет</numerusform></translation> </message> - </context> + <message> + <source>%1 didn't yet exit safely...</source> + <translation>%1 ещё не завершился безопасно...</translation> + </message> +</context> <context> <name>QObject::QObject</name> - </context> + <message> + <source>Error: Specified data directory "%1" does not exist.</source> + <translation>Ошибка: указанный каталог "%1" не существует.</translation> + </message> + <message> + <source>Error: Cannot parse configuration file: %1. Only use key=value syntax.</source> + <translation>Ошибка: не удалось разобрать конфигурационный файл: %1. Используйте синтаксис вида ключ=значение.</translation> + </message> + <message> + <source>Error: %1</source> + <translation>Ошибка: %1</translation> + </message> +</context> <context> <name>QRImageWidget</name> <message> @@ -2185,6 +2205,14 @@ <translation>Внимание: неизвестный адрес для сдачи</translation> </message> <message> + <source>Confirm custom change address</source> + <translation>Подтвердите свой адрес для сдачи</translation> + </message> + <message> + <source>The address you selected for change is not part of this wallet. Any or all funds in your wallet may be sent to this address. Are you sure?</source> + <translation>Выбранный вами адрес для сдачи не принадлежит этому кошельку. Часть или все средства могут быть отправлены на этот адрес. Вы уверены?</translation> + </message> + <message> <source>(no label)</source> <translation>(нет метки)</translation> </message> @@ -2445,6 +2473,10 @@ </context> <context> <name>TransactionDesc</name> + <message numerus="yes"> + <source>Open for %n more block(s)</source> + <translation><numerusform>Открыто для ещё %n блока</numerusform><numerusform>Открыто для ещё %n блоков</numerusform><numerusform>Открыто для ещё %n блоков</numerusform><numerusform>Открыто для ещё %n блоков</numerusform></translation> + </message> <message> <source>Open until %1</source> <translation>Открыто до %1</translation> @@ -2533,6 +2565,10 @@ <source>Credit</source> <translation>Кредит</translation> </message> + <message numerus="yes"> + <source>matures in %n more block(s)</source> + <translation><numerusform>будет доступно через %n блок</numerusform><numerusform>будет доступно через %n блока</numerusform><numerusform>будет доступно через %n блоков</numerusform><numerusform>будет доступно через %n блоков</numerusform></translation> + </message> <message> <source>not accepted</source> <translation>не принято</translation> @@ -2635,6 +2671,10 @@ <source>Label</source> <translation>Метка</translation> </message> + <message numerus="yes"> + <source>Open for %n more block(s)</source> + <translation><numerusform>Открыто для ещё %n блока</numerusform><numerusform>Открыто для ещё %n блоков</numerusform><numerusform>Открыто для ещё %n блоков</numerusform><numerusform>Открыто для ещё %n блоков</numerusform></translation> + </message> <message> <source>Open until %1</source> <translation>Открыто до %1</translation> @@ -2990,10 +3030,6 @@ <translation>Удаление: последняя синхронизация кошелька вышла за рамки удаленных данных. Вам нужен -reindex (скачать всю цепь блоков в случае удаленного узла)</translation> </message> <message> - <source>Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> - <translation>Уменьшить размер хранилища за счёт удаления (обрезания) старых блоков. Этот режим несовместим с -txindex и -rescan. Внимание: переключение этой опции обратно потребует полной загрузки цепи блоков. (по умолчанию: 0 = отключить удаление блоков, >%u = целевой размер в Мб для файлов блоков)</translation> - </message> - <message> <source>Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.</source> <translation>Повторное сканирование не возможно в режиме удаления. Вам надо будет использовать -reindex, который загрузит заново всю цепь блоков.</translation> </message> @@ -3078,6 +3114,10 @@ <translation>База данных блоков содержит блок, который появляется из будущего. Это может из-за некорректно установленных даты и времени на вашем компьютере. Остается только перестроивать базу блоков, если вы уверены, что дата и время корректны.</translation> </message> <message> + <source>This is a pre-release test build - use at your own risk - do not use for mining or merchant applications</source> + <translation>Это пре-релизная тестовая сборка - используйте на свой страх и риск - не используйте для добычи или торговых приложений</translation> + </message> + <message> <source>Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain</source> <translation>Невозможно отмотать базу данных до пред-форкового состояния. Вам будет необходимо перекачать цепочку блоков.</translation> </message> @@ -3086,6 +3126,22 @@ <translation>Использовать UPnP для проброса порта (по умолчанию: 1, если используется прослушивание и нет -proxy)</translation> </message> <message> + <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times</source> + <translation>Имя пользователя и хэш пароля для JSON-RPC соединений. Поле <userpw> использует формат: <USERNAME>:<SALT>$<HASH>. Каноничный пример скрипта на питоне находится в share/rpcuser. Эта опция может быть указана несколько раз</translation> + </message> + <message> + <source>Wallet will not create transactions that violate mempool chain limits (default: %u)</source> + <translation>Бумажник не будет создавать транзакции, которые нарушают лимиты цепочки пула в памяти (по умолчанию: %u)</translation> + </message> + <message> + <source>Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.</source> + <translation>Внимание: похоже, в сети нет полного согласия! Некоторые майнеры, возможно, испытывают проблемы.</translation> + </message> + <message> + <source>Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.</source> + <translation>Внимание: мы не полностью согласны с подключенными участниками! Вам или другим участникам, возможно, следует обновиться.</translation> + </message> + <message> <source>You need to rebuild the database using -reindex-chainstate to change -txindex</source> <translation>Вам необходимо пересобрать базы данных с помощью -reindex-chainstate, чтобы изменить -txindex</translation> </message> @@ -3118,6 +3174,10 @@ <translation>Не удаётся разрешить адрес в параметре -%s: '%s'</translation> </message> <message> + <source>Chain selection options:</source> + <translation>Параметры выбора цепочки:</translation> + </message> + <message> <source>Change index out of range</source> <translation>Изменение индекса вне диапазона</translation> </message> @@ -3314,6 +3374,10 @@ <translation>Использовать UPnP для проброса порта (по умолчанию: %u)</translation> </message> <message> + <source>Use the test chain</source> + <translation>Использовать тестовую цепочку</translation> + </message> + <message> <source>User Agent comment (%s) contains unsafe characters.</source> <translation>Комментарий пользователя (%s) содержит небезопасные символы.</translation> </message> @@ -3646,10 +3710,6 @@ <translation>Использовать отдельный прокси SOCKS5 для соединения с участниками через скрытые сервисы Tor (по умолчанию: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Имя пользователя и хэш пароля для JSON-RPC соединений. Поле <userpw> использует формат: <USERNAME>:<SALT>$<HASH>. Каноничный пример скрипта на питоне включен в "share/rpcuser". Эта опция может быть указана несколько раз</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>Внимание: Получена неизвестная версия блока! Возможно неизвестные правила вступили в силу.</translation> </message> diff --git a/src/qt/locale/bitcoin_sk.ts b/src/qt/locale/bitcoin_sk.ts index 6c5828c4f8..87dc620f0e 100644 --- a/src/qt/locale/bitcoin_sk.ts +++ b/src/qt/locale/bitcoin_sk.ts @@ -277,10 +277,6 @@ <source>Processing blocks on disk...</source> <translation>Spracovávam bloky na disku...</translation> </message> - <message> - <source>No block source available...</source> - <translation>Nedostupný zdroj blokov...</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>Spracovaných %n blok transakčnej histórie.</numerusform><numerusform>Spracovaných %n bloky transakčnej histórie.</numerusform><numerusform>Spracovaných %n blokov transakčnej histórie.</numerusform></translation> @@ -2295,10 +2291,6 @@ <translation>Použiť samostatný SOCKS5 proxy server na dosiahnutie počítačov cez skryté služby Tor (predvolené: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Užívateľské hash meno a heslo pre JSON-RPC pripojenia. Pole <userpw> je vo formáte <USERNAME>:<SALT>$<HASH>. Kanonický python skript je zahrnutý v share/rpcuser. Toto nastavenie môže byť špecifikované viac krát</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>Varovanie: Neznáma verzia blokov sa doluje! Je možné, že neznáme pravidlá majú efekt</translation> </message> diff --git a/src/qt/locale/bitcoin_sl_SI.ts b/src/qt/locale/bitcoin_sl_SI.ts index 1c8cb4ce56..8a21f978ef 100644 --- a/src/qt/locale/bitcoin_sl_SI.ts +++ b/src/qt/locale/bitcoin_sl_SI.ts @@ -261,10 +261,6 @@ <source>Processing blocks on disk...</source> <translation>Obdelava blokov na disku ...</translation> </message> - <message> - <source>No block source available...</source> - <translation>Ni virov za prenos blokov ...</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>%n obdelan blok zgodovine transakcij.</numerusform><numerusform>%n obdelana bloka zgodovine transakcij.</numerusform><numerusform>%n obdelani bloki zgodovine transakcij.</numerusform><numerusform>%n obdelanih blokov zgodovine transakcij.</numerusform></translation> diff --git a/src/qt/locale/bitcoin_sr.ts b/src/qt/locale/bitcoin_sr.ts index 0fda9b9350..f880dd227e 100644 --- a/src/qt/locale/bitcoin_sr.ts +++ b/src/qt/locale/bitcoin_sr.ts @@ -37,6 +37,22 @@ <source>&Delete</source> <translation>&Избриши</translation> </message> + <message> + <source>Choose the address to send coins to</source> + <translation>Izbirajte adresu za slanje</translation> + </message> + <message> + <source>Choose the address to receive coins with</source> + <translation>Izbirajte adresu za primanje</translation> + </message> + <message> + <source>Sending addresses</source> + <translation>Adresa za slanje</translation> + </message> + <message> + <source>Receiving addresses</source> + <translation>Adresa za primanje</translation> + </message> </context> <context> <name>AddressTableModel</name> @@ -146,6 +162,10 @@ <translation>Трака са картицама</translation> </message> <message> + <source>Error</source> + <translation>Greška</translation> + </message> + <message> <source>Up to date</source> <translation>Ажурно</translation> </message> @@ -220,6 +240,10 @@ </context> <context> <name>Intro</name> + <message> + <source>Error</source> + <translation>Greška</translation> + </message> </context> <context> <name>ModalOverlay</name> @@ -278,6 +302,14 @@ </context> <context> <name>RPCConsole</name> + <message> + <source>Yes</source> + <translation>Da</translation> + </message> + <message> + <source>No</source> + <translation>Ne</translation> + </message> </context> <context> <name>ReceiveCoinsDialog</name> @@ -293,6 +325,10 @@ <source>&Message:</source> <translation>Poruka:</translation> </message> + <message> + <source>Show</source> + <translation>Prikaži</translation> + </message> </context> <context> <name>ReceiveRequestDialog</name> @@ -348,7 +384,11 @@ </context> <context> <name>SendConfirmationDialog</name> - </context> + <message> + <source>Yes</source> + <translation>Da</translation> + </message> +</context> <context> <name>ShutdownWindow</name> </context> @@ -432,6 +472,10 @@ <translation>učitavam adrese....</translation> </message> <message> + <source>Insufficient funds</source> + <translation>Nedovoljno sredstava</translation> + </message> + <message> <source>Loading block index...</source> <translation>Učitavam blok indeksa...</translation> </message> @@ -447,5 +491,9 @@ <source>Done loading</source> <translation>Završeno učitavanje</translation> </message> - </context> + <message> + <source>Error</source> + <translation>Greška</translation> + </message> +</context> </TS>
\ No newline at end of file diff --git a/src/qt/locale/bitcoin_sv.ts b/src/qt/locale/bitcoin_sv.ts index cc6b47e4d6..2986115a62 100644 --- a/src/qt/locale/bitcoin_sv.ts +++ b/src/qt/locale/bitcoin_sv.ts @@ -319,6 +319,22 @@ Var vänlig och försök igen.</translation> <translation>Öppna &URI...</translation> </message> <message> + <source>Click to disable network activity.</source> + <translation>Klicka för att inaktivera nätverksaktivitet.</translation> + </message> + <message> + <source>Network activity disabled.</source> + <translation>Nätverksaktivitet inaktiverad.</translation> + </message> + <message> + <source>Click to enable network activity again.</source> + <translation>Klicka för att aktivera nätverksaktivitet igen.</translation> + </message> + <message> + <source>Syncing Headers (%1%)...</source> + <translation>Synkar huvuden (%1%)...</translation> + </message> + <message> <source>Reindexing blocks on disk...</source> <translation>Återindexerar block på disken...</translation> </message> @@ -430,10 +446,6 @@ Var vänlig och försök igen.</translation> <source>Processing blocks on disk...</source> <translation>Bearbetar block på disken...</translation> </message> - <message> - <source>No block source available...</source> - <translation>Ingen block-källa tillgänglig...</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>Bearbetade %n block av transaktionshistoriken.</numerusform><numerusform>Bearbetade %n block av transaktionshistoriken.</numerusform></translation> @@ -475,6 +487,10 @@ Var vänlig och försök igen.</translation> <translation>%1-klient</translation> </message> <message> + <source>Connecting to peers...</source> + <translation>Ansluter till noder...</translation> + </message> + <message> <source>Catching up...</source> <translation>Hämtar senaste...</translation> </message> @@ -608,6 +624,18 @@ Var vänlig och försök igen.</translation> <translation>Kopiera belopp</translation> </message> <message> + <source>Copy transaction ID</source> + <translation>Kopiera transaktions-ID</translation> + </message> + <message> + <source>yes</source> + <translation>ja</translation> + </message> + <message> + <source>no</source> + <translation>nej</translation> + </message> + <message> <source>(no label)</source> <translation>(Ingen etikett)</translation> </message> @@ -634,6 +662,10 @@ Var vänlig och försök igen.</translation> <source>&Address</source> <translation>&Adress</translation> </message> + <message> + <source>Could not unlock wallet.</source> + <translation>Kunde inte låsa upp plånboken.</translation> + </message> </context> <context> <name>FreespaceChecker</name> @@ -763,14 +795,34 @@ Var vänlig och försök igen.</translation> <translation>Formulär</translation> </message> <message> + <source>Number of blocks left</source> + <translation>Antal block kvar</translation> + </message> + <message> + <source>Unknown...</source> + <translation>Okänt...</translation> + </message> + <message> <source>Last block time</source> <translation>Sista blocktid</translation> </message> <message> + <source>Progress</source> + <translation>Förlopp</translation> + </message> + <message> + <source>calculating...</source> + <translation>beräknar...</translation> + </message> + <message> <source>Hide</source> <translation>Göm</translation> </message> - </context> + <message> + <source>Unknown. Syncing Headers (%1)...</source> + <translation>Okänd. Synkar huvuden (%1)...</translation> + </message> +</context> <context> <name>OpenURIDialog</name> <message> @@ -1102,6 +1154,18 @@ Var vänlig och försök igen.</translation> </context> <context> <name>PaymentServer</name> + <message> + <source>URI handling</source> + <translation>URI-hantering</translation> + </message> + <message> + <source>Refund from %1</source> + <translation>Återbetalning från %1</translation> + </message> + <message> + <source>Bad response from server %1</source> + <translation>Felaktigt svar från server %1</translation> + </message> </context> <context> <name>PeerTableModel</name> @@ -1113,7 +1177,11 @@ Var vänlig och försök igen.</translation> <source>Node/Service</source> <translation>Nod/Tjänst</translation> </message> - </context> + <message> + <source>Ping</source> + <translation>Ping</translation> + </message> +</context> <context> <name>QObject</name> <message> @@ -1152,14 +1220,42 @@ Var vänlig och försök igen.</translation> <source>%1 ms</source> <translation>%1 ms</translation> </message> + <message numerus="yes"> + <source>%n second(s)</source> + <translation><numerusform>%n sekund</numerusform><numerusform>%n sekunder</numerusform></translation> + </message> + <message numerus="yes"> + <source>%n minute(s)</source> + <translation><numerusform>%n minut</numerusform><numerusform>%n minuter</numerusform></translation> + </message> + <message numerus="yes"> + <source>%n hour(s)</source> + <translation><numerusform>%n timme</numerusform><numerusform>%n timmar</numerusform></translation> + </message> + <message numerus="yes"> + <source>%n day(s)</source> + <translation><numerusform>%n dag</numerusform><numerusform>%n dagar</numerusform></translation> + </message> + <message numerus="yes"> + <source>%n week(s)</source> + <translation><numerusform>%n vecka</numerusform><numerusform>%n veckor</numerusform></translation> + </message> <message> <source>%1 and %2</source> <translation>%1 och %2</translation> </message> + <message numerus="yes"> + <source>%n year(s)</source> + <translation><numerusform>%n år</numerusform><numerusform>%n år</numerusform></translation> + </message> </context> <context> <name>QObject::QObject</name> - </context> + <message> + <source>Error: %1</source> + <translation>Fel: %1</translation> + </message> +</context> <context> <name>QRImageWidget</name> </context> @@ -1513,10 +1609,18 @@ Var vänlig och försök igen.</translation> <translation>Ta bort</translation> </message> <message> + <source>Copy URI</source> + <translation>Kopiera URI</translation> + </message> + <message> <source>Copy label</source> <translation>Kopiera etikett</translation> </message> <message> + <source>Copy message</source> + <translation>Kopiera meddelande</translation> + </message> + <message> <source>Copy amount</source> <translation>Kopiera belopp</translation> </message> @@ -1540,6 +1644,14 @@ Var vänlig och försök igen.</translation> <translation>&Spara Bild...</translation> </message> <message> + <source>Payment information</source> + <translation>Betalinformaton</translation> + </message> + <message> + <source>URI</source> + <translation>URI</translation> + </message> + <message> <source>Address</source> <translation>Adress</translation> </message> @@ -1547,17 +1659,33 @@ Var vänlig och försök igen.</translation> <source>Label</source> <translation>Etikett</translation> </message> + <message> + <source>Message</source> + <translation>Meddelande</translation> + </message> </context> <context> <name>RecentRequestsTableModel</name> <message> + <source>Date</source> + <translation>Datum</translation> + </message> + <message> <source>Label</source> <translation>Etikett</translation> </message> <message> + <source>Message</source> + <translation>Meddelande</translation> + </message> + <message> <source>(no label)</source> <translation>(Ingen etikett)</translation> </message> + <message> + <source>(no message)</source> + <translation>(inget meddelande)</translation> + </message> </context> <context> <name>SendCoinsDialog</name> @@ -1706,6 +1834,18 @@ Var vänlig och försök igen.</translation> <translation>Kopiera belopp</translation> </message> <message> + <source>%1 to %2</source> + <translation>%1 till %2</translation> + </message> + <message> + <source>or</source> + <translation>eller</translation> + </message> + <message numerus="yes"> + <source>%n block(s)</source> + <translation><numerusform>%n block</numerusform><numerusform>%n block</numerusform></translation> + </message> + <message> <source>(no label)</source> <translation>(Ingen etikett)</translation> </message> @@ -1791,7 +1931,11 @@ Var vänlig och försök igen.</translation> </context> <context> <name>SendConfirmationDialog</name> - </context> + <message> + <source>Yes</source> + <translation>Ja</translation> + </message> +</context> <context> <name>ShutdownWindow</name> <message> @@ -1889,7 +2033,19 @@ Var vänlig och försök igen.</translation> <source>Reset all verify message fields</source> <translation>Rensa alla fält</translation> </message> - </context> + <message> + <source>Message signed.</source> + <translation>Meddelande signerat.</translation> + </message> + <message> + <source>The signature could not be decoded.</source> + <translation>Signaturen kunde inte avkodas.</translation> + </message> + <message> + <source>Message verified.</source> + <translation>Meddelande verifierat.</translation> + </message> +</context> <context> <name>SplashScreen</name> <message> @@ -1906,6 +2062,18 @@ Var vänlig och försök igen.</translation> </context> <context> <name>TransactionDesc</name> + <message> + <source>Status</source> + <translation>Status</translation> + </message> + <message> + <source>Date</source> + <translation>Datum</translation> + </message> + <message> + <source>Message</source> + <translation>Meddelande</translation> + </message> </context> <context> <name>TransactionDescDialog</name> @@ -1917,6 +2085,10 @@ Var vänlig och försök igen.</translation> <context> <name>TransactionTableModel</name> <message> + <source>Date</source> + <translation>Datum</translation> + </message> + <message> <source>Label</source> <translation>Etikett</translation> </message> @@ -1940,10 +2112,18 @@ Var vänlig och försök igen.</translation> <translation>Kopiera belopp</translation> </message> <message> + <source>Copy transaction ID</source> + <translation>Kopiera transaktions-ID</translation> + </message> + <message> <source>Comma separated file (*.csv)</source> <translation>Kommaseparerad fil (*.csv)</translation> </message> <message> + <source>Date</source> + <translation>Datum</translation> + </message> + <message> <source>Label</source> <translation>Etikett</translation> </message> @@ -2007,10 +2187,6 @@ Var vänlig och försök igen.</translation> <translation>Beskärning: sista plånbokssynkroniseringen ligger utanför beskuren data. Du måste använda -reindex (ladda ner hela blockkedjan igen eftersom noden beskurits)</translation> </message> <message> - <source>Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> - <translation>Minska lagringsbehovet genom att beskära (ta bort) gamla block. Detta läge är inkompatibelt med -txindex och -rescan. Varning: Ändras denna inställning måste hela blockkedjan laddas ner igen. (förvalt: 0 = inaktivera beskärning av block, >%u = målstorlek i MiB att använda för blockfiler)</translation> - </message> - <message> <source>Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.</source> <translation>Omskanningar kan inte göras i beskuret läge. Du måste använda -reindex vilket kommer ladda ner hela blockkedjan igen.</translation> </message> @@ -2647,10 +2823,6 @@ Var vänlig och försök igen.</translation> <translation>Använd separat SOCKS5 proxy för att nå kollegor via dolda tjänster i Tor (förvalt: -%s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Användarnamn och hashat lösenord för JSON-RPC-anslutningar. Fältet <userpw> kommer i formatet: <USERNAME>:<SALT>$<HASH>. Ett kanoniskt pythonskript finns inkluderat i share/rpcuser. Detta alternativ kan anges flera gånger</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>Varning: Okända blockversioner bryts! Det är möjligt att okända regler används</translation> </message> diff --git a/src/qt/locale/bitcoin_th_TH.ts b/src/qt/locale/bitcoin_th_TH.ts index 18e78847fc..ea84d11d39 100644 --- a/src/qt/locale/bitcoin_th_TH.ts +++ b/src/qt/locale/bitcoin_th_TH.ts @@ -41,6 +41,18 @@ <source>&Delete</source> <translation>&ลบ</translation> </message> + <message> + <source>Choose the address to send coins to</source> + <translation>เลือกที่อยู่เพื่อส่งเหรียญไปไว้</translation> + </message> + <message> + <source>Choose the address to receive coins with</source> + <translation>เลือกที่อยู่เพื่อส่งเหรียญไปไว้</translation> + </message> + <message> + <source>Sending addresses</source> + <translation>ส่งที่อยู่</translation> + </message> </context> <context> <name>AddressTableModel</name> @@ -273,10 +285,6 @@ <source>Processing blocks on disk...</source> <translation>กำลังดำเนินการกับบล็อกในดิสก์...</translation> </message> - <message> - <source>No block source available...</source> - <translation>ไม่มีบล็อกเริ่มต้น ให้ใช้ได้...</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>%n บล็อกในประวัติรายการ ได้รับการดำเนินการเรียบร้อยแล้ว</numerusform></translation> diff --git a/src/qt/locale/bitcoin_tr.ts b/src/qt/locale/bitcoin_tr.ts index b210d01990..c37a518968 100644 --- a/src/qt/locale/bitcoin_tr.ts +++ b/src/qt/locale/bitcoin_tr.ts @@ -361,10 +361,6 @@ <source>Processing blocks on disk...</source> <translation>Bloklar diske yazıdırılıyor...</translation> </message> - <message> - <source>No block source available...</source> - <translation>Hiçbir blok kaynağı mevcut değil...</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>Muamele tarihçesinden %n blok işlendi.</numerusform><numerusform>Muamele tarihçesinden %n blok işlendi</numerusform></translation> @@ -1938,10 +1934,6 @@ <translation>Budama: son cüzdan eşleşmesi budanmış verilerin ötesine gitmektedir. -reindex kullanmanız gerekmektedir (Budanmış düğüm ise tüm blok zincirini tekrar indirmeniz gerekir.)</translation> </message> <message> - <source>Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> - <translation>Depolama gerekliliğini eski blokları budayarak (silerek) düşür. Bu kip -txindex ve -rescan ile uyumsuzdur. İkaz: Bu ayarı geri almak tüm blok zincirini yeniden indirmeyi gerektirir. (varsayılan: 0 = blokları silmeyi devre dışı bırak, >%u = MiB olarak blok dosyaları için kullanılacak hedef boyut)</translation> - </message> - <message> <source>Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.</source> <translation>Tekrar taramalar budanmış kipte mümkün değildir. Tüm blok zincirini tekrar indirecek olan -reindex seçeneğini kullanmanız gerekecektir.</translation> </message> @@ -2578,10 +2570,6 @@ <translation>Eşlere gizli Tor servisleri ile ulaşmak için ayrı SOCKS5 vekil sunucusu kullan (varsayılan: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>JSON-RPC bağlantıları için kullanıcı ismi ve karmalanmış parola. <userpw> alanı şu biçimdedir: <USERNAME>:<SALT>$<HASH>. Kanonik bir Python betiği share/rpcuser klasöründe bulunabilir. Bu seçenek birden çok kez belirtilebilir.</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>İkaz: bilinmeyen blok sürümü oluşturulmaya çalışılıyor. Bilinmeyen kuralların işlemesi mümkündür.</translation> </message> diff --git a/src/qt/locale/bitcoin_uk.ts b/src/qt/locale/bitcoin_uk.ts index e423977eba..2c017fc52e 100644 --- a/src/qt/locale/bitcoin_uk.ts +++ b/src/qt/locale/bitcoin_uk.ts @@ -257,10 +257,6 @@ <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>%n активне з'єднання з мережею Bitcoin</numerusform><numerusform>%n активні з'єднання з мережею Bitcoin</numerusform><numerusform>%n активних з'єднань з мережею Bitcoin</numerusform></translation> </message> - <message> - <source>No block source available...</source> - <translation>Недоступно жодного джерела блоків...</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>Оброблено %n блок історії транзакцій.</numerusform><numerusform>Оброблено %n блоки історії транзакцій.</numerusform><numerusform>Оброблено %n блоків історії транзакцій.</numerusform></translation> @@ -1678,10 +1674,6 @@ <translation>Операція відсікання: остання синхронізація вмісту гаманцю не обмежується діями над скороченими данними. Вам необхідно зробити переіндексацію -reindex (заново завантажити веcь ланцюжок блоків в разі появи скороченого ланцюга)</translation> </message> <message> - <source>Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> - <translation>Зменшити вимоги до наявного простору на носії даних за допомогою скорочення ланцюжка (видалення старих блоків). Цей режим несумісний з параметрами -txindex та -rescan. Увага: при поверненні до типового значення видалені частини ланцюжка буде повторно завантажено. (типово: 0 = вимкнути скорочення ланцюжка, >%u = очікуваний розмір файлів блоків в МіБ)</translation> - </message> - <message> <source>Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.</source> <translation>Неможливо провести повторне сканування зі скороченим ланцюжком. Вам необхідно використати -reindex для завантаження повного ланцюжка блоків.</translation> </message> @@ -2158,10 +2150,6 @@ <translation>Використовувати окремий SOCKS5-проксі для з'єднання з учасниками через приховані сервіси Tor (типово: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Логін та хешований пароль для зв'язків JSON-RPC. Поле <userpw> має формат: <USERNAME>:<SALT>$<HASH>. Класичний Python script додано до share/rpcuser. Цей параметр може бути застосований декілька разів.</translation> - </message> - <message> <source>(default: %s)</source> <translation>(типово: %s)</translation> </message> diff --git a/src/qt/locale/bitcoin_uz@Cyrl.ts b/src/qt/locale/bitcoin_uz@Cyrl.ts index fe10a8a8e7..3898c441af 100644 --- a/src/qt/locale/bitcoin_uz@Cyrl.ts +++ b/src/qt/locale/bitcoin_uz@Cyrl.ts @@ -246,10 +246,6 @@ <translation><numerusform>%n та Bitcoin тармоғига фаол уланиш мавжуд</numerusform></translation> </message> <message> - <source>No block source available...</source> - <translation>Блок манбалари мавжуд эмас...</translation> - </message> - <message> <source>%1 behind</source> <translation>%1 орқада</translation> </message> diff --git a/src/qt/locale/bitcoin_zh_CN.ts b/src/qt/locale/bitcoin_zh_CN.ts index 41ec0574d5..e98bc080e2 100644 --- a/src/qt/locale/bitcoin_zh_CN.ts +++ b/src/qt/locale/bitcoin_zh_CN.ts @@ -441,10 +441,6 @@ <source>Processing blocks on disk...</source> <translation>正在处理数据块...</translation> </message> - <message> - <source>No block source available...</source> - <translation>沒有可用的区块来源...</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>已处理 %n 个交易历史数据块。</numerusform></translation> @@ -2971,10 +2967,6 @@ <translation>修剪:最后的钱包同步超过了修剪的数据。你需要通过 -reindex (重新下载整个区块链以防修剪节点)</translation> </message> <message> - <source>Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> - <translation>通过修剪(删除)旧数据块减少存储需求。此模式与 -txindex 和 -rescan不兼容。警告:还原此设置需要重新下载整个区块链。(默认: 0 = 禁用修剪数据块, >%u = 数据块文件目标大小,单位 MiB)</translation> - </message> - <message> <source>Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.</source> <translation>无法在开启修剪的状态下重扫描,请使用 -reindex重新下载完整的区块链。</translation> </message> @@ -3506,10 +3498,6 @@ <translation>通过Tor隐藏服务连接节点时 使用不同的SOCKS5代理 (默认: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>JSON-RPC 连接要使用的用户名和散列密码。<userpw> 的格式是:<用户名>:<盐>$<散列值>。在 share/rpcuser 目录下有一个示范的 python 脚本。这个选项可以被多次指定。</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>警告: 未知的区块版本被挖掘!未知规则可能已生效</translation> </message> diff --git a/src/qt/locale/bitcoin_zh_TW.ts b/src/qt/locale/bitcoin_zh_TW.ts index 44d596013b..bd0533a83e 100644 --- a/src/qt/locale/bitcoin_zh_TW.ts +++ b/src/qt/locale/bitcoin_zh_TW.ts @@ -330,6 +330,10 @@ <translation>按一下就又會使用網路。</translation> </message> <message> + <source>Syncing Headers (%1%)...</source> + <translation>正在同步前導資料(%1%)中...</translation> + </message> + <message> <source>Reindexing blocks on disk...</source> <translation>正在為磁碟裡的區塊重建索引...</translation> </message> @@ -441,10 +445,6 @@ <source>Processing blocks on disk...</source> <translation>正在處理磁碟裡的區塊資料...</translation> </message> - <message> - <source>No block source available...</source> - <translation>沒有可用的區塊來源...</translation> - </message> <message numerus="yes"> <source>Processed %n block(s) of transaction history.</source> <translation><numerusform>已經處理了 %n 個區塊的交易紀錄。</numerusform></translation> @@ -486,6 +486,10 @@ <translation>%1 客戶端軟體</translation> </message> <message> + <source>Connecting to peers...</source> + <translation>正在跟其他節點連線中...</translation> + </message> + <message> <source>Catching up...</source> <translation>正在趕進度...</translation> </message> @@ -2201,6 +2205,14 @@ <translation>警告: 不明的找零位址</translation> </message> <message> + <source>Confirm custom change address</source> + <translation>自定找零位址確認</translation> + </message> + <message> + <source>The address you selected for change is not part of this wallet. Any or all funds in your wallet may be sent to this address. Are you sure?</source> + <translation>選擇的找零位址並不屬於這個錢包。部份或是全部的錢會被送到這個位址去。你確定嗎?</translation> + </message> + <message> <source>(no label)</source> <translation>(無標記)</translation> </message> @@ -3019,10 +3031,6 @@ <translation>修剪模式:錢包的最後同步狀態是在被修剪掉的區塊資料中。你需要用 -reindex 參數執行(會重新下載整個區塊鏈)</translation> </message> <message> - <source>Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> - <translation>修剪(刪除)掉老舊區塊來減少需要的儲存空間。這種模式會關閉錢包功能,並且和 -txindex 及 -rescan 參數不相容。警告: 從這種模式還原會需要重新下載一整個區塊鏈。(預設值: 0 表示不修剪區塊,>%u 表示為區塊檔案的目標大小,單位是百萬位元組 MiB)</translation> - </message> - <message> <source>Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.</source> <translation>在修剪模式下沒辦法重新掃描區塊鏈。你需要配合使用 -reindex 參數來重新下載整個區塊鏈。</translation> </message> @@ -3087,6 +3095,14 @@ <translation>當錢包有交易改變時要執行的指令(指令中的 %s 會被取代成交易識別碼)</translation> </message> <message> + <source>Extra transactions to keep in memory for compact block reconstructions (default: %u)</source> + <translation>為了將摘要區塊完整回組而額外保留在記憶體中的交易數量(預設值: %u)</translation> + </message> + <message> + <source>If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: %s, testnet: %s)</source> + <translation>假設已經在區塊鏈中的區塊以及其先前的區塊都合法,因此對它們略過指令碼驗證(0 表示一律要驗證,預設值: %s, 測試網路: %s)</translation> + </message> + <message> <source>Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds)</source> <translation>跟其他節點的時間差最高可接受的中位數值。本機所認為的時間可能會被其他節點影響,往前或往後在這個值之內。(預設值: %u 秒)</translation> </message> @@ -3103,6 +3119,14 @@ <translation>如果你覺得 %s 有用,可以幫助我們。關於這個軟體的更多資訊請見 %s。</translation> </message> <message> + <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>修剪(刪除)掉老舊區塊以降低需要的儲存空間。這樣會增加一個 RPC 指令 pruneblockchain,可以使用它來刪除指定的區塊;也可以指定目標儲存空間大小,以啟用對老舊區塊的自動修剪功能。這個模式跟 -txindex 和 -rescan 參數不相容。警告: 還原回不修剪模式會需要重新下載一整個區塊鏈。(預設值: 0 表示不修剪區塊,1 表示允許使用 RPC 指令做修剪,>%u 的值表示為區塊資料的目標大小,單位是百萬位元組,MiB)</translation> + </message> + <message> + <source>Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s)</source> + <translation>設定製造區塊時,所要包含交易每千位元組的最低手續費(單位是 %s)。(預設值: %s)</translation> + </message> + <message> <source>Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)</source> <translation>設定指令碼驗證的執行緒數目 (%u 到 %d,0 表示程式自動決定,小於 0 表示保留處理器核心不用的數目,預設值: %d)</translation> </message> @@ -3123,6 +3147,10 @@ <translation>是否要使用「通用即插即用」協定(UPnP),來設定聽候連線的通訊埠的對應(預設值: 當有聽候連線且沒有指定 -proxy 參數時為 1)</translation> </message> <message> + <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times</source> + <translation>JSON-RPC 連線要用的使用者名稱和雜湊密碼。<userpw> 的格式是:<使用者名稱>:<調味值>$<雜湊值>。在 share/rpcuser 目錄下有一個示範的 python 程式。之後客戶端程式就可以用這對參數正常連線:rpcuser=<使用者名稱> 和 rpcpassword=<密碼>。這個選項可以給很多次。</translation> + </message> + <message> <source>Wallet will not create transactions that violate mempool chain limits (default: %u)</source> <translation>錢包軟體不會產生違反記憶池交易鏈限制的交易(預設值: %u)</translation> </message> @@ -3368,7 +3396,7 @@ </message> <message> <source>Use the test chain</source> - <translation>使用測試鏈</translation> + <translation>使用測試區塊鏈</translation> </message> <message> <source>User Agent comment (%s) contains unsafe characters.</source> @@ -3703,10 +3731,6 @@ <translation>使用另外的 SOCK5 代理伺服器,來透過 Tor 隱藏服務跟其他節點聯絡(預設值: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>JSON-RPC 連線要用的使用者名稱和雜湊密碼。<userpw> 的格式是:<使用者名稱>:<調味值>$<雜湊值>。在 share/rpcuser 目錄下有一個示範的 python 程式。這個選項可以給很多次。</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>警告: 有礦工正在開採不明版本的區塊!這表示有不明的交易規則正在作用中</translation> </message> diff --git a/src/qt/modaloverlay.cpp b/src/qt/modaloverlay.cpp index 3e8282e63d..4779ffa43f 100644 --- a/src/qt/modaloverlay.cpp +++ b/src/qt/modaloverlay.cpp @@ -7,6 +7,8 @@ #include "guiutil.h" +#include "chainparams.h" + #include <QResizeEvent> #include <QPropertyAnimation> @@ -105,7 +107,7 @@ void ModalOverlay::tipUpdate(int count, const QDateTime& blockDate, double nVeri ui->progressIncreasePerH->setText(QString::number(progressPerHour*100, 'f', 2)+"%"); // show expected remaining time - ui->expectedTimeLeft->setText(GUIUtil::formateNiceTimeOffset(remainingMSecs/1000.0)); + ui->expectedTimeLeft->setText(GUIUtil::formatNiceTimeOffset(remainingMSecs/1000.0)); static const int MAX_SAMPLES = 5000; if (blockProcessTime.count() > MAX_SAMPLES) @@ -125,11 +127,11 @@ void ModalOverlay::tipUpdate(int count, const QDateTime& blockDate, double nVeri // estimate the number of headers left based on nPowTargetSpacing // and check if the gui is not aware of the the best header (happens rarely) - int estimateNumHeadersLeft = bestHeaderDate.secsTo(currentDate) / 600; + int estimateNumHeadersLeft = bestHeaderDate.secsTo(currentDate) / Params().GetConsensus().nPowTargetSpacing; bool hasBestHeader = bestHeaderHeight >= count; // show remaining number of blocks - if (estimateNumHeadersLeft < 24 && hasBestHeader) { + if (estimateNumHeadersLeft < HEADER_HEIGHT_DELTA_SYNC && hasBestHeader) { ui->numberOfBlocksLeft->setText(QString::number(bestHeaderHeight - count)); } else { ui->numberOfBlocksLeft->setText(tr("Unknown. Syncing Headers (%1)...").arg(bestHeaderHeight)); diff --git a/src/qt/modaloverlay.h b/src/qt/modaloverlay.h index 70d37b87ad..21ccdbd839 100644 --- a/src/qt/modaloverlay.h +++ b/src/qt/modaloverlay.h @@ -8,6 +8,9 @@ #include <QDateTime> #include <QWidget> +//! The required delta of headers to the estimated number of available headers until we show the IBD progress +static constexpr int HEADER_HEIGHT_DELTA_SYNC = 24; + namespace Ui { class ModalOverlay; } diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index 6b2871424d..688e8123af 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -10,7 +10,7 @@ #include "base58.h" #include "chainparams.h" -#include "validation.h" // For minRelayTxFee +#include "policy/policy.h" #include "ui_interface.h" #include "util.h" #include "wallet/wallet.h" @@ -582,7 +582,7 @@ bool PaymentServer::processPaymentRequest(const PaymentRequestPlus& request, Sen // Extract and check amounts CTxOut txOut(sendingTo.second, sendingTo.first); - if (txOut.IsDust(::minRelayTxFee)) { + if (txOut.IsDust(dustRelayFee)) { Q_EMIT message(tr("Payment request error"), tr("Requested payment amount of %1 is too small (considered dust).") .arg(BitcoinUnits::formatWithUnit(optionsModel->getDisplayUnit(), sendingTo.second)), CClientUIInterface::MSG_ERROR); diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h index 37ee522d54..9d46280a37 100644 --- a/src/qt/paymentserver.h +++ b/src/qt/paymentserver.h @@ -21,10 +21,10 @@ // // When startup is finished and the main window is // shown, a signal is sent to slot uiReady(), which -// emits a receivedURL() signal for any payment +// emits a receivedURI() signal for any payment // requests that happened during startup. // -// After startup, receivedURL() happens as usual. +// After startup, receivedURI() happens as usual. // // This class has one more feature: a static // method that finds URIs passed in the command line diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 87d73b5f08..60406c2059 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -1023,11 +1023,11 @@ void RPCConsole::updateNodeDetail(const CNodeCombinedStats *stats) peerAddrDetails += "<br />" + tr("via %1").arg(QString::fromStdString(stats->nodeStats.addrLocal)); ui->peerHeading->setText(peerAddrDetails); ui->peerServices->setText(GUIUtil::formatServicesStr(stats->nodeStats.nServices)); - ui->peerLastSend->setText(stats->nodeStats.nLastSend ? GUIUtil::formatDurationStr(GetTime() - stats->nodeStats.nLastSend) : tr("never")); - ui->peerLastRecv->setText(stats->nodeStats.nLastRecv ? GUIUtil::formatDurationStr(GetTime() - stats->nodeStats.nLastRecv) : tr("never")); + ui->peerLastSend->setText(stats->nodeStats.nLastSend ? GUIUtil::formatDurationStr(GetSystemTimeInSeconds() - stats->nodeStats.nLastSend) : tr("never")); + ui->peerLastRecv->setText(stats->nodeStats.nLastRecv ? GUIUtil::formatDurationStr(GetSystemTimeInSeconds() - stats->nodeStats.nLastRecv) : tr("never")); ui->peerBytesSent->setText(FormatBytes(stats->nodeStats.nSendBytes)); ui->peerBytesRecv->setText(FormatBytes(stats->nodeStats.nRecvBytes)); - ui->peerConnTime->setText(GUIUtil::formatDurationStr(GetTime() - stats->nodeStats.nTimeConnected)); + ui->peerConnTime->setText(GUIUtil::formatDurationStr(GetSystemTimeInSeconds() - stats->nodeStats.nTimeConnected)); ui->peerPingTime->setText(GUIUtil::formatPingTime(stats->nodeStats.dPingTime)); ui->peerPingWait->setText(GUIUtil::formatPingTime(stats->nodeStats.dPingWait)); ui->peerMinPing->setText(GUIUtil::formatPingTime(stats->nodeStats.dMinPing)); diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 5aeda7a30d..1c0ed663c1 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -16,6 +16,7 @@ #include "walletmodel.h" #include "base58.h" +#include "chainparams.h" #include "wallet/coincontrol.h" #include "validation.h" // mempool and minRelayTxFee #include "ui_interface.h" @@ -607,8 +608,8 @@ void SendCoinsDialog::updateGlobalFeeVariables() // set nMinimumTotalFee to 0 to not accidentally pay a custom fee CoinControlDialog::coinControl->nMinimumTotalFee = 0; - // show the estimated reuquired time for confirmation - ui->confirmationTargetLabel->setText(GUIUtil::formatDurationStr(nConfirmTarget*600)+" / "+tr("%n block(s)", "", nConfirmTarget)); + // show the estimated required time for confirmation + ui->confirmationTargetLabel->setText(GUIUtil::formatDurationStr(nConfirmTarget * Params().GetConsensus().nPowTargetSpacing) + " / " + tr("%n block(s)", "", nConfirmTarget)); } else { diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp index fbec5722b6..bd496f149c 100644 --- a/src/qt/test/rpcnestedtests.cpp +++ b/src/qt/test/rpcnestedtests.cpp @@ -29,7 +29,7 @@ static UniValue rpcNestedTest_rpc(const JSONRPCRequest& request) static const CRPCCommand vRPCCommands[] = { - { "test", "rpcNestedTest", &rpcNestedTest_rpc, true }, + { "test", "rpcNestedTest", &rpcNestedTest_rpc, true, {} }, }; void RPCNestedTests::rpcNestedTests() diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index cbdedbf68b..a9d9b6887e 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -47,14 +47,15 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet * // // Credit // - BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout) + for(unsigned int i = 0; i < wtx.tx->vout.size(); i++) { + const CTxOut& txout = wtx.tx->vout[i]; isminetype mine = wallet->IsMine(txout); if(mine) { TransactionRecord sub(hash, nTime); CTxDestination address; - sub.idx = parts.size(); // sequence number + sub.idx = i; // vout index sub.credit = txout.nValue; sub.involvesWatchAddress = mine & ISMINE_WATCH_ONLY; if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*wallet, address)) @@ -118,7 +119,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet * { const CTxOut& txout = wtx.tx->vout[nOut]; TransactionRecord sub(hash, nTime); - sub.idx = parts.size(); + sub.idx = nOut; sub.involvesWatchAddress = involvesWatchAddress; if(wallet->IsMine(txout)) diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp index 70efe27990..7ab4125284 100644 --- a/src/qt/utilitydialog.cpp +++ b/src/qt/utilitydialog.cpp @@ -39,7 +39,7 @@ HelpMessageDialog::HelpMessageDialog(QWidget *parent, bool about) : QString version = tr(PACKAGE_NAME) + " " + tr("version") + " " + QString::fromStdString(FormatFullVersion()); /* On x86 add a bit specifier to the version so that users can distinguish between - * 32 and 64 bit builds. On other architectures, 32/64 bit may be more ambigious. + * 32 and 64 bit builds. On other architectures, 32/64 bit may be more ambiguous. */ #if defined(__x86_64__) version += " " + tr("(%1-bit)").arg(64); diff --git a/src/rest.cpp b/src/rest.cpp index 8a7c985e72..54eefcafe3 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -17,12 +17,9 @@ #include "version.h" #include <boost/algorithm/string.hpp> -#include <boost/dynamic_bitset.hpp> #include <univalue.h> -using namespace std; - static const size_t MAX_GETUTXOS_OUTPOINTS = 15; //allow a max of 15 outpoints to be queried at once enum RetFormat { @@ -65,7 +62,7 @@ extern UniValue mempoolToJSON(bool fVerbose = false); extern void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex); extern UniValue blockheaderToJSON(const CBlockIndex* blockindex); -static bool RESTERR(HTTPRequest* req, enum HTTPStatusCode status, string message) +static bool RESTERR(HTTPRequest* req, enum HTTPStatusCode status, std::string message) { req->WriteHeader("Content-Type", "text/plain"); req->WriteReply(status, message + "\r\n"); @@ -93,9 +90,9 @@ static enum RetFormat ParseDataFormat(std::string& param, const std::string& str return rf_names[0].rf; } -static string AvailableDataFormatsString() +static std::string AvailableDataFormatsString() { - string formats = ""; + std::string formats = ""; for (unsigned int i = 0; i < ARRAYLEN(rf_names); i++) if (strlen(rf_names[i].name) > 0) { formats.append("."); @@ -109,7 +106,7 @@ static string AvailableDataFormatsString() return formats; } -static bool ParseHashStr(const string& strReq, uint256& v) +static bool ParseHashStr(const std::string& strReq, uint256& v) { if (!IsHex(strReq) || (strReq.size() != 64)) return false; @@ -133,7 +130,7 @@ static bool rest_headers(HTTPRequest* req, return false; std::string param; const RetFormat rf = ParseDataFormat(param, strURIPart); - vector<string> path; + std::vector<std::string> path; boost::split(path, param, boost::is_any_of("/")); if (path.size() != 2) @@ -143,7 +140,7 @@ static bool rest_headers(HTTPRequest* req, if (count < 1 || count > 2000) return RESTERR(req, HTTP_BAD_REQUEST, "Header count out of range: " + path[0]); - string hashStr = path[1]; + std::string hashStr = path[1]; uint256 hash; if (!ParseHashStr(hashStr, hash)) return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr); @@ -169,14 +166,14 @@ static bool rest_headers(HTTPRequest* req, switch (rf) { case RF_BINARY: { - string binaryHeader = ssHeader.str(); + std::string binaryHeader = ssHeader.str(); req->WriteHeader("Content-Type", "application/octet-stream"); req->WriteReply(HTTP_OK, binaryHeader); return true; } case RF_HEX: { - string strHex = HexStr(ssHeader.begin(), ssHeader.end()) + "\n"; + std::string strHex = HexStr(ssHeader.begin(), ssHeader.end()) + "\n"; req->WriteHeader("Content-Type", "text/plain"); req->WriteReply(HTTP_OK, strHex); return true; @@ -186,7 +183,7 @@ static bool rest_headers(HTTPRequest* req, BOOST_FOREACH(const CBlockIndex *pindex, headers) { jsonHeaders.push_back(blockheaderToJSON(pindex)); } - string strJSON = jsonHeaders.write() + "\n"; + std::string strJSON = jsonHeaders.write() + "\n"; req->WriteHeader("Content-Type", "application/json"); req->WriteReply(HTTP_OK, strJSON); return true; @@ -233,14 +230,14 @@ static bool rest_block(HTTPRequest* req, switch (rf) { case RF_BINARY: { - string binaryBlock = ssBlock.str(); + std::string binaryBlock = ssBlock.str(); req->WriteHeader("Content-Type", "application/octet-stream"); req->WriteReply(HTTP_OK, binaryBlock); return true; } case RF_HEX: { - string strHex = HexStr(ssBlock.begin(), ssBlock.end()) + "\n"; + std::string strHex = HexStr(ssBlock.begin(), ssBlock.end()) + "\n"; req->WriteHeader("Content-Type", "text/plain"); req->WriteReply(HTTP_OK, strHex); return true; @@ -248,7 +245,7 @@ static bool rest_block(HTTPRequest* req, case RF_JSON: { UniValue objBlock = blockToJSON(block, pblockindex, showTxDetails); - string strJSON = objBlock.write() + "\n"; + std::string strJSON = objBlock.write() + "\n"; req->WriteHeader("Content-Type", "application/json"); req->WriteReply(HTTP_OK, strJSON); return true; @@ -288,7 +285,7 @@ static bool rest_chaininfo(HTTPRequest* req, const std::string& strURIPart) JSONRPCRequest jsonRequest; jsonRequest.params = UniValue(UniValue::VARR); UniValue chainInfoObject = getblockchaininfo(jsonRequest); - string strJSON = chainInfoObject.write() + "\n"; + std::string strJSON = chainInfoObject.write() + "\n"; req->WriteHeader("Content-Type", "application/json"); req->WriteReply(HTTP_OK, strJSON); return true; @@ -313,7 +310,7 @@ static bool rest_mempool_info(HTTPRequest* req, const std::string& strURIPart) case RF_JSON: { UniValue mempoolInfoObject = mempoolInfoToJSON(); - string strJSON = mempoolInfoObject.write() + "\n"; + std::string strJSON = mempoolInfoObject.write() + "\n"; req->WriteHeader("Content-Type", "application/json"); req->WriteReply(HTTP_OK, strJSON); return true; @@ -338,7 +335,7 @@ static bool rest_mempool_contents(HTTPRequest* req, const std::string& strURIPar case RF_JSON: { UniValue mempoolObject = mempoolToJSON(true); - string strJSON = mempoolObject.write() + "\n"; + std::string strJSON = mempoolObject.write() + "\n"; req->WriteHeader("Content-Type", "application/json"); req->WriteReply(HTTP_OK, strJSON); return true; @@ -373,14 +370,14 @@ static bool rest_tx(HTTPRequest* req, const std::string& strURIPart) switch (rf) { case RF_BINARY: { - string binaryTx = ssTx.str(); + std::string binaryTx = ssTx.str(); req->WriteHeader("Content-Type", "application/octet-stream"); req->WriteReply(HTTP_OK, binaryTx); return true; } case RF_HEX: { - string strHex = HexStr(ssTx.begin(), ssTx.end()) + "\n"; + std::string strHex = HexStr(ssTx.begin(), ssTx.end()) + "\n"; req->WriteHeader("Content-Type", "text/plain"); req->WriteReply(HTTP_OK, strHex); return true; @@ -389,7 +386,7 @@ static bool rest_tx(HTTPRequest* req, const std::string& strURIPart) case RF_JSON: { UniValue objTx(UniValue::VOBJ); TxToJSON(*tx, hashBlock, objTx); - string strJSON = objTx.write() + "\n"; + std::string strJSON = objTx.write() + "\n"; req->WriteHeader("Content-Type", "application/json"); req->WriteReply(HTTP_OK, strJSON); return true; @@ -411,7 +408,7 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart) std::string param; const RetFormat rf = ParseDataFormat(param, strURIPart); - vector<string> uriParts; + std::vector<std::string> uriParts; if (param.length() > 1) { std::string strUriParams = param.substr(1); @@ -425,7 +422,7 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart) bool fInputParsed = false; bool fCheckMemPool = false; - vector<COutPoint> vOutPoints; + std::vector<COutPoint> vOutPoints; // parse/deserialize input // input-format = output-format, rest/getutxos/bin requires binary input, gives binary output, ... @@ -499,10 +496,11 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart) return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Error: max outpoints exceeded (max: %d, tried: %d)", MAX_GETUTXOS_OUTPOINTS, vOutPoints.size())); // check spentness and form a bitmap (as well as a JSON capable human-readable string representation) - vector<unsigned char> bitmap; - vector<CCoin> outs; + std::vector<unsigned char> bitmap; + std::vector<CCoin> outs; std::string bitmapStringRepresentation; - boost::dynamic_bitset<unsigned char> hits(vOutPoints.size()); + std::vector<bool> hits; + bitmap.resize((vOutPoints.size() + 7) / 8); { LOCK2(cs_main, mempool.cs); @@ -518,10 +516,11 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart) for (size_t i = 0; i < vOutPoints.size(); i++) { CCoins coins; uint256 hash = vOutPoints[i].hash; + bool hit = false; if (view.GetCoins(hash, coins)) { mempool.pruneSpent(hash, coins); if (coins.IsAvailable(vOutPoints[i].n)) { - hits[i] = true; + hit = true; // Safe to index into vout here because IsAvailable checked if it's off the end of the array, or if // n is valid but points to an already spent output (IsNull). CCoin coin; @@ -533,10 +532,11 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart) } } - bitmapStringRepresentation.append(hits[i] ? "1" : "0"); // form a binary string representation (human-readable for json output) + hits.push_back(hit); + bitmapStringRepresentation.append(hit ? "1" : "0"); // form a binary string representation (human-readable for json output) + bitmap[i / 8] |= ((uint8_t)hit) << (i % 8); } } - boost::to_block_range(hits, std::back_inserter(bitmap)); switch (rf) { case RF_BINARY: { @@ -544,7 +544,7 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart) // use exact same output as mentioned in Bip64 CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION); ssGetUTXOResponse << chainActive.Height() << chainActive.Tip()->GetBlockHash() << bitmap << outs; - string ssGetUTXOResponseString = ssGetUTXOResponse.str(); + std::string ssGetUTXOResponseString = ssGetUTXOResponse.str(); req->WriteHeader("Content-Type", "application/octet-stream"); req->WriteReply(HTTP_OK, ssGetUTXOResponseString); @@ -554,7 +554,7 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart) case RF_HEX: { CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION); ssGetUTXOResponse << chainActive.Height() << chainActive.Tip()->GetBlockHash() << bitmap << outs; - string strHex = HexStr(ssGetUTXOResponse.begin(), ssGetUTXOResponse.end()) + "\n"; + std::string strHex = HexStr(ssGetUTXOResponse.begin(), ssGetUTXOResponse.end()) + "\n"; req->WriteHeader("Content-Type", "text/plain"); req->WriteReply(HTTP_OK, strHex); @@ -586,7 +586,7 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart) objGetUTXOResponse.push_back(Pair("utxos", utxos)); // return json string - string strJSON = objGetUTXOResponse.write() + "\n"; + std::string strJSON = objGetUTXOResponse.write() + "\n"; req->WriteHeader("Content-Type", "application/json"); req->WriteReply(HTTP_OK, strJSON); return true; diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 0b42c1d625..368654bfa6 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -336,8 +336,8 @@ std::string EntryDescriptionString() " \"modifiedfee\" : n, (numeric) transaction fee with fee deltas used for mining priority\n" " \"time\" : n, (numeric) local time transaction entered pool in seconds since 1 Jan 1970 GMT\n" " \"height\" : n, (numeric) block height when transaction entered pool\n" - " \"startingpriority\" : n, (numeric) priority when transaction entered pool\n" - " \"currentpriority\" : n, (numeric) transaction priority now\n" + " \"startingpriority\" : n, (numeric) DEPRECATED. Priority when transaction entered pool\n" + " \"currentpriority\" : n, (numeric) DEPRECATED. Transaction priority now\n" " \"descendantcount\" : n, (numeric) number of in-mempool descendant transactions (including this one)\n" " \"descendantsize\" : n, (numeric) virtual transaction size of in-mempool descendants (including this one)\n" " \"descendantfees\" : n, (numeric) modified fees (see above) of in-mempool descendants (including this one)\n" @@ -651,9 +651,9 @@ UniValue getblockheader(const JSONRPCRequest& request) " \"nonce\" : n, (numeric) The nonce\n" " \"bits\" : \"1d00ffff\", (string) The bits\n" " \"difficulty\" : x.xxx, (numeric) The difficulty\n" + " \"chainwork\" : \"0000...1f3\" (string) Expected number of hashes required to produce the current chain (in hex)\n" " \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n" " \"nextblockhash\" : \"hash\", (string) The hash of the next block\n" - " \"chainwork\" : \"0000...1f3\" (string) Expected number of hashes required to produce the current chain (in hex)\n" "}\n" "\nResult (for verbose=false):\n" "\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n" @@ -814,6 +814,53 @@ static bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats) return true; } +UniValue pruneblockchain(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 1) + throw runtime_error( + "pruneblockchain\n" + "\nArguments:\n" + "1. \"height\" (numeric, required) The block height to prune up to. May be set to a discrete height, or to a unix timestamp to prune based on block time.\n" + "\nResult:\n" + "n (numeric) Height of the last block pruned.\n" + "\nExamples:\n" + + HelpExampleCli("pruneblockchain", "1000") + + HelpExampleRpc("pruneblockchain", "1000")); + + if (!fPruneMode) + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Cannot prune blocks because node is not in prune mode."); + + LOCK(cs_main); + + int heightParam = request.params[0].get_int(); + if (heightParam < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative block height."); + + // Height value more than a billion is too high to be a block height, and + // too low to be a block time (corresponds to timestamp from Sep 2001). + if (heightParam > 1000000000) { + CBlockIndex* pindex = chainActive.FindEarliestAtLeast(heightParam); + if (!pindex) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Could not find block with at least the specified timestamp."); + } + heightParam = pindex->nHeight; + } + + unsigned int height = (unsigned int) heightParam; + unsigned int chainHeight = (unsigned int) chainActive.Height(); + if (chainHeight < Params().PruneAfterHeight()) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Blockchain is too short for pruning."); + else if (height > chainHeight) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Blockchain is shorter than the attempted prune height."); + else if (height > chainHeight - MIN_BLOCKS_TO_KEEP) { + LogPrint("rpc", "Attempt to prune blocks close to the tip. Retaining the minimum number of blocks."); + height = chainHeight - MIN_BLOCKS_TO_KEEP; + } + + PruneBlockFilesManual(height); + return uint64_t(height); +} + UniValue gettxoutsetinfo(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 0) @@ -1073,7 +1120,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) obj.push_back(Pair("bestblockhash", chainActive.Tip()->GetBlockHash().GetHex())); obj.push_back(Pair("difficulty", (double)GetDifficulty())); obj.push_back(Pair("mediantime", (int64_t)chainActive.Tip()->GetMedianTimePast())); - obj.push_back(Pair("verificationprogress", Checkpoints::GuessVerificationProgress(Params().Checkpoints(), chainActive.Tip()))); + obj.push_back(Pair("verificationprogress", GuessVerificationProgress(Params().TxData(), chainActive.Tip()))); obj.push_back(Pair("chainwork", chainActive.Tip()->nChainWork.GetHex())); obj.push_back(Pair("pruned", fPruneMode)); @@ -1384,6 +1431,7 @@ static const CRPCCommand commands[] = { "blockchain", "getrawmempool", &getrawmempool, true, {"verbose"} }, { "blockchain", "gettxout", &gettxout, true, {"txid","n","include_mempool"} }, { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true, {} }, + { "blockchain", "pruneblockchain", &pruneblockchain, true, {"height"} }, { "blockchain", "verifychain", &verifychain, true, {"checklevel","nblocks"} }, { "blockchain", "preciousblock", &preciousblock, true, {"blockhash"} }, diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 422d005f0c..5bdd84e555 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -103,6 +103,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "importmulti", 1, "options" }, { "verifychain", 0, "checklevel" }, { "verifychain", 1, "nblocks" }, + { "pruneblockchain", 0, "height" }, { "keypoolrefill", 0, "newsize" }, { "getrawmempool", 0, "verbose" }, { "estimatefee", 0, "nblocks" }, @@ -116,6 +117,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "setnetworkactive", 0, "state" }, { "getmempoolancestors", 1, "verbose" }, { "getmempooldescendants", 1, "verbose" }, + { "bumpfee", 1, "options" }, // Echo with conversion (For testing only) { "echojson", 0, "arg0" }, { "echojson", 1, "arg1" }, diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index e46f55a8aa..c594daca0d 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -697,7 +697,7 @@ public: bool found; CValidationState state; - submitblock_StateCatcher(const uint256 &hashIn) : hash(hashIn), found(false), state() {}; + submitblock_StateCatcher(const uint256 &hashIn) : hash(hashIn), found(false), state() {} protected: virtual void BlockChecked(const CBlock& block, const CValidationState& stateIn) { @@ -705,7 +705,7 @@ protected: return; found = true; state = stateIn; - }; + } }; UniValue submitblock(const JSONRPCRequest& request) diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 54d8c3e035..480c45516c 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -431,22 +431,16 @@ UniValue setmocktime(const JSONRPCRequest& request) if (!Params().MineBlocksOnDemand()) throw runtime_error("setmocktime for regression testing (-regtest mode) only"); - // cs_vNodes is locked and node send/receive times are updated - // atomically with the time change to prevent peers from being - // disconnected because we think we haven't communicated with them - // in a long time. + // For now, don't change mocktime if we're in the middle of validation, as + // this could have an effect on mempool time-based eviction, as well as + // IsCurrentForFeeEstimation() and IsInitialBlockDownload(). + // TODO: figure out the right way to synchronize around mocktime, and + // ensure all callsites of GetTime() are accessing this safely. LOCK(cs_main); RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VNUM)); SetMockTime(request.params[0].get_int64()); - uint64_t t = GetTime(); - if(g_connman) { - g_connman->ForEachNode([t](CNode* pnode) { - pnode->nLastSend = pnode->nLastRecv = t; - }); - } - return NullUniValue; } diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index acbeb189e0..f590db5efa 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -10,6 +10,7 @@ #include "net.h" #include "net_processing.h" #include "netbase.h" +#include "policy/policy.h" #include "protocol.h" #include "sync.h" #include "timedata.h" @@ -100,13 +101,14 @@ UniValue getpeerinfo(const JSONRPCRequest& request) " \"inflight\": [\n" " n, (numeric) The heights of blocks we're currently asking from this peer\n" " ...\n" - " ]\n" + " ],\n" + " \"whitelisted\": true|false, (boolean) Whether the peer is whitelisted\n" " \"bytessent_per_msg\": {\n" - " \"addr\": n, (numeric) The total bytes sent aggregated by message type\n" + " \"addr\": n, (numeric) The total bytes sent aggregated by message type\n" " ...\n" - " }\n" + " },\n" " \"bytesrecv_per_msg\": {\n" - " \"addr\": n, (numeric) The total bytes received aggregated by message type\n" + " \"addr\": n, (numeric) The total bytes received aggregated by message type\n" " ...\n" " }\n" " }\n" @@ -149,7 +151,7 @@ UniValue getpeerinfo(const JSONRPCRequest& request) obj.push_back(Pair("pingwait", stats.dPingWait)); obj.push_back(Pair("version", stats.nVersion)); // Use the sanitized form of subver here, to avoid tricksy remote peers from - // corrupting or modifiying the JSON output by putting special characters in + // corrupting or modifying the JSON output by putting special characters in // their ver message. obj.push_back(Pair("subver", stats.cleanSubVer)); obj.push_back(Pair("inbound", stats.fInbound)); @@ -411,10 +413,12 @@ UniValue getnetworkinfo(const JSONRPCRequest& request) " \"limited\": true|false, (boolean) is the network limited using -onlynet?\n" " \"reachable\": true|false, (boolean) is the network reachable?\n" " \"proxy\": \"host:port\" (string) the proxy that is used for this network, or empty if none\n" + " \"proxy_randomize_credentials\": true|false, (string) Whether randomized credentials are used\n" " }\n" " ,...\n" " ],\n" " \"relayfee\": x.xxxxxxxx, (numeric) minimum relay fee for non-free transactions in " + CURRENCY_UNIT + "/kB\n" + " \"incrementalfee\": x.xxxxxxxx, (numeric) minimum fee increment for mempool limiting or BIP 125 replacement in " + CURRENCY_UNIT + "/kB\n" " \"localaddresses\": [ (array) list of local addresses\n" " {\n" " \"address\": \"xxxx\", (string) network address\n" @@ -423,7 +427,7 @@ UniValue getnetworkinfo(const JSONRPCRequest& request) " }\n" " ,...\n" " ]\n" - " \"warnings\": \"...\" (string) any network warnings (such as alert messages) \n" + " \"warnings\": \"...\" (string) any network warnings\n" "}\n" "\nExamples:\n" + HelpExampleCli("getnetworkinfo", "") @@ -445,6 +449,7 @@ UniValue getnetworkinfo(const JSONRPCRequest& request) } obj.push_back(Pair("networks", GetNetworksInfo())); obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()))); + obj.push_back(Pair("incrementalfee", ValueFromAmount(::incrementalRelayFee.GetFeePerK()))); UniValue localAddresses(UniValue::VARR); { LOCK(cs_mapLocalHost); diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 4996eb2a69..21396ebb09 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -128,9 +128,11 @@ UniValue getrawtransaction(const JSONRPCRequest& request) if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw runtime_error( "getrawtransaction \"txid\" ( verbose )\n" - "\nNOTE: By default this function only works sometimes. This is when the tx is in the mempool\n" - "or there is an unspent output in the utxo for this transaction. To make it always work,\n" - "you need to maintain a transaction index, using the -txindex command line option.\n" + + "\nNOTE: By default this function only works for mempool transactions. If the -txindex option is\n" + "enabled, it also works for blockchain transactions.\n" + "DEPRECATED: for now, it also works for transactions with unspent outputs.\n" + "\nReturn the raw transaction data.\n" "\nIf verbose is 'true', returns an Object with information about 'txid'.\n" "If verbose is 'false' or omitted, returns a string that is serialized, hex-encoded data for 'txid'.\n" @@ -218,7 +220,9 @@ UniValue getrawtransaction(const JSONRPCRequest& request) CTransactionRef tx; uint256 hashBlock; if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction"); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string(fTxIndex ? "No such mempool or blockchain transaction" + : "No such mempool transaction. Use -txindex to enable blockchain transaction queries") + + ". Use gettransaction for wallet transactions."); string strHex = EncodeHexTx(*tx, RPCSerializationFlags()); @@ -831,7 +835,9 @@ UniValue signrawtransaction(const JSONRPCRequest& request) // ... and merge in other signatures: BOOST_FOREACH(const CMutableTransaction& txv, txVariants) { - sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(txv, i)); + if (txv.vin.size() > i) { + sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(txv, i)); + } } UpdateTransaction(mergedTx, i, sigdata); @@ -899,7 +905,7 @@ UniValue sendrawtransaction(const JSONRPCRequest& request) // push to local node and sync with wallets CValidationState state; bool fMissingInputs; - if (!AcceptToMemoryPool(mempool, state, std::move(tx), fLimitFree, &fMissingInputs, false, nMaxRawTxFee)) { + if (!AcceptToMemoryPool(mempool, state, std::move(tx), fLimitFree, &fMissingInputs, NULL, false, nMaxRawTxFee)) { if (state.IsInvalid()) { throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason())); } else { diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 1b94e10071..283d458c8d 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -79,16 +79,20 @@ void RPCTypeCheck(const UniValue& params, break; const UniValue& v = params[i]; - if (!((v.type() == t) || (fAllowNull && (v.isNull())))) - { - string err = strprintf("Expected type %s, got %s", - uvTypeName(t), uvTypeName(v.type())); - throw JSONRPCError(RPC_TYPE_ERROR, err); + if (!(fAllowNull && v.isNull())) { + RPCTypeCheckArgument(v, t); } i++; } } +void RPCTypeCheckArgument(const UniValue& value, UniValue::VType typeExpected) +{ + if (value.type() != typeExpected) { + throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected type %s, got %s", uvTypeName(typeExpected), uvTypeName(value.type()))); + } +} + void RPCTypeCheckObj(const UniValue& o, const map<string, UniValueType>& typesExpected, bool fAllowNull, diff --git a/src/rpc/server.h b/src/rpc/server.h index fed3d8c90f..52f82866dc 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -78,6 +78,11 @@ bool RPCIsInWarmup(std::string *statusOut); void RPCTypeCheck(const UniValue& params, const std::list<UniValue::VType>& typesExpected, bool fAllowNull=false); +/** + * Type-check one argument; throws JSONRPCError if wrong type given. + */ +void RPCTypeCheckArgument(const UniValue& value, UniValue::VType typeExpected); + /* Check for expected keys/value types in an Object. */ diff --git a/src/script/script.cpp b/src/script/script.cpp index 828ce1a056..9f4741b1cd 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -129,7 +129,7 @@ const char* GetOpName(opcodetype opcode) case OP_CHECKMULTISIG : return "OP_CHECKMULTISIG"; case OP_CHECKMULTISIGVERIFY : return "OP_CHECKMULTISIGVERIFY"; - // expanson + // expansion case OP_NOP1 : return "OP_NOP1"; case OP_CHECKLOCKTIMEVERIFY : return "OP_CHECKLOCKTIMEVERIFY"; case OP_CHECKSEQUENCEVERIFY : return "OP_CHECKSEQUENCEVERIFY"; diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp index 9345a44fb0..a8f09ba6ae 100644 --- a/src/test/DoS_tests.cpp +++ b/src/test/DoS_tests.cpp @@ -55,6 +55,7 @@ BOOST_AUTO_TEST_CASE(DoS_banning) dummyNode1.SetSendVersion(PROTOCOL_VERSION); GetNodeSignals().InitializeNode(&dummyNode1, *connman); dummyNode1.nVersion = 1; + dummyNode1.fSuccessfullyConnected = true; Misbehaving(dummyNode1.GetId(), 100); // Should get banned SendMessages(&dummyNode1, *connman, interruptDummy); BOOST_CHECK(connman->IsBanned(addr1)); @@ -65,6 +66,7 @@ BOOST_AUTO_TEST_CASE(DoS_banning) dummyNode2.SetSendVersion(PROTOCOL_VERSION); GetNodeSignals().InitializeNode(&dummyNode2, *connman); dummyNode2.nVersion = 1; + dummyNode2.fSuccessfullyConnected = true; Misbehaving(dummyNode2.GetId(), 50); SendMessages(&dummyNode2, *connman, interruptDummy); BOOST_CHECK(!connman->IsBanned(addr2)); // 2 not banned yet... @@ -85,6 +87,7 @@ BOOST_AUTO_TEST_CASE(DoS_banscore) dummyNode1.SetSendVersion(PROTOCOL_VERSION); GetNodeSignals().InitializeNode(&dummyNode1, *connman); dummyNode1.nVersion = 1; + dummyNode1.fSuccessfullyConnected = true; Misbehaving(dummyNode1.GetId(), 100); SendMessages(&dummyNode1, *connman, interruptDummy); BOOST_CHECK(!connman->IsBanned(addr1)); @@ -110,6 +113,7 @@ BOOST_AUTO_TEST_CASE(DoS_bantime) dummyNode.SetSendVersion(PROTOCOL_VERSION); GetNodeSignals().InitializeNode(&dummyNode, *connman); dummyNode.nVersion = 1; + dummyNode.fSuccessfullyConnected = true; Misbehaving(dummyNode.GetId(), 100); SendMessages(&dummyNode, *connman, interruptDummy); diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp index 48313915e7..322addc9f6 100644 --- a/src/test/addrman_tests.cpp +++ b/src/test/addrman_tests.cpp @@ -297,7 +297,7 @@ BOOST_AUTO_TEST_CASE(addrman_find) // Test 18; Find does not discriminate by port number. CAddrInfo* info2 = addrman.Find(addr2); BOOST_CHECK(info2); - if (info2) + if (info2 && info1) BOOST_CHECK(info2->ToString() == info1->ToString()); // Test 19: Find returns another IP matching what we searched on. diff --git a/src/test/bitcoin-util-test.py b/src/test/bitcoin-util-test.py index 1c090b3f3f..e2087187aa 100755 --- a/src/test/bitcoin-util-test.py +++ b/src/test/bitcoin-util-test.py @@ -5,7 +5,6 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. from __future__ import division,print_function,unicode_literals import os -import sys import bctest import buildenv import argparse diff --git a/src/test/blockencodings_tests.cpp b/src/test/blockencodings_tests.cpp index e3876e9695..311ac024f3 100644 --- a/src/test/blockencodings_tests.cpp +++ b/src/test/blockencodings_tests.cpp @@ -11,6 +11,8 @@ #include <boost/test/unit_test.hpp> +std::vector<std::pair<uint256, CTransactionRef>> extra_txn; + struct RegtestingSetup : public TestingSetup { RegtestingSetup() : TestingSetup(CBaseChainParams::REGTEST) {} }; @@ -73,7 +75,7 @@ BOOST_AUTO_TEST_CASE(SimpleRoundTripTest) stream >> shortIDs2; PartiallyDownloadedBlock partialBlock(&pool); - BOOST_CHECK(partialBlock.InitData(shortIDs2) == READ_STATUS_OK); + BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == READ_STATUS_OK); BOOST_CHECK( partialBlock.IsTxAvailable(0)); BOOST_CHECK(!partialBlock.IsTxAvailable(1)); BOOST_CHECK( partialBlock.IsTxAvailable(2)); @@ -179,7 +181,7 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest) stream >> shortIDs2; PartiallyDownloadedBlock partialBlock(&pool); - BOOST_CHECK(partialBlock.InitData(shortIDs2) == READ_STATUS_OK); + BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == READ_STATUS_OK); BOOST_CHECK(!partialBlock.IsTxAvailable(0)); BOOST_CHECK( partialBlock.IsTxAvailable(1)); BOOST_CHECK( partialBlock.IsTxAvailable(2)); @@ -245,7 +247,7 @@ BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest) stream >> shortIDs2; PartiallyDownloadedBlock partialBlock(&pool); - BOOST_CHECK(partialBlock.InitData(shortIDs2) == READ_STATUS_OK); + BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == READ_STATUS_OK); BOOST_CHECK( partialBlock.IsTxAvailable(0)); BOOST_CHECK( partialBlock.IsTxAvailable(1)); BOOST_CHECK( partialBlock.IsTxAvailable(2)); @@ -300,7 +302,7 @@ BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest) stream >> shortIDs2; PartiallyDownloadedBlock partialBlock(&pool); - BOOST_CHECK(partialBlock.InitData(shortIDs2) == READ_STATUS_OK); + BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == READ_STATUS_OK); BOOST_CHECK(partialBlock.IsTxAvailable(0)); CBlock block2; diff --git a/src/test/cuckoocache_tests.cpp b/src/test/cuckoocache_tests.cpp index 1bc50d5ea9..00446aa11e 100644 --- a/src/test/cuckoocache_tests.cpp +++ b/src/test/cuckoocache_tests.cpp @@ -15,7 +15,7 @@ * with deterministic seeds) * 2) Some test methods are templated to allow for easier testing * against new versions / comparing - * 3) Results should be treated as a regression test, ie, did the behavior + * 3) Results should be treated as a regression test, i.e., did the behavior * change significantly from what was expected. This can be OK, depending on * the nature of the change, but requires updating the tests to reflect the new * expected behavior. For example improving the hit rate may cause some tests diff --git a/src/test/data/bitcoin-util-test.json b/src/test/data/bitcoin-util-test.json index d001b2056f..a80ab51901 100644 --- a/src/test/data/bitcoin-util-test.json +++ b/src/test/data/bitcoin-util-test.json @@ -118,6 +118,46 @@ "description": "Parses a transation with no inputs and a single output script (output in json)" }, { "exec": "./bitcoin-tx", + "args": ["-create", "outscript=0:OP_DROP", "nversion=1"], + "output_cmp": "txcreatescript1.hex", + "description": "Create a new transaction with a single output script (OP_DROP)" + }, + { "exec": "./bitcoin-tx", + "args": ["-json", "-create", "outscript=0:OP_DROP", "nversion=1"], + "output_cmp": "txcreatescript1.json", + "description": "Create a new transaction with a single output script (OP_DROP) (output as json)" + }, + { "exec": "./bitcoin-tx", + "args": ["-create", "outscript=0:OP_DROP:S", "nversion=1"], + "output_cmp": "txcreatescript2.hex", + "description": "Create a new transaction with a single output script (OP_DROP) in a P2SH" + }, + { "exec": "./bitcoin-tx", + "args": ["-json", "-create", "outscript=0:OP_DROP:S", "nversion=1"], + "output_cmp": "txcreatescript2.json", + "description": "Create a new transaction with a single output script (OP_DROP) in a P2SH (output as json)" + }, + { "exec": "./bitcoin-tx", + "args": ["-create", "outscript=0:OP_DROP:W", "nversion=1"], + "output_cmp": "txcreatescript3.hex", + "description": "Create a new transaction with a single output script (OP_DROP) in a P2WSH" + }, + { "exec": "./bitcoin-tx", + "args": ["-json", "-create", "outscript=0:OP_DROP:W", "nversion=1"], + "output_cmp": "txcreatescript3.json", + "description": "Create a new transaction with a single output script (OP_DROP) in a P2WSH (output as json)" + }, + { "exec": "./bitcoin-tx", + "args": ["-create", "outscript=0:OP_DROP:WS", "nversion=1"], + "output_cmp": "txcreatescript4.hex", + "description": "Create a new transaction with a single output script (OP_DROP) in a P2WSH, wrapped in a P2SH" + }, + { "exec": "./bitcoin-tx", + "args": ["-json", "-create", "outscript=0:OP_DROP:WS", "nversion=1"], + "output_cmp": "txcreatescript4.json", + "description": "Create a new transaction with a single output script (OP_DROP) in a P2SH, wrapped in a P2SH (output as json)" + }, + { "exec": "./bitcoin-tx", "args": ["-create", "nversion=1", "in=4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59485:0", @@ -153,6 +193,42 @@ }, { "exec": "./bitcoin-tx", "args": + ["-create", "outpubkey=0:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397", "nversion=1"], + "output_cmp": "txcreateoutpubkey1.hex", + "description": "Creates a new transaction with a single pay-to-pubkey output" + }, + { "exec": "./bitcoin-tx", + "args": + ["-json", "-create", "outpubkey=0:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397", "nversion=1"], + "output_cmp": "txcreateoutpubkey1.json", + "description": "Creates a new transaction with a single pay-to-pubkey output (output as json)" + }, + { "exec": "./bitcoin-tx", + "args": + ["-create", "outpubkey=0:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:W", "nversion=1"], + "output_cmp": "txcreateoutpubkey2.hex", + "description": "Creates a new transaction with a single pay-to-witness-pubkey output" + }, + { "exec": "./bitcoin-tx", + "args": + ["-json", "-create", "outpubkey=0:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:W", "nversion=1"], + "output_cmp": "txcreateoutpubkey2.json", + "description": "Creates a new transaction with a single pay-to-witness-pubkey output (output as json)" + }, + { "exec": "./bitcoin-tx", + "args": + ["-create", "outpubkey=0:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:WS", "nversion=1"], + "output_cmp": "txcreateoutpubkey3.hex", + "description": "Creates a new transaction with a single pay-to-witness-pubkey, wrapped in P2SH output" + }, + { "exec": "./bitcoin-tx", + "args": + ["-json", "-create", "outpubkey=0:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:WS", "nversion=1"], + "output_cmp": "txcreateoutpubkey3.json", + "description": "Creates a new transaction with a single pay-to-pub-key output, wrapped in P2SH (output as json)" + }, + { "exec": "./bitcoin-tx", + "args": ["-create", "in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0", "outdata=4:badhexdata"], @@ -236,5 +312,45 @@ "in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0:1"], "output_cmp": "txcreatedata_seq1.json", "description": "Adds a new input with sequence number to a transaction (output in json)" + }, + { "exec": "./bitcoin-tx", + "args": ["-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485", "nversion=1"], + "output_cmp": "txcreatemultisig1.hex", + "description": "Creates a new transaction with a single 2-of-3 multisig output" + }, + { "exec": "./bitcoin-tx", + "args": ["-json", "-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485", "nversion=1"], + "output_cmp": "txcreatemultisig1.json", + "description": "Creates a new transaction with a single 2-of-3 multisig output (output in json)" + }, + { "exec": "./bitcoin-tx", + "args": ["-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485:S", "nversion=1"], + "output_cmp": "txcreatemultisig2.hex", + "description": "Creates a new transaction with a single 2-of-3 multisig in a P2SH output" + }, + { "exec": "./bitcoin-tx", + "args": ["-json", "-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485:S", "nversion=1"], + "output_cmp": "txcreatemultisig2.json", + "description": "Creates a new transaction with a single 2-of-3 multisig in a P2SH output (output in json)" + }, + { "exec": "./bitcoin-tx", + "args": ["-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485:W", "nversion=1"], + "output_cmp": "txcreatemultisig3.hex", + "description": "Creates a new transaction with a single 2-of-3 multisig in a P2WSH output" + }, + { "exec": "./bitcoin-tx", + "args": ["-json", "-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485:W", "nversion=1"], + "output_cmp": "txcreatemultisig3.json", + "description": "Creates a new transaction with a single 2-of-3 multisig in a P2WSH output (output in json)" + }, + { "exec": "./bitcoin-tx", + "args": ["-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485:WS", "nversion=1"], + "output_cmp": "txcreatemultisig4.hex", + "description": "Creates a new transaction with a single 2-of-3 multisig in a P2WSH output, wrapped in P2SH" + }, + { "exec": "./bitcoin-tx", + "args": ["-json", "-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485:WS", "nversion=1"], + "output_cmp": "txcreatemultisig4.json", + "description": "Creates a new transaction with a single 2-of-3 multisig in a P2WSH output, wrapped in P2SH (output in json)" } ] diff --git a/src/test/data/txcreatemultisig1.hex b/src/test/data/txcreatemultisig1.hex new file mode 100644 index 0000000000..9c00004d38 --- /dev/null +++ b/src/test/data/txcreatemultisig1.hex @@ -0,0 +1 @@ +01000000000100e1f5050000000069522102a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff39721021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d2102df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb48553ae00000000 diff --git a/src/test/data/txcreatemultisig1.json b/src/test/data/txcreatemultisig1.json new file mode 100644 index 0000000000..f6ce43c202 --- /dev/null +++ b/src/test/data/txcreatemultisig1.json @@ -0,0 +1,26 @@ +{ + "txid": "0d1d4edfc217d9db3ab6a9298f26a52eae3c52f55a6cb8ccbc14f7c727572894", + "hash": "0d1d4edfc217d9db3ab6a9298f26a52eae3c52f55a6cb8ccbc14f7c727572894", + "version": 1, + "locktime": 0, + "vin": [ + ], + "vout": [ + { + "value": 1.00, + "n": 0, + "scriptPubKey": { + "asm": "2 02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397 021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d 02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485 3 OP_CHECKMULTISIG", + "hex": "522102a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff39721021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d2102df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb48553ae", + "reqSigs": 2, + "type": "multisig", + "addresses": [ + "1FoG2386FG2tAJS9acMuiDsKy67aGg9MKz", + "1FXtz9KU8JNmQDyHdiEm5HDiALuP3zdHvV", + "14LuavcBbXZYJ6Tsz3cAUQj9SuQoL2xCQX" + ] + } + } + ], + "hex": "01000000000100e1f5050000000069522102a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff39721021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d2102df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb48553ae00000000" +} diff --git a/src/test/data/txcreatemultisig2.hex b/src/test/data/txcreatemultisig2.hex new file mode 100644 index 0000000000..07835c54d3 --- /dev/null +++ b/src/test/data/txcreatemultisig2.hex @@ -0,0 +1 @@ +01000000000100e1f5050000000017a9141c6fbaf46d64221e80cbae182c33ddf81b9294ac8700000000 diff --git a/src/test/data/txcreatemultisig2.json b/src/test/data/txcreatemultisig2.json new file mode 100644 index 0000000000..e09d22060f --- /dev/null +++ b/src/test/data/txcreatemultisig2.json @@ -0,0 +1,24 @@ +{ + "txid": "0d861f278a3b7bce7cb5a88d71e6e6a903336f95ad5a2c29b295b63835b6eee3", + "hash": "0d861f278a3b7bce7cb5a88d71e6e6a903336f95ad5a2c29b295b63835b6eee3", + "version": 1, + "locktime": 0, + "vin": [ + ], + "vout": [ + { + "value": 1.00, + "n": 0, + "scriptPubKey": { + "asm": "OP_HASH160 1c6fbaf46d64221e80cbae182c33ddf81b9294ac OP_EQUAL", + "hex": "a9141c6fbaf46d64221e80cbae182c33ddf81b9294ac87", + "reqSigs": 1, + "type": "scripthash", + "addresses": [ + "34HNh57oBCRKkxNyjTuWAJkTbuGh6jg2Ms" + ] + } + } + ], + "hex": "01000000000100e1f5050000000017a9141c6fbaf46d64221e80cbae182c33ddf81b9294ac8700000000" +} diff --git a/src/test/data/txcreatemultisig3.hex b/src/test/data/txcreatemultisig3.hex new file mode 100644 index 0000000000..8d34f28f87 --- /dev/null +++ b/src/test/data/txcreatemultisig3.hex @@ -0,0 +1 @@ +01000000000100e1f50500000000220020e15a86a23178f433d514dbbce042e87d72662b8b5edcacfd2e37ab7a2d135f0500000000 diff --git a/src/test/data/txcreatemultisig3.json b/src/test/data/txcreatemultisig3.json new file mode 100644 index 0000000000..88e32bd310 --- /dev/null +++ b/src/test/data/txcreatemultisig3.json @@ -0,0 +1,20 @@ +{ + "txid": "ccc552220b46a3b5140048b03395987ce4f0fa1ddf8c635bba1fa44e0f8c1d7f", + "hash": "ccc552220b46a3b5140048b03395987ce4f0fa1ddf8c635bba1fa44e0f8c1d7f", + "version": 1, + "locktime": 0, + "vin": [ + ], + "vout": [ + { + "value": 1.00, + "n": 0, + "scriptPubKey": { + "asm": "0 e15a86a23178f433d514dbbce042e87d72662b8b5edcacfd2e37ab7a2d135f05", + "hex": "0020e15a86a23178f433d514dbbce042e87d72662b8b5edcacfd2e37ab7a2d135f05", + "type": "witness_v0_scripthash" + } + } + ], + "hex": "01000000000100e1f50500000000220020e15a86a23178f433d514dbbce042e87d72662b8b5edcacfd2e37ab7a2d135f0500000000" +} diff --git a/src/test/data/txcreatemultisig4.hex b/src/test/data/txcreatemultisig4.hex new file mode 100644 index 0000000000..7da54366c7 --- /dev/null +++ b/src/test/data/txcreatemultisig4.hex @@ -0,0 +1 @@ +01000000000100e1f5050000000017a9146edf12858999f0dae74f9c692e6694ee3621b2ac8700000000 diff --git a/src/test/data/txcreatemultisig4.json b/src/test/data/txcreatemultisig4.json new file mode 100644 index 0000000000..fc69c7269c --- /dev/null +++ b/src/test/data/txcreatemultisig4.json @@ -0,0 +1,24 @@ +{ + "txid": "5e8b1cc73234e208d4b7ca9075f136b908c34101be7a048df4ba9ac758b61567", + "hash": "5e8b1cc73234e208d4b7ca9075f136b908c34101be7a048df4ba9ac758b61567", + "version": 1, + "locktime": 0, + "vin": [ + ], + "vout": [ + { + "value": 1.00, + "n": 0, + "scriptPubKey": { + "asm": "OP_HASH160 6edf12858999f0dae74f9c692e6694ee3621b2ac OP_EQUAL", + "hex": "a9146edf12858999f0dae74f9c692e6694ee3621b2ac87", + "reqSigs": 1, + "type": "scripthash", + "addresses": [ + "3BoFUz1StqcNcgUTZE5cC1eFhuYFzj3fGH" + ] + } + } + ], + "hex": "01000000000100e1f5050000000017a9146edf12858999f0dae74f9c692e6694ee3621b2ac8700000000" +} diff --git a/src/test/data/txcreateoutpubkey1.hex b/src/test/data/txcreateoutpubkey1.hex new file mode 100644 index 0000000000..4a08244b2f --- /dev/null +++ b/src/test/data/txcreateoutpubkey1.hex @@ -0,0 +1 @@ +0100000000010000000000000000232102a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397ac00000000 diff --git a/src/test/data/txcreateoutpubkey1.json b/src/test/data/txcreateoutpubkey1.json new file mode 100644 index 0000000000..6019fa2dcd --- /dev/null +++ b/src/test/data/txcreateoutpubkey1.json @@ -0,0 +1,24 @@ +{ + "txid": "f42b38ac12e3fafc96ba1a9ba70cbfe326744aef75df5fb9db5d6e2855ca415f", + "hash": "f42b38ac12e3fafc96ba1a9ba70cbfe326744aef75df5fb9db5d6e2855ca415f", + "version": 1, + "locktime": 0, + "vin": [ + ], + "vout": [ + { + "value": 0.00, + "n": 0, + "scriptPubKey": { + "asm": "02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397 OP_CHECKSIG", + "hex": "2102a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397ac", + "reqSigs": 1, + "type": "pubkey", + "addresses": [ + "1FoG2386FG2tAJS9acMuiDsKy67aGg9MKz" + ] + } + } + ], + "hex": "0100000000010000000000000000232102a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397ac00000000" +} diff --git a/src/test/data/txcreateoutpubkey2.hex b/src/test/data/txcreateoutpubkey2.hex new file mode 100644 index 0000000000..8283c722ab --- /dev/null +++ b/src/test/data/txcreateoutpubkey2.hex @@ -0,0 +1 @@ +0100000000010000000000000000160014a2516e770582864a6a56ed21a102044e388c62e300000000 diff --git a/src/test/data/txcreateoutpubkey2.json b/src/test/data/txcreateoutpubkey2.json new file mode 100644 index 0000000000..6fc3d57527 --- /dev/null +++ b/src/test/data/txcreateoutpubkey2.json @@ -0,0 +1,20 @@ +{ + "txid": "70f2a088cde460e677415fa1fb71895e90c231e6ed38ed203a35b6f848e9cc73", + "hash": "70f2a088cde460e677415fa1fb71895e90c231e6ed38ed203a35b6f848e9cc73", + "version": 1, + "locktime": 0, + "vin": [ + ], + "vout": [ + { + "value": 0.00, + "n": 0, + "scriptPubKey": { + "asm": "0 a2516e770582864a6a56ed21a102044e388c62e3", + "hex": "0014a2516e770582864a6a56ed21a102044e388c62e3", + "type": "witness_v0_keyhash" + } + } + ], + "hex": "0100000000010000000000000000160014a2516e770582864a6a56ed21a102044e388c62e300000000" +} diff --git a/src/test/data/txcreateoutpubkey3.hex b/src/test/data/txcreateoutpubkey3.hex new file mode 100644 index 0000000000..84adff4d89 --- /dev/null +++ b/src/test/data/txcreateoutpubkey3.hex @@ -0,0 +1 @@ +010000000001000000000000000017a914a5ab14c9804d0d8bf02f1aea4e82780733ad0a838700000000 diff --git a/src/test/data/txcreateoutpubkey3.json b/src/test/data/txcreateoutpubkey3.json new file mode 100644 index 0000000000..a1a25fc834 --- /dev/null +++ b/src/test/data/txcreateoutpubkey3.json @@ -0,0 +1,24 @@ +{ + "txid": "bfc7e898ee9f6a9652d7b8cca147e2da134502e2ada0f279ed634fc8cf833f8c", + "hash": "bfc7e898ee9f6a9652d7b8cca147e2da134502e2ada0f279ed634fc8cf833f8c", + "version": 1, + "locktime": 0, + "vin": [ + ], + "vout": [ + { + "value": 0.00, + "n": 0, + "scriptPubKey": { + "asm": "OP_HASH160 a5ab14c9804d0d8bf02f1aea4e82780733ad0a83 OP_EQUAL", + "hex": "a914a5ab14c9804d0d8bf02f1aea4e82780733ad0a8387", + "reqSigs": 1, + "type": "scripthash", + "addresses": [ + "3GnzN8FqgvYGYdhj8NW6UNxxVv3Uj1ApQn" + ] + } + } + ], + "hex": "010000000001000000000000000017a914a5ab14c9804d0d8bf02f1aea4e82780733ad0a838700000000" +} diff --git a/src/test/data/txcreatescript1.hex b/src/test/data/txcreatescript1.hex new file mode 100644 index 0000000000..0adce270fb --- /dev/null +++ b/src/test/data/txcreatescript1.hex @@ -0,0 +1 @@ +0100000000010000000000000000017500000000 diff --git a/src/test/data/txcreatescript1.json b/src/test/data/txcreatescript1.json new file mode 100644 index 0000000000..8ffecba411 --- /dev/null +++ b/src/test/data/txcreatescript1.json @@ -0,0 +1,20 @@ +{ + "txid": "f0851b68202f736b792649cfc960259c2374badcb644ab20cac726b5f72f61c9", + "hash": "f0851b68202f736b792649cfc960259c2374badcb644ab20cac726b5f72f61c9", + "version": 1, + "locktime": 0, + "vin": [ + ], + "vout": [ + { + "value": 0.00, + "n": 0, + "scriptPubKey": { + "asm": "OP_DROP", + "hex": "75", + "type": "nonstandard" + } + } + ], + "hex": "0100000000010000000000000000017500000000" +} diff --git a/src/test/data/txcreatescript2.hex b/src/test/data/txcreatescript2.hex new file mode 100644 index 0000000000..5afe8786e3 --- /dev/null +++ b/src/test/data/txcreatescript2.hex @@ -0,0 +1 @@ +010000000001000000000000000017a91471ed53322d470bb96657deb786b94f97dd46fb158700000000 diff --git a/src/test/data/txcreatescript2.json b/src/test/data/txcreatescript2.json new file mode 100644 index 0000000000..41eb69f1af --- /dev/null +++ b/src/test/data/txcreatescript2.json @@ -0,0 +1,24 @@ +{ + "txid": "6e07a7cc075e0703f32ee8c4e5373fe654bfbc315148fda364e1be286ff290d0", + "hash": "6e07a7cc075e0703f32ee8c4e5373fe654bfbc315148fda364e1be286ff290d0", + "version": 1, + "locktime": 0, + "vin": [ + ], + "vout": [ + { + "value": 0.00, + "n": 0, + "scriptPubKey": { + "asm": "OP_HASH160 71ed53322d470bb96657deb786b94f97dd46fb15 OP_EQUAL", + "hex": "a91471ed53322d470bb96657deb786b94f97dd46fb1587", + "reqSigs": 1, + "type": "scripthash", + "addresses": [ + "3C5QarEGh9feKbDJ3QbMf2YNjnMoiPDhNp" + ] + } + } + ], + "hex": "010000000001000000000000000017a91471ed53322d470bb96657deb786b94f97dd46fb158700000000" +} diff --git a/src/test/data/txcreatescript3.hex b/src/test/data/txcreatescript3.hex new file mode 100644 index 0000000000..8a2b973bf0 --- /dev/null +++ b/src/test/data/txcreatescript3.hex @@ -0,0 +1 @@ +01000000000100000000000000002200200bfe935e70c321c7ca3afc75ce0d0ca2f98b5422e008bb31c00c6d7f1f1c0ad600000000 diff --git a/src/test/data/txcreatescript3.json b/src/test/data/txcreatescript3.json new file mode 100644 index 0000000000..90e7e27f9f --- /dev/null +++ b/src/test/data/txcreatescript3.json @@ -0,0 +1,20 @@ +{ + "txid": "8a234037b088e987c877030efc83374a07441c321bf9dc6dd2f206bc26507df8", + "hash": "8a234037b088e987c877030efc83374a07441c321bf9dc6dd2f206bc26507df8", + "version": 1, + "locktime": 0, + "vin": [ + ], + "vout": [ + { + "value": 0.00, + "n": 0, + "scriptPubKey": { + "asm": "0 0bfe935e70c321c7ca3afc75ce0d0ca2f98b5422e008bb31c00c6d7f1f1c0ad6", + "hex": "00200bfe935e70c321c7ca3afc75ce0d0ca2f98b5422e008bb31c00c6d7f1f1c0ad6", + "type": "witness_v0_scripthash" + } + } + ], + "hex": "01000000000100000000000000002200200bfe935e70c321c7ca3afc75ce0d0ca2f98b5422e008bb31c00c6d7f1f1c0ad600000000" +} diff --git a/src/test/data/txcreatescript4.hex b/src/test/data/txcreatescript4.hex new file mode 100644 index 0000000000..b4cfe58f42 --- /dev/null +++ b/src/test/data/txcreatescript4.hex @@ -0,0 +1 @@ +010000000001000000000000000017a9146a2c482f4985f57e702f325816c90e3723ca81ae8700000000 diff --git a/src/test/data/txcreatescript4.json b/src/test/data/txcreatescript4.json new file mode 100644 index 0000000000..11783751a4 --- /dev/null +++ b/src/test/data/txcreatescript4.json @@ -0,0 +1,24 @@ +{ + "txid": "24225cf5e9391100d6b218134b9f03383ca4c880a1f634ac12990cf28b66adbc", + "hash": "24225cf5e9391100d6b218134b9f03383ca4c880a1f634ac12990cf28b66adbc", + "version": 1, + "locktime": 0, + "vin": [ + ], + "vout": [ + { + "value": 0.00, + "n": 0, + "scriptPubKey": { + "asm": "OP_HASH160 6a2c482f4985f57e702f325816c90e3723ca81ae OP_EQUAL", + "hex": "a9146a2c482f4985f57e702f325816c90e3723ca81ae87", + "reqSigs": 1, + "type": "scripthash", + "addresses": [ + "3BNQbeFeJJGMAyDxPwWPuqxPMrjsFLjk3f" + ] + } + } + ], + "hex": "010000000001000000000000000017a9146a2c482f4985f57e702f325816c90e3723ca81ae8700000000" +} diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp index e5cb48ffcf..d5d158027b 100644 --- a/src/test/dbwrapper_tests.cpp +++ b/src/test/dbwrapper_tests.cpp @@ -76,7 +76,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper_batch) BOOST_CHECK(dbw.Read(key2, res)); BOOST_CHECK_EQUAL(res.ToString(), in2.ToString()); - // key3 never should've been written + // key3 should've never been written BOOST_CHECK(dbw.Read(key3, res) == false); } } diff --git a/src/test/limitedmap_tests.cpp b/src/test/limitedmap_tests.cpp index 55b9be5b00..b071ab117b 100644 --- a/src/test/limitedmap_tests.cpp +++ b/src/test/limitedmap_tests.cpp @@ -27,7 +27,7 @@ BOOST_AUTO_TEST_CASE(limitedmap_test) // make sure that the size is updated BOOST_CHECK(map.size() == 1); - // make sure that the new items is in the map + // make sure that the new item is in the map BOOST_CHECK(map.count(-1) == 1); // insert 10 new items diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 2f74f57d00..f856d8a91a 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -9,6 +9,7 @@ #include "consensus/validation.h" #include "validation.h" #include "miner.h" +#include "policy/policy.h" #include "pubkey.h" #include "script/standard.h" #include "txmempool.h" @@ -24,6 +25,8 @@ BOOST_FIXTURE_TEST_SUITE(miner_tests, TestingSetup) +static CFeeRate blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE); + static struct { unsigned char extranonce; @@ -112,7 +115,7 @@ void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey, BOOST_CHECK(pblocktemplate->block.vtx[2]->GetHash() == hashHighFeeTx); BOOST_CHECK(pblocktemplate->block.vtx[3]->GetHash() == hashMediumFeeTx); - // Test that a package below the min relay fee doesn't get included + // Test that a package below the block min tx fee doesn't get included tx.vin[0].prevout.hash = hashHighFeeTx; tx.vout[0].nValue = 5000000000LL - 1000 - 50000; // 0 fee uint256 hashFreeTx = tx.GetHash(); @@ -120,8 +123,8 @@ void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey, size_t freeTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); // Calculate a fee on child transaction that will put the package just - // below the min relay fee (assuming 1 child tx of the same size). - CAmount feeToUse = minRelayTxFee.GetFee(2*freeTxSize) - 1; + // below the block min tx fee (assuming 1 child tx of the same size). + CAmount feeToUse = blockMinFeeRate.GetFee(2*freeTxSize) - 1; tx.vin[0].prevout.hash = hashFreeTx; tx.vout[0].nValue = 5000000000LL - 1000 - 50000 - feeToUse; @@ -158,7 +161,7 @@ void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey, // This tx can't be mined by itself tx.vin[0].prevout.hash = hashFreeTx2; tx.vout.resize(1); - feeToUse = minRelayTxFee.GetFee(freeTxSize); + feeToUse = blockMinFeeRate.GetFee(freeTxSize); tx.vout[0].nValue = 5000000000LL - 100000000 - feeToUse; uint256 hashLowFeeTx2 = tx.GetHash(); mempool.addUnchecked(hashLowFeeTx2, entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx)); diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp index 0bd7869f32..b9ed4952bb 100644 --- a/src/test/net_tests.cpp +++ b/src/test/net_tests.cpp @@ -162,12 +162,12 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test) bool fInboundIn = false; // Test that fFeeler is false by default. - CNode* pnode1 = new CNode(id++, NODE_NETWORK, height, hSocket, addr, 0, 0, pszDest, fInboundIn); + std::unique_ptr<CNode> pnode1(new CNode(id++, NODE_NETWORK, height, hSocket, addr, 0, 0, pszDest, fInboundIn)); BOOST_CHECK(pnode1->fInbound == false); BOOST_CHECK(pnode1->fFeeler == false); fInboundIn = true; - CNode* pnode2 = new CNode(id++, NODE_NETWORK, height, hSocket, addr, 1, 1, pszDest, fInboundIn); + std::unique_ptr<CNode> pnode2(new CNode(id++, NODE_NETWORK, height, hSocket, addr, 1, 1, pszDest, fInboundIn)); BOOST_CHECK(pnode2->fInbound == true); BOOST_CHECK(pnode2->fFeeler == false); } diff --git a/src/test/raii_event_tests.cpp b/src/test/raii_event_tests.cpp index 87d25c0e2c..0f40874f55 100644 --- a/src/test/raii_event_tests.cpp +++ b/src/test/raii_event_tests.cpp @@ -3,6 +3,10 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <event2/event.h> + +#ifdef EVENT_SET_MEM_FUNCTIONS_IMPLEMENTED +// It would probably be ideal to define dummy test(s) that report skipped, but boost::test doesn't seem to make that practical (at least not in versions available with common distros) + #include <map> #include <stdlib.h> @@ -86,3 +90,5 @@ BOOST_AUTO_TEST_CASE(raii_event_order) } BOOST_AUTO_TEST_SUITE_END() + +#endif // EVENT_SET_MEM_FUNCTIONS_IMPLEMENTED diff --git a/src/test/scheduler_tests.cpp b/src/test/scheduler_tests.cpp index e1b0ab9258..e4ddf9d618 100644 --- a/src/test/scheduler_tests.cpp +++ b/src/test/scheduler_tests.cpp @@ -47,8 +47,8 @@ BOOST_AUTO_TEST_CASE(manythreads) // // So... ten shared counters, which if all the tasks execute // properly will sum to the number of tasks done. - // Each task adds or subtracts from one of the counters a - // random amount, and then schedules another task 0-1000 + // Each task adds or subtracts a random amount from one of the + // counters, and then schedules another task 0-1000 // microseconds in the future to subtract or add from // the counter -random_amount+1, so in the end the shared // counters should sum to the number of initial tasks performed. diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 6f057f43b4..32184165f7 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -465,7 +465,7 @@ public: std::string JSONPrettyPrint(const UniValue& univalue) { std::string ret = univalue.write(4); - // Workaround for libunivalue pretty printer, which puts a space between comma's and newlines + // Workaround for libunivalue pretty printer, which puts a space between commas and newlines size_t pos = 0; while ((pos = ret.find(" \n", pos)) != std::string::npos) { ret.replace(pos, 2, "\n"); diff --git a/src/test/scriptnum_tests.cpp b/src/test/scriptnum_tests.cpp index 6b6689c7d3..1d5893bdc3 100644 --- a/src/test/scriptnum_tests.cpp +++ b/src/test/scriptnum_tests.cpp @@ -12,8 +12,10 @@ BOOST_FIXTURE_TEST_SUITE(scriptnum_tests, BasicTestingSetup) -static const int64_t values[] = \ -{ 0, 1, CHAR_MIN, CHAR_MAX, UCHAR_MAX, SHRT_MIN, USHRT_MAX, INT_MIN, INT_MAX, UINT_MAX, LONG_MIN, LONG_MAX }; +/** A selection of numbers that do not trigger int64_t overflow + * when added/subtracted. */ +static const int64_t values[] = { 0, 1, -2, 127, 128, -255, 256, (1LL << 15) - 1, -(1LL << 16), (1LL << 24) - 1, (1LL << 31), 1 - (1LL << 32), 1LL << 40 }; + static const int64_t offsets[] = { 1, 0x79, 0x80, 0x81, 0xFF, 0x7FFF, 0x8000, 0xFFFF, 0x10000}; static bool verify(const CScriptNum10& bignum, const CScriptNum& scriptnum) diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp index 2d54668eaf..9661a66514 100644 --- a/src/test/serialize_tests.cpp +++ b/src/test/serialize_tests.cpp @@ -90,7 +90,7 @@ BOOST_AUTO_TEST_CASE(sizes) BOOST_AUTO_TEST_CASE(floats_conversion) { - // Choose values that map unambigiously to binary floating point to avoid + // Choose values that map unambiguously to binary floating point to avoid // rounding issues at the compiler side. BOOST_CHECK_EQUAL(ser_uint32_to_float(0x00000000), 0.0F); BOOST_CHECK_EQUAL(ser_uint32_to_float(0x3f000000), 0.5F); @@ -109,7 +109,7 @@ BOOST_AUTO_TEST_CASE(floats_conversion) BOOST_AUTO_TEST_CASE(doubles_conversion) { - // Choose values that map unambigiously to binary floating point to avoid + // Choose values that map unambiguously to binary floating point to avoid // rounding issues at the compiler side. BOOST_CHECK_EQUAL(ser_uint64_to_double(0x0000000000000000ULL), 0.0); BOOST_CHECK_EQUAL(ser_uint64_to_double(0x3fe0000000000000ULL), 0.5); diff --git a/src/test/skiplist_tests.cpp b/src/test/skiplist_tests.cpp index 5b4ef3fe7d..0b2fe0ef9d 100644 --- a/src/test/skiplist_tests.cpp +++ b/src/test/skiplist_tests.cpp @@ -100,4 +100,47 @@ BOOST_AUTO_TEST_CASE(getlocator_test) } } +BOOST_AUTO_TEST_CASE(findearliestatleast_test) +{ + std::vector<uint256> vHashMain(100000); + std::vector<CBlockIndex> vBlocksMain(100000); + for (unsigned int i=0; i<vBlocksMain.size(); i++) { + vHashMain[i] = ArithToUint256(i); // Set the hash equal to the height + vBlocksMain[i].nHeight = i; + vBlocksMain[i].pprev = i ? &vBlocksMain[i - 1] : NULL; + vBlocksMain[i].phashBlock = &vHashMain[i]; + vBlocksMain[i].BuildSkip(); + if (i < 10) { + vBlocksMain[i].nTime = i; + vBlocksMain[i].nTimeMax = i; + } else { + // randomly choose something in the range [MTP, MTP*2] + int64_t medianTimePast = vBlocksMain[i].GetMedianTimePast(); + int r = insecure_rand() % medianTimePast; + vBlocksMain[i].nTime = r + medianTimePast; + vBlocksMain[i].nTimeMax = std::max(vBlocksMain[i].nTime, vBlocksMain[i-1].nTimeMax); + } + } + // Check that we set nTimeMax up correctly. + unsigned int curTimeMax = 0; + for (unsigned int i=0; i<vBlocksMain.size(); ++i) { + curTimeMax = std::max(curTimeMax, vBlocksMain[i].nTime); + BOOST_CHECK(curTimeMax == vBlocksMain[i].nTimeMax); + } + + // Build a CChain for the main branch. + CChain chain; + chain.SetTip(&vBlocksMain.back()); + + // Verify that FindEarliestAtLeast is correct. + for (unsigned int i=0; i<10000; ++i) { + // Pick a random element in vBlocksMain. + int r = insecure_rand() % vBlocksMain.size(); + int64_t test_time = vBlocksMain[r].nTime; + CBlockIndex *ret = chain.FindEarliestAtLeast(test_time); + BOOST_CHECK(ret->nTimeMax >= test_time); + BOOST_CHECK((ret->pprev==NULL) || ret->pprev->nTimeMax < test_time); + BOOST_CHECK(vBlocksMain[r].GetAncestor(ret->nHeight) == ret); + } +} BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index f0eaab2217..4785415e3c 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -147,7 +147,7 @@ CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction &tx, CT } CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CTransaction &txn, CTxMemPool *pool) { - // Hack to assume either its completely dependent on other mempool txs or not at all + // Hack to assume either it's completely dependent on other mempool txs or not at all CAmount inChainValue = pool && pool->HasNoInputsOf(txn) ? txn.GetValueOut() : 0; return CTxMemPoolEntry(MakeTransactionRef(txn), nFee, nTime, dPriority, nHeight, diff --git a/src/test/test_bitcoin_fuzzy.cpp b/src/test/test_bitcoin_fuzzy.cpp index 376d8e428a..c4983f6f5c 100644 --- a/src/test/test_bitcoin_fuzzy.cpp +++ b/src/test/test_bitcoin_fuzzy.cpp @@ -18,6 +18,7 @@ #include "streams.h" #include "undo.h" #include "version.h" +#include "pubkey.h" #include <stdint.h> #include <unistd.h> @@ -60,6 +61,7 @@ bool read_stdin(std::vector<char> &data) { int main(int argc, char **argv) { + ECCVerifyHandle globalVerifyHandle; std::vector<char> buffer; if (!read_stdin(buffer)) return 0; diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 8c9aaef02f..374423179c 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -690,7 +690,7 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) BOOST_CHECK(IsStandardTx(t, reason)); // Check dust with default relay fee: - CAmount nDustThreshold = 182 * minRelayTxFee.GetFeePerK()/1000 * 3; + CAmount nDustThreshold = 182 * dustRelayFee.GetFeePerK()/1000 * 3; BOOST_CHECK_EQUAL(nDustThreshold, 546); // dust: t.vout[0].nValue = nDustThreshold - 1; @@ -701,14 +701,14 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) // Check dust with odd relay fee to verify rounding: // nDustThreshold = 182 * 1234 / 1000 * 3 - minRelayTxFee = CFeeRate(1234); + dustRelayFee = CFeeRate(1234); // dust: t.vout[0].nValue = 672 - 1; BOOST_CHECK(!IsStandardTx(t, reason)); // not dust: t.vout[0].nValue = 672; BOOST_CHECK(IsStandardTx(t, reason)); - minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE); + dustRelayFee = CFeeRate(DUST_RELAY_TX_FEE); t.vout[0].scriptPubKey = CScript() << OP_1; BOOST_CHECK(!IsStandardTx(t, reason)); diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp index acccaee98c..c5367208ba 100644 --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -23,7 +23,7 @@ ToMemPool(CMutableTransaction& tx) LOCK(cs_main); CValidationState state; - return AcceptToMemoryPool(mempool, state, MakeTransactionRef(tx), false, NULL, true, 0); + return AcceptToMemoryPool(mempool, state, MakeTransactionRef(tx), false, NULL, NULL, true, 0); } BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup) diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp index bae0eff7e5..e2b5573abd 100644 --- a/src/test/versionbits_tests.cpp +++ b/src/test/versionbits_tests.cpp @@ -292,7 +292,7 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion) blocksToMine--; nTime += 600; nHeight += 1; - }; + } nTime = nTimeout; // FAILED is only triggered at the end of a period, so CBV should be setting diff --git a/src/timedata.cpp b/src/timedata.cpp index c72252e6d8..2ff6437c73 100644 --- a/src/timedata.cpp +++ b/src/timedata.cpp @@ -17,8 +17,6 @@ #include <boost/foreach.hpp> -using namespace std; - static CCriticalSection cs_nTimeOffset; static int64_t nTimeOffset = 0; @@ -51,7 +49,7 @@ void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample) { LOCK(cs_nTimeOffset); // Ignore duplicates - static set<CNetAddr> setKnown; + static std::set<CNetAddr> setKnown; if (setKnown.size() == BITCOIN_TIMEDATA_MAX_SAMPLES) return; if (!setKnown.insert(ip).second) diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index 5571b7de44..c49c5d9eb2 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -372,7 +372,7 @@ private: struct event *reconnect_ev; float reconnect_timeout; CService service; - /** Cooie for SAFECOOKIE auth */ + /** Cookie for SAFECOOKIE auth */ std::vector<uint8_t> cookie; /** ClientNonce for SAFECOOKIE auth */ std::vector<uint8_t> clientNonce; diff --git a/src/txdb.cpp b/src/txdb.cpp index c223abd590..1a30bb58ad 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -14,8 +14,6 @@ #include <boost/thread.hpp> -using namespace std; - static const char DB_COINS = 'c'; static const char DB_BLOCK_FILES = 'f'; static const char DB_TXINDEX = 't'; @@ -32,11 +30,11 @@ CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(Get } bool CCoinsViewDB::GetCoins(const uint256 &txid, CCoins &coins) const { - return db.Read(make_pair(DB_COINS, txid), coins); + return db.Read(std::make_pair(DB_COINS, txid), coins); } bool CCoinsViewDB::HaveCoins(const uint256 &txid) const { - return db.Exists(make_pair(DB_COINS, txid)); + return db.Exists(std::make_pair(DB_COINS, txid)); } uint256 CCoinsViewDB::GetBestBlock() const { @@ -53,9 +51,9 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) { if (it->second.flags & CCoinsCacheEntry::DIRTY) { if (it->second.coins.IsPruned()) - batch.Erase(make_pair(DB_COINS, it->first)); + batch.Erase(std::make_pair(DB_COINS, it->first)); else - batch.Write(make_pair(DB_COINS, it->first), it->second.coins); + batch.Write(std::make_pair(DB_COINS, it->first), it->second.coins); changed++; } count++; @@ -73,7 +71,7 @@ CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CDBWra } bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) { - return Read(make_pair(DB_BLOCK_FILES, nFile), info); + return Read(std::make_pair(DB_BLOCK_FILES, nFile), info); } bool CBlockTreeDB::WriteReindexing(bool fReindexing) { @@ -139,23 +137,23 @@ void CCoinsViewDBCursor::Next() bool CBlockTreeDB::WriteBatchSync(const std::vector<std::pair<int, const CBlockFileInfo*> >& fileInfo, int nLastFile, const std::vector<const CBlockIndex*>& blockinfo) { CDBBatch batch(*this); for (std::vector<std::pair<int, const CBlockFileInfo*> >::const_iterator it=fileInfo.begin(); it != fileInfo.end(); it++) { - batch.Write(make_pair(DB_BLOCK_FILES, it->first), *it->second); + batch.Write(std::make_pair(DB_BLOCK_FILES, it->first), *it->second); } batch.Write(DB_LAST_BLOCK, nLastFile); for (std::vector<const CBlockIndex*>::const_iterator it=blockinfo.begin(); it != blockinfo.end(); it++) { - batch.Write(make_pair(DB_BLOCK_INDEX, (*it)->GetBlockHash()), CDiskBlockIndex(*it)); + batch.Write(std::make_pair(DB_BLOCK_INDEX, (*it)->GetBlockHash()), CDiskBlockIndex(*it)); } return WriteBatch(batch, true); } bool CBlockTreeDB::ReadTxIndex(const uint256 &txid, CDiskTxPos &pos) { - return Read(make_pair(DB_TXINDEX, txid), pos); + return Read(std::make_pair(DB_TXINDEX, txid), pos); } bool CBlockTreeDB::WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> >&vect) { CDBBatch batch(*this); for (std::vector<std::pair<uint256,CDiskTxPos> >::const_iterator it=vect.begin(); it!=vect.end(); it++) - batch.Write(make_pair(DB_TXINDEX, it->first), it->second); + batch.Write(std::make_pair(DB_TXINDEX, it->first), it->second); return WriteBatch(batch); } @@ -175,7 +173,7 @@ bool CBlockTreeDB::LoadBlockIndexGuts(boost::function<CBlockIndex*(const uint256 { std::unique_ptr<CDBIterator> pcursor(NewIterator()); - pcursor->Seek(make_pair(DB_BLOCK_INDEX, uint256())); + pcursor->Seek(std::make_pair(DB_BLOCK_INDEX, uint256())); // Load mapBlockIndex while (pcursor->Valid()) { diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 4f4540a1fc..236527a2ba 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -18,8 +18,6 @@ #include "utiltime.h" #include "version.h" -using namespace std; - CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee, int64_t _nTime, double _entryPriority, unsigned int _entryHeight, CAmount _inChainInputValue, @@ -173,6 +171,8 @@ void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashes bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry, setEntries &setAncestors, uint64_t limitAncestorCount, uint64_t limitAncestorSize, uint64_t limitDescendantCount, uint64_t limitDescendantSize, std::string &errString, bool fSearchForParents /* = true */) const { + LOCK(cs); + setEntries parentHashes; const CTransaction &tx = entry.GetTx(); @@ -359,7 +359,6 @@ CTxMemPool::CTxMemPool(const CFeeRate& _minReasonableRelayFee) : nCheckFrequency = 0; minerPolicyEstimator = new CBlockPolicyEstimator(_minReasonableRelayFee); - minReasonableRelayFee = _minReasonableRelayFee; } CTxMemPool::~CTxMemPool() @@ -394,6 +393,7 @@ void CTxMemPool::AddTransactionsUpdated(unsigned int n) bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, setEntries &setAncestors, bool validFeeEstimate) { + NotifyEntryAdded(entry.GetSharedTx()); // Add to memory pool without checking anything. // Used by main.cpp AcceptToMemoryPool(), which DOES do // all the appropriate checks. @@ -450,8 +450,9 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, return true; } -void CTxMemPool::removeUnchecked(txiter it) +void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason) { + NotifyEntryRemoved(it->GetSharedTx(), reason); const uint256 hash = it->GetTx().GetHash(); BOOST_FOREACH(const CTxIn& txin, it->GetTx().vin) mapNextTx.erase(txin.prevout); @@ -503,7 +504,7 @@ void CTxMemPool::CalculateDescendants(txiter entryit, setEntries &setDescendants } } -void CTxMemPool::removeRecursive(const CTransaction &origTx) +void CTxMemPool::removeRecursive(const CTransaction &origTx, MemPoolRemovalReason reason) { // Remove transaction from memory pool { @@ -530,7 +531,8 @@ void CTxMemPool::removeRecursive(const CTransaction &origTx) BOOST_FOREACH(txiter it, txToRemove) { CalculateDescendants(it, setAllRemoves); } - RemoveStaged(setAllRemoves, false); + + RemoveStaged(setAllRemoves, false, reason); } } @@ -568,7 +570,7 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMem for (txiter it : txToRemove) { CalculateDescendants(it, setAllRemoves); } - RemoveStaged(setAllRemoves, false); + RemoveStaged(setAllRemoves, false, MemPoolRemovalReason::REORG); } void CTxMemPool::removeConflicts(const CTransaction &tx) @@ -581,8 +583,8 @@ void CTxMemPool::removeConflicts(const CTransaction &tx) const CTransaction &txConflict = *it->second; if (txConflict != tx) { - removeRecursive(txConflict); ClearPrioritisation(txConflict.GetHash()); + removeRecursive(txConflict, MemPoolRemovalReason::CONFLICT); } } } @@ -611,7 +613,7 @@ void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigne if (it != mapTx.end()) { setEntries stage; stage.insert(it); - RemoveStaged(stage, true); + RemoveStaged(stage, true, MemPoolRemovalReason::BLOCK); } removeConflicts(*tx); ClearPrioritisation(tx->GetHash()); @@ -656,7 +658,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const const int64_t nSpendHeight = GetSpendHeight(mempoolDuplicate); LOCK(cs); - list<const CTxMemPoolEntry*> waitingOnDependants; + std::list<const CTxMemPoolEntry*> waitingOnDependants; for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { unsigned int i = 0; checkTotal += it->GetTxSize(); @@ -814,7 +816,7 @@ std::vector<CTxMemPool::indexed_transaction_set::const_iterator> CTxMemPool::Get return iters; } -void CTxMemPool::queryHashes(vector<uint256>& vtxid) +void CTxMemPool::queryHashes(std::vector<uint256>& vtxid) { LOCK(cs); auto iters = GetSortedDepthAndScore(); @@ -918,7 +920,7 @@ CTxMemPool::ReadFeeEstimates(CAutoFile& filein) return true; } -void CTxMemPool::PrioritiseTransaction(const uint256 hash, const string strHash, double dPriorityDelta, const CAmount& nFeeDelta) +void CTxMemPool::PrioritiseTransaction(const uint256 hash, const std::string strHash, double dPriorityDelta, const CAmount& nFeeDelta) { { LOCK(cs); @@ -990,11 +992,11 @@ size_t CTxMemPool::DynamicMemoryUsage() const { return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 15 * sizeof(void*)) * mapTx.size() + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + memusage::DynamicUsage(mapLinks) + memusage::DynamicUsage(vTxHashes) + cachedInnerUsage; } -void CTxMemPool::RemoveStaged(setEntries &stage, bool updateDescendants) { +void CTxMemPool::RemoveStaged(setEntries &stage, bool updateDescendants, MemPoolRemovalReason reason) { AssertLockHeld(cs); UpdateForRemoveFromMempool(stage, updateDescendants); BOOST_FOREACH(const txiter& it, stage) { - removeUnchecked(it); + removeUnchecked(it, reason); } } @@ -1010,7 +1012,7 @@ int CTxMemPool::Expire(int64_t time) { BOOST_FOREACH(txiter removeit, toremove) { CalculateDescendants(removeit, stage); } - RemoveStaged(stage, false); + RemoveStaged(stage, false, MemPoolRemovalReason::EXPIRY); return stage.size(); } @@ -1076,12 +1078,12 @@ CFeeRate CTxMemPool::GetMinFee(size_t sizelimit) const { rollingMinimumFeeRate = rollingMinimumFeeRate / pow(2.0, (time - lastRollingFeeUpdate) / halflife); lastRollingFeeUpdate = time; - if (rollingMinimumFeeRate < (double)minReasonableRelayFee.GetFeePerK() / 2) { + if (rollingMinimumFeeRate < (double)incrementalRelayFee.GetFeePerK() / 2) { rollingMinimumFeeRate = 0; return CFeeRate(0); } } - return std::max(CFeeRate(rollingMinimumFeeRate), minReasonableRelayFee); + return std::max(CFeeRate(rollingMinimumFeeRate), incrementalRelayFee); } void CTxMemPool::trackPackageRemoved(const CFeeRate& rate) { @@ -1105,7 +1107,7 @@ void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<uint256>* pvNoSpendsRe // to have 0 fee). This way, we don't allow txn to enter mempool with feerate // equal to txn which were removed with no block in between. CFeeRate removed(it->GetModFeesWithDescendants(), it->GetSizeWithDescendants()); - removed += minReasonableRelayFee; + removed += incrementalRelayFee; trackPackageRemoved(removed); maxFeeRateRemoved = std::max(maxFeeRateRemoved, removed); @@ -1119,7 +1121,7 @@ void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<uint256>* pvNoSpendsRe BOOST_FOREACH(txiter iter, stage) txn.push_back(iter->GetTx()); } - RemoveStaged(stage, false); + RemoveStaged(stage, false, MemPoolRemovalReason::SIZELIMIT); if (pvNoSpendsRemaining) { BOOST_FOREACH(const CTransaction& tx, txn) { BOOST_FOREACH(const CTxIn& txin, tx.vin) { diff --git a/src/txmempool.h b/src/txmempool.h index 6a00b540a5..db1a02455f 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -25,6 +25,8 @@ #include "boost/multi_index/ordered_index.hpp" #include "boost/multi_index/hashed_index.hpp" +#include <boost/signals2/signal.hpp> + class CAutoFile; class CBlockIndex; @@ -333,6 +335,19 @@ struct TxMempoolInfo int64_t nFeeDelta; }; +/** Reason why a transaction was removed from the mempool, + * this is passed to the notification signal. + */ +enum class MemPoolRemovalReason { + UNKNOWN = 0, //! Manually removed or unknown reason + EXPIRY, //! Expired from mempool + SIZELIMIT, //! Removed in size limiting + REORG, //! Removed for reorganization + BLOCK, //! Removed for block + CONFLICT, //! Removed for conflict with in-block transaction + REPLACED //! Removed for replacement +}; + /** * CTxMemPool stores valid-according-to-the-current-best-chain transactions * that may be included in the next block. @@ -340,7 +355,7 @@ struct TxMempoolInfo * Transactions are added when they are seen on the network (or created by the * local node), but not all transactions seen are added to the pool. For * example, the following new transactions will not be added to the mempool: - * - a transaction which doesn't make the mimimum fee requirements. + * - a transaction which doesn't meet the minimum fee requirements. * - a new transaction that double-spends an input of a transaction already in * the pool where the new transaction does not meet the Replace-By-Fee * requirements as defined in BIP 125. @@ -423,8 +438,6 @@ private: uint64_t totalTxSize; //!< sum of all mempool tx's virtual sizes. Differs from serialized tx size since witness data is discounted. Defined in BIP 141. uint64_t cachedInnerUsage; //!< sum of dynamic memory usage of all the map elements (NOT the maps themselves) - CFeeRate minReasonableRelayFee; - mutable int64_t lastRollingFeeUpdate; mutable bool blockSinceLastRollingFeeBump; mutable double rollingMinimumFeeRate; //!< minimum fee to get into the pool, decreases exponentially @@ -503,9 +516,6 @@ public: std::map<uint256, std::pair<double, CAmount> > mapDeltas; /** Create a new CTxMemPool. - * minReasonableRelayFee should be a feerate which is, roughly, somewhere - * around what it "costs" to relay a transaction around the network and - * below which we would reasonably say a transaction has 0-effective-fee. */ CTxMemPool(const CFeeRate& _minReasonableRelayFee); ~CTxMemPool(); @@ -526,10 +536,11 @@ public: bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, bool validFeeEstimate = true); bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, setEntries &setAncestors, bool validFeeEstimate = true); - void removeRecursive(const CTransaction &tx); + void removeRecursive(const CTransaction &tx, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN); void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags); void removeConflicts(const CTransaction &tx); void removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight); + void clear(); void _clear(); //lock free bool CompareDepthAndScore(const uint256& hasha, const uint256& hashb); @@ -556,7 +567,7 @@ public: * Set updateDescendants to true when removing a tx that was in a block, so * that any in-mempool descendants have their ancestor state updated. */ - void RemoveStaged(setEntries &stage, bool updateDescendants); + void RemoveStaged(setEntries &stage, bool updateDescendants, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN); /** When adding transactions from a disconnected block back to the mempool, * new mempool entries may have children in the mempool (which is generally @@ -588,7 +599,7 @@ public: /** The minimum fee to get into the mempool, which may itself not be enough * for larger-sized transactions. - * The minReasonableRelayFee constructor arg is used to bound the time it + * The incrementalRelayFee policy variable is used to bound the time it * takes the fee rate to go back down all the way to 0. When the feerate * would otherwise be half of this, it is set to 0 instead. */ @@ -652,6 +663,9 @@ public: size_t DynamicMemoryUsage() const; + boost::signals2::signal<void (CTransactionRef)> NotifyEntryAdded; + boost::signals2::signal<void (CTransactionRef, MemPoolRemovalReason)> NotifyEntryRemoved; + private: /** UpdateForDescendants is used by UpdateTransactionsFromBlock to update * the descendants for a single transaction that has been added to the @@ -688,7 +702,7 @@ private: * transactions in a chain before we've updated all the state for the * removal. */ - void removeUnchecked(txiter entry); + void removeUnchecked(txiter entry, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN); }; /** diff --git a/src/util.cpp b/src/util.cpp index 08ee6b8b87..ba157625d8 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -723,13 +723,17 @@ void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) { void ShrinkDebugFile() { + // Amount of debug.log to save at end when shrinking (must fit in memory) + constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000; // Scroll debug.log if it's getting too big boost::filesystem::path pathLog = GetDataDir() / "debug.log"; FILE* file = fopen(pathLog.string().c_str(), "r"); - if (file && boost::filesystem::file_size(pathLog) > 10 * 1000000) + // If debug.log file is more than 10% bigger the RECENT_DEBUG_HISTORY_SIZE + // trim it down by saving only the last RECENT_DEBUG_HISTORY_SIZE bytes + if (file && boost::filesystem::file_size(pathLog) > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10)) { // Restart the file with some of the end - std::vector <char> vch(200000,0); + std::vector<char> vch(RECENT_DEBUG_HISTORY_SIZE, 0); fseek(file, -((long)vch.size()), SEEK_END); int nBytes = fread(vch.data(), 1, vch.size(), file); fclose(file); diff --git a/src/utiltime.cpp b/src/utiltime.cpp index 7c5ee77265..c7b3e4f168 100644 --- a/src/utiltime.cpp +++ b/src/utiltime.cpp @@ -46,6 +46,11 @@ int64_t GetTimeMicros() return now; } +int64_t GetSystemTimeInSeconds() +{ + return GetTimeMicros()/1000000; +} + /** Return a time useful for the debug log */ int64_t GetLogTimeMicros() { @@ -58,7 +63,7 @@ void MilliSleep(int64_t n) { /** - * Boost's sleep_for was uninterruptable when backed by nanosleep from 1.50 + * Boost's sleep_for was uninterruptible when backed by nanosleep from 1.50 * until fixed in 1.52. Use the deprecated sleep method for the broken case. * See: https://svn.boost.org/trac/boost/ticket/7238 */ diff --git a/src/utiltime.h b/src/utiltime.h index b2807267db..05c6790495 100644 --- a/src/utiltime.h +++ b/src/utiltime.h @@ -9,9 +9,20 @@ #include <stdint.h> #include <string> +/** + * GetTimeMicros() and GetTimeMillis() both return the system time, but in + * different units. GetTime() returns the sytem time in seconds, but also + * supports mocktime, where the time can be specified by the user, eg for + * testing (eg with the setmocktime rpc, or -mocktime argument). + * + * TODO: Rework these functions to be type-safe (so that we don't inadvertently + * compare numbers with different units, or compare a mocktime to system time). + */ + int64_t GetTime(); int64_t GetTimeMillis(); int64_t GetTimeMicros(); +int64_t GetSystemTimeInSeconds(); // Like GetTime(), but not mockable int64_t GetLogTimeMicros(); void SetMockTime(int64_t nMockTimeIn); void MilliSleep(int64_t n); diff --git a/src/validation.cpp b/src/validation.cpp index 37a4186e0a..fe8f8365be 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -46,8 +46,6 @@ #include <boost/math/distributions/poisson.hpp> #include <boost/thread.hpp> -using namespace std; - #if defined(NDEBUG) # error "Bitcoin cannot be compiled without assertions." #endif @@ -78,6 +76,7 @@ uint64_t nPruneTarget = 0; int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE; bool fEnableReplacement = DEFAULT_ENABLE_REPLACEMENT; +uint256 hashAssumeValid; CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE); CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE; @@ -89,7 +88,7 @@ static void CheckBlockIndex(const Consensus::Params& consensusParams); /** Constant stuff for coinbase transactions we create: */ CScript COINBASE_FLAGS; -const string strMessageMagic = "Bitcoin Signed Message:\n"; +const std::string strMessageMagic = "Bitcoin Signed Message:\n"; // Internal stuff namespace { @@ -122,11 +121,11 @@ namespace { * as good as our current tip or better. Entries may be failed, though, and pruning nodes may be * missing the data for the block. */ - set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates; + std::set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates; /** All pairs A->B, where A (or one of its ancestors) misses transactions, but B has transactions. * Pruned nodes may have entries where B is missing data. */ - multimap<CBlockIndex*, CBlockIndex*> mapBlocksUnlinked; + std::multimap<CBlockIndex*, CBlockIndex*> mapBlocksUnlinked; CCriticalSection cs_LastBlockFile; std::vector<CBlockFileInfo> vinfoBlockFile; @@ -150,12 +149,45 @@ namespace { arith_uint256 nLastPreciousChainwork = 0; /** Dirty block index entries. */ - set<CBlockIndex*> setDirtyBlockIndex; + std::set<CBlockIndex*> setDirtyBlockIndex; /** Dirty block file entries. */ - set<int> setDirtyFileInfo; + std::set<int> setDirtyFileInfo; } // anon namespace +/* Use this class to start tracking transactions that are removed from the + * mempool and pass all those transactions through SyncTransaction when the + * object goes out of scope. This is currently only used to call SyncTransaction + * on conflicts removed from the mempool during block connection. Applied in + * ActivateBestChain around ActivateBestStep which in turn calls: + * ConnectTip->removeForBlock->removeConflicts + */ +class MemPoolConflictRemovalTracker +{ +private: + std::vector<CTransactionRef> conflictedTxs; + CTxMemPool &pool; + +public: + MemPoolConflictRemovalTracker(CTxMemPool &_pool) : pool(_pool) { + pool.NotifyEntryRemoved.connect(boost::bind(&MemPoolConflictRemovalTracker::NotifyEntryRemoved, this, _1, _2)); + } + + void NotifyEntryRemoved(CTransactionRef txRemoved, MemPoolRemovalReason reason) { + if (reason == MemPoolRemovalReason::CONFLICT) { + conflictedTxs.push_back(txRemoved); + } + } + + ~MemPoolConflictRemovalTracker() { + pool.NotifyEntryRemoved.disconnect(boost::bind(&MemPoolConflictRemovalTracker::NotifyEntryRemoved, this, _1, _2)); + for (const auto& tx : conflictedTxs) { + GetMainSignals().SyncTransaction(*tx, NULL, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK); + } + conflictedTxs.clear(); + } +}; + CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator) { // Find the first block the caller has in the main chain @@ -185,7 +217,8 @@ enum FlushStateMode { }; // See definition for documentation -bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode); +bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode, int nManualPruneHeight=0); +void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight); bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) { @@ -482,7 +515,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe // Check for duplicate inputs - note that this check is slow so we skip it in CheckBlock if (fCheckDuplicateInputs) { - set<COutPoint> vInOutPoints; + std::set<COutPoint> vInOutPoints; for (const auto& txin : tx.vin) { if (!vInOutPoints.insert(txin.prevout).second) @@ -538,8 +571,8 @@ static bool IsCurrentForFeeEstimation() } bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx, bool fLimitFree, - bool* pfMissingInputs, int64_t nAcceptTime, bool fOverrideMempoolLimit, const CAmount& nAbsurdFee, - std::vector<uint256>& vHashTxnToUncache) + bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced, + bool fOverrideMempoolLimit, const CAmount& nAbsurdFee, std::vector<uint256>& vHashTxnToUncache) { const CTransaction& tx = *ptx; const uint256 hash = tx.GetHash(); @@ -561,7 +594,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C } // Rather not work on nonstandard transactions (unless -testnet/-regtest) - string reason; + std::string reason; if (fRequireStandard && !IsStandardTx(tx, reason, witnessEnabled)) return state.DoS(0, false, REJECT_NONSTANDARD, reason); @@ -576,7 +609,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-in-mempool"); // Check for conflicts with in-memory transactions - set<uint256> setConflicts; + std::set<uint256> setConflicts; { LOCK(pool.cs); // protect pool.mapNextTx BOOST_FOREACH(const CTxIn &txin, tx.vin) @@ -792,10 +825,11 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C // subsequent RemoveStaged() and addUnchecked() calls don't guarantee // mempool consistency for us. LOCK(pool.cs); - if (setConflicts.size()) + const bool fReplacementTransaction = setConflicts.size(); + if (fReplacementTransaction) { CFeeRate newFeeRate(nModifiedFees, nSize); - set<uint256> setConflictsParents; + std::set<uint256> setConflictsParents; const int maxDescendantsToVisit = 100; CTxMemPool::setEntries setIterConflicting; BOOST_FOREACH(const uint256 &hashConflicting, setConflicts) @@ -896,14 +930,14 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C // Finally in addition to paying more fees than the conflicts the // new transaction must pay for its own bandwidth. CAmount nDeltaFees = nModifiedFees - nConflictingFees; - if (nDeltaFees < ::minRelayTxFee.GetFee(nSize)) + if (nDeltaFees < ::incrementalRelayFee.GetFee(nSize)) { return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient fee", false, strprintf("rejecting replacement %s, not enough additional fees to relay; %s < %s", hash.ToString(), FormatMoney(nDeltaFees), - FormatMoney(::minRelayTxFee.GetFee(nSize)))); + FormatMoney(::incrementalRelayFee.GetFee(nSize)))); } } @@ -919,12 +953,13 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C // SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we // need to turn both off, and compare against just turning off CLEANSTACK // to see if the failure is specifically due to witness validation. - if (!tx.HasWitness() && CheckInputs(tx, state, view, true, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, txdata) && - !CheckInputs(tx, state, view, true, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, txdata)) { + CValidationState stateDummy; // Want reported failures to be from first CheckInputs + if (!tx.HasWitness() && CheckInputs(tx, stateDummy, view, true, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, txdata) && + !CheckInputs(tx, stateDummy, view, true, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, txdata)) { // Only the witness is missing, so the transaction itself may be fine. state.SetCorruptionPossible(); } - return false; + return false; // state filled in by CheckInputs } // Check again against just the consensus-critical mandatory script @@ -950,13 +985,16 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C hash.ToString(), FormatMoney(nModifiedFees - nConflictingFees), (int)nSize - (int)nConflictingSize); + if (plTxnReplaced) + plTxnReplaced->push_back(it->GetSharedTx()); } - pool.RemoveStaged(allConflicting, false); + pool.RemoveStaged(allConflicting, false, MemPoolRemovalReason::REPLACED); - // This transaction should only count for fee estimation if - // the node is not behind and it is not dependent on any other - // transactions in the mempool - bool validForFeeEstimation = IsCurrentForFeeEstimation() && pool.HasNoInputsOf(tx); + // This transaction should only count for fee estimation if it isn't a + // BIP 125 replacement transaction (may not be widely supported), the + // node is not behind, and the transaction is not dependent on any other + // transactions in the mempool. + bool validForFeeEstimation = !fReplacementTransaction && IsCurrentForFeeEstimation() && pool.HasNoInputsOf(tx); // Store transaction in memory pool.addUnchecked(hash, entry, setAncestors, validForFeeEstimation); @@ -975,10 +1013,11 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C } bool AcceptToMemoryPoolWithTime(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree, - bool* pfMissingInputs, int64_t nAcceptTime, bool fOverrideMempoolLimit, const CAmount nAbsurdFee) + bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced, + bool fOverrideMempoolLimit, const CAmount nAbsurdFee) { std::vector<uint256> vHashTxToUncache; - bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, nAcceptTime, fOverrideMempoolLimit, nAbsurdFee, vHashTxToUncache); + bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, nAcceptTime, plTxnReplaced, fOverrideMempoolLimit, nAbsurdFee, vHashTxToUncache); if (!res) { BOOST_FOREACH(const uint256& hashTx, vHashTxToUncache) pcoinsTip->Uncache(hashTx); @@ -990,9 +1029,10 @@ bool AcceptToMemoryPoolWithTime(CTxMemPool& pool, CValidationState &state, const } bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree, - bool* pfMissingInputs, bool fOverrideMempoolLimit, const CAmount nAbsurdFee) + bool* pfMissingInputs, std::list<CTransactionRef>* plTxnReplaced, + bool fOverrideMempoolLimit, const CAmount nAbsurdFee) { - return AcceptToMemoryPoolWithTime(pool, state, tx, fLimitFree, pfMissingInputs, GetTime(), fOverrideMempoolLimit, nAbsurdFee); + return AcceptToMemoryPoolWithTime(pool, state, tx, fLimitFree, pfMissingInputs, GetTime(), plTxnReplaced, fOverrideMempoolLimit, nAbsurdFee); } /** Return transaction in txOut, and if it was found inside a block, its hash is placed in hashBlock */ @@ -1388,11 +1428,10 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi // Only if ALL inputs pass do we perform expensive ECDSA signature checks. // Helps prevent CPU exhaustion attacks. - // Skip ECDSA signature verification when connecting blocks before the - // last block chain checkpoint. Assuming the checkpoints are valid this + // Skip script verification when connecting blocks under the + // assumedvalid block. Assuming the assumedvalid block is valid this // is safe because block merkle hashes are still computed and checked, - // and any change will be caught at the next checkpoint. Of course, if - // the checkpoint is for a chain that's invalid due to false scriptSigs + // Of course, if an assumed valid block is invalid due to false scriptSigs // this optimization would allow an invalid chain to be accepted. if (fScriptChecks) { for (unsigned int i = 0; i < tx.vin.size(); i++) { @@ -1720,11 +1759,28 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } bool fScriptChecks = true; - if (fCheckpointsEnabled) { - CBlockIndex *pindexLastCheckpoint = Checkpoints::GetLastCheckpoint(chainparams.Checkpoints()); - if (pindexLastCheckpoint && pindexLastCheckpoint->GetAncestor(pindex->nHeight) == pindex) { - // This block is an ancestor of a checkpoint: disable script checks - fScriptChecks = false; + if (!hashAssumeValid.IsNull()) { + // We've been configured with the hash of a block which has been externally verified to have a valid history. + // A suitable default value is included with the software and updated from time to time. Because validity + // relative to a piece of software is an objective fact these defaults can be easily reviewed. + // This setting doesn't force the selection of any particular chain but makes validating some faster by + // effectively caching the result of part of the verification. + BlockMap::const_iterator it = mapBlockIndex.find(hashAssumeValid); + if (it != mapBlockIndex.end()) { + if (it->second->GetAncestor(pindex->nHeight) == pindex && + pindexBestHeader->GetAncestor(pindex->nHeight) == pindex && + pindexBestHeader->nChainWork >= UintToArith256(chainparams.GetConsensus().nMinimumChainWork)) { + // This block is a member of the assumed verified chain and an ancestor of the best header. + // The equivalent time check discourages hashpower from extorting the network via DOS attack + // into accepting an invalid block through telling users they must manually set assumevalid. + // Requiring a software change or burying the invalid block, regardless of the setting, makes + // it hard to hide the implication of the demand. This also avoids having release candidates + // that are hardly doing any signature verification at all in testing without having to + // artificially set the default assumed verified block further back. + // The test against nMinimumChainWork prevents the skipping when denied access to any chain at + // least as good as the expected chain. + fScriptChecks = (GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, chainparams.GetConsensus()) <= 60 * 60 * 24 * 7 * 2); + } } } @@ -1934,7 +1990,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin * if they're too large, if it's been a while since the last write, * or always and in all cases if we're in prune mode and are deleting files. */ -bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { +bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode, int nManualPruneHeight) { int64_t nMempoolUsage = mempool.DynamicMemoryUsage(); const CChainParams& chainparams = Params(); LOCK2(cs_main, cs_LastBlockFile); @@ -1944,9 +2000,13 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { std::set<int> setFilesToPrune; bool fFlushForPrune = false; try { - if (fPruneMode && fCheckForPruning && !fReindex) { - FindFilesToPrune(setFilesToPrune, chainparams.PruneAfterHeight()); - fCheckForPruning = false; + if (fPruneMode && (fCheckForPruning || nManualPruneHeight > 0) && !fReindex) { + if (nManualPruneHeight > 0) { + FindFilesToPruneManual(setFilesToPrune, nManualPruneHeight); + } else { + FindFilesToPrune(setFilesToPrune, chainparams.PruneAfterHeight()); + fCheckForPruning = false; + } if (!setFilesToPrune.empty()) { fFlushForPrune = true; if (!fHavePruned) { @@ -1990,18 +2050,18 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { { std::vector<std::pair<int, const CBlockFileInfo*> > vFiles; vFiles.reserve(setDirtyFileInfo.size()); - for (set<int>::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end(); ) { - vFiles.push_back(make_pair(*it, &vinfoBlockFile[*it])); + for (std::set<int>::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end(); ) { + vFiles.push_back(std::make_pair(*it, &vinfoBlockFile[*it])); setDirtyFileInfo.erase(it++); } std::vector<const CBlockIndex*> vBlocks; vBlocks.reserve(setDirtyBlockIndex.size()); - for (set<CBlockIndex*>::iterator it = setDirtyBlockIndex.begin(); it != setDirtyBlockIndex.end(); ) { + for (std::set<CBlockIndex*>::iterator it = setDirtyBlockIndex.begin(); it != setDirtyBlockIndex.end(); ) { vBlocks.push_back(*it); setDirtyBlockIndex.erase(it++); } if (!pblocktree->WriteBatchSync(vFiles, nLastBlockFile, vBlocks)) { - return AbortNode(state, "Files to write to block index database"); + return AbortNode(state, "Failed to write to block index database"); } } // Finally remove any pruned files @@ -2101,7 +2161,7 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) { chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), chainActive.Tip()->nVersion, log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx, DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), - Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.Tip()), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize()); + GuessVerificationProgress(chainParams.TxData(), chainActive.Tip()), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize()); if (!warningMessages.empty()) LogPrintf(" warning='%s'", boost::algorithm::join(warningMessages, ", ")); LogPrintf("\n"); @@ -2138,8 +2198,8 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara const CTransaction& tx = *it; // ignore validation errors in resurrected transactions CValidationState stateDummy; - if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, it, false, NULL, true)) { - mempool.removeRecursive(tx); + if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, it, false, NULL, NULL, true)) { + mempool.removeRecursive(tx, MemPoolRemovalReason::REORG); } else if (mempool.exists(tx.GetHash())) { vHashUpdate.push_back(tx.GetHash()); } @@ -2409,6 +2469,11 @@ static void NotifyHeaderTip() { * that is already loaded (to avoid loading it again from disk). */ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) { + // Note that while we're often called here from ProcessNewBlock, this is + // far from a guarantee. Things in the P2P/RPC will often end up calling + // us in the middle of ProcessNewBlock - do not assume pblock is set + // sanely for performance or correctness! + CBlockIndex *pindexMostWork = NULL; CBlockIndex *pindexNewTip = NULL; do { @@ -2421,6 +2486,14 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, bool fInitialDownload; { LOCK(cs_main); + { // TODO: Tempoarily ensure that mempool removals are notified before + // connected transactions. This shouldn't matter, but the abandoned + // state of transactions in our wallet is currently cleared when we + // receive another notification and there is a race condition where + // notification of a connected conflict might cause an outside process + // to abandon a transaction and then have it inadvertantly cleared by + // the notification that the conflicted transaction was evicted. + MemPoolConflictRemovalTracker mrt(mempool); CBlockIndex *pindexOldTip = chainActive.Tip(); if (pindexMostWork == NULL) { pindexMostWork = FindMostWorkChain(); @@ -2442,20 +2515,23 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, pindexNewTip = chainActive.Tip(); pindexFork = chainActive.FindFork(pindexOldTip); fInitialDownload = IsInitialBlockDownload(); + + // throw all transactions though the signal-interface + + } // MemPoolConflictRemovalTracker destroyed and conflict evictions are notified + + // Transactions in the connnected block are notified + for (const auto& pair : connectTrace.blocksConnected) { + assert(pair.second); + const CBlock& block = *(pair.second); + for (unsigned int i = 0; i < block.vtx.size(); i++) + GetMainSignals().SyncTransaction(*block.vtx[i], pair.first, i); + } } // When we reach this point, we switched to a new tip (stored in pindexNewTip). // Notifications/callbacks that can run without cs_main - // throw all transactions though the signal-interface - // while _not_ holding the cs_main lock - for (const auto& pair : connectTrace.blocksConnected) { - assert(pair.second); - const CBlock& block = *(pair.second); - for (unsigned int i = 0; i < block.vtx.size(); i++) - GetMainSignals().SyncTransaction(*block.vtx[i], pair.first, i); - } - // Notify external listeners about the new tip. GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload); @@ -2592,7 +2668,7 @@ CBlockIndex* AddToBlockIndex(const CBlockHeader& block) // to avoid miners withholding blocks but broadcasting headers, to get a // competitive advantage. pindexNew->nSequenceId = 0; - BlockMap::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; + BlockMap::iterator mi = mapBlockIndex.insert(std::make_pair(hash, pindexNew)).first; pindexNew->phashBlock = &((*mi).first); BlockMap::iterator miPrev = mapBlockIndex.find(block.hashPrevBlock); if (miPrev != mapBlockIndex.end()) @@ -2601,6 +2677,7 @@ CBlockIndex* AddToBlockIndex(const CBlockHeader& block) pindexNew->nHeight = pindexNew->pprev->nHeight + 1; pindexNew->BuildSkip(); } + pindexNew->nTimeMax = (pindexNew->pprev ? std::max(pindexNew->pprev->nTimeMax, pindexNew->nTime) : pindexNew->nTime); pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + GetBlockProof(*pindexNew); pindexNew->RaiseValidity(BLOCK_VALID_TREE); if (pindexBestHeader == NULL || pindexBestHeader->nChainWork < pindexNew->nChainWork) @@ -2628,7 +2705,7 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl if (pindexNew->pprev == NULL || pindexNew->pprev->nChainTx) { // If pindexNew is the genesis block or all parents are BLOCK_VALID_TRANSACTIONS. - deque<CBlockIndex*> queue; + std::deque<CBlockIndex*> queue; queue.push_back(pindexNew); // Recursively process any descendant blocks that now may be eligible to be connected. @@ -3051,14 +3128,18 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state } // Exposed wrapper for AcceptBlockHeader -bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex) +bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex) { { LOCK(cs_main); for (const CBlockHeader& header : headers) { - if (!AcceptBlockHeader(header, state, chainparams, ppindex)) { + CBlockIndex *pindex = NULL; // Use a temp pindex instead of ppindex to avoid a const_cast + if (!AcceptBlockHeader(header, state, chainparams, &pindex)) { return false; } + if (ppindex) { + *ppindex = pindex; + } } } NotifyHeaderTip(); @@ -3066,8 +3147,10 @@ bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidatio } /** Store block on disk. If dbp is non-NULL, the file is known to already reside on disk */ -static bool AcceptBlock(const CBlock& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const CDiskBlockPos* dbp, bool* fNewBlock) +static bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const CDiskBlockPos* dbp, bool* fNewBlock) { + const CBlock& block = *pblock; + if (fNewBlock) *fNewBlock = false; AssertLockHeld(cs_main); @@ -3113,6 +3196,11 @@ static bool AcceptBlock(const CBlock& block, CValidationState& state, const CCha return error("%s: %s", __func__, FormatStateMessage(state)); } + // Header is valid/has work, merkle tree and segwit merkle tree are good...RELAY NOW + // (but if it does not build on our best tip, let the SendMessages loop relay it) + if (!IsInitialBlockDownload() && chainActive.Tip() == pindex->pprev) + GetMainSignals().NewPoWValidBlock(pindex, pblock); + int nHeight = pindex->nHeight; // Write block to history file @@ -3147,7 +3235,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons CBlockIndex *pindex = NULL; if (fNewBlock) *fNewBlock = false; CValidationState state; - bool ret = AcceptBlock(*pblock, state, chainparams, &pindex, fForceProcessing, NULL, fNewBlock); + bool ret = AcceptBlock(pblock, state, chainparams, &pindex, fForceProcessing, NULL, fNewBlock); CheckBlockIndex(chainparams.GetConsensus()); if (!ret) { GetMainSignals().BlockChecked(*pblock, state); @@ -3239,7 +3327,7 @@ void PruneOneBlockFile(const int fileNumber) void UnlinkPrunedFiles(std::set<int>& setFilesToPrune) { - for (set<int>::iterator it = setFilesToPrune.begin(); it != setFilesToPrune.end(); ++it) { + for (std::set<int>::iterator it = setFilesToPrune.begin(); it != setFilesToPrune.end(); ++it) { CDiskBlockPos pos(*it, 0); boost::filesystem::remove(GetBlockPosFilename(pos, "blk")); boost::filesystem::remove(GetBlockPosFilename(pos, "rev")); @@ -3247,6 +3335,35 @@ void UnlinkPrunedFiles(std::set<int>& setFilesToPrune) } } +/* Calculate the block/rev files to delete based on height specified by user with RPC command pruneblockchain */ +void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight) +{ + assert(fPruneMode && nManualPruneHeight > 0); + + LOCK2(cs_main, cs_LastBlockFile); + if (chainActive.Tip() == NULL) + return; + + // last block to prune is the lesser of (user-specified height, MIN_BLOCKS_TO_KEEP from the tip) + unsigned int nLastBlockWeCanPrune = std::min((unsigned)nManualPruneHeight, chainActive.Tip()->nHeight - MIN_BLOCKS_TO_KEEP); + int count=0; + for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) { + if (vinfoBlockFile[fileNumber].nSize == 0 || vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) + continue; + PruneOneBlockFile(fileNumber); + setFilesToPrune.insert(fileNumber); + count++; + } + LogPrintf("Prune (Manual): prune_height=%d removed %d blk/rev pairs\n", nLastBlockWeCanPrune, count); +} + +/* This function is called from the RPC code for pruneblockchain */ +void PruneBlockFilesManual(int nManualPruneHeight) +{ + CValidationState state; + FlushStateToDisk(state, FLUSH_STATE_NONE, nManualPruneHeight); +} + /* Calculate the block/rev files that should be deleted to remain under target*/ void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight) { @@ -3355,8 +3472,8 @@ CBlockIndex * InsertBlockIndex(uint256 hash) // Create new CBlockIndex* pindexNew = new CBlockIndex(); if (!pindexNew) - throw runtime_error(std::string(__func__) + ": new CBlockIndex failed"); - mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; + throw std::runtime_error(std::string(__func__) + ": new CBlockIndex failed"); + mi = mapBlockIndex.insert(std::make_pair(hash, pindexNew)).first; pindexNew->phashBlock = &((*mi).first); return pindexNew; @@ -3370,18 +3487,19 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) boost::this_thread::interruption_point(); // Calculate nChainWork - vector<pair<int, CBlockIndex*> > vSortedByHeight; + std::vector<std::pair<int, CBlockIndex*> > vSortedByHeight; vSortedByHeight.reserve(mapBlockIndex.size()); BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex) { CBlockIndex* pindex = item.second; - vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex)); + vSortedByHeight.push_back(std::make_pair(pindex->nHeight, pindex)); } sort(vSortedByHeight.begin(), vSortedByHeight.end()); BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight) { CBlockIndex* pindex = item.second; pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex); + pindex->nTimeMax = (pindex->pprev ? std::max(pindex->pprev->nTimeMax, pindex->nTime) : pindex->nTime); // We can link the chain of blocks for which we've received transactions at some point. // Pruned nodes may have deleted the block. if (pindex->nTx > 0) { @@ -3425,7 +3543,7 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) // Check presence of blk files LogPrintf("Checking all blk files are present...\n"); - set<int> setBlkDataFiles; + std::set<int> setBlkDataFiles; BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex) { CBlockIndex* pindex = item.second; @@ -3466,7 +3584,7 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) LogPrintf("%s: hashBestChain=%s height=%d date=%s progress=%f\n", __func__, chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), - Checkpoints::GuessVerificationProgress(chainparams.Checkpoints(), chainActive.Tip())); + GuessVerificationProgress(chainparams.TxData(), chainActive.Tip())); return true; } @@ -3524,7 +3642,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); // check level 1: verify block validity if (nCheckLevel >= 1 && !CheckBlock(block, state, chainparams.GetConsensus())) - return error("%s: *** found bad block at %d, hash=%s (%s)\n", __func__, + return error("%s: *** found bad block at %d, hash=%s (%s)\n", __func__, pindex->nHeight, pindex->GetBlockHash().ToString(), FormatStateMessage(state)); // check level 2: verify undo validity if (nCheckLevel >= 2 && pindex) { @@ -3695,7 +3813,7 @@ bool LoadBlockIndex(const CChainParams& chainparams) return true; } -bool InitBlockIndex(const CChainParams& chainparams) +bool InitBlockIndex(const CChainParams& chainparams) { LOCK(cs_main); @@ -3774,7 +3892,8 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB dbp->nPos = nBlockPos; blkdat.SetLimit(nBlockPos + nSize); blkdat.SetPos(nBlockPos); - CBlock block; + std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>(); + CBlock& block = *pblock; blkdat >> block; nRewind = blkdat.GetPos(); @@ -3792,7 +3911,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB if (mapBlockIndex.count(hash) == 0 || (mapBlockIndex[hash]->nStatus & BLOCK_HAVE_DATA) == 0) { LOCK(cs_main); CValidationState state; - if (AcceptBlock(block, state, chainparams, NULL, true, dbp, NULL)) + if (AcceptBlock(pblock, state, chainparams, NULL, true, dbp, NULL)) nLoaded++; if (state.IsError()) break; @@ -3811,7 +3930,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB NotifyHeaderTip(); // Recursively process earlier encountered successors of this block - deque<uint256> queue; + std::deque<uint256> queue; queue.push_back(hash); while (!queue.empty()) { uint256 head = queue.front(); @@ -3819,16 +3938,17 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB std::pair<std::multimap<uint256, CDiskBlockPos>::iterator, std::multimap<uint256, CDiskBlockPos>::iterator> range = mapBlocksUnknownParent.equal_range(head); while (range.first != range.second) { std::multimap<uint256, CDiskBlockPos>::iterator it = range.first; - if (ReadBlockFromDisk(block, it->second, chainparams.GetConsensus())) + std::shared_ptr<CBlock> pblockrecursive = std::make_shared<CBlock>(); + if (ReadBlockFromDisk(*pblockrecursive, it->second, chainparams.GetConsensus())) { - LogPrint("reindex", "%s: Processing out of order child %s of %s\n", __func__, block.GetHash().ToString(), + LogPrint("reindex", "%s: Processing out of order child %s of %s\n", __func__, pblockrecursive->GetHash().ToString(), head.ToString()); LOCK(cs_main); CValidationState dummy; - if (AcceptBlock(block, dummy, chainparams, NULL, true, &it->second, NULL)) + if (AcceptBlock(pblockrecursive, dummy, chainparams, NULL, true, &it->second, NULL)) { nLoaded++; - queue.push_back(block.GetHash()); + queue.push_back(pblockrecursive->GetHash()); } } range.first++; @@ -4127,7 +4247,7 @@ void DumpMempool(void) { LOCK(mempool.cs); for (const auto &i : mempool.mapDeltas) { - mapDeltas[i.first] = i.second.first; + mapDeltas[i.first] = i.second.second; } vinfo = mempool.infoAll(); } @@ -4164,6 +4284,24 @@ void DumpMempool(void) } } +//! Guess how far we are in the verification process at the given block index +double GuessVerificationProgress(const ChainTxData& data, CBlockIndex *pindex) { + if (pindex == NULL) + return 0.0; + + int64_t nNow = time(NULL); + + double fTxTotal; + + if (pindex->nChainTx <= data.nTxCount) { + fTxTotal = data.nTxCount + (nNow - data.nTime) * data.dTxRate; + } else { + fTxTotal = pindex->nChainTx + (nNow - pindex->GetBlockTime()) * data.dTxRate; + } + + return pindex->nChainTx / fTxTotal; +} + class CMainCleanup { public: diff --git a/src/validation.h b/src/validation.h index 631602a701..6fcbb1c108 100644 --- a/src/validation.h +++ b/src/validation.h @@ -42,6 +42,7 @@ class CScriptCheck; class CTxMemPool; class CValidationInterface; class CValidationState; +struct ChainTxData; struct PrecomputedTransactionData; struct LockPoints; @@ -58,12 +59,6 @@ static const CAmount DEFAULT_TRANSACTION_MAXFEE = 0.1 * COIN; static const CAmount HIGH_TX_FEE_PER_KB = 0.01 * COIN; //! -maxtxfee will warn if called with a higher fee than this amount (in satoshis) static const CAmount HIGH_MAX_TX_FEE = 100 * HIGH_TX_FEE_PER_KB; -/** Default for -maxorphantx, maximum number of orphan transactions kept in memory */ -static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100; -/** Expiration time for orphan transactions in seconds */ -static const int64_t ORPHAN_TX_EXPIRE_TIME = 20 * 60; -/** Minimum time between orphan transactions expire time checks in seconds */ -static const int64_t ORPHAN_TX_EXPIRE_INTERVAL = 5 * 60; /** Default for -limitancestorcount, max number of in-mempool ancestors */ static const unsigned int DEFAULT_ANCESTOR_LIMIT = 25; /** Default for -limitancestorsize, maximum kilobytes of tx + all in-mempool ancestors */ @@ -185,6 +180,9 @@ extern CAmount maxTxFee; extern int64_t nMaxTipAge; extern bool fEnableReplacement; +/** Block hash whose ancestors we will assume to have valid scripts without checking them. */ +extern uint256 hashAssumeValid; + /** Best header we've seen so far (used for getheaders queries' starting points). */ extern CBlockIndex *pindexBestHeader; @@ -245,7 +243,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons * @param[in] chainparams The params for the chain we want to connect to * @param[out] ppindex If set, the pointer will be set to point to the last new block index object for the given headers */ -bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex=NULL); +bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex=NULL); /** Check whether enough disk space is available for an incoming block */ bool CheckDiskSpace(uint64_t nAdditionalBytes = 0); @@ -281,6 +279,9 @@ bool GetTransaction(const uint256 &hash, CTransactionRef &tx, const Consensus::P bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock = std::shared_ptr<const CBlock>()); CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams); +/** Guess verification progress (as a fraction between 0.0=genesis and 1.0=current tip). */ +double GuessVerificationProgress(const ChainTxData& data, CBlockIndex* pindex); + /** * Prune block and undo files (blk???.dat and undo???.dat) so that the disk space used is less than a user-defined target. * The user sets the target (in MB) on the command line or in config file. This will be run on startup and whenever new @@ -309,14 +310,19 @@ CBlockIndex * InsertBlockIndex(uint256 hash); void FlushStateToDisk(); /** Prune block files and flush state to disk. */ void PruneAndFlush(); +/** Prune block files up to a given height */ +void PruneBlockFilesManual(int nPruneUpToHeight); -/** (try to) add transaction to memory pool **/ +/** (try to) add transaction to memory pool + * plTxnReplaced will be appended to with all transactions replaced from mempool **/ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree, - bool* pfMissingInputs, bool fOverrideMempoolLimit=false, const CAmount nAbsurdFee=0); + bool* pfMissingInputs, std::list<CTransactionRef>* plTxnReplaced = NULL, + bool fOverrideMempoolLimit=false, const CAmount nAbsurdFee=0); /** (try to) add transaction to memory pool with a specified acceptance time **/ bool AcceptToMemoryPoolWithTime(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree, - bool* pfMissingInputs, int64_t nAcceptTime, bool fOverrideMempoolLimit=false, const CAmount nAbsurdFee=0); + bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced = NULL, + bool fOverrideMempoolLimit=false, const CAmount nAbsurdFee=0); /** Convert CValidationState to a human-readable message for logging */ std::string FormatStateMessage(const CValidationState &state); diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index 215c342dea..d4121a28bc 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -22,6 +22,7 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) { g_signals.BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2)); g_signals.ScriptForMining.connect(boost::bind(&CValidationInterface::GetScriptForMining, pwalletIn, _1)); g_signals.BlockFound.connect(boost::bind(&CValidationInterface::ResetRequestCount, pwalletIn, _1)); + g_signals.NewPoWValidBlock.connect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2)); } void UnregisterValidationInterface(CValidationInterface* pwalletIn) { @@ -34,6 +35,7 @@ void UnregisterValidationInterface(CValidationInterface* pwalletIn) { g_signals.UpdatedTransaction.disconnect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1)); g_signals.SyncTransaction.disconnect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2, _3)); g_signals.UpdatedBlockTip.disconnect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1, _2, _3)); + g_signals.NewPoWValidBlock.disconnect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2)); } void UnregisterAllValidationInterfaces() { @@ -46,4 +48,5 @@ void UnregisterAllValidationInterfaces() { g_signals.UpdatedTransaction.disconnect_all_slots(); g_signals.SyncTransaction.disconnect_all_slots(); g_signals.UpdatedBlockTip.disconnect_all_slots(); + g_signals.NewPoWValidBlock.disconnect_all_slots(); } diff --git a/src/validationinterface.h b/src/validationinterface.h index 717026389c..a2e76f2036 100644 --- a/src/validationinterface.h +++ b/src/validationinterface.h @@ -8,6 +8,7 @@ #include <boost/signals2/signal.hpp> #include <boost/shared_ptr.hpp> +#include <memory> class CBlock; class CBlockIndex; @@ -40,6 +41,7 @@ protected: virtual void BlockChecked(const CBlock&, const CValidationState&) {} virtual void GetScriptForMining(boost::shared_ptr<CReserveScript>&) {}; virtual void ResetRequestCount(const uint256 &hash) {}; + virtual void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& block) {}; friend void ::RegisterValidationInterface(CValidationInterface*); friend void ::UnregisterValidationInterface(CValidationInterface*); friend void ::UnregisterAllValidationInterfaces(); @@ -48,9 +50,16 @@ protected: struct CMainSignals { /** Notifies listeners of updated block chain tip */ boost::signals2::signal<void (const CBlockIndex *, const CBlockIndex *, bool fInitialDownload)> UpdatedBlockTip; - /** A posInBlock value for SyncTransaction which indicates the transaction was conflicted, disconnected, or not in a block */ + /** A posInBlock value for SyncTransaction calls for tranactions not + * included in connected blocks such as transactions removed from mempool, + * accepted to mempool or appearing in disconnected blocks.*/ static const int SYNC_TRANSACTION_NOT_IN_BLOCK = -1; - /** Notifies listeners of updated transaction data (transaction, and optionally the block it is found in. */ + /** Notifies listeners of updated transaction data (transaction, and + * optionally the block it is found in). Called with block data when + * transaction is included in a connected block, and without block data when + * transaction was accepted to mempool, removed from mempool (only when + * removal was due to conflict from connected block), or appeared in a + * disconnected block.*/ boost::signals2::signal<void (const CTransaction &, const CBlockIndex *pindex, int posInBlock)> SyncTransaction; /** Notifies listeners of an updated transaction without new data (for now: a coinbase potentially becoming visible). */ boost::signals2::signal<void (const uint256 &)> UpdatedTransaction; @@ -66,6 +75,10 @@ struct CMainSignals { boost::signals2::signal<void (boost::shared_ptr<CReserveScript>&)> ScriptForMining; /** Notifies listeners that a block has been successfully mined */ boost::signals2::signal<void (const uint256 &)> BlockFound; + /** + * 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; }; CMainSignals& GetMainSignals(); diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 800e1f4e29..7d1b429b30 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -24,9 +24,6 @@ using namespace std; -unsigned int nWalletDBUpdated; - - // // CDB // diff --git a/src/wallet/db.h b/src/wallet/db.h index bc15f2147f..b4ce044e7f 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -23,8 +23,6 @@ static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100; static const bool DEFAULT_WALLET_PRIVDB = true; -extern unsigned int nWalletDBUpdated; - class CDBEnv { private: diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index a1912a78ec..7d4ed70ed9 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -1048,8 +1048,8 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) } } - if (fRescan && fRunScan && requests.size() && nLowestTimestamp <= chainActive.Tip()->GetBlockTime()) { - CBlockIndex* pindex = nLowestTimestamp > minimumTimestamp ? chainActive.FindLatestBefore(nLowestTimestamp) : chainActive.Genesis(); + if (fRescan && fRunScan && requests.size() && nLowestTimestamp <= chainActive.Tip()->GetBlockTimeMax()) { + CBlockIndex* pindex = nLowestTimestamp > minimumTimestamp ? chainActive.FindEarliestAtLeast(nLowestTimestamp) : chainActive.Genesis(); if (pindex) { pwalletMain->ScanForWalletTransactions(pindex, true); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 4dda480216..45b572aa2e 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -11,8 +11,10 @@ #include "init.h" #include "validation.h" #include "net.h" +#include "policy/policy.h" #include "policy/rbf.h" #include "rpc/server.h" +#include "script/sign.h" #include "timedata.h" #include "util.h" #include "utilmoneystr.h" @@ -446,9 +448,9 @@ UniValue listaddressgroupings(const JSONRPCRequest& request) "[\n" " [\n" " [\n" - " \"address\", (string) The bitcoin address\n" + " \"address\", (string) The bitcoin address\n" " amount, (numeric) The amount in " + CURRENCY_UNIT + "\n" - " \"account\" (string, optional) The account (DEPRECATED)\n" + " \"account\" (string, optional) DEPRECATED. The account\n" " ]\n" " ,...\n" " ]\n" @@ -666,9 +668,20 @@ UniValue getbalance(const JSONRPCRequest& request) "Note that the account \"\" is not the same as leaving the parameter out.\n" "The server total may be different to the balance in the default \"\" account.\n" "\nArguments:\n" - "1. \"account\" (string, optional) DEPRECATED. The selected account, or \"*\" for entire wallet. It may be the default account using \"\".\n" - "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" - "3. include_watchonly (bool, optional, default=false) Also include balance in watchonly addresses (see 'importaddress')\n" + "1. \"account\" (string, optional) DEPRECATED. The account string may be given as a\n" + " specific account name to find the balance associated with wallet keys in\n" + " a named account, or as the empty string (\"\") to find the balance\n" + " associated with wallet keys not in any named account, or as \"*\" to find\n" + " the balance associated with all wallet keys regardless of account.\n" + " When this option is specified, it calculates the balance in a different\n" + " way than when it is not specified, and which can count spends twice when\n" + " there are conflicting pending transactions (such as those created by\n" + " the bumpfee command), temporarily resulting in low or even negative\n" + " balances. In general, account balance calculation is not considered\n" + " reliable and has resulted in confusing outcomes, so it is recommended to\n" + " avoid passing this argument.\n" + "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" + "3. include_watchonly (bool, optional, default=false) Also include balance in watch-only addresses (see 'importaddress')\n" "\nResult:\n" "amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this account.\n" "\nExamples:\n" @@ -694,9 +707,12 @@ UniValue getbalance(const JSONRPCRequest& request) filter = filter | ISMINE_WATCH_ONLY; if (request.params[0].get_str() == "*") { - // Calculate total balance a different way from GetBalance() - // (GetBalance() sums up all unspent TxOuts) - // getbalance and "getbalance * 1 true" should return the same number + // Calculate total balance in a very different way from GetBalance(). + // The biggest difference is that GetBalance() sums up all unspent + // TxOuts paying to the wallet, while this sums up both spent and + // unspent TxOuts paying to the wallet, and then subtracts the values of + // TxIns spending from the wallet. This also has fewer restrictions on + // which unconfirmed transactions are considered trusted. CAmount nBalance = 0; for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { @@ -1258,7 +1274,7 @@ UniValue listreceivedbyaddress(const JSONRPCRequest& request) "\nArguments:\n" "1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n" "2. include_empty (bool, optional, default=false) Whether to include addresses that haven't received any payments.\n" - "3. include_watchonly (bool, optional, default=false) Whether to include watchonly addresses (see 'importaddress').\n" + "3. include_watchonly (bool, optional, default=false) Whether to include watch-only addresses (see 'importaddress').\n" "\nResult:\n" "[\n" @@ -1268,7 +1284,11 @@ UniValue listreceivedbyaddress(const JSONRPCRequest& request) " \"account\" : \"accountname\", (string) DEPRECATED. The account of the receiving address. The default account is \"\".\n" " \"amount\" : x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " received by the address\n" " \"confirmations\" : n, (numeric) The number of confirmations of the most recent transaction included\n" - " \"label\" : \"label\" (string) A comment for the address/transaction, if any\n" + " \"label\" : \"label\", (string) A comment for the address/transaction, if any\n" + " \"txids\": [\n" + " n, (numeric) The ids of transactions received with the address \n" + " ...\n" + " ]\n" " }\n" " ,...\n" "]\n" @@ -1296,7 +1316,7 @@ UniValue listreceivedbyaccount(const JSONRPCRequest& request) "\nArguments:\n" "1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n" "2. include_empty (bool, optional, default=false) Whether to include accounts that haven't received any payments.\n" - "3. include_watchonly (bool, optional, default=false) Whether to include watchonly addresses (see 'importaddress').\n" + "3. include_watchonly (bool, optional, default=false) Whether to include watch-only addresses (see 'importaddress').\n" "\nResult:\n" "[\n" @@ -1433,7 +1453,7 @@ UniValue listtransactions(const JSONRPCRequest& request) "1. \"account\" (string, optional) DEPRECATED. The account name. Should be \"*\".\n" "2. count (numeric, optional, default=10) The number of transactions to return\n" "3. skip (numeric, optional, default=0) The number of transactions to skip\n" - "4. include_watchonly (bool, optional, default=false) Include transactions to watchonly addresses (see 'importaddress')\n" + "4. include_watchonly (bool, optional, default=false) Include transactions to watch-only addresses (see 'importaddress')\n" "\nResult:\n" "[\n" " {\n" @@ -1448,14 +1468,14 @@ UniValue listtransactions(const JSONRPCRequest& request) " \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and for the\n" " 'move' category for moves outbound. It is positive for the 'receive' category,\n" " and for the 'move' category for inbound funds.\n" + " \"label\": \"label\", (string) A comment for the address/transaction, if any\n" " \"vout\": n, (numeric) the vout value\n" " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n" " 'send' category of transactions.\n" - " \"abandoned\": xxx (bool) 'true' if the transaction has been abandoned (inputs are respendable).\n" " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and \n" " 'receive' category of transactions. Negative confirmations indicate the\n" " transaction conflicts with the block chain\n" - " \"trusted\": xxx (bool) Whether we consider the outputs of this unconfirmed transaction safe to spend.\n" + " \"trusted\": xxx, (bool) Whether we consider the outputs of this unconfirmed transaction safe to spend.\n" " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction. Available for 'send' and 'receive'\n" " category of transactions.\n" " \"blockindex\": n, (numeric) The index of the transaction in the block that includes it. Available for 'send' and 'receive'\n" @@ -1466,12 +1486,13 @@ UniValue listtransactions(const JSONRPCRequest& request) " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (midnight Jan 1 1970 GMT). Available \n" " for 'send' and 'receive' category of transactions.\n" " \"comment\": \"...\", (string) If a comment is associated with the transaction.\n" - " \"label\": \"label\" (string) A comment for the address/transaction, if any\n" - " \"otheraccount\": \"accountname\", (string) For the 'move' category of transactions, the account the funds came \n" + " \"otheraccount\": \"accountname\", (string) DEPRECATED. For the 'move' category of transactions, the account the funds came \n" " from (for receiving funds, positive amounts), or went to (for sending funds,\n" " negative amounts).\n" - " \"bip125-replaceable\": \"yes|no|unknown\" (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n" + " \"bip125-replaceable\": \"yes|no|unknown\", (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n" " may be unknown for unconfirmed transactions not in the mempool\n" + " \"abandoned\": xxx (bool) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n" + " 'send' category of transactions.\n" " }\n" "]\n" @@ -1558,7 +1579,7 @@ UniValue listaccounts(const JSONRPCRequest& request) "\nDEPRECATED. Returns Object that has account names as keys, account balances as values.\n" "\nArguments:\n" "1. minconf (numeric, optional, default=1) Only include transactions with at least this many confirmations\n" - "2. include_watchonly (bool, optional, default=false) Include balances in watchonly addresses (see 'importaddress')\n" + "2. include_watchonly (bool, optional, default=false) Include balances in watch-only addresses (see 'importaddress')\n" "\nResult:\n" "{ (json object where keys are account names, and values are numeric balances\n" " \"account\": x.xxx, (numeric) The property name is the account name, and the value is the total balance for the account.\n" @@ -1638,7 +1659,7 @@ UniValue listsinceblock(const JSONRPCRequest& request) "\nArguments:\n" "1. \"blockhash\" (string, optional) The block hash to list transactions since\n" "2. target_confirmations: (numeric, optional) The confirmations required, must be 1 or more\n" - "3. include_watchonly: (bool, optional, default=false) Include transactions to watchonly addresses (see 'importaddress')" + "3. include_watchonly: (bool, optional, default=false) Include transactions to watch-only addresses (see 'importaddress')" "\nResult:\n" "{\n" " \"transactions\": [\n" @@ -1657,6 +1678,9 @@ UniValue listsinceblock(const JSONRPCRequest& request) " \"txid\": \"transactionid\", (string) The transaction id. Available for 'send' and 'receive' category of transactions.\n" " \"time\": xxx, (numeric) The transaction time in seconds since epoch (Jan 1 1970 GMT).\n" " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (Jan 1 1970 GMT). Available for 'send' and 'receive' category of transactions.\n" + " \"bip125-replaceable\": \"yes|no|unknown\", (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n" + " may be unknown for unconfirmed transactions not in the mempool\n" + " \"abandoned\": xxx, (bool) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the 'send' category of transactions.\n" " \"comment\": \"...\", (string) If a comment is associated with the transaction.\n" " \"label\" : \"label\" (string) A comment for the address/transaction, if any\n" " \"to\": \"...\", (string) If a comment to is associated with the transaction.\n" @@ -1671,7 +1695,7 @@ UniValue listsinceblock(const JSONRPCRequest& request) LOCK2(cs_main, pwalletMain->cs_wallet); - CBlockIndex *pindex = NULL; + const CBlockIndex *pindex = NULL; int target_confirms = 1; isminefilter filter = ISMINE_SPENDABLE; @@ -1682,7 +1706,16 @@ UniValue listsinceblock(const JSONRPCRequest& request) blockId.SetHex(request.params[0].get_str()); BlockMap::iterator it = mapBlockIndex.find(blockId); if (it != mapBlockIndex.end()) + { pindex = it->second; + if (chainActive[pindex->nHeight] != pindex) + { + // the block being asked for is a part of a deactivated chain; + // we don't want to depend on its perceived height in the block + // chain, we want to instead use the last common ancestor + pindex = chainActive.FindFork(pindex); + } + } } if (request.params.size() > 1) @@ -1693,9 +1726,10 @@ UniValue listsinceblock(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter"); } - if(request.params.size() > 2) - if(request.params[2].get_bool()) - filter = filter | ISMINE_WATCH_ONLY; + if (request.params.size() > 2 && request.params[2].get_bool()) + { + filter = filter | ISMINE_WATCH_ONLY; + } int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1; @@ -1730,10 +1764,12 @@ UniValue gettransaction(const JSONRPCRequest& request) "\nGet detailed information about in-wallet transaction <txid>\n" "\nArguments:\n" "1. \"txid\" (string, required) The transaction id\n" - "2. \"include_watchonly\" (bool, optional, default=false) Whether to include watchonly addresses in balance calculation and details[]\n" + "2. \"include_watchonly\" (bool, optional, default=false) Whether to include watch-only addresses in balance calculation and details[]\n" "\nResult:\n" "{\n" " \"amount\" : x.xxx, (numeric) The transaction amount in " + CURRENCY_UNIT + "\n" + " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n" + " 'send' category of transactions.\n" " \"confirmations\" : n, (numeric) The number of confirmations\n" " \"blockhash\" : \"hash\", (string) The block hash\n" " \"blockindex\" : xx, (numeric) The index of the transaction in the block that includes it\n" @@ -1741,16 +1777,20 @@ UniValue gettransaction(const JSONRPCRequest& request) " \"txid\" : \"transactionid\", (string) The transaction id.\n" " \"time\" : ttt, (numeric) The transaction time in seconds since epoch (1 Jan 1970 GMT)\n" " \"timereceived\" : ttt, (numeric) The time received in seconds since epoch (1 Jan 1970 GMT)\n" - " \"bip125-replaceable\": \"yes|no|unknown\" (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n" + " \"bip125-replaceable\": \"yes|no|unknown\", (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n" " may be unknown for unconfirmed transactions not in the mempool\n" " \"details\" : [\n" " {\n" - " \"account\" : \"accountname\", (string) DEPRECATED. The account name involved in the transaction, can be \"\" for the default account.\n" - " \"address\" : \"address\", (string) The bitcoin address involved in the transaction\n" + " \"account\" : \"accountname\", (string) DEPRECATED. The account name involved in the transaction, can be \"\" for the default account.\n" + " \"address\" : \"address\", (string) The bitcoin address involved in the transaction\n" " \"category\" : \"send|receive\", (string) The category, either 'send' or 'receive'\n" " \"amount\" : x.xxx, (numeric) The amount in " + CURRENCY_UNIT + "\n" " \"label\" : \"label\", (string) A comment for the address/transaction, if any\n" " \"vout\" : n, (numeric) the vout value\n" + " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n" + " 'send' category of transactions.\n" + " \"abandoned\": xxx (bool) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n" + " 'send' category of transactions.\n" " }\n" " ,...\n" " ],\n" @@ -2291,7 +2331,7 @@ UniValue getwalletinfo(const JSONRPCRequest& request) " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n" " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n" " \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n" - " \"hdmasterkeyid\": \"<hash160>\", (string) the Hash160 of the HD master pubkey\n" + " \"hdmasterkeyid\": \"<hash160>\" (string) the Hash160 of the HD master pubkey\n" "}\n" "\nExamples:\n" + HelpExampleCli("getwalletinfo", "") @@ -2350,9 +2390,9 @@ UniValue listunspent(const JSONRPCRequest& request) if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (request.fHelp || request.params.size() > 3) + if (request.fHelp || request.params.size() > 4) throw runtime_error( - "listunspent ( minconf maxconf [\"addresses\",...] )\n" + "listunspent ( minconf maxconf [\"addresses\",...] [include_unsafe] )\n" "\nReturns array of unspent transaction outputs\n" "with between minconf and maxconf (inclusive) confirmations.\n" "Optionally filter to only include txouts paid to specified addresses.\n" @@ -2364,6 +2404,10 @@ UniValue listunspent(const JSONRPCRequest& request) " \"address\" (string) bitcoin address\n" " ,...\n" " ]\n" + "4. include_unsafe (bool, optional, default=true) Include outputs that are not safe to spend\n" + " because they come from unconfirmed untrusted transactions or unconfirmed\n" + " replacement transactions (cases where we are less sure that a conflicting\n" + " transaction won't be mined).\n" "\nResult\n" "[ (array of json object)\n" " {\n" @@ -2372,7 +2416,7 @@ UniValue listunspent(const JSONRPCRequest& request) " \"address\" : \"address\", (string) the bitcoin address\n" " \"account\" : \"account\", (string) DEPRECATED. The associated account, or \"\" for the default account\n" " \"scriptPubKey\" : \"key\", (string) the script key\n" - " \"amount\" : x.xxx, (numeric) the transaction amount in " + CURRENCY_UNIT + "\n" + " \"amount\" : x.xxx, (numeric) the transaction output amount in " + CURRENCY_UNIT + "\n" " \"confirmations\" : n, (numeric) The number of confirmations\n" " \"redeemScript\" : n (string) The redeemScript if scriptPubKey is P2SH\n" " \"spendable\" : xxx, (bool) Whether we have the private keys to spend this output\n" @@ -2387,18 +2431,21 @@ UniValue listunspent(const JSONRPCRequest& request) + HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") ); - RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VNUM)(UniValue::VNUM)(UniValue::VARR)); - int nMinDepth = 1; - if (request.params.size() > 0) + if (request.params.size() > 0 && !request.params[0].isNull()) { + RPCTypeCheckArgument(request.params[0], UniValue::VNUM); nMinDepth = request.params[0].get_int(); + } int nMaxDepth = 9999999; - if (request.params.size() > 1) + if (request.params.size() > 1 && !request.params[1].isNull()) { + RPCTypeCheckArgument(request.params[1], UniValue::VNUM); nMaxDepth = request.params[1].get_int(); + } set<CBitcoinAddress> setAddress; - if (request.params.size() > 2) { + if (request.params.size() > 2 && !request.params[2].isNull()) { + RPCTypeCheckArgument(request.params[2], UniValue::VARR); UniValue inputs = request.params[2].get_array(); for (unsigned int idx = 0; idx < inputs.size(); idx++) { const UniValue& input = inputs[idx]; @@ -2411,11 +2458,17 @@ UniValue listunspent(const JSONRPCRequest& request) } } + bool include_unsafe = true; + if (request.params.size() > 3 && !request.params[3].isNull()) { + RPCTypeCheckArgument(request.params[3], UniValue::VBOOL); + include_unsafe = request.params[3].get_bool(); + } + UniValue results(UniValue::VARR); vector<COutput> vecOutputs; assert(pwalletMain != NULL); LOCK2(cs_main, pwalletMain->cs_wallet); - pwalletMain->AvailableCoins(vecOutputs, false, NULL, true); + pwalletMain->AvailableCoins(vecOutputs, !include_unsafe, NULL, true); BOOST_FOREACH(const COutput& out, vecOutputs) { if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) continue; @@ -2465,7 +2518,8 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) throw runtime_error( "fundrawtransaction \"hexstring\" ( options )\n" "\nAdd inputs to a transaction until it has enough in value to meet its out value.\n" - "This will not modify existing inputs, and will add one change output to the outputs.\n" + "This will not modify existing inputs, and will add at most one change output to the outputs.\n" + "No existing outputs will be modified unless \"subtractFeeFromOutputs\" is specified.\n" "Note that inputs which were signed may need to be resigned after completion since in/outputs have been added.\n" "The inputs added will not be signed, use signrawtransaction for that.\n" "Note that all existing inputs must have their previous output transaction be in the wallet.\n" @@ -2477,11 +2531,18 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) "1. \"hexstring\" (string, required) The hex string of the raw transaction\n" "2. options (object, optional)\n" " {\n" - " \"changeAddress\" (string, optional, default pool address) The bitcoin address to receive the change\n" - " \"changePosition\" (numeric, optional, default random) The index of the change output\n" - " \"includeWatching\" (boolean, optional, default false) Also select inputs which are watch only\n" - " \"lockUnspents\" (boolean, optional, default false) Lock selected unspent outputs\n" - " \"feeRate\" (numeric, optional, default not set: makes wallet determine the fee) Set a specific feerate (" + CURRENCY_UNIT + " per KB)\n" + " \"changeAddress\" (string, optional, default pool address) The bitcoin address to receive the change\n" + " \"changePosition\" (numeric, optional, default random) The index of the change output\n" + " \"includeWatching\" (boolean, optional, default false) Also select inputs which are watch only\n" + " \"lockUnspents\" (boolean, optional, default false) Lock selected unspent outputs\n" + " \"reserveChangeKey\" (boolean, optional, default true) Reserves the change output key from the keypool\n" + " \"feeRate\" (numeric, optional, default not set: makes wallet determine the fee) Set a specific feerate (" + CURRENCY_UNIT + " per KB)\n" + " \"subtractFeeFromOutputs\" (array, optional) A json array of integers.\n" + " The fee will be equally deducted from the amount of each specified output.\n" + " The outputs are specified by their zero-based index, before any change output is added.\n" + " 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" " }\n" " for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}\n" "\nResult:\n" @@ -2490,7 +2551,6 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) " \"fee\": n, (numeric) Fee in " + CURRENCY_UNIT + " the resulting transaction pays\n" " \"changepos\": n (numeric) The position of the added change output, or -1\n" "}\n" - "\"hex\" \n" "\nExamples:\n" "\nCreate a transaction with no inputs\n" + HelpExampleCli("createrawtransaction", "\"[]\" \"{\\\"myaddress\\\":0.01}\"") + @@ -2508,8 +2568,11 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) int changePosition = -1; bool includeWatching = false; bool lockUnspents = false; + bool reserveChangeKey = true; CFeeRate feeRate = CFeeRate(0); bool overrideEstimatedFeerate = false; + UniValue subtractFeeFromOutputs; + set<int> setSubtractFeeFromOutputs; if (request.params.size() > 1) { if (request.params[1].type() == UniValue::VBOOL) { @@ -2527,7 +2590,9 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) {"changePosition", UniValueType(UniValue::VNUM)}, {"includeWatching", UniValueType(UniValue::VBOOL)}, {"lockUnspents", UniValueType(UniValue::VBOOL)}, + {"reserveChangeKey", UniValueType(UniValue::VBOOL)}, {"feeRate", UniValueType()}, // will be checked below + {"subtractFeeFromOutputs", UniValueType(UniValue::VARR)}, }, true, true); @@ -2549,11 +2614,17 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) if (options.exists("lockUnspents")) lockUnspents = options["lockUnspents"].get_bool(); + if (options.exists("reserveChangeKey")) + reserveChangeKey = options["reserveChangeKey"].get_bool(); + if (options.exists("feeRate")) { feeRate = CFeeRate(AmountFromValue(options["feeRate"])); overrideEstimatedFeerate = true; } + + if (options.exists("subtractFeeFromOutputs")) + subtractFeeFromOutputs = options["subtractFeeFromOutputs"].get_array(); } } @@ -2568,10 +2639,21 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) if (changePosition != -1 && (changePosition < 0 || (unsigned int)changePosition > tx.vout.size())) throw JSONRPCError(RPC_INVALID_PARAMETER, "changePosition out of bounds"); + for (unsigned int idx = 0; idx < subtractFeeFromOutputs.size(); idx++) { + int pos = subtractFeeFromOutputs[idx].get_int(); + if (setSubtractFeeFromOutputs.count(pos)) + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, duplicated position: %d", pos)); + if (pos < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, negative position: %d", pos)); + if (pos >= int(tx.vout.size())) + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, position too large: %d", pos)); + setSubtractFeeFromOutputs.insert(pos); + } + CAmount nFeeOut; string strFailReason; - if(!pwalletMain->FundTransaction(tx, nFeeOut, overrideEstimatedFeerate, feeRate, changePosition, strFailReason, includeWatching, lockUnspents, changeAddress)) + if(!pwalletMain->FundTransaction(tx, nFeeOut, overrideEstimatedFeerate, feeRate, changePosition, strFailReason, includeWatching, lockUnspents, setSubtractFeeFromOutputs, reserveChangeKey, changeAddress)) throw JSONRPCError(RPC_INTERNAL_ERROR, strFailReason); UniValue result(UniValue::VOBJ); @@ -2582,6 +2664,323 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) return result; } +// Calculate the size of the transaction assuming all signatures are max size +// Use DummySignatureCreator, which inserts 72 byte signatures everywhere. +// TODO: re-use this in CWallet::CreateTransaction (right now +// CreateTransaction uses the constructed dummy-signed tx to do a priority +// calculation, but we should be able to refactor after priority is removed). +// NOTE: this requires that all inputs must be in mapWallet (eg the tx should +// be IsAllFromMe). +int64_t CalculateMaximumSignedTxSize(const CTransaction &tx) +{ + CMutableTransaction txNew(tx); + std::vector<pair<CWalletTx *, unsigned int>> vCoins; + // Look up the inputs. We should have already checked that this transaction + // IsAllFromMe(ISMINE_SPENDABLE), so every input should already be in our + // wallet, with a valid index into the vout array. + for (auto& input : tx.vin) { + const auto mi = pwalletMain->mapWallet.find(input.prevout.hash); + assert(mi != pwalletMain->mapWallet.end() && input.prevout.n < mi->second.tx->vout.size()); + vCoins.emplace_back(make_pair(&(mi->second), input.prevout.n)); + } + if (!pwalletMain->DummySignTx(txNew, vCoins)) { + // This should never happen, because IsAllFromMe(ISMINE_SPENDABLE) + // implies that we can sign for every input. + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction contains inputs that cannot be signed"); + } + return GetVirtualTransactionSize(txNew); +} + +UniValue bumpfee(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) { + return NullUniValue; + } + + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { + throw runtime_error( + "bumpfee \"txid\" ( options ) \n" + "\nBumps the fee of an opt-in-RBF transaction T, replacing it with a new transaction B.\n" + "An opt-in RBF transaction with the given txid must be in the wallet.\n" + "The command will pay the additional fee by decreasing (or perhaps removing) its change output.\n" + "If the change output is not big enough to cover the increased fee, the command will currently fail\n" + "instead of adding new inputs to compensate. (A future implementation could improve this.)\n" + "The command will fail if the wallet or mempool contains a transaction that spends one of T's outputs.\n" + "By default, the new fee will be calculated automatically using estimatefee.\n" + "The user can specify a confirmation target for estimatefee.\n" + "Alternatively, the user can specify totalFee, or use RPC setpaytxfee to set a higher fee rate.\n" + "At a minimum, the new fee rate must be high enough to pay an additional new relay fee (incrementalfee\n" + "returned by getnetworkinfo) to enter the node's mempool.\n" + "\nArguments:\n" + "1. txid (string, required) The txid to be bumped\n" + "2. options (object, optional)\n" + " {\n" + " \"confTarget\" (numeric, optional) Confirmation target (in blocks)\n" + " \"totalFee\" (numeric, optional) Total fee (NOT feerate) to pay, in satoshis.\n" + " In rare cases, the actual fee paid might be slightly higher than the specified\n" + " totalFee if the tx change output has to be removed because it is too close to\n" + " the dust threshold.\n" + " \"replaceable\" (boolean, optional, default true) Whether the new transaction should still be\n" + " marked bip-125 replaceable. If true, the sequence numbers in the transaction will\n" + " be left unchanged from the original. If false, any input sequence numbers in the\n" + " original transaction that were less than 0xfffffffe will be increased to 0xfffffffe\n" + " so the new transaction will not be explicitly bip-125 replaceable (though it may\n" + " still be replacable in practice, for example if it has unconfirmed ancestors which\n" + " are replaceable).\n" + " }\n" + "\nResult:\n" + "{\n" + " \"txid\": \"value\", (string) The id of the new transaction\n" + " \"origfee\": n, (numeric) Fee of the replaced transaction\n" + " \"fee\": n, (numeric) Fee of the new transaction\n" + " \"errors\": [ str... ] (json array of strings) Errors encountered during processing (may be empty)\n" + "}\n" + "\nExamples:\n" + "\nBump the fee, get the new transaction\'s txid\n" + + HelpExampleCli("bumpfee", "<txid>")); + } + + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)(UniValue::VOBJ)); + uint256 hash; + hash.SetHex(request.params[0].get_str()); + + // retrieve the original tx from the wallet + LOCK2(cs_main, pwalletMain->cs_wallet); + EnsureWalletIsUnlocked(); + if (!pwalletMain->mapWallet.count(hash)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); + } + CWalletTx& wtx = pwalletMain->mapWallet[hash]; + + if (pwalletMain->HasWalletSpend(hash)) { + throw JSONRPCError(RPC_MISC_ERROR, "Transaction has descendants in the wallet"); + } + + { + LOCK(mempool.cs); + auto it = mempool.mapTx.find(hash); + if (it != mempool.mapTx.end() && it->GetCountWithDescendants() > 1) { + throw JSONRPCError(RPC_MISC_ERROR, "Transaction has descendants in the mempool"); + } + } + + if (wtx.GetDepthInMainChain() != 0) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction has been mined, or is conflicted with a mined transaction"); + } + + if (!SignalsOptInRBF(wtx)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction is not BIP 125 replaceable"); + } + + if (wtx.mapValue.count("replaced_by_txid")) { + throw JSONRPCError(RPC_INVALID_REQUEST, strprintf("Cannot bump transaction %s which was already bumped by transaction %s", hash.ToString(), wtx.mapValue.at("replaced_by_txid"))); + } + + // check that original tx consists entirely of our inputs + // if not, we can't bump the fee, because the wallet has no way of knowing the value of the other inputs (thus the fee) + if (!pwalletMain->IsAllFromMe(wtx, ISMINE_SPENDABLE)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction contains inputs that don't belong to this wallet"); + } + + // figure out which output was change + // if there was no change output or multiple change outputs, fail + int nOutput = -1; + for (size_t i = 0; i < wtx.tx->vout.size(); ++i) { + if (pwalletMain->IsChange(wtx.tx->vout[i])) { + if (nOutput != -1) { + throw JSONRPCError(RPC_MISC_ERROR, "Transaction has multiple change outputs"); + } + nOutput = i; + } + } + if (nOutput == -1) { + throw JSONRPCError(RPC_MISC_ERROR, "Transaction does not have a change output"); + } + + // Calculate the expected size of the new transaction. + int64_t txSize = GetVirtualTransactionSize(*(wtx.tx)); + const int64_t maxNewTxSize = CalculateMaximumSignedTxSize(*wtx.tx); + + // optional parameters + bool specifiedConfirmTarget = false; + int newConfirmTarget = nTxConfirmTarget; + CAmount totalFee = 0; + bool replaceable = true; + if (request.params.size() > 1) { + UniValue options = request.params[1]; + RPCTypeCheckObj(options, + { + {"confTarget", UniValueType(UniValue::VNUM)}, + {"totalFee", UniValueType(UniValue::VNUM)}, + {"replaceable", UniValueType(UniValue::VBOOL)}, + }, + 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")) { + specifiedConfirmTarget = 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("totalFee")) { + totalFee = options["totalFee"].get_int64(); + CAmount requiredFee = CWallet::GetRequiredFee(maxNewTxSize); + if (totalFee < requiredFee ) { + throw JSONRPCError(RPC_INVALID_PARAMETER, + strprintf("Insufficient totalFee (cannot be less than required fee %s)", + FormatMoney(requiredFee))); + } + } + + if (options.exists("replaceable")) { + replaceable = options["replaceable"].get_bool(); + } + } + + // calculate the old fee and fee-rate + CAmount nOldFee = wtx.GetDebit(ISMINE_SPENDABLE) - wtx.tx->GetValueOut(); + CFeeRate nOldFeeRate(nOldFee, txSize); + CAmount nNewFee; + CFeeRate nNewFeeRate; + // The wallet uses a conservative WALLET_INCREMENTAL_RELAY_FEE value to + // future proof against changes to network wide policy for incremental relay + // fee that our node may not be aware of. + CFeeRate walletIncrementalRelayFee = CFeeRate(WALLET_INCREMENTAL_RELAY_FEE); + if (::incrementalRelayFee > walletIncrementalRelayFee) { + walletIncrementalRelayFee = ::incrementalRelayFee; + } + + if (totalFee > 0) { + CAmount minTotalFee = nOldFeeRate.GetFee(maxNewTxSize) + ::incrementalRelayFee.GetFee(maxNewTxSize); + if (totalFee < minTotalFee) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Insufficient totalFee, must be at least %s (oldFee %s + incrementalFee %s)", + FormatMoney(minTotalFee), FormatMoney(nOldFeeRate.GetFee(maxNewTxSize)), FormatMoney(::incrementalRelayFee.GetFee(maxNewTxSize)))); + } + nNewFee = totalFee; + nNewFeeRate = CFeeRate(totalFee, maxNewTxSize); + } else { + // if user specified a confirm target then don't consider any global payTxFee + if (specifiedConfirmTarget) { + nNewFee = CWallet::GetMinimumFee(maxNewTxSize, newConfirmTarget, mempool, CAmount(0)); + } + // otherwise use the regular wallet logic to select payTxFee or default confirm target + else { + nNewFee = CWallet::GetMinimumFee(maxNewTxSize, newConfirmTarget, mempool); + } + + nNewFeeRate = CFeeRate(nNewFee, maxNewTxSize); + + // New fee rate must be at least old rate + minimum incremental relay rate + // walletIncrementalRelayFee.GetFeePerK() should be exact, because it's initialized + // in that unit (fee per kb). + // However, nOldFeeRate is a calculated value from the tx fee/size, so + // add 1 satoshi to the result, because it may have been rounded down. + if (nNewFeeRate.GetFeePerK() < nOldFeeRate.GetFeePerK() + 1 + walletIncrementalRelayFee.GetFeePerK()) { + nNewFeeRate = CFeeRate(nOldFeeRate.GetFeePerK() + 1 + walletIncrementalRelayFee.GetFeePerK()); + nNewFee = nNewFeeRate.GetFee(maxNewTxSize); + } + } + + // Check that in all cases the new fee doesn't violate maxTxFee + if (nNewFee > maxTxFee) { + throw JSONRPCError(RPC_MISC_ERROR, + strprintf("Specified or calculated fee %s is too high (cannot be higher than maxTxFee %s)", + FormatMoney(nNewFee), FormatMoney(maxTxFee))); + } + + // check that fee rate is higher than mempool's minimum fee + // (no point in bumping fee if we know that the new tx won't be accepted to the mempool) + // This may occur if the user set TotalFee or paytxfee too low, if fallbackfee is too low, or, perhaps, + // in a rare situation where the mempool minimum fee increased significantly since the fee estimation just a + // moment earlier. In this case, we report an error to the user, who may use totalFee to make an adjustment. + CFeeRate minMempoolFeeRate = mempool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000); + if (nNewFeeRate.GetFeePerK() < minMempoolFeeRate.GetFeePerK()) { + throw JSONRPCError(RPC_MISC_ERROR, strprintf("New fee rate (%s) is less than the minimum fee rate (%s) to get into the mempool. totalFee value should to be at least %s or settxfee value should be at least %s to add transaction.", FormatMoney(nNewFeeRate.GetFeePerK()), FormatMoney(minMempoolFeeRate.GetFeePerK()), FormatMoney(minMempoolFeeRate.GetFee(maxNewTxSize)), FormatMoney(minMempoolFeeRate.GetFeePerK()))); + } + + // Now modify the output to increase the fee. + // If the output is not large enough to pay the fee, fail. + CAmount nDelta = nNewFee - nOldFee; + assert(nDelta > 0); + CMutableTransaction tx(*(wtx.tx)); + CTxOut* poutput = &(tx.vout[nOutput]); + if (poutput->nValue < nDelta) { + throw JSONRPCError(RPC_MISC_ERROR, "Change output is too small to bump the fee"); + } + + // If the output would become dust, discard it (converting the dust to fee) + poutput->nValue -= nDelta; + if (poutput->nValue <= poutput->GetDustThreshold(::dustRelayFee)) { + LogPrint("rpc", "Bumping fee and discarding dust output\n"); + nNewFee += poutput->nValue; + tx.vout.erase(tx.vout.begin() + nOutput); + } + + // Mark new tx not replaceable, if requested. + if (!replaceable) { + for (auto& input : tx.vin) { + if (input.nSequence < 0xfffffffe) input.nSequence = 0xfffffffe; + } + } + + // sign the new tx + CTransaction txNewConst(tx); + int nIn = 0; + for (auto& input : tx.vin) { + std::map<uint256, CWalletTx>::const_iterator mi = pwalletMain->mapWallet.find(input.prevout.hash); + assert(mi != pwalletMain->mapWallet.end() && input.prevout.n < mi->second.tx->vout.size()); + const CScript& scriptPubKey = mi->second.tx->vout[input.prevout.n].scriptPubKey; + const CAmount& amount = mi->second.tx->vout[input.prevout.n].nValue; + SignatureData sigdata; + if (!ProduceSignature(TransactionSignatureCreator(pwalletMain, &txNewConst, nIn, amount, SIGHASH_ALL), scriptPubKey, sigdata)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Can't sign transaction."); + } + UpdateTransaction(tx, nIn, sigdata); + nIn++; + } + + // commit/broadcast the tx + CReserveKey reservekey(pwalletMain); + CWalletTx wtxBumped(pwalletMain, MakeTransactionRef(std::move(tx))); + wtxBumped.mapValue = wtx.mapValue; + wtxBumped.mapValue["replaces_txid"] = hash.ToString(); + wtxBumped.vOrderForm = wtx.vOrderForm; + wtxBumped.strFromAccount = wtx.strFromAccount; + wtxBumped.fTimeReceivedIsTxTime = true; + wtxBumped.fFromMe = true; + CValidationState state; + if (!pwalletMain->CommitTransaction(wtxBumped, reservekey, g_connman.get(), state)) { + // NOTE: CommitTransaction never returns false, so this should never happen. + throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason())); + } + + UniValue vErrors(UniValue::VARR); + if (state.IsInvalid()) { + // This can happen if the mempool rejected the transaction. Report + // what happened in the "errors" response. + vErrors.push_back(strprintf("Error: The transaction was rejected: %s", FormatStateMessage(state))); + } + + // mark the original tx as bumped + if (!pwalletMain->MarkReplaced(wtx.GetHash(), wtxBumped.GetHash())) { + // TODO: see if JSON-RPC has a standard way of returning a response + // along with an exception. It would be good to return information about + // wtxBumped to the caller even if marking the original transaction + // replaced does not succeed for some reason. + vErrors.push_back("Error: Created new bumpfee transaction but could not mark the original transaction as replaced."); + } + + UniValue result(UniValue::VOBJ); + result.push_back(Pair("txid", wtxBumped.GetHash().GetHex())); + result.push_back(Pair("origfee", ValueFromAmount(nOldFee))); + result.push_back(Pair("fee", ValueFromAmount(nNewFee))); + result.push_back(Pair("errors", vErrors)); + + return result; +} + extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp extern UniValue importprivkey(const JSONRPCRequest& request); extern UniValue importaddress(const JSONRPCRequest& request); @@ -2601,6 +3000,7 @@ static const CRPCCommand commands[] = { "wallet", "addmultisigaddress", &addmultisigaddress, true, {"nrequired","keys","account"} }, { "wallet", "addwitnessaddress", &addwitnessaddress, true, {"address"} }, { "wallet", "backupwallet", &backupwallet, true, {"destination"} }, + { "wallet", "bumpfee", &bumpfee, true, {"txid", "options"} }, { "wallet", "dumpprivkey", &dumpprivkey, true, {"address"} }, { "wallet", "dumpwallet", &dumpwallet, true, {"filename"} }, { "wallet", "encryptwallet", &encryptwallet, true, {"passphrase"} }, @@ -2629,7 +3029,7 @@ static const CRPCCommand commands[] = { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false, {"minconf","include_empty","include_watchonly"} }, { "wallet", "listsinceblock", &listsinceblock, false, {"blockhash","target_confirmations","include_watchonly"} }, { "wallet", "listtransactions", &listtransactions, false, {"account","count","skip","include_watchonly"} }, - { "wallet", "listunspent", &listunspent, false, {"minconf","maxconf","addresses"} }, + { "wallet", "listunspent", &listunspent, false, {"minconf","maxconf","addresses","include_unsafe"} }, { "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"} }, diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index c7ce6fd243..ca086c86a8 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -23,6 +23,8 @@ using namespace std; +std::vector<std::unique_ptr<CWalletTx>> wtxn; + typedef set<pair<const CWalletTx*,unsigned int> > CoinSet; BOOST_FIXTURE_TEST_SUITE(wallet_tests, WalletTestingSetup) @@ -42,21 +44,21 @@ static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = fa // so stop vin being empty, and cache a non-zero Debit to fake out IsFromMe() tx.vin.resize(1); } - CWalletTx* wtx = new CWalletTx(&wallet, MakeTransactionRef(std::move(tx))); + std::unique_ptr<CWalletTx> wtx(new CWalletTx(&wallet, MakeTransactionRef(std::move(tx)))); if (fIsFromMe) { wtx->fDebitCached = true; wtx->nDebitCached = 1; } - COutput output(wtx, nInput, nAge, true, true); + COutput output(wtx.get(), nInput, nAge, true, true); vCoins.push_back(output); + wtxn.emplace_back(std::move(wtx)); } static void empty_wallet(void) { - BOOST_FOREACH(COutput output, vCoins) - delete output.tx; vCoins.clear(); + wtxn.clear(); } static bool equal_sets(CoinSet a, CoinSet b) @@ -349,6 +351,8 @@ BOOST_AUTO_TEST_CASE(ApproximateBestSubset) BOOST_CHECK(wallet.SelectCoinsMinConf(1003 * COIN, 1, 6, 0, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 1003 * COIN); BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); + + empty_wallet(); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 2775f4def3..a5e8752fc0 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -411,6 +411,13 @@ set<uint256> CWallet::GetConflicts(const uint256& txid) const return result; } +bool CWallet::HasWalletSpend(const uint256& txid) const +{ + AssertLockHeld(cs_wallet); + auto iter = mapTxSpends.lower_bound(COutPoint(txid, 0)); + return (iter != mapTxSpends.end() && iter->first.hash == txid); +} + void CWallet::Flush(bool shutdown) { bitdb.Flush(shutdown); @@ -826,6 +833,35 @@ void CWallet::MarkDirty() } } +bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash) +{ + LOCK(cs_wallet); + + auto mi = mapWallet.find(originalHash); + + // There is a bug if MarkReplaced is not called on an existing wallet transaction. + assert(mi != mapWallet.end()); + + CWalletTx& wtx = (*mi).second; + + // Ensure for now that we're not overwriting data + assert(wtx.mapValue.count("replaced_by_txid") == 0); + + wtx.mapValue["replaced_by_txid"] = newHash.ToString(); + + CWalletDB walletdb(strWalletFile, "r+"); + + bool success = true; + if (!walletdb.WriteTx(wtx)) { + LogPrintf("%s: Updating walletdb tx %s failed", __func__, wtx.GetHash().ToString()); + success = false; + } + + NotifyTransactionChanged(this, originalHash, CT_UPDATED); + + return success; +} + bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) { LOCK(cs_wallet); @@ -967,9 +1003,17 @@ bool CWallet::LoadToWallet(const CWalletTx& wtxIn) } /** - * Add a transaction to the wallet, or update it. - * pblock is optional, but should be provided if the transaction is known to be in a block. + * Add a transaction to the wallet, or update it. pIndex and posInBlock should + * be set when the transaction was known to be included in a block. When + * posInBlock = SYNC_TRANSACTION_NOT_IN_BLOCK (-1) , then wallet state is not + * updated in AddToWallet, but notifications happen and cached balances are + * marked dirty. * If fUpdate is true, existing transactions will be updated. + * TODO: One exception to this is that the abandoned state is cleared under the + * assumption that any further notification of a transaction that was considered + * abandoned is an indication that it is not safe to be considered abandoned. + * Abandoned state should probably be more carefuly tracked via different + * posInBlock signals or by checking mempool presence when necessary. */ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate) { @@ -1154,6 +1198,8 @@ isminetype CWallet::IsMine(const CTxIn &txin) const return ISMINE_NO; } +// Note that this function doesn't distinguish between a 0-valued input, +// and a not-"is mine" (according to the filter) input. CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const { { @@ -1236,6 +1282,27 @@ CAmount CWallet::GetDebit(const CTransaction& tx, const isminefilter& filter) co return nDebit; } +bool CWallet::IsAllFromMe(const CTransaction& tx, const isminefilter& filter) const +{ + LOCK(cs_wallet); + + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + auto mi = mapWallet.find(txin.prevout.hash); + if (mi == mapWallet.end()) + return false; // any unknown inputs can't be from us + + const CWalletTx& prev = (*mi).second; + + if (txin.prevout.n >= prev.tx->vout.size()) + return false; // invalid input! + + if (!(IsMine(prev.tx->vout[txin.prevout.n]) & filter)) + return false; + } + return true; +} + CAmount CWallet::GetCredit(const CTransaction& tx, const isminefilter& filter) const { CAmount nCredit = 0; @@ -1479,12 +1546,12 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) pindex = chainActive.Next(pindex); ShowProgress(_("Rescanning..."), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup - double dProgressStart = Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), pindex, false); - double dProgressTip = Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.Tip(), false); + double dProgressStart = GuessVerificationProgress(chainParams.TxData(), pindex); + double dProgressTip = GuessVerificationProgress(chainParams.TxData(), chainActive.Tip()); while (pindex) { if (pindex->nHeight % 100 == 0 && dProgressTip - dProgressStart > 0.0) - ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), pindex, false) - dProgressStart) / (dProgressTip - dProgressStart) * 100)))); + ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((GuessVerificationProgress(chainParams.TxData(), pindex) - dProgressStart) / (dProgressTip - dProgressStart) * 100)))); CBlock block; ReadBlockFromDisk(block, pindex, Params().GetConsensus()); @@ -1497,7 +1564,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) pindex = chainActive.Next(pindex); if (GetTime() >= nNow + 60) { nNow = GetTime(); - LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), pindex)); + LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, GuessVerificationProgress(chainParams.TxData(), pindex)); } } ShowProgress(_("Rescanning..."), 100); // hide progress dialog in GUI @@ -1958,6 +2025,37 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const if (nDepth == 0 && !pcoin->InMempool()) continue; + // We should not consider coins from transactions that are replacing + // other transactions. + // + // Example: There is a transaction A which is replaced by bumpfee + // transaction B. In this case, we want to prevent creation of + // a transaction B' which spends an output of B. + // + // Reason: If transaction A were initially confirmed, transactions B + // and B' would no longer be valid, so the user would have to create + // a new transaction C to replace B'. However, in the case of a + // one-block reorg, transactions B' and C might BOTH be accepted, + // when the user only wanted one of them. Specifically, there could + // be a 1-block reorg away from the chain where transactions A and C + // were accepted to another chain where B, B', and C were all + // accepted. + if (nDepth == 0 && fOnlyConfirmed && pcoin->mapValue.count("replaces_txid")) { + continue; + } + + // Similarly, we should not consider coins from transactions that + // have been replaced. In the example above, we would want to prevent + // creation of a transaction A' spending an output of A, because if + // transaction B were initially confirmed, conflicting with A and + // A', we wouldn't want to the user to create a transaction D + // intending to replace A', but potentially resulting in a scenario + // where A, A', and D could all be accepted (instead of just B and + // D, or just A and A' like the user would want). + if (nDepth == 0 && fOnlyConfirmed && pcoin->mapValue.count("replaced_by_txid")) { + continue; + } + for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) { isminetype mine = IsMine(pcoin->tx->vout[i]); if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO && @@ -2192,14 +2290,15 @@ bool CWallet::SelectCoins(const vector<COutput>& vAvailableCoins, const CAmount& return res; } -bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const CTxDestination& destChange) +bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, bool keepReserveKey, const CTxDestination& destChange) { vector<CRecipient> vecSend; // Turn the txout set into a CRecipient vector - BOOST_FOREACH(const CTxOut& txOut, tx.vout) + for (size_t idx = 0; idx < tx.vout.size(); idx++) { - CRecipient recipient = {txOut.scriptPubKey, txOut.nValue, false}; + const CTxOut& txOut = tx.vout[idx]; + CRecipient recipient = {txOut.scriptPubKey, txOut.nValue, setSubtractFeeFromOutputs.count(idx) == 1}; vecSend.push_back(recipient); } @@ -2221,6 +2320,10 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool ov if (nChangePosInOut != -1) tx.vout.insert(tx.vout.begin() + nChangePosInOut, wtx.tx->vout[nChangePosInOut]); + // Copy output sizes from new transaction; they may have had the fee subtracted from them + for (unsigned int idx = 0; idx < tx.vout.size(); idx++) + tx.vout[idx].nValue = wtx.tx->vout[idx].nValue; + // Add new txins (keeping original txin scriptSig/order) BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin) { @@ -2236,6 +2339,10 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool ov } } + // optionally keep the change output key + if (keepReserveKey) + reservekey.KeepKey(); + return true; } @@ -2336,7 +2443,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt } } - if (txout.IsDust(::minRelayTxFee)) + if (txout.IsDust(dustRelayFee)) { if (recipient.fSubtractFeeFromAmount && nFeeRet > 0) { @@ -2414,16 +2521,16 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt // We do not move dust-change to fees, because the sender would end up paying more than requested. // This would be against the purpose of the all-inclusive feature. // So instead we raise the change and deduct from the recipient. - if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(::minRelayTxFee)) + if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee)) { - CAmount nDust = newTxOut.GetDustThreshold(::minRelayTxFee) - newTxOut.nValue; + CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue; newTxOut.nValue += nDust; // raise change until no more dust for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient { if (vecSend[i].fSubtractFeeFromAmount) { txNew.vout[i].nValue -= nDust; - if (txNew.vout[i].IsDust(::minRelayTxFee)) + if (txNew.vout[i].IsDust(dustRelayFee)) { strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); return false; @@ -2435,7 +2542,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt // Never create dust outputs; if we would, just // add the dust to the fee. - if (newTxOut.IsDust(::minRelayTxFee)) + if (newTxOut.IsDust(dustRelayFee)) { nChangePosInOut = -1; nFeeRet += nChange; @@ -2469,28 +2576,16 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt // BIP125 defines opt-in RBF as any nSequence < maxint-1, so // we use the highest possible value in that range (maxint-2) // to avoid conflicting with other possible uses of nSequence, - // and in the spirit of "smallest posible change from prior + // and in the spirit of "smallest possible change from prior // behavior." for (const auto& coin : setCoins) txNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second,CScript(), std::numeric_limits<unsigned int>::max() - (fWalletRbf ? 2 : 1))); // Fill in dummy signatures for fee calculation. - int nIn = 0; - for (const auto& coin : setCoins) - { - const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; - SignatureData sigdata; - - if (!ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata)) - { - strFailReason = _("Signing transaction failed"); - return false; - } else { - UpdateTransaction(txNew, nIn, sigdata); - } - - nIn++; + if (!DummySignTx(txNew, setCoins)) { + strFailReason = _("Signing transaction failed"); + return false; } unsigned int nBytes = GetVirtualTransactionSize(txNew); @@ -2695,8 +2790,13 @@ CAmount CWallet::GetRequiredFee(unsigned int nTxBytes) CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool) { - // payTxFee is user-set "I want to pay this much" - CAmount nFeeNeeded = payTxFee.GetFee(nTxBytes); + // payTxFee is the user-set global for desired feerate + return GetMinimumFee(nTxBytes, nConfirmTarget, pool, payTxFee.GetFee(nTxBytes)); +} + +CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, CAmount targetFee) +{ + CAmount nFeeNeeded = targetFee; // User didn't set: use -txconfirmtarget to estimate... if (nFeeNeeded == 0) { int estimateFoundTarget = nConfirmTarget; @@ -3591,7 +3691,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) walletInstance->ScanForWalletTransactions(pindexRescan, true); LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart); walletInstance->SetBestChain(chainActive.GetLocator()); - nWalletDBUpdated++; + CWalletDB::IncrementUpdateCounter(); // Restore wallet transaction metadata after -zapwallettxes=1 if (GetBoolArg("-zapwallettxes", false) && GetArg("-zapwallettxes", "1") != "2") @@ -3847,5 +3947,5 @@ int CMerkleTx::GetBlocksToMaturity() const bool CMerkleTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state) { - return ::AcceptToMemoryPool(mempool, state, tx, true, NULL, false, nAbsurdFee); + return ::AcceptToMemoryPool(mempool, state, tx, true, NULL, NULL, false, nAbsurdFee); } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index b9fa6bb24b..ea4787c363 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -13,6 +13,7 @@ #include "utilstrencodings.h" #include "validationinterface.h" #include "script/ismine.h" +#include "script/sign.h" #include "wallet/crypter.h" #include "wallet/walletdb.h" #include "wallet/rpcwallet.h" @@ -48,6 +49,8 @@ static const CAmount DEFAULT_TRANSACTION_FEE = 0; static const CAmount DEFAULT_FALLBACK_FEE = 20000; //! -mintxfee default static const CAmount DEFAULT_TRANSACTION_MINFEE = 1000; +//! minimum recommended increment for BIP 125 replacement txs +static const CAmount WALLET_INCREMENTAL_RELAY_FEE = 5000; //! target minimum change amount static const CAmount MIN_CHANGE = CENT; //! final minimum change amount after paying for fees @@ -258,6 +261,11 @@ public: unsigned int fTimeReceivedIsTxTime; unsigned int nTimeReceived; //!< time received by this node unsigned int nTimeSmart; + /** + * From me flag is set to 1 for transactions that were created by the wallet + * on this bitcoin node, and set to 0 for transactions that were created + * externally and came in through the network or sendrawtransaction RPC. + */ char fFromMe; std::string strFromAccount; int64_t nOrderPos; //!< position in ordered transaction list @@ -780,7 +788,7 @@ public: * Insert additional inputs into the transaction by * calling CreateTransaction(); */ - bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const CTxDestination& destChange = CNoDestination()); + bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, bool keepReserveKey = true, const CTxDestination& destChange = CNoDestination()); /** * Create a new transaction paying the recipients with a set of coins @@ -794,6 +802,8 @@ public: void ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& entries); bool AddAccountingEntry(const CAccountingEntry&); bool AddAccountingEntry(const CAccountingEntry&, CWalletDB *pwalletdb); + template <typename ContainerType> + bool DummySignTx(CMutableTransaction &txNew, const ContainerType &coins); static CFeeRate minTxFee; static CFeeRate fallbackFee; @@ -803,6 +813,11 @@ public: */ static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool); /** + * Estimate the minimum fee considering required fee and targetFee or if 0 + * then fee estimation for nConfirmTarget + */ + static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, CAmount targetFee); + /** * Return the minimum required fee taking into account the * floating relay fee and user set minimum transaction fee */ @@ -825,6 +840,10 @@ public: std::set<CTxDestination> GetAccountAddresses(const std::string& strAccount) const; isminetype IsMine(const CTxIn& txin) const; + /** + * Returns amount of debit if the input matches the + * filter, otherwise returns 0 + */ CAmount GetDebit(const CTxIn& txin, const isminefilter& filter) const; isminetype IsMine(const CTxOut& txout) const; CAmount GetCredit(const CTxOut& txout, const isminefilter& filter) const; @@ -834,6 +853,8 @@ public: /** should probably be renamed to IsRelevantToMe */ bool IsFromMe(const CTransaction& tx) const; CAmount GetDebit(const CTransaction& tx, const isminefilter& filter) const; + /** Returns whether all of the inputs match the filter */ + bool IsAllFromMe(const CTransaction& tx, const isminefilter& filter) const; CAmount GetCredit(const CTransaction& tx, const isminefilter& filter) const; CAmount GetChange(const CTransaction& tx) const; void SetBestChain(const CBlockLocator& loc); @@ -885,6 +906,9 @@ public: //! Get wallet transactions that conflict with given transaction (spend same outputs) std::set<uint256> GetConflicts(const uint256& txid) const; + //! Check if a given transaction has any of its outputs spent by another transaction in the wallet + bool HasWalletSpend(const uint256& txid) const; + //! Flush wallet (bitdb flush) void Flush(bool shutdown=false); @@ -921,6 +945,9 @@ public: /* Mark a transaction (and it in-wallet descendants) as abandoned so its inputs may be respent. */ bool AbandonTransaction(const uint256& hashTx); + /** Mark a transaction as replaced by another transaction (e.g., BIP 125). */ + bool MarkReplaced(const uint256& originalHash, const uint256& newHash); + /* Returns the wallets help message */ static std::string GetWalletHelpString(bool showDebug); @@ -1009,4 +1036,28 @@ public: } }; +// Helper for producing a bunch of max-sized low-S signatures (eg 72 bytes) +// ContainerType is meant to hold pair<CWalletTx *, int>, and be iterable +// so that each entry corresponds to each vIn, in order. +template <typename ContainerType> +bool CWallet::DummySignTx(CMutableTransaction &txNew, const ContainerType &coins) +{ + // Fill in dummy signatures for fee calculation. + int nIn = 0; + for (const auto& coin : coins) + { + const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata)) + { + return false; + } else { + UpdateTransaction(txNew, nIn, sigdata); + } + + nIn++; + } + return true; +} #endif // BITCOIN_WALLET_WALLET_H diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 9cd19ab619..b00ce36b70 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -15,6 +15,8 @@ #include "utiltime.h" #include "wallet/wallet.h" +#include <atomic> + #include <boost/version.hpp> #include <boost/filesystem.hpp> #include <boost/foreach.hpp> @@ -24,13 +26,15 @@ using namespace std; static uint64_t nAccountingEntryNumber = 0; +static std::atomic<unsigned int> nWalletDBUpdateCounter; + // // CWalletDB // bool CWalletDB::WriteName(const string& strAddress, const string& strName) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(make_pair(string("name"), strAddress), strName); } @@ -38,37 +42,37 @@ bool CWalletDB::EraseName(const string& strAddress) { // This should only be used for sending addresses, never for receiving addresses, // receiving addresses must always have an address book entry if they're not change return. - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Erase(make_pair(string("name"), strAddress)); } bool CWalletDB::WritePurpose(const string& strAddress, const string& strPurpose) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(make_pair(string("purpose"), strAddress), strPurpose); } bool CWalletDB::ErasePurpose(const string& strPurpose) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Erase(make_pair(string("purpose"), strPurpose)); } bool CWalletDB::WriteTx(const CWalletTx& wtx) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::make_pair(std::string("tx"), wtx.GetHash()), wtx); } bool CWalletDB::EraseTx(uint256 hash) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Erase(std::make_pair(std::string("tx"), hash)); } bool CWalletDB::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; if (!Write(std::make_pair(std::string("keymeta"), vchPubKey), keyMeta, false)) @@ -88,7 +92,7 @@ bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey, const CKeyMetadata &keyMeta) { const bool fEraseUnencryptedKey = true; - nWalletDBUpdated++; + nWalletDBUpdateCounter++; if (!Write(std::make_pair(std::string("keymeta"), vchPubKey), keyMeta)) @@ -106,31 +110,31 @@ bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey, bool CWalletDB::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true); } bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::make_pair(std::string("cscript"), hash), *(const CScriptBase*)(&redeemScript), false); } bool CWalletDB::WriteWatchOnly(const CScript &dest) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)), '1'); } bool CWalletDB::EraseWatchOnly(const CScript &dest) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Erase(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest))); } bool CWalletDB::WriteBestBlock(const CBlockLocator& locator) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; Write(std::string("bestblock"), CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan return Write(std::string("bestblock_nomerkle"), locator); } @@ -143,13 +147,13 @@ bool CWalletDB::ReadBestBlock(CBlockLocator& locator) bool CWalletDB::WriteOrderPosNext(int64_t nOrderPosNext) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::string("orderposnext"), nOrderPosNext); } bool CWalletDB::WriteDefaultKey(const CPubKey& vchPubKey) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::string("defaultkey"), vchPubKey); } @@ -160,13 +164,13 @@ bool CWalletDB::ReadPool(int64_t nPool, CKeyPool& keypool) bool CWalletDB::WritePool(int64_t nPool, const CKeyPool& keypool) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::make_pair(std::string("pool"), nPool), keypool); } bool CWalletDB::ErasePool(int64_t nPool) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Erase(std::make_pair(std::string("pool"), nPool)); } @@ -780,20 +784,20 @@ void ThreadFlushWalletDB() if (!GetBoolArg("-flushwallet", DEFAULT_FLUSHWALLET)) return; - unsigned int nLastSeen = nWalletDBUpdated; - unsigned int nLastFlushed = nWalletDBUpdated; + unsigned int nLastSeen = CWalletDB::GetUpdateCounter(); + unsigned int nLastFlushed = CWalletDB::GetUpdateCounter(); int64_t nLastWalletUpdate = GetTime(); while (true) { MilliSleep(500); - if (nLastSeen != nWalletDBUpdated) + if (nLastSeen != CWalletDB::GetUpdateCounter()) { - nLastSeen = nWalletDBUpdated; + nLastSeen = CWalletDB::GetUpdateCounter(); nLastWalletUpdate = GetTime(); } - if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2) + if (nLastFlushed != CWalletDB::GetUpdateCounter() && GetTime() - nLastWalletUpdate >= 2) { TRY_LOCK(bitdb.cs_db,lockDb); if (lockDb) @@ -815,7 +819,7 @@ void ThreadFlushWalletDB() if (_mi != bitdb.mapFileUseCount.end()) { LogPrint("db", "Flushing %s\n", strFile); - nLastFlushed = nWalletDBUpdated; + nLastFlushed = CWalletDB::GetUpdateCounter(); int64_t nStart = GetTimeMillis(); // Flush wallet file so it's self contained @@ -922,19 +926,29 @@ bool CWalletDB::Recover(CDBEnv& dbenv, const std::string& filename) bool CWalletDB::WriteDestData(const std::string &address, const std::string &key, const std::string &value) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::make_pair(std::string("destdata"), std::make_pair(address, key)), value); } bool CWalletDB::EraseDestData(const std::string &address, const std::string &key) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Erase(std::make_pair(std::string("destdata"), std::make_pair(address, key))); } bool CWalletDB::WriteHDChain(const CHDChain& chain) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::string("hdchain"), chain); } + +void CWalletDB::IncrementUpdateCounter() +{ + nWalletDBUpdateCounter++; +} + +unsigned int CWalletDB::GetUpdateCounter() +{ + return nWalletDBUpdateCounter; +} diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index b9db55baa4..8437a95ba7 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -176,10 +176,11 @@ public: //! write the hdchain model (external chain child index counter) bool WriteHDChain(const CHDChain& chain); + static void IncrementUpdateCounter(); + static unsigned int GetUpdateCounter(); private: CWalletDB(const CWalletDB&); void operator=(const CWalletDB&); - }; void ThreadFlushWalletDB(); |