From 0da49b5926b678b2ec35fabe37034f3d2e8385f4 Mon Sep 17 00:00:00 2001 From: Johnson Lau Date: Wed, 18 Jan 2017 16:12:26 +0800 Subject: [PATCH 001/382] Skip precompute sighash for transactions without witness --- src/script/interpreter.cpp | 17 +++++++++++------ src/script/interpreter.h | 1 + 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index f9b783588..7d5d2092f 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1170,9 +1170,13 @@ uint256 GetOutputsHash(const CTransaction& txTo) { PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo) { - hashPrevouts = GetPrevoutHash(txTo); - hashSequence = GetSequenceHash(txTo); - hashOutputs = GetOutputsHash(txTo); + // Cache is calculated only for transactions with witness + if (txTo.HasWitness()) { + hashPrevouts = GetPrevoutHash(txTo); + hashSequence = GetSequenceHash(txTo); + hashOutputs = GetOutputsHash(txTo); + ready = true; + } } uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache) @@ -1181,18 +1185,19 @@ uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsig uint256 hashPrevouts; uint256 hashSequence; uint256 hashOutputs; + const bool cacheready = cache && cache->ready; if (!(nHashType & SIGHASH_ANYONECANPAY)) { - hashPrevouts = cache ? cache->hashPrevouts : GetPrevoutHash(txTo); + hashPrevouts = cacheready ? cache->hashPrevouts : GetPrevoutHash(txTo); } if (!(nHashType & SIGHASH_ANYONECANPAY) && (nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) { - hashSequence = cache ? cache->hashSequence : GetSequenceHash(txTo); + hashSequence = cacheready ? cache->hashSequence : GetSequenceHash(txTo); } if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) { - hashOutputs = cache ? cache->hashOutputs : GetOutputsHash(txTo); + hashOutputs = cacheready ? cache->hashOutputs : GetOutputsHash(txTo); } else if ((nHashType & 0x1f) == SIGHASH_SINGLE && nIn < txTo.vout.size()) { CHashWriter ss(SER_GETHASH, 0); ss << txTo.vout[nIn]; diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 79894c530..c6385cb51 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -113,6 +113,7 @@ bool CheckSignatureEncoding(const std::vector &vchSig, unsigned i struct PrecomputedTransactionData { uint256 hashPrevouts, hashSequence, hashOutputs; + bool ready = false; PrecomputedTransactionData(const CTransaction& tx); }; From f3ba86973428d7442bf95713890be6185bc40dd0 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Mon, 22 May 2017 13:55:42 +0200 Subject: [PATCH 002/382] [tests] Add libFuzzer support. See http://llvm.org/docs/LibFuzzer.html#fuzzer-usage for usage instructions. --- src/test/test_bitcoin_fuzzy.cpp | 50 +++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/src/test/test_bitcoin_fuzzy.cpp b/src/test/test_bitcoin_fuzzy.cpp index e11e46bb0..1fe9fe903 100644 --- a/src/test/test_bitcoin_fuzzy.cpp +++ b/src/test/test_bitcoin_fuzzy.cpp @@ -48,8 +48,8 @@ enum TEST_ID { TEST_ID_END }; -bool read_stdin(std::vector &data) { - char buffer[1024]; +bool read_stdin(std::vector &data) { + uint8_t buffer[1024]; ssize_t length=0; while((length = read(STDIN_FILENO, buffer, 1024)) > 0) { data.insert(data.end(), buffer, buffer+length); @@ -59,11 +59,7 @@ bool read_stdin(std::vector &data) { return length==0; } -int do_fuzz() -{ - std::vector buffer; - if (!read_stdin(buffer)) return 0; - +int test_one_input(std::vector buffer) { if (buffer.size() < sizeof(uint32_t)) return 0; uint32_t test_id = 0xffffffff; @@ -255,9 +251,32 @@ int do_fuzz() return 0; } +static std::unique_ptr globalVerifyHandle; +void initialize() { + globalVerifyHandle = std::unique_ptr(new ECCVerifyHandle()); +} + +// This function is used by libFuzzer +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + test_one_input(std::vector(data, data + size)); + return 0; +} + +// This function is used by libFuzzer +extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) { + initialize(); + return 0; +} + +// Disabled under WIN32 due to clash with Cygwin's WinMain. +#ifndef WIN32 +// Declare main(...) "weak" to allow for libFuzzer linking. libFuzzer provides +// the main(...) function. +__attribute__((weak)) +#endif int main(int argc, char **argv) { - ECCVerifyHandle globalVerifyHandle; + initialize(); #ifdef __AFL_INIT // Enable AFL deferred forkserver mode. Requires compilation using // afl-clang-fast++. See fuzzing.md for details. @@ -267,11 +286,20 @@ int main(int argc, char **argv) #ifdef __AFL_LOOP // Enable AFL persistent mode. Requires compilation using afl-clang-fast++. // See fuzzing.md for details. + int ret = 0; while (__AFL_LOOP(1000)) { - do_fuzz(); + std::vector buffer; + if (!read_stdin(buffer)) { + continue; + } + ret = test_one_input(buffer); } - return 0; + return ret; #else - return do_fuzz(); + std::vector buffer; + if (!read_stdin(buffer)) { + return 0; + } + return test_one_input(buffer); #endif } From 5f850b0311a9adca506f6a7e7c10e3d996f493af Mon Sep 17 00:00:00 2001 From: Karl-Johan Alm Date: Fri, 28 Apr 2017 16:16:33 +0900 Subject: [PATCH 003/382] [bench] Include ms/blk stats in Connect* benchmarks. --- src/validation.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/validation.cpp b/src/validation.cpp index 381d1b01c..8aedcabc4 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1492,6 +1492,7 @@ static int64_t nTimeConnect = 0; static int64_t nTimeIndex = 0; static int64_t nTimeCallbacks = 0; static int64_t nTimeTotal = 0; +static int64_t nBlocksTotal = 0; /** Apply the effects of this block (with given index) on the UTXO set represented by coins. * Validity checks that depend on the UTXO set are also done; ConnectBlock() @@ -1522,6 +1523,8 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd return true; } + nBlocksTotal++; + bool fScriptChecks = true; if (!hashAssumeValid.IsNull()) { // We've been configured with the hash of a block which has been externally verified to have a valid history. @@ -1549,7 +1552,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd } int64_t nTime1 = GetTimeMicros(); nTimeCheck += nTime1 - nTimeStart; - LogPrint(BCLog::BENCH, " - Sanity checks: %.2fms [%.2fs]\n", 0.001 * (nTime1 - nTimeStart), nTimeCheck * 0.000001); + LogPrint(BCLog::BENCH, " - Sanity checks: %.2fms [%.2fs (%.2fms/blk)]\n", 0.001 * (nTime1 - nTimeStart), nTimeCheck * 0.000001, nTimeCheck * 0.001 / nBlocksTotal); // Do not allow blocks that contain transactions which 'overwrite' older transactions, // unless those are already completely spent. @@ -1616,7 +1619,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd } int64_t nTime2 = GetTimeMicros(); nTimeForks += nTime2 - nTime1; - LogPrint(BCLog::BENCH, " - Fork checks: %.2fms [%.2fs]\n", 0.001 * (nTime2 - nTime1), nTimeForks * 0.000001); + LogPrint(BCLog::BENCH, " - Fork checks: %.2fms [%.2fs (%.2fms/blk)]\n", 0.001 * (nTime2 - nTime1), nTimeForks * 0.000001, nTimeForks * 0.001 / nBlocksTotal); CBlockUndo blockundo; @@ -1690,7 +1693,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); } int64_t nTime3 = GetTimeMicros(); nTimeConnect += nTime3 - nTime2; - LogPrint(BCLog::BENCH, " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime3 - nTime2), 0.001 * (nTime3 - nTime2) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime3 - nTime2) / (nInputs-1), nTimeConnect * 0.000001); + LogPrint(BCLog::BENCH, " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs (%.2fms/blk)]\n", (unsigned)block.vtx.size(), 0.001 * (nTime3 - nTime2), 0.001 * (nTime3 - nTime2) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime3 - nTime2) / (nInputs-1), nTimeConnect * 0.000001, nTimeConnect * 0.001 / nBlocksTotal); CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, chainparams.GetConsensus()); if (block.vtx[0]->GetValueOut() > blockReward) @@ -1702,7 +1705,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd if (!control.Wait()) return state.DoS(100, error("%s: CheckQueue failed", __func__), REJECT_INVALID, "block-validation-failed"); int64_t nTime4 = GetTimeMicros(); nTimeVerify += nTime4 - nTime2; - LogPrint(BCLog::BENCH, " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs]\n", nInputs - 1, 0.001 * (nTime4 - nTime2), nInputs <= 1 ? 0 : 0.001 * (nTime4 - nTime2) / (nInputs-1), nTimeVerify * 0.000001); + LogPrint(BCLog::BENCH, " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs (%.2fms/blk)]\n", nInputs - 1, 0.001 * (nTime4 - nTime2), nInputs <= 1 ? 0 : 0.001 * (nTime4 - nTime2) / (nInputs-1), nTimeVerify * 0.000001, nTimeVerify * 0.001 / nBlocksTotal); if (fJustCheck) return true; @@ -1734,10 +1737,10 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd view.SetBestBlock(pindex->GetBlockHash()); int64_t nTime5 = GetTimeMicros(); nTimeIndex += nTime5 - nTime4; - LogPrint(BCLog::BENCH, " - Index writing: %.2fms [%.2fs]\n", 0.001 * (nTime5 - nTime4), nTimeIndex * 0.000001); + LogPrint(BCLog::BENCH, " - Index writing: %.2fms [%.2fs (%.2fms/blk)]\n", 0.001 * (nTime5 - nTime4), nTimeIndex * 0.000001, nTimeIndex * 0.001 / nBlocksTotal); int64_t nTime6 = GetTimeMicros(); nTimeCallbacks += nTime6 - nTime5; - LogPrint(BCLog::BENCH, " - Callbacks: %.2fms [%.2fs]\n", 0.001 * (nTime6 - nTime5), nTimeCallbacks * 0.000001); + LogPrint(BCLog::BENCH, " - Callbacks: %.2fms [%.2fs (%.2fms/blk)]\n", 0.001 * (nTime6 - nTime5), nTimeCallbacks * 0.000001, nTimeCallbacks * 0.001 / nBlocksTotal); return true; } @@ -2088,17 +2091,17 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, return error("ConnectTip(): ConnectBlock %s failed", pindexNew->GetBlockHash().ToString()); } nTime3 = GetTimeMicros(); nTimeConnectTotal += nTime3 - nTime2; - LogPrint(BCLog::BENCH, " - Connect total: %.2fms [%.2fs]\n", (nTime3 - nTime2) * 0.001, nTimeConnectTotal * 0.000001); + LogPrint(BCLog::BENCH, " - Connect total: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime3 - nTime2) * 0.001, nTimeConnectTotal * 0.000001, nTimeConnectTotal * 0.001 / nBlocksTotal); bool flushed = view.Flush(); assert(flushed); } int64_t nTime4 = GetTimeMicros(); nTimeFlush += nTime4 - nTime3; - LogPrint(BCLog::BENCH, " - Flush: %.2fms [%.2fs]\n", (nTime4 - nTime3) * 0.001, nTimeFlush * 0.000001); + LogPrint(BCLog::BENCH, " - Flush: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime4 - nTime3) * 0.001, nTimeFlush * 0.000001, nTimeFlush * 0.001 / nBlocksTotal); // Write the chain state to disk, if necessary. if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED)) return false; int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4; - LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs]\n", (nTime5 - nTime4) * 0.001, nTimeChainState * 0.000001); + LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime5 - nTime4) * 0.001, nTimeChainState * 0.000001, nTimeChainState * 0.001 / nBlocksTotal); // Remove conflicting transactions from the mempool.; mempool.removeForBlock(blockConnecting.vtx, pindexNew->nHeight); disconnectpool.removeForBlock(blockConnecting.vtx); @@ -2106,8 +2109,8 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, UpdateTip(pindexNew, chainparams); int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1; - LogPrint(BCLog::BENCH, " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001); - LogPrint(BCLog::BENCH, "- Connect block: %.2fms [%.2fs]\n", (nTime6 - nTime1) * 0.001, nTimeTotal * 0.000001); + LogPrint(BCLog::BENCH, " - Connect postprocess: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001, nTimePostConnect * 0.001 / nBlocksTotal); + LogPrint(BCLog::BENCH, "- Connect block: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime6 - nTime1) * 0.001, nTimeTotal * 0.000001, nTimeTotal * 0.001 / nBlocksTotal); connectTrace.BlockConnected(pindexNew, std::move(pthisBlock)); return true; From a473eff8f9896e7bcf357b7c97aaab6c623d2732 Mon Sep 17 00:00:00 2001 From: Karl-Johan Alm Date: Thu, 1 Jun 2017 11:42:34 +0900 Subject: [PATCH 004/382] [bench] Replace 0.00(000)1 with MICRO/MILLI #defines in validation.cpp. --- src/validation.cpp | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/validation.cpp b/src/validation.cpp index 8aedcabc4..9797ce8cc 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -51,6 +51,9 @@ # error "Bitcoin cannot be compiled without assertions." #endif +#define MICRO 0.000001 +#define MILLI 0.001 + /** * Global state */ @@ -1552,7 +1555,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd } int64_t nTime1 = GetTimeMicros(); nTimeCheck += nTime1 - nTimeStart; - LogPrint(BCLog::BENCH, " - Sanity checks: %.2fms [%.2fs (%.2fms/blk)]\n", 0.001 * (nTime1 - nTimeStart), nTimeCheck * 0.000001, nTimeCheck * 0.001 / nBlocksTotal); + LogPrint(BCLog::BENCH, " - Sanity checks: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime1 - nTimeStart), nTimeCheck * MICRO, nTimeCheck * MILLI / nBlocksTotal); // Do not allow blocks that contain transactions which 'overwrite' older transactions, // unless those are already completely spent. @@ -1619,7 +1622,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd } int64_t nTime2 = GetTimeMicros(); nTimeForks += nTime2 - nTime1; - LogPrint(BCLog::BENCH, " - Fork checks: %.2fms [%.2fs (%.2fms/blk)]\n", 0.001 * (nTime2 - nTime1), nTimeForks * 0.000001, nTimeForks * 0.001 / nBlocksTotal); + LogPrint(BCLog::BENCH, " - Fork checks: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime2 - nTime1), nTimeForks * MICRO, nTimeForks * MILLI / nBlocksTotal); CBlockUndo blockundo; @@ -1693,7 +1696,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); } int64_t nTime3 = GetTimeMicros(); nTimeConnect += nTime3 - nTime2; - LogPrint(BCLog::BENCH, " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs (%.2fms/blk)]\n", (unsigned)block.vtx.size(), 0.001 * (nTime3 - nTime2), 0.001 * (nTime3 - nTime2) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime3 - nTime2) / (nInputs-1), nTimeConnect * 0.000001, nTimeConnect * 0.001 / nBlocksTotal); + LogPrint(BCLog::BENCH, " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs (%.2fms/blk)]\n", (unsigned)block.vtx.size(), MILLI * (nTime3 - nTime2), MILLI * (nTime3 - nTime2) / block.vtx.size(), nInputs <= 1 ? 0 : MILLI * (nTime3 - nTime2) / (nInputs-1), nTimeConnect * MICRO, nTimeConnect * MILLI / nBlocksTotal); CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, chainparams.GetConsensus()); if (block.vtx[0]->GetValueOut() > blockReward) @@ -1705,7 +1708,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd if (!control.Wait()) return state.DoS(100, error("%s: CheckQueue failed", __func__), REJECT_INVALID, "block-validation-failed"); int64_t nTime4 = GetTimeMicros(); nTimeVerify += nTime4 - nTime2; - LogPrint(BCLog::BENCH, " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs (%.2fms/blk)]\n", nInputs - 1, 0.001 * (nTime4 - nTime2), nInputs <= 1 ? 0 : 0.001 * (nTime4 - nTime2) / (nInputs-1), nTimeVerify * 0.000001, nTimeVerify * 0.001 / nBlocksTotal); + LogPrint(BCLog::BENCH, " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs (%.2fms/blk)]\n", nInputs - 1, MILLI * (nTime4 - nTime2), nInputs <= 1 ? 0 : MILLI * (nTime4 - nTime2) / (nInputs-1), nTimeVerify * MICRO, nTimeVerify * MILLI / nBlocksTotal); if (fJustCheck) return true; @@ -1737,10 +1740,10 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd view.SetBestBlock(pindex->GetBlockHash()); int64_t nTime5 = GetTimeMicros(); nTimeIndex += nTime5 - nTime4; - LogPrint(BCLog::BENCH, " - Index writing: %.2fms [%.2fs (%.2fms/blk)]\n", 0.001 * (nTime5 - nTime4), nTimeIndex * 0.000001, nTimeIndex * 0.001 / nBlocksTotal); + LogPrint(BCLog::BENCH, " - Index writing: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime5 - nTime4), nTimeIndex * MICRO, nTimeIndex * MILLI / nBlocksTotal); int64_t nTime6 = GetTimeMicros(); nTimeCallbacks += nTime6 - nTime5; - LogPrint(BCLog::BENCH, " - Callbacks: %.2fms [%.2fs (%.2fms/blk)]\n", 0.001 * (nTime6 - nTime5), nTimeCallbacks * 0.000001, nTimeCallbacks * 0.001 / nBlocksTotal); + LogPrint(BCLog::BENCH, " - Callbacks: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime6 - nTime5), nTimeCallbacks * MICRO, nTimeCallbacks * MILLI / nBlocksTotal); return true; } @@ -1959,7 +1962,7 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara bool flushed = view.Flush(); assert(flushed); } - LogPrint(BCLog::BENCH, "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); + LogPrint(BCLog::BENCH, "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * MILLI); // Write the chain state to disk, if necessary. if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED)) return false; @@ -2080,7 +2083,7 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, // Apply the block atomically to the chain state. int64_t nTime2 = GetTimeMicros(); nTimeReadFromDisk += nTime2 - nTime1; int64_t nTime3; - LogPrint(BCLog::BENCH, " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * 0.001, nTimeReadFromDisk * 0.000001); + LogPrint(BCLog::BENCH, " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * MILLI, nTimeReadFromDisk * MICRO); { CCoinsViewCache view(pcoinsTip); bool rv = ConnectBlock(blockConnecting, state, pindexNew, view, chainparams); @@ -2091,17 +2094,17 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, return error("ConnectTip(): ConnectBlock %s failed", pindexNew->GetBlockHash().ToString()); } nTime3 = GetTimeMicros(); nTimeConnectTotal += nTime3 - nTime2; - LogPrint(BCLog::BENCH, " - Connect total: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime3 - nTime2) * 0.001, nTimeConnectTotal * 0.000001, nTimeConnectTotal * 0.001 / nBlocksTotal); + LogPrint(BCLog::BENCH, " - Connect total: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime3 - nTime2) * MILLI, nTimeConnectTotal * MICRO, nTimeConnectTotal * MILLI / nBlocksTotal); bool flushed = view.Flush(); assert(flushed); } int64_t nTime4 = GetTimeMicros(); nTimeFlush += nTime4 - nTime3; - LogPrint(BCLog::BENCH, " - Flush: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime4 - nTime3) * 0.001, nTimeFlush * 0.000001, nTimeFlush * 0.001 / nBlocksTotal); + LogPrint(BCLog::BENCH, " - Flush: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime4 - nTime3) * MILLI, nTimeFlush * MICRO, nTimeFlush * MILLI / nBlocksTotal); // Write the chain state to disk, if necessary. if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED)) return false; int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4; - LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime5 - nTime4) * 0.001, nTimeChainState * 0.000001, nTimeChainState * 0.001 / nBlocksTotal); + LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime5 - nTime4) * MILLI, nTimeChainState * MICRO, nTimeChainState * MILLI / nBlocksTotal); // Remove conflicting transactions from the mempool.; mempool.removeForBlock(blockConnecting.vtx, pindexNew->nHeight); disconnectpool.removeForBlock(blockConnecting.vtx); @@ -2109,8 +2112,8 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, UpdateTip(pindexNew, chainparams); int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1; - LogPrint(BCLog::BENCH, " - Connect postprocess: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001, nTimePostConnect * 0.001 / nBlocksTotal); - LogPrint(BCLog::BENCH, "- Connect block: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime6 - nTime1) * 0.001, nTimeTotal * 0.000001, nTimeTotal * 0.001 / nBlocksTotal); + LogPrint(BCLog::BENCH, " - Connect postprocess: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime6 - nTime5) * MILLI, nTimePostConnect * MICRO, nTimePostConnect * MILLI / nBlocksTotal); + LogPrint(BCLog::BENCH, "- Connect block: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime6 - nTime1) * MILLI, nTimeTotal * MICRO, nTimeTotal * MILLI / nBlocksTotal); connectTrace.BlockConnected(pindexNew, std::move(pthisBlock)); return true; @@ -4123,7 +4126,7 @@ void DumpMempool(void) file.fclose(); RenameOver(GetDataDir() / "mempool.dat.new", GetDataDir() / "mempool.dat"); int64_t last = GetTimeMicros(); - LogPrintf("Dumped mempool: %gs to copy, %gs to dump\n", (mid-start)*0.000001, (last-mid)*0.000001); + LogPrintf("Dumped mempool: %gs to copy, %gs to dump\n", (mid-start)*MICRO, (last-mid)*MICRO); } catch (const std::exception& e) { LogPrintf("Failed to dump mempool: %s. Continuing anyway.\n", e.what()); } From 41f3e84aaca82540582fd5a93fd632e752c3e6bf Mon Sep 17 00:00:00 2001 From: MeshCollider Date: Tue, 27 Jun 2017 17:35:47 +1200 Subject: [PATCH 005/382] Fix inconsistencies and grammar in various files --- contrib/README.md | 2 +- contrib/bitcoind.bash-completion | 2 +- contrib/gitian-build.sh | 6 +++--- contrib/gitian-descriptors/README.md | 10 +++++----- contrib/gitian-keys/README.md | 2 +- contrib/init/bitcoind.conf | 4 ++-- contrib/init/bitcoind.openrc | 4 ++-- contrib/rpm/README.md | 14 +++++++------- 8 files changed, 22 insertions(+), 22 deletions(-) diff --git a/contrib/README.md b/contrib/README.md index 6f750106e..34c4cfc55 100644 --- a/contrib/README.md +++ b/contrib/README.md @@ -35,7 +35,7 @@ PGP keys used for signing Bitcoin Core [Gitian release](/doc/release-process.md) Scripts and notes for Mac builds. ### [RPM](/contrib/rpm) ### -RPM spec file for building bitcoin-core on RPM based distributions +RPM spec file for building bitcoin-core on RPM based distributions. ### [Gitian-build](/contrib/gitian-build.sh) ### Script for running full Gitian builds. diff --git a/contrib/bitcoind.bash-completion b/contrib/bitcoind.bash-completion index af87e97d8..cccd4bde0 100644 --- a/contrib/bitcoind.bash-completion +++ b/contrib/bitcoind.bash-completion @@ -30,7 +30,7 @@ _bitcoind() { ;; *) - # only parse -help if senseful + # only parse -help if sensible if [[ -z "$cur" || "$cur" =~ ^- ]]; then local helpopts helpopts=$($bitcoind -help 2>&1 | awk '$1 ~ /^-/ { sub(/=.*/, "="); print $1 }' ) diff --git a/contrib/gitian-build.sh b/contrib/gitian-build.sh index 6ee5df470..d94c7f4f8 100755 --- a/contrib/gitian-build.sh +++ b/contrib/gitian-build.sh @@ -40,15 +40,15 @@ version Version number, commit, or branch to build. If building a commit or bra Options: -c|--commit Indicate that the version argument is for a commit or branch -u|--url Specify the URL of the repository. Default is https://github.com/bitcoin/bitcoin --v|--verify Verify the gitian build --b|--build Do a gitian build +-v|--verify Verify the Gitian build +-b|--build Do a Gitian build -s|--sign Make signed binaries for Windows and Mac OSX -B|--buildsign Build both signed and unsigned binaries -o|--os Specify which Operating Systems the build is for. Default is lwx. l for linux, w for windows, x for osx -j Number of processes to use. Default 2 -m Memory to allocate in MiB. Default 2000 --kvm Use KVM instead of LXC ---setup Setup the gitian building environment. Uses KVM. If you want to use lxc, use the --lxc option. Only works on Debian-based systems (Ubuntu, Debian) +--setup Set up the Gitian building environment. Uses KVM. If you want to use lxc, use the --lxc option. Only works on Debian-based systems (Ubuntu, Debian) --detach-sign Create the assert file for detached signing. Will not commit anything. --no-commit Do not commit anything to git -h|--help Print this help message diff --git a/contrib/gitian-descriptors/README.md b/contrib/gitian-descriptors/README.md index 614970659..d9dbfd3cb 100644 --- a/contrib/gitian-descriptors/README.md +++ b/contrib/gitian-descriptors/README.md @@ -1,4 +1,4 @@ -### Gavin's notes on getting gitian builds up and running using KVM +### Gavin's notes on getting Gitian builds up and running using KVM These instructions distilled from [https://help.ubuntu.com/community/KVM/Installation](https://help.ubuntu.com/community/KVM/Installation). @@ -56,10 +56,10 @@ Here's a description of Gavin's setup on OSX 10.6: 4. Inside the running Ubuntu desktop, install: - sudo apt-get install debootstrap lxc ruby apache2 git apt-cacher-ng python-vm-builder + sudo apt-get install debootstrap lxc ruby apache2 git apt-cacher-ng python-vm-builder 5. Still inside Ubuntu, tell gitian-builder to use LXC, then follow the "Once you've got the right hardware and software" instructions above: - export USE_LXC=1 - git clone git://github.com/bitcoin/bitcoin.git - ... etc + export USE_LXC=1 + git clone git://github.com/bitcoin/bitcoin.git + ... etc diff --git a/contrib/gitian-keys/README.md b/contrib/gitian-keys/README.md index 439910330..4b0b7a261 100644 --- a/contrib/gitian-keys/README.md +++ b/contrib/gitian-keys/README.md @@ -3,7 +3,7 @@ PGP keys This folder contains the public keys of developers and active contributors. -The keys are mainly used to sign git commits or the build results of gitian +The keys are mainly used to sign git commits or the build results of Gitian builds. You can import the keys into gpg as follows. Also, make sure to fetch the diff --git a/contrib/init/bitcoind.conf b/contrib/init/bitcoind.conf index f9554eecd..de4ea0ed5 100644 --- a/contrib/init/bitcoind.conf +++ b/contrib/init/bitcoind.conf @@ -30,12 +30,12 @@ pre-start script echo echo "This password is security critical to securing wallets " echo "and must not be the same as the rpcuser setting." - echo "You can generate a suitable random password using the following" + echo "You can generate a suitable random password using the following " echo "command from the shell:" echo echo "bash -c 'tr -dc a-zA-Z0-9 < /dev/urandom | head -c32 && echo'" echo - echo "It is also recommended that you also set alertnotify so you are " + echo "It is recommended that you also set alertnotify so you are " echo "notified of problems:" echo echo "ie: alertnotify=echo %%s | mail -s \"Bitcoin Alert\"" \ diff --git a/contrib/init/bitcoind.openrc b/contrib/init/bitcoind.openrc index eda1a96fb..50377c099 100644 --- a/contrib/init/bitcoind.openrc +++ b/contrib/init/bitcoind.openrc @@ -76,12 +76,12 @@ checkconfig() eerror "" eerror "This password is security critical to securing wallets " eerror "and must not be the same as the rpcuser setting." - eerror "You can generate a suitable random password using the following" + eerror "You can generate a suitable random password using the following " eerror "command from the shell:" eerror "" eerror "bash -c 'tr -dc a-zA-Z0-9 < /dev/urandom | head -c32 && echo'" eerror "" - eerror "It is also recommended that you also set alertnotify so you are " + eerror "It is recommended that you also set alertnotify so you are " eerror "notified of problems:" eerror "" eerror "ie: alertnotify=echo %%s | mail -s \"Bitcoin Alert\"" \ diff --git a/contrib/rpm/README.md b/contrib/rpm/README.md index 4ab2f3568..e1e0745fd 100644 --- a/contrib/rpm/README.md +++ b/contrib/rpm/README.md @@ -84,16 +84,16 @@ If you would prefer not to build the GUI at all, you can pass the switch The desktop and KDE meta files are created in the spec file itself with the `cat` command. This is done to allow easy distribution specific changes without -needing to use any patches. A specific time stamp is given to the files so that +needing to use any patches. A specific timestamp is given to the files so that it does not they do not appear to have been updated every time the package is -built. If you do make changes to them, you probably should update time stamp -assigned to them in the `touch` command that specifies the time stamp. +built. If you do make changes to them, you probably should update timestamp +assigned to them in the `touch` command that specifies the timestamp. ## SVG, PNG, and XPM Icons The `bitcoin.svg` file is from the source listed as `Source100`. It is used as the source for the PNG and XPM files. The generated PNG and XPM files are given -the same time stamp as the source SVG file as a means of indicating they are +the same timestamp as the source SVG file as a means of indicating they are derived from it. ## Systemd @@ -105,10 +105,10 @@ distributions that still receive vendor updates do in fact use systemd. The files to control the service are created in the RPM spec file itself using the `cat` command. This is done to make it easy to modify for other distributions that may implement things differently without needing to patch -source. A specific time stamp is given to the files so that they do not appear +source. A specific timestamp is given to the files so that they do not appear to have been updated every time the package is built. If you do make changes to -them, you probably should update the time stamp assigned to them in the `touch` -command that specifies the time stamp. +them, you probably should update the timestamp assigned to them in the `touch` +command that specifies the timestamp. ## SELinux From ca67ddf0b737b65fb2909b65ab15ecc000aca06a Mon Sep 17 00:00:00 2001 From: esneider Date: Tue, 27 Jun 2017 11:41:34 -0300 Subject: [PATCH 006/382] Move the AreInputsStandard documentation next to its implementation --- src/policy/policy.cpp | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 5f68c09a8..68b5d48fb 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -52,23 +52,6 @@ bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFeeIn) return (txout.nValue < GetDustThreshold(txout, dustRelayFeeIn)); } - /** - * Check transaction inputs to mitigate two - * potential denial-of-service attacks: - * - * 1. scriptSigs with extra data stuffed into them, - * not consumed by scriptPubKey (or P2SH script) - * 2. P2SH scripts with a crazy number of expensive - * CHECKSIG/CHECKMULTISIG operations - * - * Why bother? To avoid denial-of-service attacks; an attacker - * can submit a standard HASH... OP_EQUAL transaction, - * which will get accepted into blocks. The redemption - * script can be anything; an attacker could use a very - * expensive-to-check-upon-redemption script like: - * DUP CHECKSIG DROP ... repeated 100 times... OP_1 - */ - bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType, const bool witnessEnabled) { std::vector > vSolutions; @@ -158,6 +141,22 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnes return true; } +/** + * Check transaction inputs to mitigate two + * potential denial-of-service attacks: + * + * 1. scriptSigs with extra data stuffed into them, + * not consumed by scriptPubKey (or P2SH script) + * 2. P2SH scripts with a crazy number of expensive + * CHECKSIG/CHECKMULTISIG operations + * + * Why bother? To avoid denial-of-service attacks; an attacker + * can submit a standard HASH... OP_EQUAL transaction, + * which will get accepted into blocks. The redemption + * script can be anything; an attacker could use a very + * expensive-to-check-upon-redemption script like: + * DUP CHECKSIG DROP ... repeated 100 times... OP_1 + */ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) { if (tx.IsCoinBase()) From 1d8df01412db0d94ac0030bd2225683891748aff Mon Sep 17 00:00:00 2001 From: MeshCollider Date: Sat, 1 Jul 2017 16:00:51 +1200 Subject: [PATCH 007/382] Fix MD formatting in REST-interface.md and spelling mistake in test_runner.py --- doc/REST-interface.md | 12 ++++++------ test/functional/test_runner.py | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/REST-interface.md b/doc/REST-interface.md index 7fbb17403..dfe218e89 100644 --- a/doc/REST-interface.md +++ b/doc/REST-interface.md @@ -8,14 +8,14 @@ The interface runs on the same port as the JSON-RPC interface, by default port 8 Supported API ------------- -####Transactions +#### Transactions `GET /rest/tx/.` Given a transaction hash: returns a transaction in binary, hex-encoded binary, or JSON formats. For full TX query capability, one must enable the transaction index via "txindex=1" command line / configuration option. -####Blocks +#### Blocks `GET /rest/block/.` `GET /rest/block/notxdetails/.` @@ -25,12 +25,12 @@ The HTTP request and response are both handled entirely in-memory, thus making m With the /notxdetails/ option JSON response will only contain the transaction hash instead of the complete transaction details. The option only affects the JSON response. -####Blockheaders +#### Blockheaders `GET /rest/headers//.` Given a block hash: returns amount of blockheaders in upward direction. -####Chaininfos +#### Chaininfos `GET /rest/chaininfo.json` Returns various state info regarding block chain processing. @@ -46,7 +46,7 @@ Only supports JSON as output format. * pruneheight : (numeric) heighest block available * softforks : (array) status of softforks in progress -####Query UTXO set +#### Query UTXO set `GET /rest/getutxos//-/-/.../-.` The getutxo command allows querying of the UTXO set given a set of outpoints. @@ -79,7 +79,7 @@ $ curl localhost:18332/rest/getutxos/checkmempool/b2cdfd7b89def827ff8af7cd9bff76 } ``` -####Memory pool +#### Memory pool `GET /rest/mempool/info.json` Returns various information about the TX mempool. diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 0dca318af..ffa5b0ae2 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -348,7 +348,7 @@ def print_results(test_results, max_len_name, runtime): class TestHandler: """ - Trigger the testscrips passed in via the list. + Trigger the test scripts passed in via the list. """ def __init__(self, num_tests_parallel, tests_dir, tmpdir, test_list=None, flags=None): From 581c41157d76e410ca8f968853e09068a3635815 Mon Sep 17 00:00:00 2001 From: Kyuntae Ethan Kim Date: Thu, 29 Jun 2017 01:24:41 +0900 Subject: [PATCH 008/382] Properly comment about shutdown process in init.cpp file --- src/init.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 57232c7df..9a8f1b085 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -102,12 +102,11 @@ static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat"; // created by AppInit() or the Qt main() function. // // A clean exit happens when StartShutdown() or the SIGTERM -// signal handler sets fRequestShutdown, which triggers -// the DetectShutdownThread(), which interrupts the main thread group. -// DetectShutdownThread() then exits, which causes AppInit() to -// continue (it .joins the shutdown thread). -// Shutdown() is then -// called to clean up database connections, and stop other +// signal handler sets fRequestShutdown, which makes main thread's +// WaitForShutdown() interrupts the thread group. +// And then, WaitForShutdown() makes all other on-going threads +// in the thread group join the main thread. +// Shutdown() is then called to clean up database connections, and stop other // threads that should only be stopped after the main network-processing // threads have exited. // From 49eb0916da3f07250393111b6bbff080b3d8edd6 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Sun, 9 Jul 2017 13:05:01 +0200 Subject: [PATCH 009/382] [tests] Avoid redundant assignments. Remove unused variables. --- src/test/scriptnum_tests.cpp | 8 ++------ src/test/sighash_tests.cpp | 6 ++---- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/test/scriptnum_tests.cpp b/src/test/scriptnum_tests.cpp index 1d5893bdc..280eb59ce 100644 --- a/src/test/scriptnum_tests.cpp +++ b/src/test/scriptnum_tests.cpp @@ -29,10 +29,7 @@ static void CheckCreateVch(const int64_t& num) CScriptNum scriptnum(num); BOOST_CHECK(verify(bignum, scriptnum)); - std::vector vch = bignum.getvch(); - CScriptNum10 bignum2(bignum.getvch(), false); - vch = scriptnum.getvch(); CScriptNum scriptnum2(scriptnum.getvch(), false); BOOST_CHECK(verify(bignum2, scriptnum2)); @@ -90,11 +87,10 @@ static void CheckSubtract(const int64_t& num1, const int64_t& num2) const CScriptNum10 bignum2(num2); const CScriptNum scriptnum1(num1); const CScriptNum scriptnum2(num2); - bool invalid = false; // int64_t overflow is undefined. - invalid = ((num2 > 0 && num1 < std::numeric_limits::min() + num2) || - (num2 < 0 && num1 > std::numeric_limits::max() + num2)); + bool invalid = ((num2 > 0 && num1 < std::numeric_limits::min() + num2) || + (num2 < 0 && num1 > std::numeric_limits::max() + num2)); if (!invalid) { BOOST_CHECK(verify(bignum1 - bignum2, scriptnum1 - scriptnum2)); diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp index 1ca83a7cf..ecbdf5778 100644 --- a/src/test/sighash_tests.cpp +++ b/src/test/sighash_tests.cpp @@ -124,11 +124,9 @@ BOOST_AUTO_TEST_CASE(sighash_test) #if defined(PRINT_SIGHASH_JSON) std::cout << "[\n"; std::cout << "\t[\"raw_transaction, script, input_index, hashType, signature_hash (result)\"],\n"; - #endif + int nRandomTests = 500; + #else int nRandomTests = 50000; - - #if defined(PRINT_SIGHASH_JSON) - nRandomTests = 500; #endif for (int i=0; i Date: Wed, 12 Jul 2017 17:18:35 -0700 Subject: [PATCH 010/382] Reorder C{,Mutable}Transaction for better packing --- src/primitives/transaction.cpp | 8 ++++---- src/primitives/transaction.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index a0d7793f9..cdae8e4ad 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -55,7 +55,7 @@ std::string CTxOut::ToString() const } CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::CURRENT_VERSION), nLockTime(0) {} -CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime) {} +CMutableTransaction::CMutableTransaction(const CTransaction& tx) : vin(tx.vin), vout(tx.vout), nVersion(tx.nVersion), nLockTime(tx.nLockTime) {} uint256 CMutableTransaction::GetHash() const { @@ -76,9 +76,9 @@ uint256 CTransaction::GetWitnessHash() const } /* For backward compatibility, the hash is initialized to 0. TODO: remove the need for this default constructor entirely. */ -CTransaction::CTransaction() : nVersion(CTransaction::CURRENT_VERSION), vin(), vout(), nLockTime(0), hash() {} -CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime), hash(ComputeHash()) {} -CTransaction::CTransaction(CMutableTransaction &&tx) : nVersion(tx.nVersion), vin(std::move(tx.vin)), vout(std::move(tx.vout)), nLockTime(tx.nLockTime), hash(ComputeHash()) {} +CTransaction::CTransaction() : vin(), vout(), nVersion(CTransaction::CURRENT_VERSION), nLockTime(0), hash() {} +CTransaction::CTransaction(const CMutableTransaction &tx) : vin(tx.vin), vout(tx.vout), nVersion(tx.nVersion), nLockTime(tx.nLockTime), hash(ComputeHash()) {} +CTransaction::CTransaction(CMutableTransaction &&tx) : vin(std::move(tx.vin)), vout(std::move(tx.vout)), nVersion(tx.nVersion), nLockTime(tx.nLockTime), hash(ComputeHash()) {} CAmount CTransaction::GetValueOut() const { diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 00ac0b92b..06e0e7bb9 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -279,9 +279,9 @@ class CTransaction // actually immutable; deserialization and assignment are implemented, // and bypass the constness. This is safe, as they update the entire // structure, including the hash. - const int32_t nVersion; const std::vector vin; const std::vector vout; + const int32_t nVersion; const uint32_t nLockTime; private: @@ -362,9 +362,9 @@ class CTransaction /** A mutable version of CTransaction. */ struct CMutableTransaction { - int32_t nVersion; std::vector vin; std::vector vout; + int32_t nVersion; uint32_t nLockTime; CMutableTransaction(); From 41bf1598f12d546851e509d369783d493bdd7108 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Sun, 16 Jul 2017 19:42:37 +0200 Subject: [PATCH 011/382] Remove unreachable code --- src/rest.cpp | 21 --------------------- src/wallet/db.cpp | 2 -- 2 files changed, 23 deletions(-) diff --git a/src/rest.cpp b/src/rest.cpp index 33e3fb452..5257458c2 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -190,9 +190,6 @@ static bool rest_headers(HTTPRequest* req, return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: .bin, .hex)"); } } - - // not reached - return true; // continue to process further HTTP reqs on this cxn } static bool rest_block(HTTPRequest* req, @@ -253,9 +250,6 @@ static bool rest_block(HTTPRequest* req, return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")"); } } - - // not reached - return true; // continue to process further HTTP reqs on this cxn } static bool rest_block_extended(HTTPRequest* req, const std::string& strURIPart) @@ -292,9 +286,6 @@ static bool rest_chaininfo(HTTPRequest* req, const std::string& strURIPart) return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)"); } } - - // not reached - return true; // continue to process further HTTP reqs on this cxn } static bool rest_mempool_info(HTTPRequest* req, const std::string& strURIPart) @@ -317,9 +308,6 @@ static bool rest_mempool_info(HTTPRequest* req, const std::string& strURIPart) return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)"); } } - - // not reached - return true; // continue to process further HTTP reqs on this cxn } static bool rest_mempool_contents(HTTPRequest* req, const std::string& strURIPart) @@ -342,9 +330,6 @@ static bool rest_mempool_contents(HTTPRequest* req, const std::string& strURIPar return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)"); } } - - // not reached - return true; // continue to process further HTTP reqs on this cxn } static bool rest_tx(HTTPRequest* req, const std::string& strURIPart) @@ -394,9 +379,6 @@ static bool rest_tx(HTTPRequest* req, const std::string& strURIPart) return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")"); } } - - // not reached - return true; // continue to process further HTTP reqs on this cxn } static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart) @@ -581,9 +563,6 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart) return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")"); } } - - // not reached - return true; // continue to process further HTTP reqs on this cxn } static const struct { diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index da2d18075..c52fcfe5d 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -554,7 +554,6 @@ bool CDB::Rewrite(CWalletDBWrapper& dbw, const char* pszSkip) } MilliSleep(100); } - return false; } @@ -680,7 +679,6 @@ bool CWalletDBWrapper::Backup(const std::string& strDest) } MilliSleep(100); } - return false; } void CWalletDBWrapper::Flush(bool shutdown) From c5ebddd114ea7794ca0c12af735294dcae805216 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Nyffenegger?= Date: Thu, 13 Jul 2017 13:39:27 +0200 Subject: [PATCH 012/382] Tests: address placement should be deterministic by default --- src/test/addrman_tests.cpp | 40 ++++++-------------------------------- 1 file changed, 6 insertions(+), 34 deletions(-) diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp index bc6aef2c1..c53744b49 100644 --- a/src/test/addrman_tests.cpp +++ b/src/test/addrman_tests.cpp @@ -15,9 +15,14 @@ class CAddrManTest : public CAddrMan uint64_t state; public: - CAddrManTest() + CAddrManTest(bool makeDeterministic = true) { state = 1; + + if (makeDeterministic) { + // Set addrman addr placement to be deterministic. + MakeDeterministic(); + } } //! Ensure that bucket placement is always the same for testing purposes. @@ -79,9 +84,6 @@ BOOST_AUTO_TEST_CASE(addrman_simple) { CAddrManTest addrman; - // Set addrman addr placement to be deterministic. - addrman.MakeDeterministic(); - CNetAddr source = ResolveIP("252.2.2.2"); // Test: Does Addrman respond correctly when empty. @@ -131,9 +133,6 @@ BOOST_AUTO_TEST_CASE(addrman_ports) { CAddrManTest addrman; - // Set addrman addr placement to be deterministic. - addrman.MakeDeterministic(); - CNetAddr source = ResolveIP("252.2.2.2"); BOOST_CHECK_EQUAL(addrman.size(), 0); @@ -163,9 +162,6 @@ BOOST_AUTO_TEST_CASE(addrman_select) { CAddrManTest addrman; - // Set addrman addr placement to be deterministic. - addrman.MakeDeterministic(); - CNetAddr source = ResolveIP("252.2.2.2"); // Test: Select from new with 1 addr in new. @@ -225,9 +221,6 @@ BOOST_AUTO_TEST_CASE(addrman_new_collisions) { CAddrManTest addrman; - // Set addrman addr placement to be deterministic. - addrman.MakeDeterministic(); - CNetAddr source = ResolveIP("252.2.2.2"); BOOST_CHECK_EQUAL(addrman.size(), 0); @@ -254,9 +247,6 @@ BOOST_AUTO_TEST_CASE(addrman_tried_collisions) { CAddrManTest addrman; - // Set addrman addr placement to be deterministic. - addrman.MakeDeterministic(); - CNetAddr source = ResolveIP("252.2.2.2"); BOOST_CHECK_EQUAL(addrman.size(), 0); @@ -284,9 +274,6 @@ BOOST_AUTO_TEST_CASE(addrman_find) { CAddrManTest addrman; - // Set addrman addr placement to be deterministic. - addrman.MakeDeterministic(); - BOOST_CHECK_EQUAL(addrman.size(), 0); CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE); @@ -320,9 +307,6 @@ BOOST_AUTO_TEST_CASE(addrman_create) { CAddrManTest addrman; - // Set addrman addr placement to be deterministic. - addrman.MakeDeterministic(); - BOOST_CHECK_EQUAL(addrman.size(), 0); CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE); @@ -343,9 +327,6 @@ BOOST_AUTO_TEST_CASE(addrman_delete) { CAddrManTest addrman; - // Set addrman addr placement to be deterministic. - addrman.MakeDeterministic(); - BOOST_CHECK_EQUAL(addrman.size(), 0); CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE); @@ -366,9 +347,6 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr) { CAddrManTest addrman; - // Set addrman addr placement to be deterministic. - addrman.MakeDeterministic(); - // Test: Sanity check, GetAddr should never return anything if addrman // is empty. BOOST_CHECK_EQUAL(addrman.size(), 0); @@ -430,9 +408,6 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket) { CAddrManTest addrman; - // Set addrman addr placement to be deterministic. - addrman.MakeDeterministic(); - CAddress addr1 = CAddress(ResolveService("250.1.1.1", 8333), NODE_NONE); CAddress addr2 = CAddress(ResolveService("250.1.1.1", 9999), NODE_NONE); @@ -487,9 +462,6 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket) { CAddrManTest addrman; - // Set addrman addr placement to be deterministic. - addrman.MakeDeterministic(); - CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE); CAddress addr2 = CAddress(ResolveService("250.1.2.1", 9999), NODE_NONE); From 9a1675ee5b27f8634f9917a1f80904c9319739d3 Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Wed, 12 Jul 2017 16:48:36 -0400 Subject: [PATCH 013/382] optim: mark a few classes final --- src/httpserver.cpp | 2 +- src/txdb.h | 2 +- src/wallet/wallet.h | 2 +- src/zmq/zmqnotificationinterface.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 290a2efca..f37b28f71 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -40,7 +40,7 @@ static const size_t MAX_HEADERS_SIZE = 8192; /** HTTP request work item */ -class HTTPWorkItem : public HTTPClosure +class HTTPWorkItem final : public HTTPClosure { public: HTTPWorkItem(std::unique_ptr _req, const std::string &_path, const HTTPRequestHandler& _func): diff --git a/src/txdb.h b/src/txdb.h index adcbc7338..385e3ea91 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -64,7 +64,7 @@ struct CDiskTxPos : public CDiskBlockPos }; /** CCoinsView backed by the coin database (chainstate/) */ -class CCoinsViewDB : public CCoinsView +class CCoinsViewDB final : public CCoinsView { protected: CDBWrapper db; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 06937566b..9901f5f8c 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -648,7 +648,7 @@ class CAccountingEntry * A CWallet is an extension of a keystore, which also maintains a set of transactions and balances, * and provides the ability to create new transactions. */ -class CWallet : public CCryptoKeyStore, public CValidationInterface +class CWallet final : public CCryptoKeyStore, public CValidationInterface { private: static std::atomic fFlushScheduled; diff --git a/src/zmq/zmqnotificationinterface.h b/src/zmq/zmqnotificationinterface.h index eec6f7bc6..cb92216fa 100644 --- a/src/zmq/zmqnotificationinterface.h +++ b/src/zmq/zmqnotificationinterface.h @@ -13,7 +13,7 @@ class CBlockIndex; class CZMQAbstractNotifier; -class CZMQNotificationInterface : public CValidationInterface +class CZMQNotificationInterface final : public CValidationInterface { public: virtual ~CZMQNotificationInterface(); From 40a0f9fb967a4c7e74d3a6fd4d05744af40fac68 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Sun, 16 Jul 2017 23:42:27 +0200 Subject: [PATCH 014/382] Enable devirtualization opportunities by using the final specifier (C++11) * Declaring CCoinsViewErrorCatcher final enables devirtualization of two calls * Declaring CReserveKey final enables devirtualization of one call --- src/init.cpp | 2 +- src/wallet/wallet.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index d9b98be73..b64d303bd 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -133,7 +133,7 @@ bool ShutdownRequested() * chainstate, while keeping user interface out of the common library, which is shared * between bitcoind, and bitcoin-qt and non-server tools. */ -class CCoinsViewErrorCatcher : public CCoinsViewBacked +class CCoinsViewErrorCatcher final : public CCoinsViewBacked { public: CCoinsViewErrorCatcher(CCoinsView* view) : CCoinsViewBacked(view) {} diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 9901f5f8c..11ea89dc6 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1135,7 +1135,7 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface }; /** A key allocated from the key pool. */ -class CReserveKey : public CReserveScript +class CReserveKey final : public CReserveScript { protected: CWallet* pwallet; From b82c55af78738258b56bd8fe7b5f8d5ccf85f832 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Sun, 16 Jul 2017 14:56:43 +0200 Subject: [PATCH 015/382] Add attribute [[noreturn]] (C++11) to functions that will not return Rationale: * Reduce the number of false positives from static analyzers * Potentially enable additional compiler optimizations --- src/random.cpp | 4 ++-- src/test/test_bitcoin_main.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/random.cpp b/src/random.cpp index 67efc7d94..b0044af51 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -39,10 +39,10 @@ #include #include -static void RandFailure() +[[noreturn]] static void RandFailure() { LogPrintf("Failed to read randomness, aborting\n"); - abort(); + std::abort(); } static inline int64_t GetPerformanceCounter() diff --git a/src/test/test_bitcoin_main.cpp b/src/test/test_bitcoin_main.cpp index 34beef553..b556c953b 100644 --- a/src/test/test_bitcoin_main.cpp +++ b/src/test/test_bitcoin_main.cpp @@ -10,14 +10,14 @@ std::unique_ptr g_connman; -void Shutdown(void* parg) +[[noreturn]] void Shutdown(void* parg) { - exit(EXIT_SUCCESS); + std::exit(EXIT_SUCCESS); } -void StartShutdown() +[[noreturn]] void StartShutdown() { - exit(EXIT_SUCCESS); + std::exit(EXIT_SUCCESS); } bool ShutdownRequested() From 72a184a780f29d7833d900d728c0c3a10f460969 Mon Sep 17 00:00:00 2001 From: Carl Dong Date: Tue, 18 Jul 2017 21:17:39 -0700 Subject: [PATCH 016/382] Update init.md: Fix line breaks in section 3b. --- doc/init.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/init.md b/doc/init.md index e3db5b05e..16e077149 100644 --- a/doc/init.md +++ b/doc/init.md @@ -69,8 +69,8 @@ can then be controlled by group membership. Binary: `/usr/local/bin/bitcoind` Configuration file: `~/Library/Application Support/Bitcoin/bitcoin.conf` -Data directory: `~/Library/Application Support/Bitcoin` -Lock file: `~/Library/Application Support/Bitcoin/.lock` +Data directory: `~/Library/Application Support/Bitcoin` +Lock file: `~/Library/Application Support/Bitcoin/.lock` 4. Installing Service Configuration ----------------------------------- From 352d582ba240b825cb834cdde041864bafca0e21 Mon Sep 17 00:00:00 2001 From: Marko Bencun Date: Thu, 15 Jun 2017 09:39:07 +0200 Subject: [PATCH 017/382] Add vConnect to CConnman::Options Split the "-connect" argument parsing out of CConnman and put it into AppInitMain(). --- src/init.cpp | 9 ++++++++- src/net.cpp | 19 +++++++++++++------ src/net.h | 4 +++- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index d9b98be73..ceea9aa17 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1651,7 +1651,14 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) if (gArgs.IsArgSet("-seednode")) { connOptions.vSeedNodes = gArgs.GetArgs("-seednode"); } - + // Initiate outbound connections unless connect=0 + connOptions.m_use_addrman_outgoing = !gArgs.IsArgSet("-connect"); + if (!connOptions.m_use_addrman_outgoing) { + const auto connect = gArgs.GetArgs("-connect"); + if (connect.size() != 1 || connect[0] != "0") { + connOptions.m_specified_outgoing = connect; + } + } if (!connman.Start(scheduler, connOptions)) { return false; } diff --git a/src/net.cpp b/src/net.cpp index 5bf3af7ea..c83226efa 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1674,15 +1674,15 @@ void CConnman::ProcessOneShot() } } -void CConnman::ThreadOpenConnections() +void CConnman::ThreadOpenConnections(const std::vector connect) { // Connect to specific addresses - if (gArgs.IsArgSet("-connect")) + if (!connect.empty()) { for (int64_t nLoop = 0;; nLoop++) { ProcessOneShot(); - for (const std::string& strAddr : gArgs.GetArgs("-connect")) + for (const std::string& strAddr : connect) { CAddress addr(CService(), NODE_NONE); OpenNetworkConnection(addr, false, NULL, strAddr.c_str()); @@ -2360,9 +2360,16 @@ bool CConnman::Start(CScheduler& scheduler, Options connOptions) // Initiate outbound connections from -addnode threadOpenAddedConnections = std::thread(&TraceThread >, "addcon", std::function(std::bind(&CConnman::ThreadOpenAddedConnections, this))); - // Initiate outbound connections unless connect=0 - if (!gArgs.IsArgSet("-connect") || gArgs.GetArgs("-connect").size() != 1 || gArgs.GetArgs("-connect")[0] != "0") - threadOpenConnections = std::thread(&TraceThread >, "opencon", std::function(std::bind(&CConnman::ThreadOpenConnections, this))); + if (connOptions.m_use_addrman_outgoing && !connOptions.m_specified_outgoing.empty()) { + if (clientInterface) { + clientInterface->ThreadSafeMessageBox( + _("Cannot provide specific connections and have addrman find outgoing connections at the same."), + "", CClientUIInterface::MSG_ERROR); + } + return false; + } + if (connOptions.m_use_addrman_outgoing || !connOptions.m_specified_outgoing.empty()) + threadOpenConnections = std::thread(&TraceThread >, "opencon", std::function(std::bind(&CConnman::ThreadOpenConnections, this, connOptions.m_specified_outgoing))); // Process messages threadMessageHandler = std::thread(&TraceThread >, "msghand", std::function(std::bind(&CConnman::ThreadMessageHandler, this))); diff --git a/src/net.h b/src/net.h index b9a11c62f..44e0e78fc 100644 --- a/src/net.h +++ b/src/net.h @@ -145,6 +145,8 @@ class CConnman std::vector vSeedNodes; std::vector vWhitelistedRange; std::vector vBinds, vWhiteBinds; + bool m_use_addrman_outgoing = true; + std::vector m_specified_outgoing; }; CConnman(uint64_t seed0, uint64_t seed1); ~CConnman(); @@ -293,7 +295,7 @@ class CConnman void ThreadOpenAddedConnections(); void AddOneShot(const std::string& strDest); void ProcessOneShot(); - void ThreadOpenConnections(); + void ThreadOpenConnections(std::vector connect); void ThreadMessageHandler(); void AcceptConnection(const ListenSocket& hListenSocket); void ThreadSocketHandler(); From 5cb3da04b8882ca975b4e3d6c089c64bbaf67d0d Mon Sep 17 00:00:00 2001 From: Marko Bencun Date: Sun, 23 Jul 2017 23:32:57 +0200 Subject: [PATCH 018/382] keystore GetKeys(): return result instead of writing to reference Issue: #10905 By returning the result, a few useless lines can be removed. Return-value-optimization means there should be no copy. --- src/keystore.h | 18 +++++++----------- src/wallet/crypter.h | 25 +++++++++++-------------- src/wallet/wallet.cpp | 5 +---- 3 files changed, 19 insertions(+), 29 deletions(-) diff --git a/src/keystore.h b/src/keystore.h index 965ae0c79..059a657dc 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -30,7 +30,7 @@ class CKeyStore //! Check whether a key corresponding to a given address is present in the store. virtual bool HaveKey(const CKeyID &address) const =0; virtual bool GetKey(const CKeyID &address, CKey& keyOut) const =0; - virtual void GetKeys(std::set &setAddress) const =0; + virtual std::set GetKeys() const =0; virtual bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const =0; //! Support for BIP 0013 : see https://github.com/bitcoin/bips/blob/master/bip-0013.mediawiki @@ -71,18 +71,14 @@ class CBasicKeyStore : public CKeyStore } return result; } - void GetKeys(std::set &setAddress) const override + std::set GetKeys() const override { - setAddress.clear(); - { - LOCK(cs_KeyStore); - KeyMap::const_iterator mi = mapKeys.begin(); - while (mi != mapKeys.end()) - { - setAddress.insert((*mi).first); - mi++; - } + LOCK(cs_KeyStore); + std::set set_address; + for (const auto& mi : mapKeys) { + set_address.insert(mi.first); } + return set_address; } bool GetKey(const CKeyID &address, CKey &keyOut) const override { diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h index 1dc44e424..d6f3e2772 100644 --- a/src/wallet/crypter.h +++ b/src/wallet/crypter.h @@ -16,13 +16,13 @@ const unsigned int WALLET_CRYPTO_IV_SIZE = 16; /** * Private key encryption is done based on a CMasterKey, * which holds a salt and random encryption key. - * + * * CMasterKeys are encrypted using AES-256-CBC using a key * derived using derivation method nDerivationMethod * (0 == EVP_sha512()) and derivation iterations nDeriveIterations. * vchOtherDerivationParameters is provided for alternative algorithms * which may require more parameters (such as scrypt). - * + * * Wallet Private Keys are then encrypted using AES-256-CBC * with the double-sha256 of the public key as the IV, and the * master key's key as the encryption key (see keystore.[ch]). @@ -162,28 +162,25 @@ class CCryptoKeyStore : public CBasicKeyStore { { LOCK(cs_KeyStore); - if (!IsCrypted()) + if (!IsCrypted()) { return CBasicKeyStore::HaveKey(address); + } return mapCryptedKeys.count(address) > 0; } return false; } bool GetKey(const CKeyID &address, CKey& keyOut) const override; bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override; - void GetKeys(std::set &setAddress) const override + std::set GetKeys() const override { - if (!IsCrypted()) - { - CBasicKeyStore::GetKeys(setAddress); - return; + if (!IsCrypted()) { + return CBasicKeyStore::GetKeys(); } - setAddress.clear(); - CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin(); - while (mi != mapCryptedKeys.end()) - { - setAddress.insert((*mi).first); - mi++; + std::set set_address; + for (const auto& mi : mapCryptedKeys) { + set_address.insert(mi.first); } + return set_address; } /** diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 223790aa4..fcb679e75 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3660,13 +3660,10 @@ void CWallet::GetKeyBirthTimes(std::map &mapKeyBirth) c // map in which we'll infer heights of other keys CBlockIndex *pindexMax = chainActive[std::max(0, chainActive.Height() - 144)]; // the tip can be reorganized; use a 144-block safety margin std::map mapKeyFirstBlock; - std::set setKeys; - GetKeys(setKeys); - for (const CKeyID &keyid : setKeys) { + for (const CKeyID &keyid : GetKeys()) { if (mapKeyBirth.count(keyid) == 0) mapKeyFirstBlock[keyid] = pindexMax; } - setKeys.clear(); // if there are no such keys, we're done if (mapKeyFirstBlock.empty()) From fe09b0197c20dc3c0a614c1a94dac708ef206743 Mon Sep 17 00:00:00 2001 From: Marko Bencun Date: Sun, 23 Jul 2017 23:37:56 +0200 Subject: [PATCH 019/382] add missing lock to crypter GetKeys() Issue: #10905 --- src/wallet/crypter.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h index d6f3e2772..0295004cb 100644 --- a/src/wallet/crypter.h +++ b/src/wallet/crypter.h @@ -173,6 +173,7 @@ class CCryptoKeyStore : public CBasicKeyStore bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override; std::set GetKeys() const override { + LOCK(cs_KeyStore); if (!IsCrypted()) { return CBasicKeyStore::GetKeys(); } From a622a1768325d01eeb16f42340cdd36e722a3fa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= Date: Sat, 22 Jul 2017 00:00:52 +0100 Subject: [PATCH 020/382] Fix constness of ArgsManager methods --- src/util.cpp | 32 +++++++++++++++----------------- src/util.h | 21 ++++++++++++++------- 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/src/util.cpp b/src/util.cpp index b76c173f9..d3720ab7b 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -419,49 +419,48 @@ void ArgsManager::ParseParameters(int argc, const char* const argv[]) } } -std::vector ArgsManager::GetArgs(const std::string& strArg) +std::vector ArgsManager::GetArgs(const std::string& strArg) const { LOCK(cs_args); - if (IsArgSet(strArg)) - return mapMultiArgs.at(strArg); + auto it = mapMultiArgs.find(strArg); + if (it != mapMultiArgs.end()) return it->second; return {}; } -bool ArgsManager::IsArgSet(const std::string& strArg) +bool ArgsManager::IsArgSet(const std::string& strArg) const { LOCK(cs_args); return mapArgs.count(strArg); } -std::string ArgsManager::GetArg(const std::string& strArg, const std::string& strDefault) +std::string ArgsManager::GetArg(const std::string& strArg, const std::string& strDefault) const { LOCK(cs_args); - if (mapArgs.count(strArg)) - return mapArgs[strArg]; + auto it = mapArgs.find(strArg); + if (it != mapArgs.end()) return it->second; return strDefault; } -int64_t ArgsManager::GetArg(const std::string& strArg, int64_t nDefault) +int64_t ArgsManager::GetArg(const std::string& strArg, int64_t nDefault) const { LOCK(cs_args); - if (mapArgs.count(strArg)) - return atoi64(mapArgs[strArg]); + auto it = mapArgs.find(strArg); + if (it != mapArgs.end()) return atoi64(it->second); return nDefault; } -bool ArgsManager::GetBoolArg(const std::string& strArg, bool fDefault) +bool ArgsManager::GetBoolArg(const std::string& strArg, bool fDefault) const { LOCK(cs_args); - if (mapArgs.count(strArg)) - return InterpretBool(mapArgs[strArg]); + auto it = mapArgs.find(strArg); + if (it != mapArgs.end()) return InterpretBool(it->second); return fDefault; } bool ArgsManager::SoftSetArg(const std::string& strArg, const std::string& strValue) { LOCK(cs_args); - if (mapArgs.count(strArg)) - return false; + if (IsArgSet(strArg)) return false; ForceSetArg(strArg, strValue); return true; } @@ -478,8 +477,7 @@ void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strV { LOCK(cs_args); mapArgs[strArg] = strValue; - mapMultiArgs[strArg].clear(); - mapMultiArgs[strArg].push_back(strValue); + mapMultiArgs[strArg] = {strValue}; } diff --git a/src/util.h b/src/util.h index e1bdfb198..757a6abdf 100644 --- a/src/util.h +++ b/src/util.h @@ -195,13 +195,20 @@ inline bool IsSwitchChar(char c) class ArgsManager { protected: - CCriticalSection cs_args; + mutable CCriticalSection cs_args; std::map mapArgs; - std::map > mapMultiArgs; + std::map> mapMultiArgs; public: void ParseParameters(int argc, const char*const argv[]); void ReadConfigFile(const std::string& confPath); - std::vector GetArgs(const std::string& strArg); + + /** + * Return a vector of strings of the given argument + * + * @param strArg Argument to get (e.g. "-foo") + * @return command-line arguments + */ + std::vector GetArgs(const std::string& strArg) const; /** * Return true if the given argument has been manually set @@ -209,7 +216,7 @@ class ArgsManager * @param strArg Argument to get (e.g. "-foo") * @return true if the argument has been set */ - bool IsArgSet(const std::string& strArg); + bool IsArgSet(const std::string& strArg) const; /** * Return string argument or default value @@ -218,7 +225,7 @@ class ArgsManager * @param strDefault (e.g. "1") * @return command-line argument or default value */ - std::string GetArg(const std::string& strArg, const std::string& strDefault); + std::string GetArg(const std::string& strArg, const std::string& strDefault) const; /** * Return integer argument or default value @@ -227,7 +234,7 @@ class ArgsManager * @param nDefault (e.g. 1) * @return command-line argument (0 if invalid number) or default value */ - int64_t GetArg(const std::string& strArg, int64_t nDefault); + int64_t GetArg(const std::string& strArg, int64_t nDefault) const; /** * Return boolean argument or default value @@ -236,7 +243,7 @@ class ArgsManager * @param fDefault (true or false) * @return command-line argument or default value */ - bool GetBoolArg(const std::string& strArg, bool fDefault); + bool GetBoolArg(const std::string& strArg, bool fDefault) const; /** * Set an argument if it doesn't already have a value From 6e8c48dc593d8b6e5cebd15a980074715223b572 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Thu, 9 Mar 2017 13:34:54 +0100 Subject: [PATCH 021/382] Add const to methods that do not modify the object for which it is called --- src/addrdb.h | 2 +- src/dbwrapper.cpp | 2 +- src/dbwrapper.h | 2 +- src/merkleblock.h | 2 +- src/miner.cpp | 2 +- src/miner.h | 2 +- src/qt/bitcoin.cpp | 2 +- src/qt/modaloverlay.h | 2 +- src/qt/optionsmodel.cpp | 2 +- src/qt/optionsmodel.h | 14 +++++++------- src/qt/transactionrecord.cpp | 2 +- src/qt/transactionrecord.h | 2 +- src/qt/transactiontablemodel.h | 2 +- src/qt/walletmodeltransaction.cpp | 8 ++++---- src/qt/walletmodeltransaction.h | 8 ++++---- src/scheduler.h | 2 +- src/streams.h | 4 ++-- src/sync.h | 2 +- src/test/coins_tests.cpp | 4 ++-- src/test/prevector_tests.cpp | 4 ++-- src/test/script_tests.cpp | 2 +- src/txmempool.h | 2 +- src/wallet/db.h | 2 +- 23 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/addrdb.h b/src/addrdb.h index 6cb36dfac..8105ebc9b 100644 --- a/src/addrdb.h +++ b/src/addrdb.h @@ -61,7 +61,7 @@ class CBanEntry banReason = BanReasonUnknown; } - std::string banReasonToString() + std::string banReasonToString() const { switch (banReason) { case BanReasonNodeMisbehaving: diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp index ba9e21cc1..986333550 100644 --- a/src/dbwrapper.cpp +++ b/src/dbwrapper.cpp @@ -184,7 +184,7 @@ bool CDBWrapper::IsEmpty() } CDBIterator::~CDBIterator() { delete piter; } -bool CDBIterator::Valid() { return piter->Valid(); } +bool CDBIterator::Valid() const { return piter->Valid(); } void CDBIterator::SeekToFirst() { piter->SeekToFirst(); } void CDBIterator::Next() { piter->Next(); } diff --git a/src/dbwrapper.h b/src/dbwrapper.h index 24ef71bfb..2cbb47dba 100644 --- a/src/dbwrapper.h +++ b/src/dbwrapper.h @@ -130,7 +130,7 @@ class CDBIterator parent(_parent), piter(_piter) { }; ~CDBIterator(); - bool Valid(); + bool Valid() const; void SeekToFirst(); diff --git a/src/merkleblock.h b/src/merkleblock.h index f590c487d..20f2b3688 100644 --- a/src/merkleblock.h +++ b/src/merkleblock.h @@ -63,7 +63,7 @@ class CPartialMerkleTree bool fBad; /** helper function to efficiently calculate the number of nodes at given height in the merkle tree */ - unsigned int CalcTreeWidth(int height) { + unsigned int CalcTreeWidth(int height) const { return (nTransactions+(1 << height)-1) >> height; } diff --git a/src/miner.cpp b/src/miner.cpp index 79016bfd3..6f41f8633 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -224,7 +224,7 @@ void BlockAssembler::onlyUnconfirmed(CTxMemPool::setEntries& testSet) } } -bool BlockAssembler::TestPackage(uint64_t packageSize, int64_t packageSigOpsCost) +bool BlockAssembler::TestPackage(uint64_t packageSize, int64_t packageSigOpsCost) const { // TODO: switch to weight-based accounting for packages instead of vsize-based accounting. if (nBlockWeight + WITNESS_SCALE_FACTOR * packageSize >= nBlockMaxWeight) diff --git a/src/miner.h b/src/miner.h index 5c9cfd78f..685b4e0cc 100644 --- a/src/miner.h +++ b/src/miner.h @@ -187,7 +187,7 @@ class BlockAssembler /** Remove confirmed (inBlock) entries from given set */ void onlyUnconfirmed(CTxMemPool::setEntries& testSet); /** Test if a new package would "fit" in the block */ - bool TestPackage(uint64_t packageSize, int64_t packageSigOpsCost); + bool TestPackage(uint64_t packageSize, int64_t packageSigOpsCost) const; /** Perform checks on each transaction in a package: * locktime, premature-witness, serialized size (if necessary) * These checks should always succeed, and they're here diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 4a4116c67..0567603c0 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -227,7 +227,7 @@ class BitcoinApplication: public QApplication void requestShutdown(); /// Get process return value - int getReturnValue() { return returnValue; } + int getReturnValue() const { return returnValue; } /// Get window identifier of QMainWindow (BitcoinGUI) WId getMainWinId() const; diff --git a/src/qt/modaloverlay.h b/src/qt/modaloverlay.h index 21ccdbd83..cda23f954 100644 --- a/src/qt/modaloverlay.h +++ b/src/qt/modaloverlay.h @@ -32,7 +32,7 @@ public Q_SLOTS: // will show or hide the modal layer void showHide(bool hide = false, bool userRequested = false); void closeClicked(); - bool isLayerVisible() { return layerIsVisible; } + bool isLayerVisible() const { return layerIsVisible; } protected: bool eventFilter(QObject * obj, QEvent * ev); diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index d6e740ee9..75c106da4 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -441,7 +441,7 @@ void OptionsModel::setRestartRequired(bool fRequired) return settings.setValue("fRestartRequired", fRequired); } -bool OptionsModel::isRestartRequired() +bool OptionsModel::isRestartRequired() const { QSettings settings; return settings.value("fRestartRequired", false).toBool(); diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 78529fbdc..0ac82a414 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -59,18 +59,18 @@ class OptionsModel : public QAbstractListModel void setDisplayUnit(const QVariant &value); /* Explicit getters */ - bool getHideTrayIcon() { return fHideTrayIcon; } - bool getMinimizeToTray() { return fMinimizeToTray; } - bool getMinimizeOnClose() { return fMinimizeOnClose; } - int getDisplayUnit() { return nDisplayUnit; } - QString getThirdPartyTxUrls() { return strThirdPartyTxUrls; } + bool getHideTrayIcon() const { return fHideTrayIcon; } + bool getMinimizeToTray() const { return fMinimizeToTray; } + bool getMinimizeOnClose() const { return fMinimizeOnClose; } + int getDisplayUnit() const { return nDisplayUnit; } + QString getThirdPartyTxUrls() const { return strThirdPartyTxUrls; } bool getProxySettings(QNetworkProxy& proxy) const; - bool getCoinControlFeatures() { return fCoinControlFeatures; } + bool getCoinControlFeatures() const { return fCoinControlFeatures; } const QString& getOverriddenByCommandLine() { return strOverriddenByCommandLine; } /* Restart flag helper */ void setRestartRequired(bool fRequired); - bool isRestartRequired(); + bool isRestartRequired() const; private: /* Qt-only settings */ diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 03fd734e9..0b5df8a67 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -248,7 +248,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) status.needsUpdate = false; } -bool TransactionRecord::statusUpdateNeeded() +bool TransactionRecord::statusUpdateNeeded() const { AssertLockHeld(cs_main); return status.cur_num_blocks != chainActive.Height() || status.needsUpdate; diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h index 59f681224..a26e67614 100644 --- a/src/qt/transactionrecord.h +++ b/src/qt/transactionrecord.h @@ -140,7 +140,7 @@ class TransactionRecord /** Return whether a status update is needed. */ - bool statusUpdateNeeded(); + bool statusUpdateNeeded() const; }; #endif // BITCOIN_QT_TRANSACTIONRECORD_H diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index 80aeb64c4..b1f81498b 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -79,7 +79,7 @@ class TransactionTableModel : public QAbstractTableModel QVariant data(const QModelIndex &index, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const; QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const; - bool processingQueuedTransactions() { return fProcessingQueuedTransactions; } + bool processingQueuedTransactions() const { return fProcessingQueuedTransactions; } private: CWallet* wallet; diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp index 8bc9ef725..eae2c27f8 100644 --- a/src/qt/walletmodeltransaction.cpp +++ b/src/qt/walletmodeltransaction.cpp @@ -22,12 +22,12 @@ WalletModelTransaction::~WalletModelTransaction() delete walletTransaction; } -QList WalletModelTransaction::getRecipients() +QList WalletModelTransaction::getRecipients() const { return recipients; } -CWalletTx *WalletModelTransaction::getTransaction() +CWalletTx *WalletModelTransaction::getTransaction() const { return walletTransaction; } @@ -37,7 +37,7 @@ unsigned int WalletModelTransaction::getTransactionSize() return (!walletTransaction ? 0 : ::GetVirtualTransactionSize(*walletTransaction)); } -CAmount WalletModelTransaction::getTransactionFee() +CAmount WalletModelTransaction::getTransactionFee() const { return fee; } @@ -79,7 +79,7 @@ void WalletModelTransaction::reassignAmounts(int nChangePosRet) } } -CAmount WalletModelTransaction::getTotalTransactionAmount() +CAmount WalletModelTransaction::getTotalTransactionAmount() const { CAmount totalTransactionAmount = 0; for (const SendCoinsRecipient &rcp : recipients) diff --git a/src/qt/walletmodeltransaction.h b/src/qt/walletmodeltransaction.h index 64922efad..d7ecd7aa8 100644 --- a/src/qt/walletmodeltransaction.h +++ b/src/qt/walletmodeltransaction.h @@ -22,15 +22,15 @@ class WalletModelTransaction explicit WalletModelTransaction(const QList &recipients); ~WalletModelTransaction(); - QList getRecipients(); + QList getRecipients() const; - CWalletTx *getTransaction(); + CWalletTx *getTransaction() const; unsigned int getTransactionSize(); void setTransactionFee(const CAmount& newFee); - CAmount getTransactionFee(); + CAmount getTransactionFee() const; - CAmount getTotalTransactionAmount(); + CAmount getTotalTransactionAmount() const; void newPossibleKeyChange(CWallet *wallet); CReserveKey *getPossibleKeyChange(); diff --git a/src/scheduler.h b/src/scheduler.h index 0365d668b..cc2f01f2f 100644 --- a/src/scheduler.h +++ b/src/scheduler.h @@ -81,7 +81,7 @@ class CScheduler int nThreadsServicingQueue; bool stopRequested; bool stopWhenEmpty; - bool shouldStop() { return stopRequested || (stopWhenEmpty && taskQueue.empty()); } + bool shouldStop() const { return stopRequested || (stopWhenEmpty && taskQueue.empty()); } }; /** diff --git a/src/streams.h b/src/streams.h index 245fb9cd8..ca7cd4ed2 100644 --- a/src/streams.h +++ b/src/streams.h @@ -332,7 +332,7 @@ class CDataStream // bool eof() const { return size() == 0; } CDataStream* rdbuf() { return this; } - int in_avail() { return size(); } + int in_avail() const { return size(); } void SetType(int n) { nType = n; } int GetType() const { return nType; } @@ -648,7 +648,7 @@ class CBufferedFile } // return the current reading position - uint64_t GetPos() { + uint64_t GetPos() const { return nReadPos; } diff --git a/src/sync.h b/src/sync.h index 20974f5fb..43c54b6ec 100644 --- a/src/sync.h +++ b/src/sync.h @@ -280,7 +280,7 @@ class CSemaphoreGrant Release(); } - operator bool() + operator bool() const { return fHaveGrant; } diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index e24431528..85f268ecd 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -89,8 +89,8 @@ class CCoinsViewCacheTest : public CCoinsViewCache BOOST_CHECK_EQUAL(DynamicMemoryUsage(), ret); } - CCoinsMap& map() { return cacheCoins; } - size_t& usage() { return cachedCoinsUsage; } + CCoinsMap& map() const { return cacheCoins; } + size_t& usage() const { return cachedCoinsUsage; } }; } // namespace diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp index 345c4a214..841282873 100644 --- a/src/test/prevector_tests.cpp +++ b/src/test/prevector_tests.cpp @@ -152,11 +152,11 @@ class prevector_tester { pre_vector.assign(n, value); } - Size size() { + Size size() const { return real_vector.size(); } - Size capacity() { + Size capacity() const { return pre_vector.capacity(); } diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index a18471588..42d947470 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -450,7 +450,7 @@ class TestBuilder return array; } - std::string GetComment() + std::string GetComment() const { return comment; } diff --git a/src/txmempool.h b/src/txmempool.h index d272114a7..771d29e09 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -606,7 +606,7 @@ class CTxMemPool return mapTx.size(); } - uint64_t GetTotalTxSize() + uint64_t GetTotalTxSize() const { LOCK(cs); return totalTxSize; diff --git a/src/wallet/db.h b/src/wallet/db.h index 4f3ad0c42..0ebbaadcc 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -45,7 +45,7 @@ class CDBEnv void Reset(); void MakeMock(); - bool IsMock() { return fMockDb; } + bool IsMock() const { return fMockDb; } /** * Verify that database file strFile is OK. If it is not, From d201e40c55b0c02e3bec715ded06ec40f5c06fc4 Mon Sep 17 00:00:00 2001 From: Carl Dong Date: Sat, 29 Jul 2017 00:20:43 +0800 Subject: [PATCH 022/382] Update init.md: Fix section numbering. --- doc/init.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/doc/init.md b/doc/init.md index 16e077149..75f9013f2 100644 --- a/doc/init.md +++ b/doc/init.md @@ -10,14 +10,14 @@ can be found in the contrib/init folder. contrib/init/bitcoind.conf: Upstart service configuration file contrib/init/bitcoind.init: CentOS compatible SysV style init script -1. Service User +Service User --------------------------------- All three Linux startup configurations assume the existence of a "bitcoin" user and group. They must be created before attempting to use these scripts. The OS X configuration assumes bitcoind will be set up for the current user. -2. Configuration +Configuration --------------------------------- At a bare minimum, bitcoind requires that the rpcpassword setting be set @@ -46,10 +46,10 @@ relative to the data directory. `wallet` *only* supports relative paths. For an example configuration file that describes the configuration settings, see `contrib/debian/examples/bitcoin.conf`. -3. Paths +Paths --------------------------------- -3a) Linux +### Linux All three configurations assume several paths that might need to be adjusted. @@ -65,17 +65,17 @@ reasons to make the configuration file and data directory only readable by the bitcoin user and group. Access to bitcoin-cli and other bitcoind rpc clients can then be controlled by group membership. -3b) Mac OS X +### Mac OS X Binary: `/usr/local/bin/bitcoind` Configuration file: `~/Library/Application Support/Bitcoin/bitcoin.conf` Data directory: `~/Library/Application Support/Bitcoin` Lock file: `~/Library/Application Support/Bitcoin/.lock` -4. Installing Service Configuration +Installing Service Configuration ----------------------------------- -4a) systemd +### systemd Installing this .service file consists of just copying it to /usr/lib/systemd/system directory, followed by the command @@ -84,14 +84,14 @@ Installing this .service file consists of just copying it to To test, run `systemctl start bitcoind` and to enable for system startup run `systemctl enable bitcoind` -4b) OpenRC +### OpenRC Rename bitcoind.openrc to bitcoind and drop it in /etc/init.d. Double check ownership and permissions and make it executable. Test it with `/etc/init.d/bitcoind start` and configure it to run on startup with `rc-update add bitcoind` -4c) Upstart (for Debian/Ubuntu based distributions) +### Upstart (for Debian/Ubuntu based distributions) Drop bitcoind.conf in /etc/init. Test by running `service bitcoind start` it will automatically start on reboot. @@ -99,7 +99,7 @@ it will automatically start on reboot. NOTE: This script is incompatible with CentOS 5 and Amazon Linux 2014 as they use old versions of Upstart and do not supply the start-stop-daemon utility. -4d) CentOS +### CentOS Copy bitcoind.init to /etc/init.d/bitcoind. Test by running `service bitcoind start`. @@ -107,7 +107,7 @@ Using this script, you can adjust the path and flags to the bitcoind program by setting the BITCOIND and FLAGS environment variables in the file /etc/sysconfig/bitcoind. You can also use the DAEMONOPTS environment variable here. -4e) Mac OS X +### Mac OS X Copy org.bitcoin.bitcoind.plist into ~/Library/LaunchAgents. Load the launch agent by running `launchctl load ~/Library/LaunchAgents/org.bitcoin.bitcoind.plist`. @@ -118,7 +118,7 @@ NOTE: This approach is intended for those wanting to run bitcoind as the current You will need to modify org.bitcoin.bitcoind.plist if you intend to use it as a Launch Daemon with a dedicated bitcoin user. -5. Auto-respawn +Auto-respawn ----------------------------------- Auto respawning is currently only configured for Upstart and systemd. From 98625502607dd9180ab6840b832dc89986d0b150 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Mon, 31 Jul 2017 19:44:01 +0200 Subject: [PATCH 023/382] Use the noexcept specifier (C++11) instead of deprecated throw() --- src/support/allocators/secure.h | 8 ++++---- src/support/allocators/zeroafterfree.h | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/support/allocators/secure.h b/src/support/allocators/secure.h index 9daba86ef..961dd422e 100644 --- a/src/support/allocators/secure.h +++ b/src/support/allocators/secure.h @@ -26,13 +26,13 @@ struct secure_allocator : public std::allocator { typedef typename base::reference reference; typedef typename base::const_reference const_reference; typedef typename base::value_type value_type; - secure_allocator() throw() {} - secure_allocator(const secure_allocator& a) throw() : base(a) {} + secure_allocator() noexcept {} + secure_allocator(const secure_allocator& a) noexcept : base(a) {} template - secure_allocator(const secure_allocator& a) throw() : base(a) + secure_allocator(const secure_allocator& a) noexcept : base(a) { } - ~secure_allocator() throw() {} + ~secure_allocator() noexcept {} template struct rebind { typedef secure_allocator<_Other> other; diff --git a/src/support/allocators/zeroafterfree.h b/src/support/allocators/zeroafterfree.h index 28a940ad1..c740faf37 100644 --- a/src/support/allocators/zeroafterfree.h +++ b/src/support/allocators/zeroafterfree.h @@ -22,13 +22,13 @@ struct zero_after_free_allocator : public std::allocator { typedef typename base::reference reference; typedef typename base::const_reference const_reference; typedef typename base::value_type value_type; - zero_after_free_allocator() throw() {} - zero_after_free_allocator(const zero_after_free_allocator& a) throw() : base(a) {} + zero_after_free_allocator() noexcept {} + zero_after_free_allocator(const zero_after_free_allocator& a) noexcept : base(a) {} template - zero_after_free_allocator(const zero_after_free_allocator& a) throw() : base(a) + zero_after_free_allocator(const zero_after_free_allocator& a) noexcept : base(a) { } - ~zero_after_free_allocator() throw() {} + ~zero_after_free_allocator() noexcept {} template struct rebind { typedef zero_after_free_allocator<_Other> other; From 3eb53b867153c957529484b5338d27e69de027c1 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Sun, 30 Jul 2017 23:36:33 +0200 Subject: [PATCH 024/382] Avoid returning a BIP9Stats object with uninitialized values Uninitialized data potentially used in `rpc/blockchain.cpp`: ``` static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Consensus::DeploymentPos id) { ... const ThresholdState thresholdState = VersionBitsTipState(consensusParams, id); ... if (THRESHOLD_STARTED == thresholdState) { UniValue statsUV(UniValue::VOBJ); BIP9Stats statsStruct = VersionBitsTipStatistics(consensusParams, id); statsUV.push_back(Pair("period", statsStruct.period)); statsUV.push_back(Pair("threshold", statsStruct.threshold)); statsUV.push_back(Pair("elapsed", statsStruct.elapsed)); statsUV.push_back(Pair("count", statsStruct.count)); statsUV.push_back(Pair("possible", statsStruct.possible)); rv.push_back(Pair("statistics", statsUV)); } ... return rv; } ``` --- src/versionbits.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/versionbits.cpp b/src/versionbits.cpp index 8047e17aa..2dcbf0831 100644 --- a/src/versionbits.cpp +++ b/src/versionbits.cpp @@ -107,7 +107,7 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* // return the numerical statistics of blocks signalling the specified BIP9 condition in this current period BIP9Stats AbstractThresholdConditionChecker::GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params) const { - BIP9Stats stats; + BIP9Stats stats = {}; stats.period = Period(params); stats.threshold = Threshold(params); From d3d946a294988ca3ab2b2c8848da5b393fbbcf42 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Tue, 1 Aug 2017 23:40:19 +0200 Subject: [PATCH 025/382] Pass SendCoinsRecipient (208 bytes) by const reference --- src/qt/paymentserver.cpp | 2 +- src/qt/paymentserver.h | 2 +- src/qt/test/paymentservertests.cpp | 2 +- src/qt/test/paymentservertests.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index 132ee3274..705a6ae27 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -620,7 +620,7 @@ void PaymentServer::fetchRequest(const QUrl& url) netManager->get(netRequest); } -void PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipient, QByteArray transaction) +void PaymentServer::fetchPaymentACK(CWallet* wallet, const SendCoinsRecipient& recipient, QByteArray transaction) { const payments::PaymentDetails& details = recipient.paymentRequest.getDetails(); if (!details.has_payment_url()) diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h index 7c6d4507f..b43b3f982 100644 --- a/src/qt/paymentserver.h +++ b/src/qt/paymentserver.h @@ -113,7 +113,7 @@ public Q_SLOTS: void uiReady(); // Submit Payment message to a merchant, get back PaymentACK: - void fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipient, QByteArray transaction); + void fetchPaymentACK(CWallet* wallet, const SendCoinsRecipient& recipient, QByteArray transaction); // Handle an incoming URI, URI with local file scheme or file void handleURIOrFile(const QString& s); diff --git a/src/qt/test/paymentservertests.cpp b/src/qt/test/paymentservertests.cpp index b9a8ad6e2..5b65d272e 100644 --- a/src/qt/test/paymentservertests.cpp +++ b/src/qt/test/paymentservertests.cpp @@ -205,7 +205,7 @@ void PaymentServerTests::paymentServerTests() delete server; } -void RecipientCatcher::getRecipient(SendCoinsRecipient r) +void RecipientCatcher::getRecipient(const SendCoinsRecipient& r) { recipient = r; } diff --git a/src/qt/test/paymentservertests.h b/src/qt/test/paymentservertests.h index 9ffcbb02a..faf167f2c 100644 --- a/src/qt/test/paymentservertests.h +++ b/src/qt/test/paymentservertests.h @@ -26,7 +26,7 @@ class RecipientCatcher : public QObject Q_OBJECT public Q_SLOTS: - void getRecipient(SendCoinsRecipient r); + void getRecipient(const SendCoinsRecipient& r); public: SendCoinsRecipient recipient; From cab8557e3504c4b93796a7e196b288ffd061b9b4 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Thu, 20 Apr 2017 14:23:48 +0200 Subject: [PATCH 026/382] [wallet] [moveonly] Move CAffectedKeysVisitor --- src/wallet/wallet.cpp | 64 +++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index cf75cdd74..38f527faf 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -80,6 +80,38 @@ std::string COutput::ToString() const return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue)); } +class CAffectedKeysVisitor : public boost::static_visitor { +private: + const CKeyStore &keystore; + std::vector &vKeys; + +public: + CAffectedKeysVisitor(const CKeyStore &keystoreIn, std::vector &vKeysIn) : keystore(keystoreIn), vKeys(vKeysIn) {} + + void Process(const CScript &script) { + txnouttype type; + std::vector vDest; + int nRequired; + if (ExtractDestinations(script, type, vDest, nRequired)) { + for (const CTxDestination &dest : vDest) + boost::apply_visitor(*this, dest); + } + } + + void operator()(const CKeyID &keyId) { + if (keystore.HaveKey(keyId)) + vKeys.push_back(keyId); + } + + void operator()(const CScriptID &scriptId) { + CScript script; + if (keystore.GetCScript(scriptId, script)) + Process(script); + } + + void operator()(const CNoDestination &none) {} +}; + const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const { LOCK(cs_wallet); @@ -3629,38 +3661,6 @@ void CWallet::ListLockedCoins(std::vector& vOutpts) const /** @} */ // end of Actions -class CAffectedKeysVisitor : public boost::static_visitor { -private: - const CKeyStore &keystore; - std::vector &vKeys; - -public: - CAffectedKeysVisitor(const CKeyStore &keystoreIn, std::vector &vKeysIn) : keystore(keystoreIn), vKeys(vKeysIn) {} - - void Process(const CScript &script) { - txnouttype type; - std::vector vDest; - int nRequired; - if (ExtractDestinations(script, type, vDest, nRequired)) { - for (const CTxDestination &dest : vDest) - boost::apply_visitor(*this, dest); - } - } - - void operator()(const CKeyID &keyId) { - if (keystore.HaveKey(keyId)) - vKeys.push_back(keyId); - } - - void operator()(const CScriptID &scriptId) { - CScript script; - if (keystore.GetCScript(scriptId, script)) - Process(script); - } - - void operator()(const CNoDestination &none) {} -}; - void CWallet::GetKeyBirthTimes(std::map &mapKeyBirth) const { AssertLockHeld(cs_wallet); // mapKeyMetadata mapKeyBirth.clear(); From 2376bfcf24a83c61e9bba9e12bc0fd0b044bdfc1 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Wed, 26 Jul 2017 16:37:15 -0400 Subject: [PATCH 027/382] [wallet] [moveonly] Move LoadKeyPool to cpp --- src/wallet/wallet.cpp | 17 +++++++++++++++++ src/wallet/wallet.h | 17 +---------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 38f527faf..2ef3299c2 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3240,6 +3240,23 @@ size_t CWallet::KeypoolCountExternalKeys() return setExternalKeyPool.size(); } +void CWallet::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool) +{ + if (keypool.fInternal) { + setInternalKeyPool.insert(nIndex); + } else { + setExternalKeyPool.insert(nIndex); + } + m_max_keypool_index = std::max(m_max_keypool_index, nIndex); + + // If no metadata exists yet, create a default with the pool key's + // creation time. Note that this may be overwritten by actually + // stored metadata for that key later, which is fine. + CKeyID keyid = keypool.vchPubKey.GetID(); + if (mapKeyMetadata.count(keyid) == 0) + mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime); +} + bool CWallet::TopUpKeyPool(unsigned int kpSize) { { diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 7ef2e6f1d..631cfad5b 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -746,22 +746,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface } } - void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool) - { - if (keypool.fInternal) { - setInternalKeyPool.insert(nIndex); - } else { - setExternalKeyPool.insert(nIndex); - } - m_max_keypool_index = std::max(m_max_keypool_index, nIndex); - - // If no metadata exists yet, create a default with the pool key's - // creation time. Note that this may be overwritten by actually - // stored metadata for that key later, which is fine. - CKeyID keyid = keypool.vchPubKey.GetID(); - if (mapKeyMetadata.count(keyid) == 0) - mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime); - } + void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool); // Map from Key ID (for regular keys) or Script ID (for watch-only keys) to // key metadata. From 83f1ec33ceff1245a44a24aa8a2840e1fd43e431 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Mon, 24 Jul 2017 11:24:12 -0400 Subject: [PATCH 028/382] [wallet] Don't hold cs_LastBlockFile while calling setBestChain cs_LastBlockFile shouldn't be held while calling wallet functions. --- src/validation.cpp | 163 +++++++++++++++++++++++---------------------- 1 file changed, 84 insertions(+), 79 deletions(-) diff --git a/src/validation.cpp b/src/validation.cpp index babf6f152..5133d2464 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1863,95 +1863,100 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd */ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &state, FlushStateMode mode, int nManualPruneHeight) { int64_t nMempoolUsage = mempool.DynamicMemoryUsage(); - LOCK2(cs_main, cs_LastBlockFile); + LOCK(cs_main); static int64_t nLastWrite = 0; static int64_t nLastFlush = 0; static int64_t nLastSetChain = 0; std::set setFilesToPrune; bool fFlushForPrune = false; + bool fDoFullFlush = false; + int64_t nNow = 0; try { - 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) { - pblocktree->WriteFlag("prunedblockfiles", true); - fHavePruned = true; - } - } - } - int64_t nNow = GetTimeMicros(); - // Avoid writing/flushing immediately after startup. - if (nLastWrite == 0) { - nLastWrite = nNow; - } - if (nLastFlush == 0) { - nLastFlush = nNow; - } - if (nLastSetChain == 0) { - nLastSetChain = nNow; - } - int64_t nMempoolSizeMax = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; - int64_t cacheSize = pcoinsTip->DynamicMemoryUsage(); - int64_t nTotalSpace = nCoinCacheUsage + std::max(nMempoolSizeMax - nMempoolUsage, 0); - // The cache is large and we're within 10% and 10 MiB of the limit, but we have time now (not in the middle of a block processing). - bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize > std::max((9 * nTotalSpace) / 10, nTotalSpace - MAX_BLOCK_COINSDB_USAGE * 1024 * 1024); - // The cache is over the limit, we have to write now. - bool fCacheCritical = mode == FLUSH_STATE_IF_NEEDED && cacheSize > nTotalSpace; - // It's been a while since we wrote the block index to disk. Do this frequently, so we don't need to redownload after a crash. - bool fPeriodicWrite = mode == FLUSH_STATE_PERIODIC && nNow > nLastWrite + (int64_t)DATABASE_WRITE_INTERVAL * 1000000; - // It's been very long since we flushed the cache. Do this infrequently, to optimize cache usage. - bool fPeriodicFlush = mode == FLUSH_STATE_PERIODIC && nNow > nLastFlush + (int64_t)DATABASE_FLUSH_INTERVAL * 1000000; - // Combine all conditions that result in a full cache flush. - bool fDoFullFlush = (mode == FLUSH_STATE_ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush || fFlushForPrune; - // Write blocks and block index to disk. - if (fDoFullFlush || fPeriodicWrite) { - // Depend on nMinDiskSpace to ensure we can write block index - if (!CheckDiskSpace(0)) - return state.Error("out of disk space"); - // First make sure all block and undo data is flushed to disk. - FlushBlockFile(); - // Then update all block file information (which may refer to block and undo files). - { - std::vector > vFiles; - vFiles.reserve(setDirtyFileInfo.size()); - for (std::set::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end(); ) { - vFiles.push_back(std::make_pair(*it, &vinfoBlockFile[*it])); - setDirtyFileInfo.erase(it++); + { + LOCK(cs_LastBlockFile); + if (fPruneMode && (fCheckForPruning || nManualPruneHeight > 0) && !fReindex) { + if (nManualPruneHeight > 0) { + FindFilesToPruneManual(setFilesToPrune, nManualPruneHeight); + } else { + FindFilesToPrune(setFilesToPrune, chainparams.PruneAfterHeight()); + fCheckForPruning = false; } - std::vector vBlocks; - vBlocks.reserve(setDirtyBlockIndex.size()); - for (std::set::iterator it = setDirtyBlockIndex.begin(); it != setDirtyBlockIndex.end(); ) { - vBlocks.push_back(*it); - setDirtyBlockIndex.erase(it++); + if (!setFilesToPrune.empty()) { + fFlushForPrune = true; + if (!fHavePruned) { + pblocktree->WriteFlag("prunedblockfiles", true); + fHavePruned = true; + } } - if (!pblocktree->WriteBatchSync(vFiles, nLastBlockFile, vBlocks)) { - return AbortNode(state, "Failed to write to block index database"); + } + nNow = GetTimeMicros(); + // Avoid writing/flushing immediately after startup. + if (nLastWrite == 0) { + nLastWrite = nNow; + } + if (nLastFlush == 0) { + nLastFlush = nNow; + } + if (nLastSetChain == 0) { + nLastSetChain = nNow; + } + int64_t nMempoolSizeMax = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; + int64_t cacheSize = pcoinsTip->DynamicMemoryUsage(); + int64_t nTotalSpace = nCoinCacheUsage + std::max(nMempoolSizeMax - nMempoolUsage, 0); + // The cache is large and we're within 10% and 10 MiB of the limit, but we have time now (not in the middle of a block processing). + bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize > std::max((9 * nTotalSpace) / 10, nTotalSpace - MAX_BLOCK_COINSDB_USAGE * 1024 * 1024); + // The cache is over the limit, we have to write now. + bool fCacheCritical = mode == FLUSH_STATE_IF_NEEDED && cacheSize > nTotalSpace; + // It's been a while since we wrote the block index to disk. Do this frequently, so we don't need to redownload after a crash. + bool fPeriodicWrite = mode == FLUSH_STATE_PERIODIC && nNow > nLastWrite + (int64_t)DATABASE_WRITE_INTERVAL * 1000000; + // It's been very long since we flushed the cache. Do this infrequently, to optimize cache usage. + bool fPeriodicFlush = mode == FLUSH_STATE_PERIODIC && nNow > nLastFlush + (int64_t)DATABASE_FLUSH_INTERVAL * 1000000; + // Combine all conditions that result in a full cache flush. + fDoFullFlush = (mode == FLUSH_STATE_ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush || fFlushForPrune; + // Write blocks and block index to disk. + if (fDoFullFlush || fPeriodicWrite) { + // Depend on nMinDiskSpace to ensure we can write block index + if (!CheckDiskSpace(0)) + return state.Error("out of disk space"); + // First make sure all block and undo data is flushed to disk. + FlushBlockFile(); + // Then update all block file information (which may refer to block and undo files). + { + std::vector > vFiles; + vFiles.reserve(setDirtyFileInfo.size()); + for (std::set::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end(); ) { + vFiles.push_back(std::make_pair(*it, &vinfoBlockFile[*it])); + setDirtyFileInfo.erase(it++); + } + std::vector vBlocks; + vBlocks.reserve(setDirtyBlockIndex.size()); + for (std::set::iterator it = setDirtyBlockIndex.begin(); it != setDirtyBlockIndex.end(); ) { + vBlocks.push_back(*it); + setDirtyBlockIndex.erase(it++); + } + if (!pblocktree->WriteBatchSync(vFiles, nLastBlockFile, vBlocks)) { + return AbortNode(state, "Failed to write to block index database"); + } } + // Finally remove any pruned files + if (fFlushForPrune) + UnlinkPrunedFiles(setFilesToPrune); + nLastWrite = nNow; + } + // Flush best chain related state. This can only be done if the blocks / block index write was also done. + if (fDoFullFlush) { + // Typical Coin structures on disk are around 48 bytes in size. + // Pushing a new one to the database can cause it to be written + // twice (once in the log, and once in the tables). This is already + // an overestimation, as most will delete an existing entry or + // overwrite one. Still, use a conservative safety factor of 2. + if (!CheckDiskSpace(48 * 2 * 2 * pcoinsTip->GetCacheSize())) + return state.Error("out of disk space"); + // Flush the chainstate (which may refer to block index entries). + if (!pcoinsTip->Flush()) + return AbortNode(state, "Failed to write to coin database"); + nLastFlush = nNow; } - // Finally remove any pruned files - if (fFlushForPrune) - UnlinkPrunedFiles(setFilesToPrune); - nLastWrite = nNow; - } - // Flush best chain related state. This can only be done if the blocks / block index write was also done. - if (fDoFullFlush) { - // Typical Coin structures on disk are around 48 bytes in size. - // Pushing a new one to the database can cause it to be written - // twice (once in the log, and once in the tables). This is already - // an overestimation, as most will delete an existing entry or - // overwrite one. Still, use a conservative safety factor of 2. - if (!CheckDiskSpace(48 * 2 * 2 * pcoinsTip->GetCacheSize())) - return state.Error("out of disk space"); - // Flush the chainstate (which may refer to block index entries). - if (!pcoinsTip->Flush()) - return AbortNode(state, "Failed to write to coin database"); - nLastFlush = nNow; } if (fDoFullFlush || ((mode == FLUSH_STATE_ALWAYS || mode == FLUSH_STATE_PERIODIC) && nNow > nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000)) { // Update best block in wallet (so we can detect restored wallets). From ce3baa193f32078d850945f8c6d5ecb3796adb3b Mon Sep 17 00:00:00 2001 From: "Ferdinando M. Ametrano" Date: Fri, 14 Jul 2017 16:47:10 +0200 Subject: [PATCH 029/382] changed regtest RPCport to 18443 to avoid conflict with testnet 18332 --- contrib/linearize/example-linearize.cfg | 7 +++++++ contrib/rpm/bitcoin.spec | 4 ++++ doc/REST-interface.md | 3 ++- src/chainparamsbase.cpp | 2 +- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/contrib/linearize/example-linearize.cfg b/contrib/linearize/example-linearize.cfg index d019b06b6..2315898bf 100644 --- a/contrib/linearize/example-linearize.cfg +++ b/contrib/linearize/example-linearize.cfg @@ -3,9 +3,16 @@ rpcuser=someuser rpcpassword=somepassword #datadir=~/.bitcoin host=127.0.0.1 + +#mainnet default port=8332 + +#testnet default #port=18332 +#regtest default +#port=18443 + # bootstrap.dat hashlist settings (linearize-hashes) max_height=313000 diff --git a/contrib/rpm/bitcoin.spec b/contrib/rpm/bitcoin.spec index cc54fcaf3..7c4d933ee 100644 --- a/contrib/rpm/bitcoin.spec +++ b/contrib/rpm/bitcoin.spec @@ -336,6 +336,8 @@ done %{_sbindir}/semanage port -a -t bitcoin_port_t -p tcp 8333 %{_sbindir}/semanage port -a -t bitcoin_port_t -p tcp 18332 %{_sbindir}/semanage port -a -t bitcoin_port_t -p tcp 18333 +%{_sbindir}/semanage port -a -t bitcoin_port_t -p tcp 18443 +%{_sbindir}/semanage port -a -t bitcoin_port_t -p tcp 18444 %{_sbindir}/fixfiles -R bitcoin-server restore &> /dev/null || : %{_sbindir}/restorecon -R %{_localstatedir}/lib/bitcoin || : fi @@ -355,6 +357,8 @@ if [ $1 -eq 0 ]; then %{_sbindir}/semanage port -d -p tcp 8333 %{_sbindir}/semanage port -d -p tcp 18332 %{_sbindir}/semanage port -d -p tcp 18333 + %{_sbindir}/semanage port -d -p tcp 18443 + %{_sbindir}/semanage port -d -p tcp 18444 for selinuxvariant in %{selinux_variants}; do %{_sbindir}/semodule -s ${selinuxvariant} -r bitcoin &> /dev/null || : done diff --git a/doc/REST-interface.md b/doc/REST-interface.md index caf678288..882df0f94 100644 --- a/doc/REST-interface.md +++ b/doc/REST-interface.md @@ -3,7 +3,8 @@ Unauthenticated REST Interface The REST API can be enabled with the `-rest` option. -The interface runs on the same port as the JSON-RPC interface, by default port 8332 for mainnet and port 18332 for testnet. +The interface runs on the same port as the JSON-RPC interface, by default port 8332 for mainnet, port 18332 for testnet, +and port 18443 for regtest. Supported API ------------- diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index 43c9a13c5..469f618f7 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -57,7 +57,7 @@ class CBaseRegTestParams : public CBaseChainParams public: CBaseRegTestParams() { - nRPCPort = 18332; + nRPCPort = 18443; strDataDir = "regtest"; } }; From f2123e3a7bf30545967b9ceb37961976cf7d64fb Mon Sep 17 00:00:00 2001 From: John Newbery Date: Fri, 21 Jul 2017 13:54:13 -0400 Subject: [PATCH 030/382] [wallet] Cache keyid -> keypool id mappings --- src/wallet/rpcdump.cpp | 5 ++--- src/wallet/wallet.cpp | 46 +++++++++++++----------------------------- src/wallet/wallet.h | 5 +++-- 3 files changed, 19 insertions(+), 37 deletions(-) diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 5abf32480..67c6d9ec6 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -619,9 +619,8 @@ UniValue dumpwallet(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); std::map mapKeyBirth; - std::set setKeyPool; + const std::map& mapKeyPool = pwallet->GetAllReserveKeys(); pwallet->GetKeyBirthTimes(mapKeyBirth); - pwallet->GetAllReserveKeys(setKeyPool); // sort time/key pairs std::vector > vKeyBirth; @@ -666,7 +665,7 @@ UniValue dumpwallet(const JSONRPCRequest& request) file << strprintf("label=%s", EncodeDumpString(pwallet->mapAddressBook[keyid].name)); } else if (keyid == masterKeyID) { file << "hdmaster=1"; - } else if (setKeyPool.count(keyid)) { + } else if (mapKeyPool.count(keyid)) { file << "reserve=1"; } else if (pwallet->mapKeyMetadata[keyid].hdKeypath == "m") { file << "inactivehdmaster=1"; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 2ef3299c2..afe911ae9 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3077,6 +3077,7 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet) LOCK(cs_wallet); setInternalKeyPool.clear(); setExternalKeyPool.clear(); + m_pool_key_to_index.clear(); // Note: can't top-up keypool here, because wallet is locked. // User will be prompted to unlock wallet the next operation // that requires a new key. @@ -3106,6 +3107,7 @@ DBErrors CWallet::ZapSelectTx(std::vector& vHashIn, std::vector& vWtx) LOCK(cs_wallet); setInternalKeyPool.clear(); setExternalKeyPool.clear(); + m_pool_key_to_index.clear(); // Note: can't top-up keypool here, because wallet is locked. // User will be prompted to unlock wallet the next operation // that requires a new key. @@ -3226,6 +3229,8 @@ bool CWallet::NewKeyPool() } setExternalKeyPool.clear(); + m_pool_key_to_index.clear(); + if (!TopUpKeyPool()) { return false; } @@ -3242,12 +3247,14 @@ size_t CWallet::KeypoolCountExternalKeys() void CWallet::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool) { + AssertLockHeld(cs_wallet); if (keypool.fInternal) { setInternalKeyPool.insert(nIndex); } else { setExternalKeyPool.insert(nIndex); } m_max_keypool_index = std::max(m_max_keypool_index, nIndex); + m_pool_key_to_index[keypool.vchPubKey.GetID()] = nIndex; // If no metadata exists yet, create a default with the pool key's // creation time. Note that this may be overwritten by actually @@ -3293,7 +3300,8 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize) assert(m_max_keypool_index < std::numeric_limits::max()); // How in the hell did you use so many keys? int64_t index = ++m_max_keypool_index; - if (!walletdb.WritePool(index, CKeyPool(GenerateNewKey(walletdb, internal), internal))) { + CPubKey pubkey(GenerateNewKey(walletdb, internal)); + if (!walletdb.WritePool(index, CKeyPool(pubkey, internal))) { throw std::runtime_error(std::string(__func__) + ": writing generated key failed"); } @@ -3302,6 +3310,7 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize) } else { setExternalKeyPool.insert(index); } + m_pool_key_to_index[pubkey.GetID()] = index; } if (missingInternal + missingExternal > 0) { LogPrintf("keypool added %d keys (%d internal), size=%u (%u internal)\n", missingInternal + missingExternal, missingInternal, setInternalKeyPool.size() + setExternalKeyPool.size(), setInternalKeyPool.size()); @@ -3343,6 +3352,7 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRe } assert(keypool.vchPubKey.IsValid()); + m_pool_key_to_index.erase(keypool.vchPubKey.GetID()); LogPrintf("keypool reserve %d\n", nIndex); } } @@ -3355,7 +3365,7 @@ void CWallet::KeepKey(int64_t nIndex) LogPrintf("keypool keep %d\n", nIndex); } -void CWallet::ReturnKey(int64_t nIndex, bool fInternal) +void CWallet::ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey) { // Return to key pool { @@ -3365,6 +3375,7 @@ void CWallet::ReturnKey(int64_t nIndex, bool fInternal) } else { setExternalKeyPool.insert(nIndex); } + m_pool_key_to_index[pubkey.GetID()] = nIndex; } LogPrintf("keypool return %d\n", nIndex); } @@ -3594,41 +3605,12 @@ void CReserveKey::KeepKey() void CReserveKey::ReturnKey() { if (nIndex != -1) { - pwallet->ReturnKey(nIndex, fInternal); + pwallet->ReturnKey(nIndex, fInternal, vchPubKey); } nIndex = -1; vchPubKey = CPubKey(); } -static void LoadReserveKeysToSet(std::set& setAddress, const std::set& setKeyPool, CWalletDB& walletdb) { - for (const int64_t& id : setKeyPool) - { - CKeyPool keypool; - if (!walletdb.ReadPool(id, keypool)) - throw std::runtime_error(std::string(__func__) + ": read failed"); - assert(keypool.vchPubKey.IsValid()); - CKeyID keyID = keypool.vchPubKey.GetID(); - setAddress.insert(keyID); - } -} - -void CWallet::GetAllReserveKeys(std::set& setAddress) const -{ - setAddress.clear(); - - CWalletDB walletdb(*dbw); - - LOCK2(cs_main, cs_wallet); - LoadReserveKeysToSet(setAddress, setInternalKeyPool, walletdb); - LoadReserveKeysToSet(setAddress, setExternalKeyPool, walletdb); - - for (const CKeyID& keyID : setAddress) { - if (!HaveKey(keyID)) { - throw std::runtime_error(std::string(__func__) + ": unknown key in key pool"); - } - } -} - void CWallet::GetScriptForMining(std::shared_ptr &script) { std::shared_ptr rKey = std::make_shared(this); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 631cfad5b..0d2262cfe 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -704,6 +704,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface std::set setInternalKeyPool; std::set setExternalKeyPool; int64_t m_max_keypool_index; + std::map m_pool_key_to_index; int64_t nTimeFirstKey; @@ -973,10 +974,10 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool TopUpKeyPool(unsigned int kpSize = 0); void ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal); void KeepKey(int64_t nIndex); - void ReturnKey(int64_t nIndex, bool fInternal); + void ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey); bool GetKeyFromPool(CPubKey &key, bool internal = false); int64_t GetOldestKeyPoolTime(); - void GetAllReserveKeys(std::set& setAddress) const; + const std::map& GetAllReserveKeys() const { return m_pool_key_to_index; } std::set< std::set > GetAddressGroupings(); std::map GetAddressBalances(); From c25d90f125d69e33688288eff439eb7be75012e9 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Thu, 3 Aug 2017 11:03:40 -0400 Subject: [PATCH 031/382] [wallet] Add HasUnusedKeys() helper --- src/wallet/wallet.cpp | 5 +++++ src/wallet/wallet.h | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index afe911ae9..ce345804e 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3611,6 +3611,11 @@ void CReserveKey::ReturnKey() vchPubKey = CPubKey(); } +bool CWallet::HasUnusedKeys(int min_keys) const +{ + return setExternalKeyPool.size() >= min_keys && (setInternalKeyPool.size() >= min_keys || !CanSupportFeature(FEATURE_HD_SPLIT)); +} + void CWallet::GetScriptForMining(std::shared_ptr &script) { std::shared_ptr rKey = std::make_shared(this); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 0d2262cfe..310300126 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -813,7 +813,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface const CWalletTx* GetWalletTx(const uint256& hash) const; //! check whether we are allowed to upgrade (or already support) to the named feature - bool CanSupportFeature(enum WalletFeature wf) { AssertLockHeld(cs_wallet); return nWalletMaxVersion >= wf; } + bool CanSupportFeature(enum WalletFeature wf) const { AssertLockHeld(cs_wallet); return nWalletMaxVersion >= wf; } /** * populate vCoins with vector of available COutputs. @@ -978,6 +978,8 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool GetKeyFromPool(CPubKey &key, bool internal = false); int64_t GetOldestKeyPoolTime(); const std::map& GetAllReserveKeys() const { return m_pool_key_to_index; } + /** Does the wallet have at least min_keys in the keypool? */ + bool HasUnusedKeys(int min_keys) const; std::set< std::set > GetAddressGroupings(); std::map GetAddressBalances(); From 8d82e1336a66b4d37d9759fa79711b5c4b8e9883 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Sat, 5 Aug 2017 09:50:43 +0000 Subject: [PATCH 032/382] Travis: Remove bc tool from dependencies --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 12f91096c..5d2a46f35 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,11 +24,11 @@ env: # ARM - HOST=arm-linux-gnueabihf PACKAGES="g++-arm-linux-gnueabihf" DEP_OPTS="NO_QT=1" CHECK_DOC=1 GOAL="install" BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports" # Win32 - - HOST=i686-w64-mingw32 DPKG_ADD_ARCH="i386" DEP_OPTS="NO_QT=1" PACKAGES="python3 nsis g++-mingw-w64-i686 wine1.6 bc" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-reduce-exports" + - HOST=i686-w64-mingw32 DPKG_ADD_ARCH="i386" DEP_OPTS="NO_QT=1" PACKAGES="python3 nsis g++-mingw-w64-i686 wine1.6" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-reduce-exports" # 32-bit + dash - - HOST=i686-pc-linux-gnu PACKAGES="g++-multilib bc python3-zmq" DEP_OPTS="NO_QT=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --enable-glibc-back-compat --enable-reduce-exports LDFLAGS=-static-libstdc++" USE_SHELL="/bin/dash" + - HOST=i686-pc-linux-gnu PACKAGES="g++-multilib python3-zmq" DEP_OPTS="NO_QT=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --enable-glibc-back-compat --enable-reduce-exports LDFLAGS=-static-libstdc++" USE_SHELL="/bin/dash" # Win64 - - HOST=x86_64-w64-mingw32 DPKG_ADD_ARCH="i386" DEP_OPTS="NO_QT=1" PACKAGES="python3 nsis g++-mingw-w64-x86-64 wine1.6 bc" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-reduce-exports" + - HOST=x86_64-w64-mingw32 DPKG_ADD_ARCH="i386" DEP_OPTS="NO_QT=1" PACKAGES="python3 nsis g++-mingw-w64-x86-64 wine1.6" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-reduce-exports" # x86_64 Linux (uses qt5 dev package instead of depends Qt to speed up build and avoid timeout) - HOST=x86_64-unknown-linux-gnu PACKAGES="python3-zmq qtbase5-dev qttools5-dev-tools protobuf-compiler libdbus-1-dev libharfbuzz-dev" DEP_OPTS="NO_QT=1 NO_UPNP=1 DEBUG=1 ALLOW_HOST_PACKAGES=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --with-gui=qt5 --enable-glibc-back-compat --enable-reduce-exports CPPFLAGS=-DDEBUG_LOCKORDER" # x86_64 Linux, No wallet From 6d2aac8bb142adc1e5cff9dd91ddd1d91403b33c Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 1 Dec 2015 10:46:26 +0000 Subject: [PATCH 033/382] Travis: Test build against system libs (& Qt4) --- .travis.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5d2a46f35..10d322b49 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,6 +25,8 @@ env: - HOST=arm-linux-gnueabihf PACKAGES="g++-arm-linux-gnueabihf" DEP_OPTS="NO_QT=1" CHECK_DOC=1 GOAL="install" BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports" # Win32 - HOST=i686-w64-mingw32 DPKG_ADD_ARCH="i386" DEP_OPTS="NO_QT=1" PACKAGES="python3 nsis g++-mingw-w64-i686 wine1.6" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-reduce-exports" +# Qt4 & system libs + - HOST=x86_64-unknown-linux-gnu PACKAGES="python3-zmq qt4-dev-tools libssl-dev libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-program-options-dev libboost-test-dev libboost-thread-dev libdb5.1++-dev libminiupnpc-dev libzmq3-dev libprotobuf-dev protobuf-compiler libqrencode-dev xvfb" NO_DEPENDS=1 NEED_XVFB=1 RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --enable-glibc-back-compat --enable-reduce-exports --with-gui=qt4 CPPFLAGS=-DDEBUG_LOCKORDER" # 32-bit + dash - HOST=i686-pc-linux-gnu PACKAGES="g++-multilib python3-zmq" DEP_OPTS="NO_QT=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --enable-glibc-back-compat --enable-reduce-exports LDFLAGS=-static-libstdc++" USE_SHELL="/bin/dash" # Win64 @@ -49,7 +51,9 @@ before_script: - mkdir -p depends/SDKs depends/sdk-sources - if [ -n "$OSX_SDK" -a ! -f depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then curl --location --fail $SDK_URL/MacOSX${OSX_SDK}.sdk.tar.gz -o depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz; fi - if [ -n "$OSX_SDK" -a -f depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then tar -C depends/SDKs -xf depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz; fi - - make $MAKEJOBS -C depends HOST=$HOST $DEP_OPTS + - if [ -z "$NO_DEPENDS" ]; then make $MAKEJOBS -C depends HOST=$HOST $DEP_OPTS; fi + # Start xvfb if needed, as documented at https://docs.travis-ci.com/user/gui-and-headless-browsers/#Using-xvfb-to-Run-Tests-That-Require-a-GUI + - if [ "$NEED_XVFB" = 1 ]; then export DISPLAY=:99.0; /sbin/start-stop-daemon --start --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac; fi script: - if [ "$CHECK_DOC" = 1 -a "$TRAVIS_REPO_SLUG" = "bitcoin/bitcoin" -a "$TRAVIS_PULL_REQUEST" = "false" ]; then while read LINE; do travis_retry gpg --keyserver hkp://subset.pool.sks-keyservers.net --recv-keys $LINE; done < contrib/verify-commits/trusted-keys; fi - if [ "$CHECK_DOC" = 1 -a "$TRAVIS_REPO_SLUG" = "bitcoin/bitcoin" -a "$TRAVIS_PULL_REQUEST" = "false" ]; then git fetch --unshallow; fi @@ -58,7 +62,7 @@ script: - if [ -n "$USE_SHELL" ]; then export CONFIG_SHELL="$USE_SHELL"; fi - OUTDIR=$BASE_OUTDIR/$TRAVIS_PULL_REQUEST/$TRAVIS_JOB_NUMBER-$HOST - BITCOIN_CONFIG_ALL="--disable-dependency-tracking --prefix=$TRAVIS_BUILD_DIR/depends/$HOST --bindir=$OUTDIR/bin --libdir=$OUTDIR/lib" - - depends/$HOST/native/bin/ccache --max-size=$CCACHE_SIZE + - if [ -z "$NO_DEPENDS" ]; then depends/$HOST/native/bin/ccache --max-size=$CCACHE_SIZE; fi - test -n "$USE_SHELL" && eval '"$USE_SHELL" -c "./autogen.sh"' || ./autogen.sh - mkdir build && cd build - ../configure --cache-file=config.cache $BITCOIN_CONFIG_ALL $BITCOIN_CONFIG || ( cat config.log && false) From 90d4d89230434493c3b1e9174abed2609ba74cf1 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Mon, 7 Aug 2017 07:36:37 +0200 Subject: [PATCH 034/382] scripted-diff: Use the C++11 keyword nullptr to denote the pointer literal instead of the macro NULL -BEGIN VERIFY SCRIPT- sed -i 's/\/nullptr/g' src/*.cpp src/*.h src/*/*.cpp src/*/*.h src/qt/*/*.cpp src/qt/*/*.h src/wallet/*/*.cpp src/wallet/*/*.h src/support/allocators/*.h sed -i 's/Prefer nullptr, otherwise SAFECOOKIE./Prefer NULL, otherwise SAFECOOKIE./g' src/torcontrol.cpp sed -i 's/tor: Using nullptr authentication/tor: Using NULL authentication/g' src/torcontrol.cpp sed -i 's/METHODS=nullptr/METHODS=NULL/g' src/test/torcontrol_tests.cpp src/torcontrol.cpp sed -i 's/nullptr certificates/NULL certificates/g' src/qt/paymentserver.cpp sed -i 's/"nullptr"/"NULL"/g' src/torcontrol.cpp src/test/torcontrol_tests.cpp -END VERIFY SCRIPT- --- src/addrman.cpp | 4 +- src/addrman.h | 4 +- src/arith_uint256.h | 2 +- src/base58.cpp | 2 +- src/base58.h | 4 +- src/bench/bench.cpp | 2 +- src/bitcoin-cli.cpp | 12 +- src/bitcoin-tx.cpp | 6 +- src/bitcoind.cpp | 2 +- src/chain.cpp | 14 +- src/chain.h | 24 +-- src/checkpoints.cpp | 2 +- src/checkqueue.h | 10 +- src/consensus/merkle.cpp | 4 +- src/consensus/merkle.h | 6 +- src/core_write.cpp | 2 +- src/crypto/chacha20.cpp | 2 +- src/dbwrapper.cpp | 12 +- src/dbwrapper.h | 2 +- src/httpserver.cpp | 16 +- src/init.cpp | 28 +-- src/key.cpp | 12 +- src/net.cpp | 46 ++--- src/net.h | 4 +- src/net_processing.cpp | 58 +++---- src/netaddress.cpp | 4 +- src/netaddress.h | 2 +- src/netbase.cpp | 14 +- src/pow.cpp | 2 +- src/prevector.h | 2 +- src/pubkey.cpp | 12 +- src/qt/addressbookpage.cpp | 2 +- src/qt/bitcoin.cpp | 10 +- src/qt/bitcoingui.cpp | 4 +- src/qt/bitcoingui.h | 2 +- src/qt/guiutil.cpp | 28 +-- src/qt/openuridialog.cpp | 2 +- src/qt/paymentrequestplus.cpp | 10 +- src/qt/paymentserver.cpp | 6 +- src/qt/paymentserver.h | 4 +- src/qt/receiverequestdialog.cpp | 2 +- src/qt/recentrequeststablemodel.cpp | 2 +- src/qt/rpcconsole.cpp | 6 +- src/qt/rpcconsole.h | 4 +- src/qt/test/paymentservertests.cpp | 4 +- src/qt/transactionrecord.cpp | 2 +- src/qt/transactionview.cpp | 2 +- src/qt/walletmodel.h | 2 +- src/qt/walletview.cpp | 2 +- src/qt/winshutdownmonitor.cpp | 2 +- src/random.cpp | 6 +- src/rest.cpp | 8 +- src/rpc/blockchain.cpp | 4 +- src/rpc/mining.cpp | 6 +- src/rpc/misc.cpp | 6 +- src/rpc/net.cpp | 2 +- src/rpc/rawtransaction.cpp | 8 +- src/rpc/server.cpp | 6 +- src/script/bitcoinconsensus.cpp | 6 +- src/script/bitcoinconsensus.h | 2 +- src/script/interpreter.cpp | 2 +- src/script/interpreter.h | 8 +- src/script/script.h | 4 +- src/script/standard.cpp | 2 +- src/streams.h | 20 +-- src/support/allocators/secure.h | 2 +- src/support/allocators/zeroafterfree.h | 2 +- src/support/events.h | 2 +- src/support/lockedpool.cpp | 4 +- src/sync.cpp | 2 +- src/sync.h | 2 +- src/test/addrman_tests.cpp | 6 +- src/test/miner_tests.cpp | 2 +- src/test/multisig_tests.cpp | 16 +- src/test/pow_tests.cpp | 2 +- src/test/raii_event_tests.cpp | 12 +- src/test/script_P2SH_tests.cpp | 2 +- src/test/script_tests.cpp | 36 ++-- src/test/skiplist_tests.cpp | 14 +- src/test/test_bitcoin.cpp | 2 +- src/test/txvalidationcache_tests.cpp | 2 +- src/test/util_tests.cpp | 40 ++--- src/test/versionbits_tests.cpp | 18 +- src/torcontrol.cpp | 12 +- src/txmempool.h | 4 +- src/util.cpp | 28 +-- src/util.h | 2 +- src/utilstrencodings.cpp | 12 +- src/utilstrencodings.h | 4 +- src/utiltime.cpp | 2 +- src/validation.cpp | 218 ++++++++++++------------ src/validation.h | 8 +- src/versionbits.cpp | 10 +- src/versionbits.h | 2 +- src/wallet/db.cpp | 44 ++--- src/wallet/db.h | 24 +-- src/wallet/rpcwallet.cpp | 8 +- src/wallet/rpcwallet.h | 2 +- src/wallet/test/crypto_tests.cpp | 4 +- src/wallet/test/wallet_test_fixture.cpp | 2 +- src/wallet/wallet.cpp | 46 ++--- src/wallet/wallet.h | 18 +- src/wallet/walletdb.cpp | 2 +- src/zmq/zmqnotificationinterface.cpp | 6 +- 104 files changed, 563 insertions(+), 563 deletions(-) diff --git a/src/addrman.cpp b/src/addrman.cpp index 4a408b9be..a56bb4f9c 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -69,13 +69,13 @@ CAddrInfo* CAddrMan::Find(const CNetAddr& addr, int* pnId) { std::map::iterator it = mapAddr.find(addr); if (it == mapAddr.end()) - return NULL; + return nullptr; if (pnId) *pnId = (*it).second; std::map::iterator it2 = mapInfo.find((*it).second); if (it2 != mapInfo.end()) return &(*it2).second; - return NULL; + return nullptr; } CAddrInfo* CAddrMan::Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId) diff --git a/src/addrman.h b/src/addrman.h index 70d907488..547088aed 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -220,11 +220,11 @@ class CAddrMan FastRandomContext insecure_rand; //! Find an entry. - CAddrInfo* Find(const CNetAddr& addr, int *pnId = NULL); + CAddrInfo* Find(const CNetAddr& addr, int *pnId = nullptr); //! find an entry, creating it if necessary. //! nTime and nServices of the found node are updated, if necessary. - CAddrInfo* Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId = NULL); + CAddrInfo* Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId = nullptr); //! Swap two elements in vRandom. void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2); diff --git a/src/arith_uint256.h b/src/arith_uint256.h index c7734035d..6223e4afe 100644 --- a/src/arith_uint256.h +++ b/src/arith_uint256.h @@ -283,7 +283,7 @@ class arith_uint256 : public base_uint<256> { * complexities of the sign bit and using base 256 are probably an * implementation accident. */ - arith_uint256& SetCompact(uint32_t nCompact, bool *pfNegative = NULL, bool *pfOverflow = NULL); + arith_uint256& SetCompact(uint32_t nCompact, bool *pfNegative = nullptr, bool *pfOverflow = nullptr); uint32_t GetCompact(bool fNegative = false) const; friend uint256 ArithToUint256(const arith_uint256 &); diff --git a/src/base58.cpp b/src/base58.cpp index 17022a6bc..c70e358eb 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -37,7 +37,7 @@ bool DecodeBase58(const char* psz, std::vector& vch) while (*psz && !isspace(*psz)) { // Decode base58 character const char* ch = strchr(pszBase58, *psz); - if (ch == NULL) + if (ch == nullptr) return false; // Apply "b256 = b256 * 58 + ch". int carry = ch - pszBase58; diff --git a/src/base58.h b/src/base58.h index 4de5cc6ce..db12208f7 100644 --- a/src/base58.h +++ b/src/base58.h @@ -26,7 +26,7 @@ /** * Encode a byte sequence as a base58-encoded string. - * pbegin and pend cannot be NULL, unless both are. + * pbegin and pend cannot be nullptr, unless both are. */ std::string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend); @@ -38,7 +38,7 @@ std::string EncodeBase58(const std::vector& vch); /** * Decode a base58-encoded string (psz) into a byte vector (vchRet). * return true if decoding is successful. - * psz cannot be NULL. + * psz cannot be nullptr. */ bool DecodeBase58(const char* psz, std::vector& vchRet); diff --git a/src/bench/bench.cpp b/src/bench/bench.cpp index 33631d2d1..ac14444ee 100644 --- a/src/bench/bench.cpp +++ b/src/bench/bench.cpp @@ -17,7 +17,7 @@ benchmark::BenchRunner::BenchmarkMap &benchmark::BenchRunner::benchmarks() { static double gettimedouble(void) { struct timeval tv; - gettimeofday(&tv, NULL); + gettimeofday(&tv, nullptr); return tv.tv_usec * 0.000001 + tv.tv_sec; } diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index c1a7c927e..0b74f31bf 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -161,8 +161,8 @@ static void http_request_done(struct evhttp_request *req, void *ctx) { HTTPReply *reply = static_cast(ctx); - if (req == NULL) { - /* If req is NULL, it means an error occurred while connecting: the + if (req == nullptr) { + /* If req is nullptr, it means an error occurred while connecting: the * error code will have been passed to http_error_cb. */ reply->status = 0; @@ -210,7 +210,7 @@ UniValue CallRPC(const std::string& strMethod, const UniValue& params) HTTPReply response; raii_evhttp_request req = obtain_evhttp_request(http_request_done, (void*)&response); - if (req == NULL) + if (req == nullptr) throw std::runtime_error("create http request failed"); #if LIBEVENT_VERSION_NUMBER >= 0x02010300 evhttp_request_set_error_cb(req.get(), http_error_cb); @@ -370,7 +370,7 @@ int CommandLineRPC(int argc, char *argv[]) nRet = EXIT_FAILURE; } catch (...) { - PrintExceptionContinue(NULL, "CommandLineRPC()"); + PrintExceptionContinue(nullptr, "CommandLineRPC()"); throw; } @@ -397,7 +397,7 @@ int main(int argc, char* argv[]) PrintExceptionContinue(&e, "AppInitRPC()"); return EXIT_FAILURE; } catch (...) { - PrintExceptionContinue(NULL, "AppInitRPC()"); + PrintExceptionContinue(nullptr, "AppInitRPC()"); return EXIT_FAILURE; } @@ -408,7 +408,7 @@ int main(int argc, char* argv[]) catch (const std::exception& e) { PrintExceptionContinue(&e, "CommandLineRPC()"); } catch (...) { - PrintExceptionContinue(NULL, "CommandLineRPC()"); + PrintExceptionContinue(nullptr, "CommandLineRPC()"); } return ret; } diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 9acb3fd30..e5ea9f322 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -822,7 +822,7 @@ static int CommandLineRawTx(int argc, char* argv[]) nRet = EXIT_FAILURE; } catch (...) { - PrintExceptionContinue(NULL, "CommandLineRawTx()"); + PrintExceptionContinue(nullptr, "CommandLineRawTx()"); throw; } @@ -845,7 +845,7 @@ int main(int argc, char* argv[]) PrintExceptionContinue(&e, "AppInitRawTx()"); return EXIT_FAILURE; } catch (...) { - PrintExceptionContinue(NULL, "AppInitRawTx()"); + PrintExceptionContinue(nullptr, "AppInitRawTx()"); return EXIT_FAILURE; } @@ -856,7 +856,7 @@ int main(int argc, char* argv[]) catch (const std::exception& e) { PrintExceptionContinue(&e, "CommandLineRawTx()"); } catch (...) { - PrintExceptionContinue(NULL, "CommandLineRawTx()"); + PrintExceptionContinue(nullptr, "CommandLineRawTx()"); } return ret; } diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index f3844e9d4..a36944952 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -170,7 +170,7 @@ bool AppInit(int argc, char* argv[]) catch (const std::exception& e) { PrintExceptionContinue(&e, "AppInit()"); } catch (...) { - PrintExceptionContinue(NULL, "AppInit()"); + PrintExceptionContinue(nullptr, "AppInit()"); } if (!fRet) diff --git a/src/chain.cpp b/src/chain.cpp index ffd58d471..47acde882 100644 --- a/src/chain.cpp +++ b/src/chain.cpp @@ -9,7 +9,7 @@ * CChain implementation */ void CChain::SetTip(CBlockIndex *pindex) { - if (pindex == NULL) { + if (pindex == nullptr) { vChain.clear(); return; } @@ -49,8 +49,8 @@ CBlockLocator CChain::GetLocator(const CBlockIndex *pindex) const { } const CBlockIndex *CChain::FindFork(const CBlockIndex *pindex) const { - if (pindex == NULL) { - return NULL; + if (pindex == nullptr) { + return nullptr; } if (pindex->nHeight > Height()) pindex = pindex->GetAncestor(Height()); @@ -63,7 +63,7 @@ CBlockIndex* CChain::FindEarliestAtLeast(int64_t nTime) const { std::vector::const_iterator lower = std::lower_bound(vChain.begin(), vChain.end(), nTime, [](CBlockIndex* pBlock, const int64_t& time) -> bool { return pBlock->GetBlockTimeMax() < time; }); - return (lower == vChain.end() ? NULL : *lower); + return (lower == vChain.end() ? nullptr : *lower); } /** Turn the lowest '1' bit in the binary representation of a number into a '0'. */ @@ -83,14 +83,14 @@ int static inline GetSkipHeight(int height) { CBlockIndex* CBlockIndex::GetAncestor(int height) { if (height > nHeight || height < 0) - return NULL; + return nullptr; CBlockIndex* pindexWalk = this; int heightWalk = nHeight; while (heightWalk > height) { int heightSkip = GetSkipHeight(heightWalk); int heightSkipPrev = GetSkipHeight(heightWalk - 1); - if (pindexWalk->pskip != NULL && + if (pindexWalk->pskip != nullptr && (heightSkip == height || (heightSkip > height && !(heightSkipPrev < heightSkip - 2 && heightSkipPrev >= height)))) { @@ -150,7 +150,7 @@ int64_t GetBlockProofEquivalentTime(const CBlockIndex& to, const CBlockIndex& fr } /** Find the last common ancestor two blocks have. - * Both pa and pb must be non-NULL. */ + * Both pa and pb must be non-nullptr. */ const CBlockIndex* LastCommonAncestor(const CBlockIndex* pa, const CBlockIndex* pb) { if (pa->nHeight > pb->nHeight) { pa = pa->GetAncestor(pb->nHeight); diff --git a/src/chain.h b/src/chain.h index c5304b7d6..1223539e7 100644 --- a/src/chain.h +++ b/src/chain.h @@ -221,9 +221,9 @@ class CBlockIndex void SetNull() { - phashBlock = NULL; - pprev = NULL; - pskip = NULL; + phashBlock = nullptr; + pprev = nullptr; + pskip = nullptr; nHeight = 0; nFile = 0; nDataPos = 0; @@ -437,20 +437,20 @@ class CChain { std::vector vChain; public: - /** Returns the index entry for the genesis block of this chain, or NULL if none. */ + /** Returns the index entry for the genesis block of this chain, or nullptr if none. */ CBlockIndex *Genesis() const { - return vChain.size() > 0 ? vChain[0] : NULL; + return vChain.size() > 0 ? vChain[0] : nullptr; } - /** Returns the index entry for the tip of this chain, or NULL if none. */ + /** Returns the index entry for the tip of this chain, or nullptr if none. */ CBlockIndex *Tip() const { - return vChain.size() > 0 ? vChain[vChain.size() - 1] : NULL; + return vChain.size() > 0 ? vChain[vChain.size() - 1] : nullptr; } - /** Returns the index entry at a particular height in this chain, or NULL if no such height exists. */ + /** Returns the index entry at a particular height in this chain, or nullptr if no such height exists. */ CBlockIndex *operator[](int nHeight) const { if (nHeight < 0 || nHeight >= (int)vChain.size()) - return NULL; + return nullptr; return vChain[nHeight]; } @@ -465,12 +465,12 @@ class CChain { return (*this)[pindex->nHeight] == pindex; } - /** Find the successor of a block in this chain, or NULL if the given index is not found or is the tip. */ + /** Find the successor of a block in this chain, or nullptr if the given index is not found or is the tip. */ CBlockIndex *Next(const CBlockIndex *pindex) const { if (Contains(pindex)) return (*this)[pindex->nHeight + 1]; else - return NULL; + return nullptr; } /** Return the maximal height in the chain. Is equal to chain.Tip() ? chain.Tip()->nHeight : -1. */ @@ -482,7 +482,7 @@ class CChain { void SetTip(CBlockIndex *pindex); /** Return a CBlockLocator that refers to a block in this chain (by default the tip). */ - CBlockLocator GetLocator(const CBlockIndex *pindex = NULL) const; + CBlockLocator GetLocator(const CBlockIndex *pindex = nullptr) const; /** Find the last common block between this chain and a block index entry. */ const CBlockIndex *FindFork(const CBlockIndex *pindex) const; diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index e6b5fb72a..5afe3e66b 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -26,7 +26,7 @@ namespace Checkpoints { if (t != mapBlockIndex.end()) return t->second; } - return NULL; + return nullptr; } } // namespace Checkpoints diff --git a/src/checkqueue.h b/src/checkqueue.h index 408e278d2..4bc6be45f 100644 --- a/src/checkqueue.h +++ b/src/checkqueue.h @@ -183,15 +183,15 @@ class CCheckQueueControl CCheckQueueControl& operator=(const CCheckQueueControl&) = delete; explicit CCheckQueueControl(CCheckQueue * const pqueueIn) : pqueue(pqueueIn), fDone(false) { - // passed queue is supposed to be unused, or NULL - if (pqueue != NULL) { + // passed queue is supposed to be unused, or nullptr + if (pqueue != nullptr) { ENTER_CRITICAL_SECTION(pqueue->ControlMutex); } } bool Wait() { - if (pqueue == NULL) + if (pqueue == nullptr) return true; bool fRet = pqueue->Wait(); fDone = true; @@ -200,7 +200,7 @@ class CCheckQueueControl void Add(std::vector& vChecks) { - if (pqueue != NULL) + if (pqueue != nullptr) pqueue->Add(vChecks); } @@ -208,7 +208,7 @@ class CCheckQueueControl { if (!fDone) Wait(); - if (pqueue != NULL) { + if (pqueue != nullptr) { LEAVE_CRITICAL_SECTION(pqueue->ControlMutex); } } diff --git a/src/consensus/merkle.cpp b/src/consensus/merkle.cpp index 1ce5a9d87..798ce4b5f 100644 --- a/src/consensus/merkle.cpp +++ b/src/consensus/merkle.cpp @@ -132,13 +132,13 @@ static void MerkleComputation(const std::vector& leaves, uint256* proot uint256 ComputeMerkleRoot(const std::vector& leaves, bool* mutated) { uint256 hash; - MerkleComputation(leaves, &hash, mutated, -1, NULL); + MerkleComputation(leaves, &hash, mutated, -1, nullptr); return hash; } std::vector ComputeMerkleBranch(const std::vector& leaves, uint32_t position) { std::vector ret; - MerkleComputation(leaves, NULL, NULL, position, &ret); + MerkleComputation(leaves, nullptr, nullptr, position, &ret); return ret; } diff --git a/src/consensus/merkle.h b/src/consensus/merkle.h index 194aea9b7..33764c746 100644 --- a/src/consensus/merkle.h +++ b/src/consensus/merkle.h @@ -12,7 +12,7 @@ #include "primitives/block.h" #include "uint256.h" -uint256 ComputeMerkleRoot(const std::vector& leaves, bool* mutated = NULL); +uint256 ComputeMerkleRoot(const std::vector& leaves, bool* mutated = nullptr); std::vector ComputeMerkleBranch(const std::vector& leaves, uint32_t position); uint256 ComputeMerkleRootFromBranch(const uint256& leaf, const std::vector& branch, uint32_t position); @@ -20,13 +20,13 @@ uint256 ComputeMerkleRootFromBranch(const uint256& leaf, const std::vectorsecond + "]"; diff --git a/src/crypto/chacha20.cpp b/src/crypto/chacha20.cpp index 816ae870e..4038ae9f8 100644 --- a/src/crypto/chacha20.cpp +++ b/src/crypto/chacha20.cpp @@ -75,7 +75,7 @@ void ChaCha20::Output(unsigned char* c, size_t bytes) { uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; - unsigned char *ctarget = NULL; + unsigned char *ctarget = nullptr; unsigned char tmp[64]; unsigned int i; diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp index 3626e0177..27767f90b 100644 --- a/src/dbwrapper.cpp +++ b/src/dbwrapper.cpp @@ -92,7 +92,7 @@ static leveldb::Options GetOptions(size_t nCacheSize) CDBWrapper::CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory, bool fWipe, bool obfuscate) { - penv = NULL; + penv = nullptr; readoptions.verify_checksums = true; iteroptions.verify_checksums = true; iteroptions.fill_cache = false; @@ -144,15 +144,15 @@ CDBWrapper::CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory, bo CDBWrapper::~CDBWrapper() { delete pdb; - pdb = NULL; + pdb = nullptr; delete options.filter_policy; - options.filter_policy = NULL; + options.filter_policy = nullptr; delete options.info_log; - options.info_log = NULL; + options.info_log = nullptr; delete options.block_cache; - options.block_cache = NULL; + options.block_cache = nullptr; delete penv; - options.env = NULL; + options.env = nullptr; } bool CDBWrapper::WriteBatch(CDBBatch& batch, bool fSync) diff --git a/src/dbwrapper.h b/src/dbwrapper.h index d36188652..7575d207a 100644 --- a/src/dbwrapper.h +++ b/src/dbwrapper.h @@ -177,7 +177,7 @@ class CDBWrapper { friend const std::vector& dbwrapper_private::GetObfuscateKey(const CDBWrapper &w); private: - //! custom environment this database is using (may be NULL in case of default environment) + //! custom environment this database is using (may be nullptr in case of default environment) leveldb::Env* penv; //! database options used diff --git a/src/httpserver.cpp b/src/httpserver.cpp index ba0125540..5adfca409 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -292,7 +292,7 @@ static void http_request_cb(struct evhttp_request* req, void* arg) static void http_reject_request_cb(struct evhttp_request* req, void*) { LogPrint(BCLog::HTTP, "Rejecting request while shutting down\n"); - evhttp_send_error(req, HTTP_SERVUNAVAIL, NULL); + evhttp_send_error(req, HTTP_SERVUNAVAIL, nullptr); } /** Event dispatcher thread */ @@ -334,7 +334,7 @@ static bool HTTPBindAddresses(struct evhttp* http) // Bind addresses for (std::vector >::iterator i = endpoints.begin(); i != endpoints.end(); ++i) { LogPrint(BCLog::HTTP, "Binding RPC on address %s port %i\n", i->first, i->second); - evhttp_bound_socket *bind_handle = evhttp_bind_socket_with_handle(http, i->first.empty() ? NULL : i->first.c_str(), i->second); + evhttp_bound_socket *bind_handle = evhttp_bind_socket_with_handle(http, i->first.empty() ? nullptr : i->first.c_str(), i->second); if (bind_handle) { boundSockets.push_back(bind_handle); } else { @@ -404,7 +404,7 @@ bool InitHTTPServer() evhttp_set_timeout(http, GetArg("-rpcservertimeout", DEFAULT_HTTP_SERVER_TIMEOUT)); evhttp_set_max_headers_size(http, MAX_HEADERS_SIZE); evhttp_set_max_body_size(http, MAX_SIZE); - evhttp_set_gencb(http, http_request_cb, NULL); + evhttp_set_gencb(http, http_request_cb, nullptr); if (!HTTPBindAddresses(http)) { LogPrintf("Unable to bind any endpoint for RPC server\n"); @@ -464,7 +464,7 @@ void InterruptHTTPServer() evhttp_del_accept_socket(eventHTTP, socket); } // Reject requests on current connections - evhttp_set_gencb(eventHTTP, http_reject_request_cb, NULL); + evhttp_set_gencb(eventHTTP, http_reject_request_cb, nullptr); } if (workQueue) workQueue->Interrupt(); @@ -530,7 +530,7 @@ HTTPEvent::~HTTPEvent() } void HTTPEvent::trigger(struct timeval* tv) { - if (tv == NULL) + if (tv == nullptr) event_active(ev, 0, 0); // immediately trigger event in main thread else evtimer_add(ev, tv); // trigger after timeval passed @@ -573,7 +573,7 @@ std::string HTTPRequest::ReadBody() * abstraction to consume the evbuffer on the fly in the parsing algorithm. */ const char* data = (const char*)evbuffer_pullup(buf, size); - if (!data) // returns NULL in case of empty buffer + if (!data) // returns nullptr in case of empty buffer return ""; std::string rv(data, size); evbuffer_drain(buf, size); @@ -600,7 +600,7 @@ void HTTPRequest::WriteReply(int nStatus, const std::string& strReply) assert(evb); evbuffer_add(evb, strReply.data(), strReply.size()); HTTPEvent* ev = new HTTPEvent(eventBase, true, - std::bind(evhttp_send_reply, req, nStatus, (const char*)NULL, (struct evbuffer *)NULL)); + std::bind(evhttp_send_reply, req, nStatus, (const char*)nullptr, (struct evbuffer *)nullptr)); ev->trigger(0); replySent = true; req = 0; // transferred back to main thread @@ -669,7 +669,7 @@ void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch) std::string urlDecode(const std::string &urlEncoded) { std::string res; if (!urlEncoded.empty()) { - char *decoded = evhttp_uridecode(urlEncoded.c_str(), false, NULL); + char *decoded = evhttp_uridecode(urlEncoded.c_str(), false, nullptr); if (decoded) { res = std::string(decoded); free(decoded); diff --git a/src/init.cpp b/src/init.cpp index 0ee828ce9..7a37b8d80 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -76,7 +76,7 @@ std::unique_ptr g_connman; std::unique_ptr peerLogic; #if ENABLE_ZMQ -static CZMQNotificationInterface* pzmqNotificationInterface = NULL; +static CZMQNotificationInterface* pzmqNotificationInterface = nullptr; #endif #ifdef WIN32 @@ -153,7 +153,7 @@ class CCoinsViewErrorCatcher : public CCoinsViewBacked // Writes do not need similar protection, as failure to write is handled by the caller. }; -static CCoinsViewErrorCatcher *pcoinscatcher = NULL; +static CCoinsViewErrorCatcher *pcoinscatcher = nullptr; static std::unique_ptr globalVerifyHandle; void Interrupt(boost::thread_group& threadGroup) @@ -232,17 +232,17 @@ void Shutdown() { LOCK(cs_main); - if (pcoinsTip != NULL) { + if (pcoinsTip != nullptr) { FlushStateToDisk(); } delete pcoinsTip; - pcoinsTip = NULL; + pcoinsTip = nullptr; delete pcoinscatcher; - pcoinscatcher = NULL; + pcoinscatcher = nullptr; delete pcoinsdbview; - pcoinsdbview = NULL; + pcoinsdbview = nullptr; delete pblocktree; - pblocktree = NULL; + pblocktree = nullptr; } #ifdef ENABLE_WALLET for (CWalletRef pwallet : vpwallets) { @@ -254,7 +254,7 @@ void Shutdown() if (pzmqNotificationInterface) { UnregisterValidationInterface(pzmqNotificationInterface); delete pzmqNotificationInterface; - pzmqNotificationInterface = NULL; + pzmqNotificationInterface = nullptr; } #endif @@ -300,7 +300,7 @@ static void registerSignalHandler(int signal, void(*handler)(int)) sa.sa_handler = handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; - sigaction(signal, &sa, NULL); + sigaction(signal, &sa, nullptr); } #endif @@ -558,7 +558,7 @@ static CConditionVariable condvar_GenesisWait; static void BlockNotifyGenesisWait(bool, const CBlockIndex *pBlockIndex) { - if (pBlockIndex != NULL) { + if (pBlockIndex != nullptr) { { boost::unique_lock lock_GenesisWait(cs_GenesisWait); fHaveGenesis = true; @@ -843,7 +843,7 @@ bool AppInitBasicSetup() #ifdef _MSC_VER // Turn off Microsoft heap dump noise _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); - _CrtSetReportFile(_CRT_WARN, CreateFileA("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0)); + _CrtSetReportFile(_CRT_WARN, CreateFileA("NUL", GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, 0)); // Disable confusing "helpful" text message on abort, Ctrl-C _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); #endif @@ -858,7 +858,7 @@ bool AppInitBasicSetup() #endif typedef BOOL (WINAPI *PSETPROCDEPPOL)(DWORD); PSETPROCDEPPOL setProcDEPPol = (PSETPROCDEPPOL)GetProcAddress(GetModuleHandleA("Kernel32.dll"), "SetProcessDEPPolicy"); - if (setProcDEPPol != NULL) setProcDEPPol(PROCESS_DEP_ENABLE); + if (setProcDEPPol != nullptr) setProcDEPPol(PROCESS_DEP_ENABLE); #endif if (!SetupNetworking()) @@ -1473,7 +1473,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) strLoadError = _("Error initializing block database"); break; } - assert(chainActive.Tip() != NULL); + assert(chainActive.Tip() != nullptr); } if (!fReindex) { @@ -1600,7 +1600,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) // Either install a handler to notify us when genesis activates, or set fHaveGenesis directly. // No locking, as this happens before any background thread is started. - if (chainActive.Tip() == NULL) { + if (chainActive.Tip() == nullptr) { uiInterface.NotifyBlockTip.connect(BlockNotifyGenesisWait); } else { fHaveGenesis = true; diff --git a/src/key.cpp b/src/key.cpp index 5a991fc1d..315a3978c 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -13,7 +13,7 @@ #include #include -static secp256k1_context* secp256k1_context_sign = NULL; +static secp256k1_context* secp256k1_context_sign = nullptr; /** These functions are taken from the libsecp256k1 distribution and are very ugly. */ static int ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *out32, const unsigned char *privkey, size_t privkeylen) { @@ -165,7 +165,7 @@ bool CKey::Sign(const uint256 &hash, std::vector& vchSig, uint32_ unsigned char extra_entropy[32] = {0}; WriteLE32(extra_entropy, test_case); secp256k1_ecdsa_signature sig; - int ret = secp256k1_ecdsa_sign(secp256k1_context_sign, &sig, hash.begin(), begin(), secp256k1_nonce_function_rfc6979, test_case ? extra_entropy : NULL); + int ret = secp256k1_ecdsa_sign(secp256k1_context_sign, &sig, hash.begin(), begin(), secp256k1_nonce_function_rfc6979, test_case ? extra_entropy : nullptr); assert(ret); secp256k1_ecdsa_signature_serialize_der(secp256k1_context_sign, (unsigned char*)vchSig.data(), &nSigLen, &sig); vchSig.resize(nSigLen); @@ -192,7 +192,7 @@ bool CKey::SignCompact(const uint256 &hash, std::vector& vchSig) vchSig.resize(65); int rec = -1; secp256k1_ecdsa_recoverable_signature sig; - int ret = secp256k1_ecdsa_sign_recoverable(secp256k1_context_sign, &sig, hash.begin(), begin(), secp256k1_nonce_function_rfc6979, NULL); + int ret = secp256k1_ecdsa_sign_recoverable(secp256k1_context_sign, &sig, hash.begin(), begin(), secp256k1_nonce_function_rfc6979, nullptr); assert(ret); secp256k1_ecdsa_recoverable_signature_serialize_compact(secp256k1_context_sign, (unsigned char*)&vchSig[1], &rec, &sig); assert(ret); @@ -289,10 +289,10 @@ bool ECC_InitSanityCheck() { } void ECC_Start() { - assert(secp256k1_context_sign == NULL); + assert(secp256k1_context_sign == nullptr); secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); - assert(ctx != NULL); + assert(ctx != nullptr); { // Pass in a random blinding seed to the secp256k1 context. @@ -307,7 +307,7 @@ void ECC_Start() { void ECC_Stop() { secp256k1_context *ctx = secp256k1_context_sign; - secp256k1_context_sign = NULL; + secp256k1_context_sign = nullptr; if (ctx) { secp256k1_context_destroy(ctx); diff --git a/src/net.cpp b/src/net.cpp index ca9a173ab..0ed219606 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -306,7 +306,7 @@ CNode* CConnman::FindNode(const CNetAddr& ip) for (CNode* pnode : vNodes) if ((CNetAddr)pnode->addr == ip) return (pnode); - return NULL; + return nullptr; } CNode* CConnman::FindNode(const CSubNet& subNet) @@ -315,7 +315,7 @@ CNode* CConnman::FindNode(const CSubNet& subNet) for (CNode* pnode : vNodes) if (subNet.Match((CNetAddr)pnode->addr)) return (pnode); - return NULL; + return nullptr; } CNode* CConnman::FindNode(const std::string& addrName) @@ -326,7 +326,7 @@ CNode* CConnman::FindNode(const std::string& addrName) return (pnode); } } - return NULL; + return nullptr; } CNode* CConnman::FindNode(const CService& addr) @@ -335,7 +335,7 @@ CNode* CConnman::FindNode(const CService& addr) for (CNode* pnode : vNodes) if ((CService)pnode->addr == addr) return (pnode); - return NULL; + return nullptr; } bool CConnman::CheckIncomingNonce(uint64_t nonce) @@ -366,16 +366,16 @@ static CAddress GetBindAddress(SOCKET sock) CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure) { - if (pszDest == NULL) { + if (pszDest == nullptr) { if (IsLocal(addrConnect)) - return NULL; + return nullptr; // Look for an existing connection CNode* pnode = FindNode((CService)addrConnect); if (pnode) { LogPrintf("Failed to open new connection, already connected\n"); - return NULL; + return nullptr; } } @@ -393,7 +393,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo if (!IsSelectableSocket(hSocket)) { LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\n"); CloseSocket(hSocket); - return NULL; + return nullptr; } if (pszDest && addrConnect.IsValid()) { @@ -408,7 +408,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo pnode->MaybeSetAddrName(std::string(pszDest)); CloseSocket(hSocket); LogPrintf("Failed to open new connection, already connected\n"); - return NULL; + return nullptr; } } @@ -429,7 +429,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo addrman.Attempt(addrConnect, fCountFailure); } - return NULL; + return nullptr; } void CConnman::DumpBanlist() @@ -966,7 +966,7 @@ bool CConnman::AttemptToEvictConnection() NodeEvictionCandidate candidate = {node->GetId(), node->nTimeConnected, node->nMinPingUsecTime, node->nLastBlockTime, node->nLastTXTime, (node->nServices & nRelevantServices) == nRelevantServices, - node->fRelayTxes, node->pfilter != NULL, node->addr, node->nKeyedNetGroup}; + node->fRelayTxes, node->pfilter != nullptr, node->addr, node->nKeyedNetGroup}; vEvictionCandidates.push_back(candidate); } } @@ -1524,7 +1524,7 @@ void ThreadMapPort() void MapPort(bool fUseUPnP) { - static boost::thread* upnp_thread = NULL; + static boost::thread* upnp_thread = nullptr; if (fUseUPnP) { @@ -1539,7 +1539,7 @@ void MapPort(bool fUseUPnP) upnp_thread->interrupt(); upnp_thread->join(); delete upnp_thread; - upnp_thread = NULL; + upnp_thread = nullptr; } } @@ -1685,7 +1685,7 @@ void CConnman::ThreadOpenConnections() for (const std::string& strAddr : gArgs.GetArgs("-connect")) { CAddress addr(CService(), NODE_NONE); - OpenNetworkConnection(addr, false, NULL, strAddr.c_str()); + OpenNetworkConnection(addr, false, nullptr, strAddr.c_str()); for (int i = 0; i < 10 && i < nLoop; i++) { if (!interruptNet.sleep_for(std::chrono::milliseconds(500))) @@ -1841,7 +1841,7 @@ void CConnman::ThreadOpenConnections() LogPrint(BCLog::NET, "Making feeler connection to %s\n", addrConnect.ToString()); } - OpenNetworkConnection(addrConnect, (int)setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant, NULL, false, fFeeler); + OpenNetworkConnection(addrConnect, (int)setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant, nullptr, false, fFeeler); } } } @@ -2153,9 +2153,9 @@ void Discover(boost::thread_group& threadGroup) struct ifaddrs* myaddrs; if (getifaddrs(&myaddrs) == 0) { - for (struct ifaddrs* ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next) + for (struct ifaddrs* ifa = myaddrs; ifa != nullptr; ifa = ifa->ifa_next) { - if (ifa->ifa_addr == NULL) continue; + if (ifa->ifa_addr == nullptr) continue; if ((ifa->ifa_flags & IFF_UP) == 0) continue; if (strcmp(ifa->ifa_name, "lo") == 0) continue; if (strcmp(ifa->ifa_name, "lo0") == 0) continue; @@ -2208,8 +2208,8 @@ CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In) : nSeed0(nSeed0In), nSe nLastNodeId = 0; nSendBufferMaxSize = 0; nReceiveFloodSize = 0; - semOutbound = NULL; - semAddnode = NULL; + semOutbound = nullptr; + semAddnode = nullptr; flagInterruptMsgProc = false; Options connOptions; @@ -2312,11 +2312,11 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions) fAddressesInitialized = true; - if (semOutbound == NULL) { + if (semOutbound == nullptr) { // initialize semaphore semOutbound = new CSemaphore(std::min((nMaxOutbound + nMaxFeeler), nMaxConnections)); } - if (semAddnode == NULL) { + if (semAddnode == nullptr) { // initialize semaphore semAddnode = new CSemaphore(nMaxAddnode); } @@ -2434,9 +2434,9 @@ void CConnman::Stop() vNodesDisconnected.clear(); vhListenSocket.clear(); delete semOutbound; - semOutbound = NULL; + semOutbound = nullptr; delete semAddnode; - semAddnode = NULL; + semAddnode = nullptr; } void CConnman::DeleteNode(CNode* pnode) diff --git a/src/net.h b/src/net.h index 93a76fc09..a32736aa9 100644 --- a/src/net.h +++ b/src/net.h @@ -170,7 +170,7 @@ class CConnman void Interrupt(); bool GetNetworkActive() const { return fNetworkActive; }; void SetNetworkActive(bool active); - bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false, bool fFeeler = false, bool fAddnode = false); + bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = nullptr, const char *strDest = nullptr, bool fOneShot = false, bool fFeeler = false, bool fAddnode = false); bool CheckIncomingNonce(uint64_t nonce); bool ForNode(NodeId id, std::function func); @@ -470,7 +470,7 @@ bool AddLocal(const CNetAddr& addr, int nScore = LOCAL_NONE); bool RemoveLocal(const CService& addr); bool SeenLocal(const CService& addr); bool IsLocal(const CService& addr); -bool GetLocal(CService &addr, const CNetAddr *paddrPeer = NULL); +bool GetLocal(CService &addr, const CNetAddr *paddrPeer = nullptr); bool IsReachable(enum Network net); bool IsReachable(const CNetAddr &addr); CAddress GetLocalAddress(const CNetAddr *paddrPeer, ServiceFlags nLocalServices); diff --git a/src/net_processing.cpp b/src/net_processing.cpp index a743f04dd..2e14d01f0 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -202,10 +202,10 @@ struct CNodeState { fCurrentlyConnected = false; nMisbehavior = 0; fShouldBan = false; - pindexBestKnownBlock = NULL; + pindexBestKnownBlock = nullptr; hashLastUnknownBlock.SetNull(); - pindexLastCommonBlock = NULL; - pindexBestHeaderSent = NULL; + pindexLastCommonBlock = nullptr; + pindexBestHeaderSent = nullptr; nUnconnectingHeaders = 0; fSyncStarted = false; nHeadersSyncTimeout = 0; @@ -230,7 +230,7 @@ std::map mapNodeState; CNodeState *State(NodeId pnode) { std::map::iterator it = mapNodeState.find(pnode); if (it == mapNodeState.end()) - return NULL; + return nullptr; return &it->second; } @@ -336,9 +336,9 @@ 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 CBlockIndex* pindex = NULL, std::list::iterator** pit = NULL) { +bool MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const CBlockIndex* pindex = nullptr, std::list::iterator** pit = nullptr) { CNodeState *state = State(nodeid); - assert(state != NULL); + assert(state != nullptr); // Short-circuit most stuff in case its from the same node std::map::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash); @@ -353,14 +353,14 @@ bool MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const CBlockIndex* MarkBlockAsReceived(hash); std::list::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), - {hash, pindex, pindex != NULL, std::unique_ptr(pit ? new PartiallyDownloadedBlock(&mempool) : NULL)}); + {hash, pindex, pindex != nullptr, std::unique_ptr(pit ? new PartiallyDownloadedBlock(&mempool) : nullptr)}); state->nBlocksInFlight++; state->nBlocksInFlightValidHeaders += it->fValidatedHeaders; if (state->nBlocksInFlight == 1) { // We're starting a block download (batch) from this peer. state->nDownloadingSince = GetTimeMicros(); } - if (state->nBlocksInFlightValidHeaders == 1 && pindex != NULL) { + if (state->nBlocksInFlightValidHeaders == 1 && pindex != nullptr) { nPeersWithValidatedDownloads++; } itInFlight = mapBlocksInFlight.insert(std::make_pair(hash, std::make_pair(nodeid, it))).first; @@ -372,12 +372,12 @@ bool MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const CBlockIndex* /** Check whether the last unknown block a peer advertised is not yet known. */ void ProcessBlockAvailability(NodeId nodeid) { CNodeState *state = State(nodeid); - assert(state != NULL); + assert(state != nullptr); if (!state->hashLastUnknownBlock.IsNull()) { BlockMap::iterator itOld = mapBlockIndex.find(state->hashLastUnknownBlock); if (itOld != mapBlockIndex.end() && itOld->second->nChainWork > 0) { - if (state->pindexBestKnownBlock == NULL || itOld->second->nChainWork >= state->pindexBestKnownBlock->nChainWork) + if (state->pindexBestKnownBlock == nullptr || itOld->second->nChainWork >= state->pindexBestKnownBlock->nChainWork) state->pindexBestKnownBlock = itOld->second; state->hashLastUnknownBlock.SetNull(); } @@ -387,14 +387,14 @@ void ProcessBlockAvailability(NodeId nodeid) { /** Update tracking information about which blocks a peer is assumed to have. */ void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) { CNodeState *state = State(nodeid); - assert(state != NULL); + assert(state != nullptr); ProcessBlockAvailability(nodeid); BlockMap::iterator it = mapBlockIndex.find(hash); if (it != mapBlockIndex.end() && it->second->nChainWork > 0) { // An actually better block was announced. - if (state->pindexBestKnownBlock == NULL || it->second->nChainWork >= state->pindexBestKnownBlock->nChainWork) + if (state->pindexBestKnownBlock == nullptr || it->second->nChainWork >= state->pindexBestKnownBlock->nChainWork) state->pindexBestKnownBlock = it->second; } else { // An unknown block was announced; just assume that the latest one is the best one. @@ -461,17 +461,17 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vectorpindexBestKnownBlock == NULL || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork || state->pindexBestKnownBlock->nChainWork < UintToArith256(consensusParams.nMinimumChainWork)) { + if (state->pindexBestKnownBlock == nullptr || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork || state->pindexBestKnownBlock->nChainWork < UintToArith256(consensusParams.nMinimumChainWork)) { // This peer has nothing interesting. return; } - if (state->pindexLastCommonBlock == NULL) { + if (state->pindexLastCommonBlock == nullptr) { // Bootstrap quickly by guessing a parent of our best tip is the forking point. // Guessing wrong in either direction is not a problem. state->pindexLastCommonBlock = chainActive[std::min(state->pindexBestKnownBlock->nHeight, chainActive.Height())]; @@ -546,7 +546,7 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vectornMisbehavior; stats.nSyncHeight = state->pindexBestKnownBlock ? state->pindexBestKnownBlock->nHeight : -1; @@ -700,7 +700,7 @@ void Misbehaving(NodeId pnode, int howmuch) return; CNodeState *state = State(pnode); - if (state == NULL) + if (state == nullptr) return; state->nMisbehavior += howmuch; @@ -1006,7 +1006,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam // To prevent fingerprinting attacks, only send blocks outside of the active // chain if they are valid, and no more than a month older (both in time, and in // best equivalent proof of work) than the best header chain we know about. - send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != NULL) && + send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() < nOneMonth) && (GetBlockProofEquivalentTime(*pindexBestHeader, *mi->second, *pindexBestHeader, consensusParams) < nOneMonth); if (!send) { @@ -1017,7 +1017,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam // disconnect node in case we have reached the outbound limit for serving historical blocks // never disconnect whitelisted nodes static const int nOneWeek = 7 * 24 * 60 * 60; // assume > 1 week = historical - if (send && connman.OutboundTargetReached(true) && ( ((pindexBestHeader != NULL) && (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() > nOneWeek)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted) + if (send && connman.OutboundTargetReached(true) && ( ((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() > nOneWeek)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted) { LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom->GetId()); @@ -1723,7 +1723,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr } CNodeState *nodestate = State(pfrom->GetId()); - const CBlockIndex* pindex = NULL; + const CBlockIndex* pindex = nullptr; if (locator.IsNull()) { // If locator is null, return the hashStop block @@ -1750,7 +1750,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop) break; } - // pindex can be NULL either if we sent chainActive.Tip() OR + // pindex can be nullptr either if we sent chainActive.Tip() OR // 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. @@ -1969,7 +1969,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr } } - const CBlockIndex *pindex = NULL; + const CBlockIndex *pindex = nullptr; CValidationState state; if (!ProcessNewBlockHeaders({cmpctblock.header}, state, chainparams, &pindex)) { int nDoS; @@ -2041,7 +2041,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr if (pindex->nHeight <= chainActive.Height() + 2) { if ((!fAlreadyInFlight && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) || (fAlreadyInFlight && blockInFlightIt->second.first == pfrom->GetId())) { - std::list::iterator* queuedBlockIt = NULL; + std::list::iterator* queuedBlockIt = nullptr; if (!MarkBlockAsInFlight(pfrom->GetId(), pindex->GetBlockHash(), pindex, &queuedBlockIt)) { if (!(*queuedBlockIt)->partialBlock) (*queuedBlockIt)->partialBlock.reset(new PartiallyDownloadedBlock(&mempool)); @@ -2239,7 +2239,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr return true; } - const CBlockIndex *pindexLast = NULL; + const CBlockIndex *pindexLast = nullptr; { LOCK(cs_main); CNodeState *nodestate = State(pfrom->GetId()); @@ -2744,7 +2744,7 @@ bool ProcessMessages(CNode* pfrom, CConnman& connman, const std::atomic& i catch (const std::exception& e) { PrintExceptionContinue(&e, "ProcessMessages()"); } catch (...) { - PrintExceptionContinue(NULL, "ProcessMessages()"); + PrintExceptionContinue(nullptr, "ProcessMessages()"); } if (!fRet) { @@ -2859,7 +2859,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic& interr } // Start block sync - if (pindexBestHeader == NULL) + if (pindexBestHeader == nullptr) pindexBestHeader = chainActive.Tip(); bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->fOneShot); // Download if this is a nice peer, or we have no nice peers and this one might do. if (!state.fSyncStarted && !pto->fClient && !fImporting && !fReindex) { @@ -2907,7 +2907,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic& interr bool fRevertToInv = ((!state.fPreferHeaders && (!state.fPreferHeaderAndIDs || pto->vBlockHashesToAnnounce.size() > 1)) || pto->vBlockHashesToAnnounce.size() > MAX_BLOCKS_TO_ANNOUNCE); - const CBlockIndex *pBestIndex = NULL; // last header queued for delivery + const CBlockIndex *pBestIndex = nullptr; // last header queued for delivery ProcessBlockAvailability(pto->GetId()); // ensure pindexBestKnownBlock is up-to-date if (!fRevertToInv) { @@ -2924,7 +2924,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic& interr fRevertToInv = true; break; } - if (pBestIndex != NULL && pindex->pprev != pBestIndex) { + if (pBestIndex != nullptr && pindex->pprev != pBestIndex) { // This means that the list of blocks to announce don't // connect to each other. // This shouldn't really be possible to hit during @@ -2945,7 +2945,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic& interr vHeaders.push_back(pindex->GetBlockHeader()); } else if (PeerHasHeader(&state, pindex)) { continue; // keep looking for the first new block - } else if (pindex->pprev == NULL || PeerHasHeader(&state, pindex->pprev)) { + } else if (pindex->pprev == nullptr || PeerHasHeader(&state, pindex->pprev)) { // Peer doesn't have this header but they do have the prior one. // Start sending headers. fFoundStartingHeader = true; diff --git a/src/netaddress.cpp b/src/netaddress.cpp index f31b2fc49..b8a261c92 100644 --- a/src/netaddress.cpp +++ b/src/netaddress.cpp @@ -271,7 +271,7 @@ std::string CNetAddr::ToStringIP() const socklen_t socklen = sizeof(sockaddr); if (serv.GetSockAddr((struct sockaddr*)&sockaddr, &socklen)) { char name[1025] = ""; - if (!getnameinfo((const struct sockaddr*)&sockaddr, socklen, name, sizeof(name), NULL, 0, NI_NUMERICHOST)) + if (!getnameinfo((const struct sockaddr*)&sockaddr, socklen, name, sizeof(name), nullptr, 0, NI_NUMERICHOST)) return std::string(name); } if (IsIPv4()) @@ -407,7 +407,7 @@ static const int NET_UNKNOWN = NET_MAX + 0; static const int NET_TEREDO = NET_MAX + 1; int static GetExtNetwork(const CNetAddr *addr) { - if (addr == NULL) + if (addr == nullptr) return NET_UNKNOWN; if (addr->IsRFC4380()) return NET_TEREDO; diff --git a/src/netaddress.h b/src/netaddress.h index 61afe0f1f..57ce897db 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -80,7 +80,7 @@ class CNetAddr uint64_t GetHash() const; bool GetInAddr(struct in_addr* pipv4Addr) const; std::vector GetGroup() const; - int GetReachabilityFrom(const CNetAddr *paddrPartner = NULL) const; + int GetReachabilityFrom(const CNetAddr *paddrPartner = nullptr) const; CNetAddr(const struct in6_addr& pipv6Addr, const uint32_t scope = 0); bool GetIn6Addr(struct in6_addr* pipv6Addr) const; diff --git a/src/netbase.cpp b/src/netbase.cpp index 8952468ec..05f9f6961 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -81,13 +81,13 @@ bool static LookupIntern(const char *pszName, std::vector& vIP, unsign #else aiHint.ai_flags = fAllowLookup ? AI_ADDRCONFIG : AI_NUMERICHOST; #endif - struct addrinfo *aiRes = NULL; - int nErr = getaddrinfo(pszName, NULL, &aiHint, &aiRes); + struct addrinfo *aiRes = nullptr; + int nErr = getaddrinfo(pszName, nullptr, &aiHint, &aiRes); if (nErr) return false; struct addrinfo *aiTrav = aiRes; - while (aiTrav != NULL && (nMaxSolutions == 0 || vIP.size() < nMaxSolutions)) + while (aiTrav != nullptr && (nMaxSolutions == 0 || vIP.size() < nMaxSolutions)) { CNetAddr resolved; if (aiTrav->ai_family == AF_INET) @@ -227,7 +227,7 @@ static IntrRecvError InterruptibleRecv(char* data, size_t len, int timeout, cons fd_set fdset; FD_ZERO(&fdset); FD_SET(hSocket, &fdset); - int nRet = select(hSocket + 1, &fdset, NULL, NULL, &tval); + int nRet = select(hSocket + 1, &fdset, nullptr, nullptr, &tval); if (nRet == SOCKET_ERROR) { return IntrRecvError::NetworkError; } @@ -439,7 +439,7 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe fd_set fdset; FD_ZERO(&fdset); FD_SET(hSocket, &fdset); - int nRet = select(hSocket + 1, NULL, &fdset, NULL, &timeout); + int nRet = select(hSocket + 1, nullptr, &fdset, nullptr, &timeout); if (nRet == 0) { LogPrint(BCLog::NET, "connection to %s timeout\n", addrConnect.ToString()); @@ -642,8 +642,8 @@ std::string NetworkErrorString(int err) char buf[256]; buf[0] = 0; if(FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, - NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - buf, sizeof(buf), NULL)) + nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + buf, sizeof(buf), nullptr)) { return strprintf("%s (%d)", buf, err); } diff --git a/src/pow.cpp b/src/pow.cpp index e06d9662e..7d87c6bbb 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -12,7 +12,7 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params) { - assert(pindexLast != NULL); + assert(pindexLast != nullptr); unsigned int nProofOfWorkLimit = UintToArith256(params.powLimit).GetCompact(); // Only change once per difficulty adjustment interval diff --git a/src/prevector.h b/src/prevector.h index 46640d6ff..f7bde8911 100644 --- a/src/prevector.h +++ b/src/prevector.h @@ -448,7 +448,7 @@ class prevector { } if (!is_direct()) { free(_union.indirect); - _union.indirect = NULL; + _union.indirect = nullptr; } } diff --git a/src/pubkey.cpp b/src/pubkey.cpp index 91af4e56f..2da7be783 100644 --- a/src/pubkey.cpp +++ b/src/pubkey.cpp @@ -10,7 +10,7 @@ namespace { /* Global secp256k1_context object used for verification. */ -secp256k1_context* secp256k1_context_verify = NULL; +secp256k1_context* secp256k1_context_verify = nullptr; } // namespace /** This function is taken from the libsecp256k1 distribution and implements @@ -274,7 +274,7 @@ bool CExtPubKey::Derive(CExtPubKey &out, unsigned int _nChild) const { if (!ecdsa_signature_parse_der_lax(secp256k1_context_verify, &sig, vchSig.data(), vchSig.size())) { return false; } - return (!secp256k1_ecdsa_signature_normalize(secp256k1_context_verify, NULL, &sig)); + return (!secp256k1_ecdsa_signature_normalize(secp256k1_context_verify, nullptr, &sig)); } /* static */ int ECCVerifyHandle::refcount = 0; @@ -282,9 +282,9 @@ bool CExtPubKey::Derive(CExtPubKey &out, unsigned int _nChild) const { ECCVerifyHandle::ECCVerifyHandle() { if (refcount == 0) { - assert(secp256k1_context_verify == NULL); + assert(secp256k1_context_verify == nullptr); secp256k1_context_verify = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); - assert(secp256k1_context_verify != NULL); + assert(secp256k1_context_verify != nullptr); } refcount++; } @@ -293,8 +293,8 @@ ECCVerifyHandle::~ECCVerifyHandle() { refcount--; if (refcount == 0) { - assert(secp256k1_context_verify != NULL); + assert(secp256k1_context_verify != nullptr); secp256k1_context_destroy(secp256k1_context_verify); - secp256k1_context_verify = NULL; + secp256k1_context_verify = nullptr; } } diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index cebac46b9..f295bd468 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -273,7 +273,7 @@ void AddressBookPage::on_exportButton_clicked() // CSV is currently the only supported format QString filename = GUIUtil::getSaveFileName(this, tr("Export Address List"), QString(), - tr("Comma separated file (*.csv)"), NULL); + tr("Comma separated file (*.csv)"), nullptr); if (filename.isNull()) return; diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 4a4116c67..b6cfdf888 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -305,7 +305,7 @@ void BitcoinCore::initialize() } catch (const std::exception& e) { handleRunawayException(&e); } catch (...) { - handleRunawayException(NULL); + handleRunawayException(nullptr); } } @@ -322,7 +322,7 @@ void BitcoinCore::shutdown() } catch (const std::exception& e) { handleRunawayException(&e); } catch (...) { - handleRunawayException(NULL); + handleRunawayException(nullptr); } } @@ -383,7 +383,7 @@ void BitcoinApplication::createPaymentServer() void BitcoinApplication::createOptionsModel(bool resetSettings) { - optionsModel = new OptionsModel(NULL, resetSettings); + optionsModel = new OptionsModel(nullptr, resetSettings); } void BitcoinApplication::createWindow(const NetworkStyle *networkStyle) @@ -608,7 +608,7 @@ int main(int argc, char *argv[]) // but before showing splash screen. if (IsArgSet("-?") || IsArgSet("-h") || IsArgSet("-help") || IsArgSet("-version")) { - HelpMessageDialog help(NULL, IsArgSet("-version")); + HelpMessageDialog help(nullptr, IsArgSet("-version")); help.showOrPrint(); return EXIT_SUCCESS; } @@ -723,7 +723,7 @@ int main(int argc, char *argv[]) PrintExceptionContinue(&e, "Runaway exception"); app.handleRunawayException(QString::fromStdString(GetWarnings("gui"))); } catch (...) { - PrintExceptionContinue(NULL, "Runaway exception"); + PrintExceptionContinue(nullptr, "Runaway exception"); app.handleRunawayException(QString::fromStdString(GetWarnings("gui"))); } return rv; diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 429c18cba..e3970298e 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -479,7 +479,7 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel) connect(_clientModel, SIGNAL(networkActiveChanged(bool)), this, SLOT(setNetworkActive(bool))); modalOverlay->setKnownBestHeight(_clientModel->getHeaderTipHeight(), QDateTime::fromTime_t(_clientModel->getHeaderTipTime())); - setNumBlocks(_clientModel->getNumBlocks(), _clientModel->getLastBlockDate(), _clientModel->getVerificationProgress(NULL), false); + setNumBlocks(_clientModel->getNumBlocks(), _clientModel->getLastBlockDate(), _clientModel->getVerificationProgress(nullptr), false); connect(_clientModel, SIGNAL(numBlocksChanged(int,QDateTime,double,bool)), this, SLOT(setNumBlocks(int,QDateTime,double,bool))); // Receive and report messages from client model @@ -922,7 +922,7 @@ void BitcoinGUI::message(const QString &title, const QString &message, unsigned showNormalIfMinimized(); QMessageBox mBox((QMessageBox::Icon)nMBoxIcon, strTitle, message, buttons, this); int r = mBox.exec(); - if (ret != NULL) + if (ret != nullptr) *ret = r == QMessageBox::Ok; } else diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 8731caafc..aa45ea1f0 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -168,7 +168,7 @@ public Q_SLOTS: @see CClientUIInterface::MessageBoxFlags @param[in] ret pointer to a bool that will be modified to whether Ok was clicked (modal only) */ - void message(const QString &title, const QString &message, unsigned int style, bool *ret = NULL); + void message(const QString &title, const QString &message, unsigned int style, bool *ret = nullptr); #ifdef ENABLE_WALLET /** Set the encryption status as shown in the UI. diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index bffa81137..28ffda998 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -634,11 +634,11 @@ bool SetStartOnSystemStartup(bool fAutoStart) if (fAutoStart) { - CoInitialize(NULL); + CoInitialize(nullptr); // Get a pointer to the IShellLink interface. - IShellLink* psl = NULL; - HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, + IShellLink* psl = nullptr; + HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink, reinterpret_cast(&psl)); @@ -646,7 +646,7 @@ bool SetStartOnSystemStartup(bool fAutoStart) { // Get the current executable path TCHAR pszExePath[MAX_PATH]; - GetModuleFileName(NULL, pszExePath, sizeof(pszExePath)); + GetModuleFileName(nullptr, pszExePath, sizeof(pszExePath)); // Start client minimized QString strArgs = "-min"; @@ -674,7 +674,7 @@ bool SetStartOnSystemStartup(bool fAutoStart) // Query IShellLink for the IPersistFile interface for // saving the shortcut in persistent storage. - IPersistFile* ppf = NULL; + IPersistFile* ppf = nullptr; hres = psl->QueryInterface(IID_IPersistFile, reinterpret_cast(&ppf)); if (SUCCEEDED(hres)) { @@ -781,21 +781,21 @@ LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef findUrl) { // loop through the list of startup items and try to find the bitcoin app - CFArrayRef listSnapshot = LSSharedFileListCopySnapshot(list, NULL); + CFArrayRef listSnapshot = LSSharedFileListCopySnapshot(list, nullptr); for(int i = 0; i < CFArrayGetCount(listSnapshot); i++) { LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(listSnapshot, i); UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes; - CFURLRef currentItemURL = NULL; + CFURLRef currentItemURL = nullptr; #if defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED >= 10100 if(&LSSharedFileListItemCopyResolvedURL) - currentItemURL = LSSharedFileListItemCopyResolvedURL(item, resolutionFlags, NULL); + currentItemURL = LSSharedFileListItemCopyResolvedURL(item, resolutionFlags, nullptr); #if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && MAC_OS_X_VERSION_MIN_REQUIRED < 10100 else - LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, NULL); + LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, nullptr); #endif #else - LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, NULL); + LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, nullptr); #endif if(currentItemURL && CFEqual(currentItemURL, findUrl)) { @@ -807,13 +807,13 @@ LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef CFRelease(currentItemURL); } } - return NULL; + return nullptr; } bool GetStartOnSystemStartup() { CFURLRef bitcoinAppUrl = CFBundleCopyBundleURL(CFBundleGetMainBundle()); - LSSharedFileListRef loginItems = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL); + LSSharedFileListRef loginItems = LSSharedFileListCreate(nullptr, kLSSharedFileListSessionLoginItems, nullptr); LSSharedFileListItemRef foundItem = findStartupItemInList(loginItems, bitcoinAppUrl); return !!foundItem; // return boolified object } @@ -821,12 +821,12 @@ bool GetStartOnSystemStartup() bool SetStartOnSystemStartup(bool fAutoStart) { CFURLRef bitcoinAppUrl = CFBundleCopyBundleURL(CFBundleGetMainBundle()); - LSSharedFileListRef loginItems = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL); + LSSharedFileListRef loginItems = LSSharedFileListCreate(nullptr, kLSSharedFileListSessionLoginItems, nullptr); LSSharedFileListItemRef foundItem = findStartupItemInList(loginItems, bitcoinAppUrl); if(fAutoStart && !foundItem) { // add bitcoin app to startup item list - LSSharedFileListInsertItemURL(loginItems, kLSSharedFileListItemBeforeFirst, NULL, NULL, bitcoinAppUrl, NULL, NULL); + LSSharedFileListInsertItemURL(loginItems, kLSSharedFileListItemBeforeFirst, nullptr, nullptr, bitcoinAppUrl, nullptr, nullptr); } else if(!fAutoStart && foundItem) { // remove item diff --git a/src/qt/openuridialog.cpp b/src/qt/openuridialog.cpp index 5a6616134..3ee656d47 100644 --- a/src/qt/openuridialog.cpp +++ b/src/qt/openuridialog.cpp @@ -44,7 +44,7 @@ void OpenURIDialog::accept() void OpenURIDialog::on_selectFileButton_clicked() { - QString filename = GUIUtil::getOpenFileName(this, tr("Select payment request file to open"), "", "", NULL); + QString filename = GUIUtil::getOpenFileName(this, tr("Select payment request file to open"), "", "", nullptr); if(filename.isEmpty()) return; QUrl fileUri = QUrl::fromLocalFile(filename); diff --git a/src/qt/paymentrequestplus.cpp b/src/qt/paymentrequestplus.cpp index 01ec41661..897762b1a 100644 --- a/src/qt/paymentrequestplus.cpp +++ b/src/qt/paymentrequestplus.cpp @@ -66,7 +66,7 @@ bool PaymentRequestPlus::getMerchant(X509_STORE* certStore, QString& merchant) c // One day we'll support more PKI types, but just // x509 for now: - const EVP_MD* digestAlgorithm = NULL; + const EVP_MD* digestAlgorithm = nullptr; if (paymentRequest.pki_type() == "x509+sha256") { digestAlgorithm = EVP_sha256(); } @@ -104,7 +104,7 @@ bool PaymentRequestPlus::getMerchant(X509_STORE* certStore, QString& merchant) c } #endif const unsigned char *data = (const unsigned char *)certChain.certificate(i).data(); - X509 *cert = d2i_X509(NULL, &data, certChain.certificate(i).size()); + X509 *cert = d2i_X509(nullptr, &data, certChain.certificate(i).size()); if (cert) certs.push_back(cert); } @@ -129,7 +129,7 @@ bool PaymentRequestPlus::getMerchant(X509_STORE* certStore, QString& merchant) c return false; } - char *website = NULL; + char *website = nullptr; bool fResult = true; try { @@ -169,7 +169,7 @@ bool PaymentRequestPlus::getMerchant(X509_STORE* certStore, QString& merchant) c #endif EVP_PKEY *pubkey = X509_get_pubkey(signing_cert); EVP_MD_CTX_init(ctx); - if (!EVP_VerifyInit_ex(ctx, digestAlgorithm, NULL) || + if (!EVP_VerifyInit_ex(ctx, digestAlgorithm, nullptr) || !EVP_VerifyUpdate(ctx, data_to_verify.data(), data_to_verify.size()) || !EVP_VerifyFinal(ctx, (const unsigned char*)paymentRequest.signature().data(), (unsigned int)paymentRequest.signature().size(), pubkey)) { throw SSLVerifyError("Bad signature, invalid payment request."); @@ -179,7 +179,7 @@ bool PaymentRequestPlus::getMerchant(X509_STORE* certStore, QString& merchant) c #endif // OpenSSL API for getting human printable strings from certs is baroque. - int textlen = X509_NAME_get_text_by_NID(certname, NID_commonName, NULL, 0); + int textlen = X509_NAME_get_text_by_NID(certname, NID_commonName, nullptr, 0); website = new char[textlen + 1]; if (X509_NAME_get_text_by_NID(certname, NID_commonName, website, textlen + 1) == textlen && textlen > 0) { merchant = website; diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index 132ee3274..c5e2b78c5 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -274,7 +274,7 @@ bool PaymentServer::ipcSendCommandLine() if (!socket->waitForConnected(BITCOIN_IPC_CONNECT_TIMEOUT)) { delete socket; - socket = NULL; + socket = nullptr; return false; } @@ -290,7 +290,7 @@ bool PaymentServer::ipcSendCommandLine() socket->disconnectFromServer(); delete socket; - socket = NULL; + socket = nullptr; fResult = true; } @@ -364,7 +364,7 @@ void PaymentServer::initNetManager() { if (!optionsModel) return; - if (netManager != NULL) + if (netManager != nullptr) delete netManager; // netManager is used to fetch paymentrequests given in bitcoin: URIs diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h index 7c6d4507f..2fe6e3e7b 100644 --- a/src/qt/paymentserver.h +++ b/src/qt/paymentserver.h @@ -75,12 +75,12 @@ class PaymentServer : public QObject PaymentServer(QObject* parent, bool startLocalServer = true); ~PaymentServer(); - // Load root certificate authorities. Pass NULL (default) + // Load root certificate authorities. Pass nullptr (default) // to read from the file specified in the -rootcertificates setting, // or, if that's not set, to use the system default root certificates. // If you pass in a store, you should not X509_STORE_free it: it will be // freed either at exit or when another set of CAs are loaded. - static void LoadRootCAs(X509_STORE* store = NULL); + static void LoadRootCAs(X509_STORE* store = nullptr); // Return certificate store static X509_STORE* getCertStore(); diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp index 3752fa4b6..4aa6375d8 100644 --- a/src/qt/receiverequestdialog.cpp +++ b/src/qt/receiverequestdialog.cpp @@ -68,7 +68,7 @@ void QRImageWidget::saveImage() { if(!pixmap()) return; - QString fn = GUIUtil::getSaveFileName(this, tr("Save QR Code"), QString(), tr("PNG Image (*.png)"), NULL); + QString fn = GUIUtil::getSaveFileName(this, tr("Save QR Code"), QString(), tr("PNG Image (*.png)"), nullptr); if (!fn.isEmpty()) { exportImage().save(fn); diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp index 4e88c8802..1c4f7aca8 100644 --- a/src/qt/recentrequeststablemodel.cpp +++ b/src/qt/recentrequeststablemodel.cpp @@ -123,7 +123,7 @@ void RecentRequestsTableModel::updateAmountColumnTitle() /** Gets title for amount column including current display unit if optionsModel reference available. */ QString RecentRequestsTableModel::getAmountTitle() { - return (this->walletModel->getOptionsModel() != NULL) ? tr("Requested") + " ("+BitcoinUnits::name(this->walletModel->getOptionsModel()->getDisplayUnit()) + ")" : ""; + return (this->walletModel->getOptionsModel() != nullptr) ? tr("Requested") + " ("+BitcoinUnits::name(this->walletModel->getOptionsModel()->getDisplayUnit()) + ")" : ""; } QModelIndex RecentRequestsTableModel::index(int row, int column, const QModelIndex &parent) const diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 232068bf4..3590a98ef 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -60,7 +60,7 @@ const struct { {"cmd-reply", ":/icons/tx_output"}, {"cmd-error", ":/icons/tx_output"}, {"misc", ":/icons/tx_inout"}, - {NULL, NULL} + {nullptr, nullptr} }; namespace { @@ -532,7 +532,7 @@ void RPCConsole::setClientModel(ClientModel *model) setNumConnections(model->getNumConnections()); connect(model, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int))); - setNumBlocks(model->getNumBlocks(), model->getLastBlockDate(), model->getVerificationProgress(NULL), false); + setNumBlocks(model->getNumBlocks(), model->getLastBlockDate(), model->getVerificationProgress(nullptr), false); connect(model, SIGNAL(numBlocksChanged(int,QDateTime,double,bool)), this, SLOT(setNumBlocks(int,QDateTime,double,bool))); updateNetworkState(); @@ -982,7 +982,7 @@ void RPCConsole::peerLayoutChanged() if (!clientModel || !clientModel->getPeerTableModel()) return; - const CNodeCombinedStats *stats = NULL; + const CNodeCombinedStats *stats = nullptr; bool fUnselect = false; bool fReselect = false; diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index ec531c99c..da06818f8 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -36,8 +36,8 @@ class RPCConsole: public QWidget explicit RPCConsole(const PlatformStyle *platformStyle, QWidget *parent); ~RPCConsole(); - static bool RPCParseCommandLine(std::string &strResult, const std::string &strCommand, bool fExecute, std::string * const pstrFilteredOut = NULL); - static bool RPCExecuteCommandLine(std::string &strResult, const std::string &strCommand, std::string * const pstrFilteredOut = NULL) { + static bool RPCParseCommandLine(std::string &strResult, const std::string &strCommand, bool fExecute, std::string * const pstrFilteredOut = nullptr); + static bool RPCExecuteCommandLine(std::string &strResult, const std::string &strCommand, std::string * const pstrFilteredOut = nullptr) { return RPCParseCommandLine(strResult, strCommand, true, pstrFilteredOut); } diff --git a/src/qt/test/paymentservertests.cpp b/src/qt/test/paymentservertests.cpp index b9a8ad6e2..d9fb86982 100644 --- a/src/qt/test/paymentservertests.cpp +++ b/src/qt/test/paymentservertests.cpp @@ -25,7 +25,7 @@ X509 *parse_b64der_cert(const char* cert_data) std::vector data = DecodeBase64(cert_data); assert(data.size() > 0); const unsigned char* dptr = &data[0]; - X509 *cert = d2i_X509(NULL, &dptr, data.size()); + X509 *cert = d2i_X509(nullptr, &dptr, data.size()); assert(cert); return cert; } @@ -66,7 +66,7 @@ void PaymentServerTests::paymentServerTests() { SelectParams(CBaseChainParams::MAIN); OptionsModel optionsModel; - PaymentServer* server = new PaymentServer(NULL, false); + PaymentServer* server = new PaymentServer(nullptr, false); X509_STORE* caStore = X509_STORE_new(); X509_STORE_add_cert(caStore, parse_b64der_cert(caCert1_BASE64)); PaymentServer::LoadRootCAs(caStore); diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 03fd734e9..2ece0d0f2 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -167,7 +167,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) // Determine transaction status // Find the block the tx is in - CBlockIndex* pindex = NULL; + CBlockIndex* pindex = nullptr; BlockMap::iterator mi = mapBlockIndex.find(wtx.hashBlock); if (mi != mapBlockIndex.end()) pindex = (*mi).second; diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 43d6e8826..53c38da9d 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -343,7 +343,7 @@ void TransactionView::exportClicked() // CSV is currently the only supported format QString filename = GUIUtil::getSaveFileName(this, tr("Export Transaction History"), QString(), - tr("Comma separated file (*.csv)"), NULL); + tr("Comma separated file (*.csv)"), nullptr); if (filename.isNull()) return; diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 5258dc669..6be36a57e 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -129,7 +129,7 @@ class WalletModel : public QObject TransactionTableModel *getTransactionTableModel(); RecentRequestsTableModel *getRecentRequestsTableModel(); - CAmount getBalance(const CCoinControl *coinControl = NULL) const; + CAmount getBalance(const CCoinControl *coinControl = nullptr) const; CAmount getUnconfirmedBalance() const; CAmount getImmatureBalance() const; bool haveWatchOnly() const; diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index 4a18c0bd4..971f5e0e1 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -246,7 +246,7 @@ void WalletView::backupWallet() { QString filename = GUIUtil::getSaveFileName(this, tr("Backup Wallet"), QString(), - tr("Wallet Data (*.dat)"), NULL); + tr("Wallet Data (*.dat)"), nullptr); if (filename.isEmpty()) return; diff --git a/src/qt/winshutdownmonitor.cpp b/src/qt/winshutdownmonitor.cpp index d6f40c38b..d78d9a235 100644 --- a/src/qt/winshutdownmonitor.cpp +++ b/src/qt/winshutdownmonitor.cpp @@ -57,7 +57,7 @@ void WinShutdownMonitor::registerShutdownBlockReason(const QString& strReason, c { typedef BOOL (WINAPI *PSHUTDOWNBRCREATE)(HWND, LPCWSTR); PSHUTDOWNBRCREATE shutdownBRCreate = (PSHUTDOWNBRCREATE)GetProcAddress(GetModuleHandleA("User32.dll"), "ShutdownBlockReasonCreate"); - if (shutdownBRCreate == NULL) { + if (shutdownBRCreate == nullptr) { qWarning() << "registerShutdownBlockReason: GetProcAddress for ShutdownBlockReasonCreate failed"; return; } diff --git a/src/random.cpp b/src/random.cpp index b308e8f4a..3a48c72b6 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -152,7 +152,7 @@ static void RandAddSeedPerfmon() const size_t nMaxSize = 10000000; // Bail out at more than 10MB of performance data while (true) { nSize = vData.size(); - ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", NULL, NULL, vData.data(), &nSize); + ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", nullptr, nullptr, vData.data(), &nSize); if (ret != ERROR_MORE_DATA || vData.size() >= nMaxSize) break; vData.resize(std::max((vData.size() * 3) / 2, nMaxSize)); // Grow size of buffer exponentially @@ -200,7 +200,7 @@ void GetOSRand(unsigned char *ent32) { #if defined(WIN32) HCRYPTPROV hProvider; - int ret = CryptAcquireContextW(&hProvider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); + int ret = CryptAcquireContextW(&hProvider, nullptr, nullptr, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); if (!ret) { RandFailure(); } @@ -245,7 +245,7 @@ void GetOSRand(unsigned char *ent32) int have = 0; do { size_t len = NUM_OS_RANDOM_BYTES - have; - if (sysctl(name, ARRAYLEN(name), ent32 + have, &len, NULL, 0) != 0) { + if (sysctl(name, ARRAYLEN(name), ent32 + have, &len, nullptr, 0) != 0) { RandFailure(); } have += len; diff --git a/src/rest.cpp b/src/rest.cpp index 33e3fb452..6a4b005f9 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -134,7 +134,7 @@ static bool rest_headers(HTTPRequest* req, if (path.size() != 2) return RESTERR(req, HTTP_BAD_REQUEST, "No header count specified. Use /rest/headers//.."); - long count = strtol(path[0].c_str(), NULL, 10); + long count = strtol(path[0].c_str(), nullptr, 10); if (count < 1 || count > 2000) return RESTERR(req, HTTP_BAD_REQUEST, "Header count out of range: " + path[0]); @@ -148,8 +148,8 @@ static bool rest_headers(HTTPRequest* req, { LOCK(cs_main); BlockMap::const_iterator it = mapBlockIndex.find(hash); - const CBlockIndex *pindex = (it != mapBlockIndex.end()) ? it->second : NULL; - while (pindex != NULL && chainActive.Contains(pindex)) { + const CBlockIndex *pindex = (it != mapBlockIndex.end()) ? it->second : nullptr; + while (pindex != nullptr && chainActive.Contains(pindex)) { headers.push_back(pindex); if (headers.size() == (unsigned long)count) break; @@ -209,7 +209,7 @@ static bool rest_block(HTTPRequest* req, return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr); CBlock block; - CBlockIndex* pblockindex = NULL; + CBlockIndex* pblockindex = nullptr; { LOCK(cs_main); if (mapBlockIndex.count(hash) == 0) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index d65e107e3..6975d26c6 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -48,9 +48,9 @@ extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& double GetDifficulty(const CBlockIndex* blockindex) { - if (blockindex == NULL) + if (blockindex == nullptr) { - if (chainActive.Tip() == NULL) + if (chainActive.Tip() == nullptr) return 1.0; else blockindex = chainActive.Tip(); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 7d292a463..f498b5c8e 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -52,7 +52,7 @@ UniValue GetNetworkHashPS(int lookup, int height) { if (height >= 0 && height < chainActive.Height()) pb = chainActive[height]; - if (pb == NULL || !pb->nHeight) + if (pb == nullptr || !pb->nHeight) return 0; // If lookup is -1, then use blocks since last difficulty change. @@ -139,7 +139,7 @@ UniValue generateBlocks(std::shared_ptr coinbaseScript, int nGen continue; } std::shared_ptr shared_pblock = std::make_shared(*pblock); - if (!ProcessNewBlock(Params(), shared_pblock, true, NULL)) + if (!ProcessNewBlock(Params(), shared_pblock, true, nullptr)) throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted"); ++nHeight; blockHashes.push_back(pblock->GetHash().GetHex()); @@ -754,7 +754,7 @@ UniValue submitblock(const JSONRPCRequest& request) submitblock_StateCatcher sc(block.GetHash()); RegisterValidationInterface(&sc); - bool fAccepted = ProcessNewBlock(Params(), blockptr, true, NULL); + bool fAccepted = ProcessNewBlock(Params(), blockptr, true, nullptr); UnregisterValidationInterface(&sc); if (fBlockPresent) { if (fAccepted && !sc.found) { diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index f3c86038a..08920915a 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -77,7 +77,7 @@ UniValue getinfo(const JSONRPCRequest& request) #ifdef ENABLE_WALLET CWallet * const pwallet = GetWalletForJSONRPCRequest(request); - LOCK2(cs_main, pwallet ? &pwallet->cs_wallet : NULL); + LOCK2(cs_main, pwallet ? &pwallet->cs_wallet : nullptr); #else LOCK(cs_main); #endif @@ -201,7 +201,7 @@ UniValue validateaddress(const JSONRPCRequest& request) #ifdef ENABLE_WALLET CWallet * const pwallet = GetWalletForJSONRPCRequest(request); - LOCK2(cs_main, pwallet ? &pwallet->cs_wallet : NULL); + LOCK2(cs_main, pwallet ? &pwallet->cs_wallet : nullptr); #else LOCK(cs_main); #endif @@ -321,7 +321,7 @@ UniValue createmultisig(const JSONRPCRequest& request) #ifdef ENABLE_WALLET CWallet * const pwallet = GetWalletForJSONRPCRequest(request); #else - CWallet * const pwallet = NULL; + CWallet * const pwallet = nullptr; #endif if (request.fHelp || request.params.size() < 2 || request.params.size() > 2) diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index b4d6795e6..e890f0ddc 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -216,7 +216,7 @@ UniValue addnode(const JSONRPCRequest& request) if (strCommand == "onetry") { CAddress addr; - g_connman->OpenNetworkConnection(addr, false, NULL, strNode.c_str()); + g_connman->OpenNetworkConnection(addr, false, nullptr, strNode.c_str()); return NullUniValue; } diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index b967f2dbf..810051185 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -208,7 +208,7 @@ UniValue gettxoutproof(const JSONRPCRequest& request) LOCK(cs_main); - CBlockIndex* pblockindex = NULL; + CBlockIndex* pblockindex = nullptr; uint256 hashBlock; if (!request.params[1].isNull()) @@ -228,7 +228,7 @@ UniValue gettxoutproof(const JSONRPCRequest& request) } } - if (pblockindex == NULL) + if (pblockindex == nullptr) { CTransactionRef tx; if (!GetTransaction(oneTxid, tx, Params().GetConsensus(), hashBlock, false) || hashBlock.IsNull()) @@ -707,7 +707,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request) ); #ifdef ENABLE_WALLET - LOCK2(cs_main, pwallet ? &pwallet->cs_wallet : NULL); + LOCK2(cs_main, pwallet ? &pwallet->cs_wallet : nullptr); #else LOCK(cs_main); #endif @@ -937,7 +937,7 @@ UniValue sendrawtransaction(const JSONRPCRequest& request) CValidationState state; bool fMissingInputs; bool fLimitFree = true; - if (!AcceptToMemoryPool(mempool, state, std::move(tx), fLimitFree, &fMissingInputs, NULL, false, nMaxRawTxFee)) { + if (!AcceptToMemoryPool(mempool, state, std::move(tx), fLimitFree, &fMissingInputs, nullptr, 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 63e4e9c63..c3c9d10e2 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -30,7 +30,7 @@ static bool fRPCInWarmup = true; static std::string rpcWarmupStatus("RPC server started"); static CCriticalSection cs_rpcWarmup; /* Timer-creating functions */ -static RPCTimerInterface* timerInterface = NULL; +static RPCTimerInterface* timerInterface = nullptr; /* Map of name to timer. */ static std::map > deadlineTimers; @@ -301,7 +301,7 @@ const CRPCCommand *CRPCTable::operator[](const std::string &name) const { std::map::const_iterator it = mapCommands.find(name); if (it == mapCommands.end()) - return NULL; + return nullptr; return (*it).second; } @@ -547,7 +547,7 @@ void RPCSetTimerInterface(RPCTimerInterface *iface) void RPCUnsetTimerInterface(RPCTimerInterface *iface) { if (timerInterface == iface) - timerInterface = NULL; + timerInterface = nullptr; } void RPCRunLater(const std::string& name, std::function func, int64_t nSeconds) diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index 4b71a42cd..03128917f 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -28,10 +28,10 @@ class TxInputStream if (nSize > m_remaining) throw std::ios_base::failure(std::string(__func__) + ": end of data"); - if (pch == NULL) + if (pch == nullptr) throw std::ios_base::failure(std::string(__func__) + ": bad destination buffer"); - if (m_data == NULL) + if (m_data == nullptr) throw std::ios_base::failure(std::string(__func__) + ": bad source buffer"); memcpy(pch, m_data, nSize); @@ -95,7 +95,7 @@ static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptP set_error(err, bitcoinconsensus_ERR_OK); PrecomputedTransactionData txdata(tx); - return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), &tx.vin[nIn].scriptWitness, flags, TransactionSignatureChecker(&tx, nIn, amount, txdata), NULL); + return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), &tx.vin[nIn].scriptWitness, flags, TransactionSignatureChecker(&tx, nIn, amount, txdata), nullptr); } catch (const std::exception&) { return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing } diff --git a/src/script/bitcoinconsensus.h b/src/script/bitcoinconsensus.h index 1bef4fe9e..33bf80e5a 100644 --- a/src/script/bitcoinconsensus.h +++ b/src/script/bitcoinconsensus.h @@ -63,7 +63,7 @@ enum /// Returns 1 if the input nIn of the serialized transaction pointed to by /// txTo correctly spends the scriptPubKey pointed to by scriptPubKey under /// the additional constraints specified by flags. -/// If not NULL, err will contain an error/success code for the operation +/// If not nullptr, err will contain an error/success code for the operation EXPORT_SYMBOL int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, const unsigned char *txTo , unsigned int txToLen, unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err); diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 8a121774a..f9716dfc6 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1407,7 +1407,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror) { static const CScriptWitness emptyWitness; - if (witness == NULL) { + if (witness == nullptr) { witness = &emptyWitness; } bool hadWitness = false; diff --git a/src/script/interpreter.h b/src/script/interpreter.h index ab1dc4e68..437826b5d 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -123,7 +123,7 @@ enum SigVersion SIGVERSION_WITNESS_V0 = 1, }; -uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = NULL); +uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = nullptr); class BaseSignatureChecker { @@ -158,7 +158,7 @@ class TransactionSignatureChecker : public BaseSignatureChecker virtual bool VerifySignature(const std::vector& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const; public: - TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(NULL) {} + TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(nullptr) {} TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {} bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override; bool CheckLockTime(const CScriptNum& nLockTime) const override; @@ -174,8 +174,8 @@ class MutableTransactionSignatureChecker : public TransactionSignatureChecker MutableTransactionSignatureChecker(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : TransactionSignatureChecker(&txTo, nInIn, amountIn), txTo(*txToIn) {} }; -bool EvalScript(std::vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* error = NULL); -bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror = NULL); +bool EvalScript(std::vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* error = nullptr); +bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror = nullptr); size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags); diff --git a/src/script/script.h b/src/script/script.h index d16bfd0e0..711ffa97f 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -498,7 +498,7 @@ class CScript : public CScriptBase bool GetOp(iterator& pc, opcodetype& opcodeRet) { const_iterator pc2 = pc; - bool fRet = GetOp2(pc2, opcodeRet, NULL); + bool fRet = GetOp2(pc2, opcodeRet, nullptr); pc = begin() + (pc2 - begin()); return fRet; } @@ -510,7 +510,7 @@ class CScript : public CScriptBase bool GetOp(const_iterator& pc, opcodetype& opcodeRet) const { - return GetOp2(pc, opcodeRet, NULL); + return GetOp2(pc, opcodeRet, nullptr); } bool GetOp2(const_iterator& pc, opcodetype& opcodeRet, std::vector* pvchRet) const diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 760a5305e..9570b8c8d 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -31,7 +31,7 @@ const char* GetTxnOutputType(txnouttype t) case TX_WITNESS_V0_KEYHASH: return "witness_v0_keyhash"; case TX_WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash"; } - return NULL; + return nullptr; } /** diff --git a/src/streams.h b/src/streams.h index 245fb9cd8..a9668b68b 100644 --- a/src/streams.h +++ b/src/streams.h @@ -479,7 +479,7 @@ class CAutoFile { if (file) { ::fclose(file); - file = NULL; + file = nullptr; } } @@ -487,7 +487,7 @@ class CAutoFile * @note This will invalidate the CAutoFile object, and makes it the responsibility of the caller * of this function to clean up the returned FILE*. */ - FILE* release() { FILE* ret = file; file = NULL; return ret; } + FILE* release() { FILE* ret = file; file = nullptr; return ret; } /** Get wrapped FILE* without transfer of ownership. * @note Ownership of the FILE* will remain with this class. Use this only if the scope of the @@ -495,9 +495,9 @@ class CAutoFile */ FILE* Get() const { return file; } - /** Return true if the wrapped FILE* is NULL, false otherwise. + /** Return true if the wrapped FILE* is nullptr, false otherwise. */ - bool IsNull() const { return (file == NULL); } + bool IsNull() const { return (file == nullptr); } // // Stream subset @@ -508,7 +508,7 @@ class CAutoFile void read(char* pch, size_t nSize) { if (!file) - throw std::ios_base::failure("CAutoFile::read: file handle is NULL"); + throw std::ios_base::failure("CAutoFile::read: file handle is nullptr"); if (fread(pch, 1, nSize, file) != nSize) throw std::ios_base::failure(feof(file) ? "CAutoFile::read: end of file" : "CAutoFile::read: fread failed"); } @@ -516,7 +516,7 @@ class CAutoFile void ignore(size_t nSize) { if (!file) - throw std::ios_base::failure("CAutoFile::ignore: file handle is NULL"); + throw std::ios_base::failure("CAutoFile::ignore: file handle is nullptr"); unsigned char data[4096]; while (nSize > 0) { size_t nNow = std::min(nSize, sizeof(data)); @@ -529,7 +529,7 @@ class CAutoFile void write(const char* pch, size_t nSize) { if (!file) - throw std::ios_base::failure("CAutoFile::write: file handle is NULL"); + throw std::ios_base::failure("CAutoFile::write: file handle is nullptr"); if (fwrite(pch, 1, nSize, file) != nSize) throw std::ios_base::failure("CAutoFile::write: write failed"); } @@ -539,7 +539,7 @@ class CAutoFile { // Serialize to this stream if (!file) - throw std::ios_base::failure("CAutoFile::operator<<: file handle is NULL"); + throw std::ios_base::failure("CAutoFile::operator<<: file handle is nullptr"); ::Serialize(*this, obj); return (*this); } @@ -549,7 +549,7 @@ class CAutoFile { // Unserialize from this stream if (!file) - throw std::ios_base::failure("CAutoFile::operator>>: file handle is NULL"); + throw std::ios_base::failure("CAutoFile::operator>>: file handle is nullptr"); ::Unserialize(*this, obj); return (*this); } @@ -616,7 +616,7 @@ class CBufferedFile { if (src) { ::fclose(src); - src = NULL; + src = nullptr; } } diff --git a/src/support/allocators/secure.h b/src/support/allocators/secure.h index 9daba86ef..f20f42494 100644 --- a/src/support/allocators/secure.h +++ b/src/support/allocators/secure.h @@ -45,7 +45,7 @@ struct secure_allocator : public std::allocator { void deallocate(T* p, std::size_t n) { - if (p != NULL) { + if (p != nullptr) { memory_cleanse(p, sizeof(T) * n); } LockedPoolManager::Instance().free(p); diff --git a/src/support/allocators/zeroafterfree.h b/src/support/allocators/zeroafterfree.h index 28a940ad1..581d5d631 100644 --- a/src/support/allocators/zeroafterfree.h +++ b/src/support/allocators/zeroafterfree.h @@ -36,7 +36,7 @@ struct zero_after_free_allocator : public std::allocator { void deallocate(T* p, std::size_t n) { - if (p != NULL) + if (p != nullptr) memory_cleanse(p, sizeof(T) * n); std::allocator::deallocate(p, n); } diff --git a/src/support/events.h b/src/support/events.h index 90690876e..cc6d29aec 100644 --- a/src/support/events.h +++ b/src/support/events.h @@ -47,7 +47,7 @@ inline raii_evhttp_request obtain_evhttp_request(void(*cb)(struct evhttp_request } inline raii_evhttp_connection obtain_evhttp_connection_base(struct event_base* base, std::string host, uint16_t port) { - auto result = raii_evhttp_connection(evhttp_connection_base_new(base, NULL, host.c_str(), port)); + auto result = raii_evhttp_connection(evhttp_connection_base_new(base, nullptr, host.c_str(), port)); if (!result.get()) throw std::runtime_error("create connection failed"); return result; diff --git a/src/support/lockedpool.cpp b/src/support/lockedpool.cpp index 2df6b84a5..2ead72185 100644 --- a/src/support/lockedpool.cpp +++ b/src/support/lockedpool.cpp @@ -28,7 +28,7 @@ #include -LockedPoolManager* LockedPoolManager::_instance = NULL; +LockedPoolManager* LockedPoolManager::_instance = nullptr; std::once_flag LockedPoolManager::init_flag; /*******************************************************************************/ @@ -87,7 +87,7 @@ template bool extend(Iterator it, const Pair& other void Arena::free(void *ptr) { - // Freeing the NULL pointer is OK. + // Freeing the nullptr pointer is OK. if (ptr == nullptr) { return; } diff --git a/src/sync.cpp b/src/sync.cpp index c359e8220..b82f3770e 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -100,7 +100,7 @@ static void potential_deadlock_detected(const std::pair& mismatch, static void push_lock(void* c, const CLockLocation& locklocation, bool fTry) { - if (lockstack.get() == NULL) + if (lockstack.get() == nullptr) lockstack.reset(new LockStack); boost::unique_lock lock(lockdata.dd_mutex); diff --git a/src/sync.h b/src/sync.h index 20974f5fb..4921aedf3 100644 --- a/src/sync.h +++ b/src/sync.h @@ -265,7 +265,7 @@ class CSemaphoreGrant fHaveGrant = false; } - CSemaphoreGrant() : sem(NULL), fHaveGrant(false) {} + CSemaphoreGrant() : sem(nullptr), fHaveGrant(false) {} CSemaphoreGrant(CSemaphore& sema, bool fTry = false) : sem(&sema), fHaveGrant(false) { diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp index bc6aef2c1..067a9c6ff 100644 --- a/src/test/addrman_tests.cpp +++ b/src/test/addrman_tests.cpp @@ -33,12 +33,12 @@ class CAddrManTest : public CAddrMan return (unsigned int)(state % nMax); } - CAddrInfo* Find(const CNetAddr& addr, int* pnId = NULL) + CAddrInfo* Find(const CNetAddr& addr, int* pnId = nullptr) { return CAddrMan::Find(addr, pnId); } - CAddrInfo* Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId = NULL) + CAddrInfo* Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId = nullptr) { return CAddrMan::Create(addr, addrSource, pnId); } @@ -359,7 +359,7 @@ BOOST_AUTO_TEST_CASE(addrman_delete) addrman.Delete(nId); BOOST_CHECK_EQUAL(addrman.size(), 0); CAddrInfo* info2 = addrman.Find(addr1); - BOOST_CHECK(info2 == NULL); + BOOST_CHECK(info2 == nullptr); } BOOST_AUTO_TEST_CASE(addrman_getaddr) diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index eaff2ac70..9fa9a8509 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -236,7 +236,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) pblock->hashMerkleRoot = BlockMerkleRoot(*pblock); pblock->nNonce = blockinfo[i].nonce; std::shared_ptr shared_pblock = std::make_shared(*pblock); - BOOST_CHECK(ProcessNewBlock(chainparams, shared_pblock, true, NULL)); + BOOST_CHECK(ProcessNewBlock(chainparams, shared_pblock, true, nullptr)); pblock->hashPrevBlock = pblock->GetHash(); } diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index c686f679c..5e89ef60d 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -79,20 +79,20 @@ BOOST_AUTO_TEST_CASE(multisig_verify) keys.assign(1,key[0]); keys.push_back(key[1]); s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK(VerifyScript(s, a_and_b, NULL, flags, MutableTransactionSignatureChecker(&txTo[0], 0, amount), &err)); + BOOST_CHECK(VerifyScript(s, a_and_b, nullptr, flags, MutableTransactionSignatureChecker(&txTo[0], 0, amount), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); for (int i = 0; i < 4; i++) { keys.assign(1,key[i]); s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, NULL, flags, MutableTransactionSignatureChecker(&txTo[0], 0, amount), &err), strprintf("a&b 1: %d", i)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, nullptr, flags, MutableTransactionSignatureChecker(&txTo[0], 0, amount), &err), strprintf("a&b 1: %d", i)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_INVALID_STACK_OPERATION, ScriptErrorString(err)); keys.assign(1,key[1]); keys.push_back(key[i]); s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, NULL, flags, MutableTransactionSignatureChecker(&txTo[0], 0, amount), &err), strprintf("a&b 2: %d", i)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, nullptr, flags, MutableTransactionSignatureChecker(&txTo[0], 0, amount), &err), strprintf("a&b 2: %d", i)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); } @@ -103,18 +103,18 @@ BOOST_AUTO_TEST_CASE(multisig_verify) s = sign_multisig(a_or_b, keys, txTo[1], 0); if (i == 0 || i == 1) { - BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, NULL, flags, MutableTransactionSignatureChecker(&txTo[1], 0, amount), &err), strprintf("a|b: %d", i)); + BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, nullptr, flags, MutableTransactionSignatureChecker(&txTo[1], 0, amount), &err), strprintf("a|b: %d", i)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); } else { - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, NULL, flags, MutableTransactionSignatureChecker(&txTo[1], 0, amount), &err), strprintf("a|b: %d", i)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, nullptr, flags, MutableTransactionSignatureChecker(&txTo[1], 0, amount), &err), strprintf("a|b: %d", i)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); } } s.clear(); s << OP_0 << OP_1; - BOOST_CHECK(!VerifyScript(s, a_or_b, NULL, flags, MutableTransactionSignatureChecker(&txTo[1], 0, amount), &err)); + BOOST_CHECK(!VerifyScript(s, a_or_b, nullptr, flags, MutableTransactionSignatureChecker(&txTo[1], 0, amount), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_SIG_DER, ScriptErrorString(err)); @@ -126,12 +126,12 @@ BOOST_AUTO_TEST_CASE(multisig_verify) s = sign_multisig(escrow, keys, txTo[2], 0); if (i < j && i < 3 && j < 3) { - BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, NULL, flags, MutableTransactionSignatureChecker(&txTo[2], 0, amount), &err), strprintf("escrow 1: %d %d", i, j)); + BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, nullptr, flags, MutableTransactionSignatureChecker(&txTo[2], 0, amount), &err), strprintf("escrow 1: %d %d", i, j)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); } else { - BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, NULL, flags, MutableTransactionSignatureChecker(&txTo[2], 0, amount), &err), strprintf("escrow 2: %d %d", i, j)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, nullptr, flags, MutableTransactionSignatureChecker(&txTo[2], 0, amount), &err), strprintf("escrow 2: %d %d", i, j)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); } } diff --git a/src/test/pow_tests.cpp b/src/test/pow_tests.cpp index b9fabd02e..b13f2625a 100644 --- a/src/test/pow_tests.cpp +++ b/src/test/pow_tests.cpp @@ -66,7 +66,7 @@ BOOST_AUTO_TEST_CASE(GetBlockProofEquivalentTime_test) const auto chainParams = CreateChainParams(CBaseChainParams::MAIN); std::vector blocks(10000); for (int i = 0; i < 10000; i++) { - blocks[i].pprev = i ? &blocks[i - 1] : NULL; + blocks[i].pprev = i ? &blocks[i - 1] : nullptr; blocks[i].nHeight = i; blocks[i].nTime = 1269211443 + i * chainParams->GetConsensus().nPowTargetSpacing; blocks[i].nBits = 0x207fffff; /* target 0x7fffff000... */ diff --git a/src/test/raii_event_tests.cpp b/src/test/raii_event_tests.cpp index 0f40874f5..0d541ec7d 100644 --- a/src/test/raii_event_tests.cpp +++ b/src/test/raii_event_tests.cpp @@ -42,7 +42,7 @@ BOOST_AUTO_TEST_CASE(raii_event_creation) { event_set_mem_functions(tag_malloc, realloc, tag_free); - void* base_ptr = NULL; + void* base_ptr = nullptr; { auto base = obtain_event_base(); base_ptr = (void*)base.get(); @@ -50,10 +50,10 @@ BOOST_AUTO_TEST_CASE(raii_event_creation) } BOOST_CHECK(tags[base_ptr] == 0); - void* event_ptr = NULL; + void* event_ptr = nullptr; { auto base = obtain_event_base(); - auto event = obtain_event(base.get(), -1, 0, NULL, NULL); + auto event = obtain_event(base.get(), -1, 0, nullptr, nullptr); base_ptr = (void*)base.get(); event_ptr = (void*)event.get(); @@ -71,11 +71,11 @@ BOOST_AUTO_TEST_CASE(raii_event_order) { event_set_mem_functions(tag_malloc, realloc, tag_free); - void* base_ptr = NULL; - void* event_ptr = NULL; + void* base_ptr = nullptr; + void* event_ptr = nullptr; { auto base = obtain_event_base(); - auto event = obtain_event(base.get(), -1, 0, NULL, NULL); + auto event = obtain_event(base.get(), -1, 0, nullptr, nullptr); base_ptr = (void*)base.get(); event_ptr = (void*)event.get(); diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index 0789b2e80..efd0f77d9 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -42,7 +42,7 @@ Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict, Scri txTo.vin[0].scriptSig = scriptSig; txTo.vout[0].nValue = 1; - return VerifyScript(scriptSig, scriptPubKey, NULL, fStrict ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, MutableTransactionSignatureChecker(&txTo, 0, txFrom.vout[0].nValue), &err); + return VerifyScript(scriptSig, scriptPubKey, nullptr, fStrict ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, MutableTransactionSignatureChecker(&txTo, 0, txFrom.vout[0].nValue), &err); } diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index a18471588..06b8274f2 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -172,10 +172,10 @@ void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, const CScript int libconsensus_flags = flags & bitcoinconsensus_SCRIPT_FLAGS_VERIFY_ALL; if (libconsensus_flags == flags) { if (flags & bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS) { - BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(scriptPubKey.data(), scriptPubKey.size(), txCredit.vout[0].nValue, (const unsigned char*)&stream[0], stream.size(), 0, libconsensus_flags, NULL) == expect, message); + BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(scriptPubKey.data(), scriptPubKey.size(), txCredit.vout[0].nValue, (const unsigned char*)&stream[0], stream.size(), 0, libconsensus_flags, nullptr) == expect, message); } else { - BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(scriptPubKey.data(), scriptPubKey.size(), 0, (const unsigned char*)&stream[0], stream.size(), 0, libconsensus_flags, NULL) == expect, message); - BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size(), 0, libconsensus_flags, NULL) == expect,message); + BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(scriptPubKey.data(), scriptPubKey.size(), 0, (const unsigned char*)&stream[0], stream.size(), 0, libconsensus_flags, nullptr) == expect, message); + BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size(), 0, libconsensus_flags, nullptr) == expect,message); } } #endif @@ -1064,18 +1064,18 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12) CMutableTransaction txTo12 = BuildSpendingTransaction(CScript(), CScriptWitness(), txFrom12); CScript goodsig1 = sign_multisig(scriptPubKey12, key1, txTo12); - BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, NULL, gFlags, MutableTransactionSignatureChecker(&txTo12, 0, txFrom12.vout[0].nValue), &err)); + BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo12, 0, txFrom12.vout[0].nValue), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); txTo12.vout[0].nValue = 2; - BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, NULL, gFlags, MutableTransactionSignatureChecker(&txTo12, 0, txFrom12.vout[0].nValue), &err)); + BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo12, 0, txFrom12.vout[0].nValue), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); CScript goodsig2 = sign_multisig(scriptPubKey12, key2, txTo12); - BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, NULL, gFlags, MutableTransactionSignatureChecker(&txTo12, 0, txFrom12.vout[0].nValue), &err)); + BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo12, 0, txFrom12.vout[0].nValue), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); CScript badsig1 = sign_multisig(scriptPubKey12, key3, txTo12); - BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, NULL, gFlags, MutableTransactionSignatureChecker(&txTo12, 0, txFrom12.vout[0].nValue), &err)); + BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo12, 0, txFrom12.vout[0].nValue), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); } @@ -1097,54 +1097,54 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23) std::vector keys; keys.push_back(key1); keys.push_back(key2); CScript goodsig1 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, NULL, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err)); + BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); keys.clear(); keys.push_back(key1); keys.push_back(key3); CScript goodsig2 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, NULL, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err)); + BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); keys.clear(); keys.push_back(key2); keys.push_back(key3); CScript goodsig3 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, NULL, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err)); + BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); keys.clear(); keys.push_back(key2); keys.push_back(key2); // Can't re-use sig CScript badsig1 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, NULL, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err)); + BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); keys.clear(); keys.push_back(key2); keys.push_back(key1); // sigs must be in correct order CScript badsig2 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, NULL, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err)); + BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); keys.clear(); keys.push_back(key3); keys.push_back(key2); // sigs must be in correct order CScript badsig3 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, NULL, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err)); + BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); keys.clear(); keys.push_back(key4); keys.push_back(key2); // sigs must match pubkeys CScript badsig4 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, NULL, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err)); + BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); keys.clear(); keys.push_back(key1); keys.push_back(key4); // sigs must match pubkeys CScript badsig5 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, NULL, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err)); + BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); keys.clear(); // Must have signatures CScript badsig6 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, NULL, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err)); + BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_INVALID_STACK_OPERATION, ScriptErrorString(err)); } @@ -1265,7 +1265,7 @@ BOOST_AUTO_TEST_CASE(script_standard_push) CScript script; script << i; BOOST_CHECK_MESSAGE(script.IsPushOnly(), "Number " << i << " is not pure push."); - BOOST_CHECK_MESSAGE(VerifyScript(script, CScript() << OP_1, NULL, SCRIPT_VERIFY_MINIMALDATA, BaseSignatureChecker(), &err), "Number " << i << " push is not minimal data."); + BOOST_CHECK_MESSAGE(VerifyScript(script, CScript() << OP_1, nullptr, SCRIPT_VERIFY_MINIMALDATA, BaseSignatureChecker(), &err), "Number " << i << " push is not minimal data."); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); } @@ -1274,7 +1274,7 @@ BOOST_AUTO_TEST_CASE(script_standard_push) CScript script; script << data; BOOST_CHECK_MESSAGE(script.IsPushOnly(), "Length " << i << " is not pure push."); - BOOST_CHECK_MESSAGE(VerifyScript(script, CScript() << OP_1, NULL, SCRIPT_VERIFY_MINIMALDATA, BaseSignatureChecker(), &err), "Length " << i << " push is not minimal data."); + BOOST_CHECK_MESSAGE(VerifyScript(script, CScript() << OP_1, nullptr, SCRIPT_VERIFY_MINIMALDATA, BaseSignatureChecker(), &err), "Length " << i << " push is not minimal data."); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); } } diff --git a/src/test/skiplist_tests.cpp b/src/test/skiplist_tests.cpp index 77c321cdf..e3654e67a 100644 --- a/src/test/skiplist_tests.cpp +++ b/src/test/skiplist_tests.cpp @@ -20,7 +20,7 @@ BOOST_AUTO_TEST_CASE(skiplist_test) for (int i=0; inHeight]); BOOST_CHECK(vIndex[i].pskip->nHeight < i); } else { - BOOST_CHECK(vIndex[i].pskip == NULL); + BOOST_CHECK(vIndex[i].pskip == nullptr); } } @@ -51,11 +51,11 @@ BOOST_AUTO_TEST_CASE(getlocator_test) for (unsigned int i=0; inHeight + 1); + BOOST_CHECK(vBlocksMain[i].pprev == nullptr || vBlocksMain[i].nHeight == vBlocksMain[i].pprev->nHeight + 1); } // Build a branch that splits off at block 49999, 50000 blocks long. @@ -68,7 +68,7 @@ BOOST_AUTO_TEST_CASE(getlocator_test) vBlocksSide[i].phashBlock = &vHashSide[i]; vBlocksSide[i].BuildSkip(); BOOST_CHECK_EQUAL((int)UintToArith256(vBlocksSide[i].GetBlockHash()).GetLow64(), vBlocksSide[i].nHeight); - BOOST_CHECK(vBlocksSide[i].pprev == NULL || vBlocksSide[i].nHeight == vBlocksSide[i].pprev->nHeight + 1); + BOOST_CHECK(vBlocksSide[i].pprev == nullptr || vBlocksSide[i].nHeight == vBlocksSide[i].pprev->nHeight + 1); } // Build a CChain for the main branch. @@ -106,7 +106,7 @@ BOOST_AUTO_TEST_CASE(findearliestatleast_test) for (unsigned int i=0; inTimeMax >= test_time); - BOOST_CHECK((ret->pprev==NULL) || ret->pprev->nTimeMax < test_time); + BOOST_CHECK((ret->pprev==nullptr) || ret->pprev->nTimeMax < test_time); BOOST_CHECK(vBlocksMain[r].GetAncestor(ret->nHeight) == ret); } } diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 545e56983..a64fb6903 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -140,7 +140,7 @@ TestChain100Setup::CreateAndProcessBlock(const std::vector& while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce; std::shared_ptr shared_pblock = std::make_shared(block); - ProcessNewBlock(chainparams, shared_pblock, true, NULL); + ProcessNewBlock(chainparams, shared_pblock, true, nullptr); CBlock result = block; return result; diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp index f609cb1af..2d25cb96c 100644 --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -29,7 +29,7 @@ ToMemPool(CMutableTransaction& tx) LOCK(cs_main); CValidationState state; - return AcceptToMemoryPool(mempool, state, MakeTransactionRef(tx), false, NULL, NULL, true, 0); + return AcceptToMemoryPool(mempool, state, MakeTransactionRef(tx), false, nullptr, nullptr, true, 0); } BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup) diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 5e9dfb730..567908696 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -328,7 +328,7 @@ BOOST_AUTO_TEST_CASE(test_ParseInt32) { int32_t n; // Valid values - BOOST_CHECK(ParseInt32("1234", NULL)); + BOOST_CHECK(ParseInt32("1234", nullptr)); BOOST_CHECK(ParseInt32("0", &n) && n == 0); BOOST_CHECK(ParseInt32("1234", &n) && n == 1234); BOOST_CHECK(ParseInt32("01234", &n) && n == 1234); // no octal @@ -347,17 +347,17 @@ BOOST_AUTO_TEST_CASE(test_ParseInt32) std::string teststr(test_bytes, sizeof(test_bytes)); BOOST_CHECK(!ParseInt32(teststr, &n)); // no embedded NULs // Overflow and underflow - BOOST_CHECK(!ParseInt32("-2147483649", NULL)); - BOOST_CHECK(!ParseInt32("2147483648", NULL)); - BOOST_CHECK(!ParseInt32("-32482348723847471234", NULL)); - BOOST_CHECK(!ParseInt32("32482348723847471234", NULL)); + BOOST_CHECK(!ParseInt32("-2147483649", nullptr)); + BOOST_CHECK(!ParseInt32("2147483648", nullptr)); + BOOST_CHECK(!ParseInt32("-32482348723847471234", nullptr)); + BOOST_CHECK(!ParseInt32("32482348723847471234", nullptr)); } BOOST_AUTO_TEST_CASE(test_ParseInt64) { int64_t n; // Valid values - BOOST_CHECK(ParseInt64("1234", NULL)); + BOOST_CHECK(ParseInt64("1234", nullptr)); BOOST_CHECK(ParseInt64("0", &n) && n == 0LL); BOOST_CHECK(ParseInt64("1234", &n) && n == 1234LL); BOOST_CHECK(ParseInt64("01234", &n) && n == 1234LL); // no octal @@ -377,17 +377,17 @@ BOOST_AUTO_TEST_CASE(test_ParseInt64) std::string teststr(test_bytes, sizeof(test_bytes)); BOOST_CHECK(!ParseInt64(teststr, &n)); // no embedded NULs // Overflow and underflow - BOOST_CHECK(!ParseInt64("-9223372036854775809", NULL)); - BOOST_CHECK(!ParseInt64("9223372036854775808", NULL)); - BOOST_CHECK(!ParseInt64("-32482348723847471234", NULL)); - BOOST_CHECK(!ParseInt64("32482348723847471234", NULL)); + BOOST_CHECK(!ParseInt64("-9223372036854775809", nullptr)); + BOOST_CHECK(!ParseInt64("9223372036854775808", nullptr)); + BOOST_CHECK(!ParseInt64("-32482348723847471234", nullptr)); + BOOST_CHECK(!ParseInt64("32482348723847471234", nullptr)); } BOOST_AUTO_TEST_CASE(test_ParseUInt32) { uint32_t n; // Valid values - BOOST_CHECK(ParseUInt32("1234", NULL)); + BOOST_CHECK(ParseUInt32("1234", nullptr)); BOOST_CHECK(ParseUInt32("0", &n) && n == 0); BOOST_CHECK(ParseUInt32("1234", &n) && n == 1234); BOOST_CHECK(ParseUInt32("01234", &n) && n == 1234); // no octal @@ -410,15 +410,15 @@ BOOST_AUTO_TEST_CASE(test_ParseUInt32) BOOST_CHECK(!ParseUInt32("-2147483648", &n)); BOOST_CHECK(!ParseUInt32("4294967296", &n)); BOOST_CHECK(!ParseUInt32("-1234", &n)); - BOOST_CHECK(!ParseUInt32("-32482348723847471234", NULL)); - BOOST_CHECK(!ParseUInt32("32482348723847471234", NULL)); + BOOST_CHECK(!ParseUInt32("-32482348723847471234", nullptr)); + BOOST_CHECK(!ParseUInt32("32482348723847471234", nullptr)); } BOOST_AUTO_TEST_CASE(test_ParseUInt64) { uint64_t n; // Valid values - BOOST_CHECK(ParseUInt64("1234", NULL)); + BOOST_CHECK(ParseUInt64("1234", nullptr)); BOOST_CHECK(ParseUInt64("0", &n) && n == 0LL); BOOST_CHECK(ParseUInt64("1234", &n) && n == 1234LL); BOOST_CHECK(ParseUInt64("01234", &n) && n == 1234LL); // no octal @@ -438,9 +438,9 @@ BOOST_AUTO_TEST_CASE(test_ParseUInt64) std::string teststr(test_bytes, sizeof(test_bytes)); BOOST_CHECK(!ParseUInt64(teststr, &n)); // no embedded NULs // Overflow and underflow - BOOST_CHECK(!ParseUInt64("-9223372036854775809", NULL)); - BOOST_CHECK(!ParseUInt64("18446744073709551616", NULL)); - BOOST_CHECK(!ParseUInt64("-32482348723847471234", NULL)); + BOOST_CHECK(!ParseUInt64("-9223372036854775809", nullptr)); + BOOST_CHECK(!ParseUInt64("18446744073709551616", nullptr)); + BOOST_CHECK(!ParseUInt64("-32482348723847471234", nullptr)); BOOST_CHECK(!ParseUInt64("-2147483648", &n)); BOOST_CHECK(!ParseUInt64("-9223372036854775808", &n)); BOOST_CHECK(!ParseUInt64("-1234", &n)); @@ -450,7 +450,7 @@ BOOST_AUTO_TEST_CASE(test_ParseDouble) { double n; // Valid values - BOOST_CHECK(ParseDouble("1234", NULL)); + BOOST_CHECK(ParseDouble("1234", nullptr)); BOOST_CHECK(ParseDouble("0", &n) && n == 0.0); BOOST_CHECK(ParseDouble("1234", &n) && n == 1234.0); BOOST_CHECK(ParseDouble("01234", &n) && n == 1234.0); // no octal @@ -470,8 +470,8 @@ BOOST_AUTO_TEST_CASE(test_ParseDouble) std::string teststr(test_bytes, sizeof(test_bytes)); BOOST_CHECK(!ParseDouble(teststr, &n)); // no embedded NULs // Overflow and underflow - BOOST_CHECK(!ParseDouble("-1e10000", NULL)); - BOOST_CHECK(!ParseDouble("1e10000", NULL)); + BOOST_CHECK(!ParseDouble("-1e10000", nullptr)); + BOOST_CHECK(!ParseDouble("1e10000", nullptr)); } BOOST_AUTO_TEST_CASE(test_FormatParagraph) diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp index 722f6ae05..f433aad88 100644 --- a/src/test/versionbits_tests.cpp +++ b/src/test/versionbits_tests.cpp @@ -69,7 +69,7 @@ class VersionBitsTester while (vpblock.size() < height) { CBlockIndex* pindex = new CBlockIndex(); pindex->nHeight = vpblock.size(); - pindex->pprev = vpblock.size() > 0 ? vpblock.back() : NULL; + pindex->pprev = vpblock.size() > 0 ? vpblock.back() : nullptr; pindex->nTime = nTime; pindex->nVersion = nVersion; pindex->BuildSkip(); @@ -81,7 +81,7 @@ class VersionBitsTester VersionBitsTester& TestStateSinceHeight(int height) { for (int i = 0; i < CHECKERS; i++) { if (InsecureRandBits(i) == 0) { - BOOST_CHECK_MESSAGE(checker[i].GetStateSinceHeightFor(vpblock.empty() ? NULL : vpblock.back()) == height, strprintf("Test %i for StateSinceHeight", num)); + BOOST_CHECK_MESSAGE(checker[i].GetStateSinceHeightFor(vpblock.empty() ? nullptr : vpblock.back()) == height, strprintf("Test %i for StateSinceHeight", num)); } } num++; @@ -91,7 +91,7 @@ class VersionBitsTester VersionBitsTester& TestDefined() { for (int i = 0; i < CHECKERS; i++) { if (InsecureRandBits(i) == 0) { - BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? NULL : vpblock.back()) == THRESHOLD_DEFINED, strprintf("Test %i for DEFINED", num)); + BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_DEFINED, strprintf("Test %i for DEFINED", num)); } } num++; @@ -101,7 +101,7 @@ class VersionBitsTester VersionBitsTester& TestStarted() { for (int i = 0; i < CHECKERS; i++) { if (InsecureRandBits(i) == 0) { - BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? NULL : vpblock.back()) == THRESHOLD_STARTED, strprintf("Test %i for STARTED", num)); + BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_STARTED, strprintf("Test %i for STARTED", num)); } } num++; @@ -111,7 +111,7 @@ class VersionBitsTester VersionBitsTester& TestLockedIn() { for (int i = 0; i < CHECKERS; i++) { if (InsecureRandBits(i) == 0) { - BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? NULL : vpblock.back()) == THRESHOLD_LOCKED_IN, strprintf("Test %i for LOCKED_IN", num)); + BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_LOCKED_IN, strprintf("Test %i for LOCKED_IN", num)); } } num++; @@ -121,7 +121,7 @@ class VersionBitsTester VersionBitsTester& TestActive() { for (int i = 0; i < CHECKERS; i++) { if (InsecureRandBits(i) == 0) { - BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? NULL : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE", num)); + BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE", num)); } } num++; @@ -131,14 +131,14 @@ class VersionBitsTester VersionBitsTester& TestFailed() { for (int i = 0; i < CHECKERS; i++) { if (InsecureRandBits(i) == 0) { - BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? NULL : vpblock.back()) == THRESHOLD_FAILED, strprintf("Test %i for FAILED", num)); + BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_FAILED, strprintf("Test %i for FAILED", num)); } } num++; return *this; } - CBlockIndex * Tip() { return vpblock.size() ? vpblock.back() : NULL; } + CBlockIndex * Tip() { return vpblock.size() ? vpblock.back() : nullptr; } }; BOOST_FIXTURE_TEST_SUITE(versionbits_tests, TestingSetup) @@ -255,7 +255,7 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion) // Before MedianTimePast of the chain has crossed nStartTime, the bit // should not be set. - CBlockIndex *lastBlock = NULL; + CBlockIndex *lastBlock = nullptr; lastBlock = firstChain.Mine(2016, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip(); BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<connected = _connected; this->disconnected = _disconnected; @@ -333,7 +333,7 @@ static std::map ParseTorReplyMapping(const std::string if (j == 3 && value[i] > '3') { j--; } - escaped_value.push_back(strtol(value.substr(i, j).c_str(), NULL, 8)); + escaped_value.push_back(strtol(value.substr(i, j).c_str(), nullptr, 8)); // Account for automatic incrementing at loop end i += j - 1; } else { @@ -367,7 +367,7 @@ static std::map ParseTorReplyMapping(const std::string static std::pair ReadBinaryFile(const fs::path &filename, size_t maxsize=std::numeric_limits::max()) { FILE *f = fsbridge::fopen(filename, "rb"); - if (f == NULL) + if (f == nullptr) return std::make_pair(false,""); std::string retval; char buffer[128]; @@ -393,7 +393,7 @@ static std::pair ReadBinaryFile(const fs::path &filename, size static bool WriteBinaryFile(const fs::path &filename, const std::string &data) { FILE *f = fsbridge::fopen(filename, "wb"); - if (f == NULL) + if (f == nullptr) return false; if (fwrite(data.data(), 1, data.size(), f) != data.size()) { fclose(f); diff --git a/src/txmempool.h b/src/txmempool.h index d272114a7..6723ea8e6 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -45,7 +45,7 @@ struct LockPoints // values are still valid even after a reorg. CBlockIndex* maxInputBlock; - LockPoints() : height(0), time(0), maxInputBlock(NULL) { } + LockPoints() : height(0), time(0), maxInputBlock(nullptr) { } }; class CTxMemPool; @@ -592,7 +592,7 @@ class CTxMemPool * pvNoSpendsRemaining, if set, will be populated with the list of outpoints * which are not in mempool which no longer have any spends in this mempool. */ - void TrimToSize(size_t sizelimit, std::vector* pvNoSpendsRemaining=NULL); + void TrimToSize(size_t sizelimit, std::vector* pvNoSpendsRemaining=nullptr); /** Expire all transaction (and their dependencies) in the mempool older than time. Return the number of removed transactions. */ int Expire(int64_t time); diff --git a/src/util.cpp b/src/util.cpp index b76c173f9..fc75b7ef8 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -144,7 +144,7 @@ class CInit // Securely erase the memory used by the PRNG RAND_cleanup(); // Shutdown OpenSSL library multithreading support - CRYPTO_set_locking_callback(NULL); + CRYPTO_set_locking_callback(nullptr); // Clear the set of locks now to maintain symmetry with the constructor. ppmutexOpenSSL.reset(); } @@ -173,8 +173,8 @@ static boost::once_flag debugPrintInitFlag = BOOST_ONCE_INIT; * the OS/libc. When the shutdown sequence is fully audited and * tested, explicit destruction of these objects can be implemented. */ -static FILE* fileout = NULL; -static boost::mutex* mutexDebugLog = NULL; +static FILE* fileout = nullptr; +static boost::mutex* mutexDebugLog = nullptr; static std::list* vMsgsBeforeOpenLog; static int FileWriteStr(const std::string &str, FILE *fp) @@ -184,7 +184,7 @@ static int FileWriteStr(const std::string &str, FILE *fp) static void DebugPrintInit() { - assert(mutexDebugLog == NULL); + assert(mutexDebugLog == nullptr); mutexDebugLog = new boost::mutex(); vMsgsBeforeOpenLog = new std::list; } @@ -194,12 +194,12 @@ void OpenDebugLog() boost::call_once(&DebugPrintInit, debugPrintInitFlag); boost::mutex::scoped_lock scoped_lock(*mutexDebugLog); - assert(fileout == NULL); + assert(fileout == nullptr); assert(vMsgsBeforeOpenLog); fs::path pathDebug = GetDataDir() / "debug.log"; fileout = fsbridge::fopen(pathDebug, "a"); if (fileout) { - setbuf(fileout, NULL); // unbuffered + setbuf(fileout, nullptr); // unbuffered // dump buffered messages from before we opened the log while (!vMsgsBeforeOpenLog->empty()) { FileWriteStr(vMsgsBeforeOpenLog->front(), fileout); @@ -208,7 +208,7 @@ void OpenDebugLog() } delete vMsgsBeforeOpenLog; - vMsgsBeforeOpenLog = NULL; + vMsgsBeforeOpenLog = nullptr; } struct CLogCategoryDesc @@ -344,7 +344,7 @@ int LogPrintStr(const std::string &str) boost::mutex::scoped_lock scoped_lock(*mutexDebugLog); // buffer if we haven't opened the log yet - if (fileout == NULL) { + if (fileout == nullptr) { assert(vMsgsBeforeOpenLog); ret = strTimestamped.length(); vMsgsBeforeOpenLog->push_back(strTimestamped); @@ -355,8 +355,8 @@ int LogPrintStr(const std::string &str) if (fReopenDebugLog) { fReopenDebugLog = false; fs::path pathDebug = GetDataDir() / "debug.log"; - if (fsbridge::freopen(pathDebug,"a",fileout) != NULL) - setbuf(fileout, NULL); // unbuffered + if (fsbridge::freopen(pathDebug,"a",fileout) != nullptr) + setbuf(fileout, nullptr); // unbuffered } ret = FileWriteStr(strTimestamped, fileout); @@ -503,7 +503,7 @@ static std::string FormatException(const std::exception* pex, const char* pszThr { #ifdef WIN32 char pszModule[MAX_PATH] = ""; - GetModuleFileNameA(NULL, pszModule, sizeof(pszModule)); + GetModuleFileNameA(nullptr, pszModule, sizeof(pszModule)); #else const char* pszModule = "bitcoin"; #endif @@ -534,7 +534,7 @@ fs::path GetDefaultDataDir() #else fs::path pathRet; char* pszHome = getenv("HOME"); - if (pszHome == NULL || strlen(pszHome) == 0) + if (pszHome == nullptr || strlen(pszHome) == 0) pathRet = fs::path("/"); else pathRet = fs::path(pszHome); @@ -791,7 +791,7 @@ void ShrinkDebugFile() fclose(file); } } - else if (file != NULL) + else if (file != nullptr) fclose(file); } @@ -800,7 +800,7 @@ fs::path GetSpecialFolderPath(int nFolder, bool fCreate) { char pszPath[MAX_PATH] = ""; - if(SHGetSpecialFolderPathA(NULL, pszPath, nFolder, fCreate)) + if(SHGetSpecialFolderPathA(nullptr, pszPath, nFolder, fCreate)) { return fs::path(pszPath); } diff --git a/src/util.h b/src/util.h index e1bdfb198..b01606cb8 100644 --- a/src/util.h +++ b/src/util.h @@ -358,7 +358,7 @@ template void TraceThread(const char* name, Callable func) throw; } catch (...) { - PrintExceptionContinue(NULL, name); + PrintExceptionContinue(nullptr, name); throw; } } diff --git a/src/utilstrencodings.cpp b/src/utilstrencodings.cpp index 9ee14070a..fd233f675 100644 --- a/src/utilstrencodings.cpp +++ b/src/utilstrencodings.cpp @@ -452,7 +452,7 @@ bool ParseInt32(const std::string& str, int32_t *out) { if (!ParsePrechecks(str)) return false; - char *endp = NULL; + char *endp = nullptr; errno = 0; // strtol will not set errno if valid long int n = strtol(str.c_str(), &endp, 10); if(out) *out = (int32_t)n; @@ -468,7 +468,7 @@ bool ParseInt64(const std::string& str, int64_t *out) { if (!ParsePrechecks(str)) return false; - char *endp = NULL; + char *endp = nullptr; errno = 0; // strtoll will not set errno if valid long long int n = strtoll(str.c_str(), &endp, 10); if(out) *out = (int64_t)n; @@ -485,7 +485,7 @@ bool ParseUInt32(const std::string& str, uint32_t *out) return false; if (str.size() >= 1 && str[0] == '-') // Reject negative values, unfortunately strtoul accepts these by default if they fit in the range return false; - char *endp = NULL; + char *endp = nullptr; errno = 0; // strtoul will not set errno if valid unsigned long int n = strtoul(str.c_str(), &endp, 10); if(out) *out = (uint32_t)n; @@ -502,7 +502,7 @@ bool ParseUInt64(const std::string& str, uint64_t *out) return false; if (str.size() >= 1 && str[0] == '-') // Reject negative values, unfortunately strtoull accepts these by default if they fit in the range return false; - char *endp = NULL; + char *endp = nullptr; errno = 0; // strtoull will not set errno if valid unsigned long long int n = strtoull(str.c_str(), &endp, 10); if(out) *out = (uint64_t)n; @@ -583,7 +583,7 @@ int64_t atoi64(const char* psz) #ifdef _MSC_VER return _atoi64(psz); #else - return strtoll(psz, NULL, 10); + return strtoll(psz, nullptr, 10); #endif } @@ -592,7 +592,7 @@ int64_t atoi64(const std::string& str) #ifdef _MSC_VER return _atoi64(str.c_str()); #else - return strtoll(str.c_str(), NULL, 10); + return strtoll(str.c_str(), nullptr, 10); #endif } diff --git a/src/utilstrencodings.h b/src/utilstrencodings.h index 707fdaad1..53da60e8f 100644 --- a/src/utilstrencodings.h +++ b/src/utilstrencodings.h @@ -39,11 +39,11 @@ std::vector ParseHex(const char* psz); std::vector ParseHex(const std::string& str); signed char HexDigit(char c); bool IsHex(const std::string& str); -std::vector DecodeBase64(const char* p, bool* pfInvalid = NULL); +std::vector DecodeBase64(const char* p, bool* pfInvalid = nullptr); std::string DecodeBase64(const std::string& str); std::string EncodeBase64(const unsigned char* pch, size_t len); std::string EncodeBase64(const std::string& str); -std::vector DecodeBase32(const char* p, bool* pfInvalid = NULL); +std::vector DecodeBase32(const char* p, bool* pfInvalid = nullptr); std::string DecodeBase32(const std::string& str); std::string EncodeBase32(const unsigned char* pch, size_t len); std::string EncodeBase32(const std::string& str); diff --git a/src/utiltime.cpp b/src/utiltime.cpp index e07069125..4cc77dbfe 100644 --- a/src/utiltime.cpp +++ b/src/utiltime.cpp @@ -21,7 +21,7 @@ int64_t GetTime() int64_t mocktime = nMockTime.load(std::memory_order_relaxed); if (mocktime) return mocktime; - time_t now = time(NULL); + time_t now = time(nullptr); assert(now > 0); return now; } diff --git a/src/validation.cpp b/src/validation.cpp index babf6f152..5e0c710de 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -61,7 +61,7 @@ CCriticalSection cs_main; BlockMap mapBlockIndex; CChain chainActive; -CBlockIndex *pindexBestHeader = NULL; +CBlockIndex *pindexBestHeader = nullptr; CWaitableCriticalSection csBestBlock; CConditionVariable cvBlockChange; int nScriptCheckThreads = 0; @@ -177,9 +177,9 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc return chain.Genesis(); } -CCoinsViewDB *pcoinsdbview = NULL; -CCoinsViewCache *pcoinsTip = NULL; -CBlockTreeDB *pblocktree = NULL; +CCoinsViewDB *pcoinsdbview = nullptr; +CCoinsViewCache *pcoinsTip = nullptr; +CBlockTreeDB *pblocktree = nullptr; enum FlushStateMode { FLUSH_STATE_NONE, @@ -378,7 +378,7 @@ void UpdateMempoolForReorg(DisconnectedBlockTransactions &disconnectpool, bool f while (it != disconnectpool.queuedTx.get().rend()) { // ignore validation errors in resurrected transactions CValidationState stateDummy; - if (!fAddToMempool || (*it)->IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, *it, false, NULL, NULL, true)) { + if (!fAddToMempool || (*it)->IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, *it, false, nullptr, nullptr, true)) { // If the transaction doesn't make it in to the mempool, remove any // transactions that depend on it (which would now be orphans). mempool.removeRecursive(**it, MemPoolRemovalReason::REORG); @@ -899,7 +899,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa /** Return transaction in txOut, and if it was found inside a block, its hash is placed in hashBlock */ bool GetTransaction(const uint256 &hash, CTransactionRef &txOut, const Consensus::Params& consensusParams, uint256 &hashBlock, bool fAllowSlow) { - CBlockIndex *pindexSlow = NULL; + CBlockIndex *pindexSlow = nullptr; LOCK(cs_main); @@ -1045,7 +1045,7 @@ bool IsInitialBlockDownload() return false; if (fImporting || fReindex) return true; - if (chainActive.Tip() == NULL) + if (chainActive.Tip() == nullptr) return true; if (chainActive.Tip()->nChainWork < UintToArith256(chainParams.GetConsensus().nMinimumChainWork)) return true; @@ -1056,7 +1056,7 @@ bool IsInitialBlockDownload() return false; } -CBlockIndex *pindexBestForkTip = NULL, *pindexBestForkBase = NULL; +CBlockIndex *pindexBestForkTip = nullptr, *pindexBestForkBase = nullptr; static void AlertNotify(const std::string& strMessage) { @@ -1086,7 +1086,7 @@ static void CheckForkWarningConditions() // If our best fork is no longer within 72 blocks (+/- 12 hours if no one mines it) // of our head, drop it if (pindexBestForkTip && chainActive.Height() - pindexBestForkTip->nHeight >= 72) - pindexBestForkTip = NULL; + pindexBestForkTip = nullptr; if (pindexBestForkTip || (pindexBestInvalid && pindexBestInvalid->nChainWork > chainActive.Tip()->nChainWork + (GetBlockProof(*chainActive.Tip()) * 6))) { @@ -1226,7 +1226,7 @@ void InitScriptExecutionCache() { * Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts) * This does not modify the UTXO set. * - * If pvChecks is not NULL, script checks are pushed onto it instead of being performed inline. Any + * If pvChecks is not nullptr, script checks are pushed onto it instead of being performed inline. Any * script checks which are not necessary (eg due to script execution cache hits) are, obviously, * not pushed onto pvChecks/run. * @@ -1633,7 +1633,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd AssertLockHeld(cs_main); assert(pindex); // pindex->phashBlock can be null if called by CreateNewBlock/TestBlockValidity - assert((pindex->phashBlock == NULL) || + assert((pindex->phashBlock == nullptr) || (*pindex->phashBlock == block.GetHash())); int64_t nTimeStart = GetTimeMicros(); @@ -1642,7 +1642,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state)); // verify that the view's current state corresponds to the previous block - uint256 hashPrevBlock = pindex->pprev == NULL ? uint256() : pindex->pprev->GetBlockHash(); + uint256 hashPrevBlock = pindex->pprev == nullptr ? uint256() : pindex->pprev->GetBlockHash(); assert(hashPrevBlock == view.GetBestBlock()); // Special case for the genesis block, skipping connection of its transactions @@ -1733,7 +1733,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd CBlockUndo blockundo; - CCheckQueueControl control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); + CCheckQueueControl control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : nullptr); std::vector prevheights; CAmount nFees = 0; @@ -1787,7 +1787,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd std::vector vChecks; bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */ - if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, fCacheResults, txdata[i], nScriptCheckThreads ? &vChecks : NULL)) + if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, fCacheResults, txdata[i], nScriptCheckThreads ? &vChecks : nullptr)) return error("ConnectBlock(): CheckInputs on %s failed with %s", tx.GetHash().ToString(), FormatStateMessage(state)); control.Add(vChecks); @@ -2014,7 +2014,7 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) { } } // Check the version of the last 100 blocks to see if we need to upgrade: - for (int i = 0; i < 100 && pindex != NULL; i++) + for (int i = 0; i < 100 && pindex != nullptr; i++) { int32_t nExpectedVersion = ComputeBlockVersion(pindex->pprev, chainParams.GetConsensus()); if (pindex->nVersion > VERSIONBITS_LAST_OLD_BLOCK_VERSION && (pindex->nVersion & ~nExpectedVersion) != 0) @@ -2047,7 +2047,7 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) { * should make the mempool consistent again by calling UpdateMempoolForReorg. * with cs_main held. * - * If disconnectpool is NULL, then no disconnected transactions are added to + * If disconnectpool is nullptr, then no disconnected transactions are added to * disconnectpool (note that the caller is responsible for mempool consistency * in any case). */ @@ -2103,7 +2103,7 @@ static int64_t nTimeChainState = 0; static int64_t nTimePostConnect = 0; struct PerBlockConnectTrace { - CBlockIndex* pindex = NULL; + CBlockIndex* pindex = nullptr; std::shared_ptr pblock; std::shared_ptr> conflictedTxs; PerBlockConnectTrace() : conflictedTxs(std::make_shared>()) {} @@ -2168,7 +2168,7 @@ class ConnectTrace { }; /** - * Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock + * Connect a new block to chainActive. pblock is either nullptr or a pointer to a CBlock * corresponding to pindexNew, to bypass loading it again from disk. * * The block is added to connectTrace if connection succeeds. @@ -2233,13 +2233,13 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, */ static CBlockIndex* FindMostWorkChain() { do { - CBlockIndex *pindexNew = NULL; + CBlockIndex *pindexNew = nullptr; // Find the best candidate header. { std::set::reverse_iterator it = setBlockIndexCandidates.rbegin(); if (it == setBlockIndexCandidates.rend()) - return NULL; + return nullptr; pindexNew = *it; } @@ -2258,7 +2258,7 @@ static CBlockIndex* FindMostWorkChain() { bool fMissingData = !(pindexTest->nStatus & BLOCK_HAVE_DATA); if (fFailedChain || fMissingData) { // Candidate chain is not usable (either invalid or missing data) - if (fFailedChain && (pindexBestInvalid == NULL || pindexNew->nChainWork > pindexBestInvalid->nChainWork)) + if (fFailedChain && (pindexBestInvalid == nullptr || pindexNew->nChainWork > pindexBestInvalid->nChainWork)) pindexBestInvalid = pindexNew; CBlockIndex *pindexFailed = pindexNew; // Remove the entire chain from the set. @@ -2299,7 +2299,7 @@ static void PruneBlockIndexCandidates() { /** * Try to make some progress towards making pindexMostWork the active block. - * pblock is either NULL or a pointer to a CBlock corresponding to pindexMostWork. + * pblock is either nullptr or a pointer to a CBlock corresponding to pindexMostWork. */ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) { @@ -2385,8 +2385,8 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c static void NotifyHeaderTip() { bool fNotify = false; bool fInitialBlockDownload = false; - static CBlockIndex* pindexHeaderOld = NULL; - CBlockIndex* pindexHeader = NULL; + static CBlockIndex* pindexHeaderOld = nullptr; + CBlockIndex* pindexHeader = nullptr; { LOCK(cs_main); pindexHeader = pindexBestHeader; @@ -2405,7 +2405,7 @@ static void NotifyHeaderTip() { /** * Make the best chain active, in multiple steps. The result is either failure - * or an activated best chain. pblock is either NULL or a pointer to a block + * or an activated best chain. pblock is either nullptr or a pointer to a block * that is already loaded (to avoid loading it again from disk). */ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr pblock) { @@ -2414,8 +2414,8 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, // us in the middle of ProcessNewBlock - do not assume pblock is set // sanely for performance or correctness! - CBlockIndex *pindexMostWork = NULL; - CBlockIndex *pindexNewTip = NULL; + CBlockIndex *pindexMostWork = nullptr; + CBlockIndex *pindexNewTip = nullptr; int nStopAtHeight = GetArg("-stopatheight", DEFAULT_STOPATHEIGHT); do { boost::this_thread::interruption_point(); @@ -2429,12 +2429,12 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, ConnectTrace connectTrace(mempool); // Destructed before cs_main is unlocked CBlockIndex *pindexOldTip = chainActive.Tip(); - if (pindexMostWork == NULL) { + if (pindexMostWork == nullptr) { pindexMostWork = FindMostWorkChain(); } // Whether we have anything to do at all. - if (pindexMostWork == NULL || pindexMostWork == chainActive.Tip()) + if (pindexMostWork == nullptr || pindexMostWork == chainActive.Tip()) return true; bool fInvalidFound = false; @@ -2444,7 +2444,7 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, if (fInvalidFound) { // Wipe cache, we may need another branch now. - pindexMostWork = NULL; + pindexMostWork = nullptr; } pindexNewTip = chainActive.Tip(); pindexFork = chainActive.FindFork(pindexOldTip); @@ -2569,14 +2569,14 @@ bool ResetBlockFailureFlags(CBlockIndex *pindex) { } if (it->second == pindexBestInvalid) { // Reset invalid block marker if it was pointing to one of those. - pindexBestInvalid = NULL; + pindexBestInvalid = nullptr; } } it++; } // Remove the invalidity flag from all ancestors too. - while (pindex != NULL) { + while (pindex != nullptr) { if (pindex->nStatus & BLOCK_FAILED_MASK) { pindex->nStatus &= ~BLOCK_FAILED_MASK; setDirtyBlockIndex.insert(pindex); @@ -2613,7 +2613,7 @@ static CBlockIndex* AddToBlockIndex(const CBlockHeader& block) 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) + if (pindexBestHeader == nullptr || pindexBestHeader->nChainWork < pindexNew->nChainWork) pindexBestHeader = pindexNew; setDirtyBlockIndex.insert(pindexNew); @@ -2636,7 +2636,7 @@ static bool ReceivedBlockTransactions(const CBlock &block, CValidationState& sta pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS); setDirtyBlockIndex.insert(pindexNew); - if (pindexNew->pprev == NULL || pindexNew->pprev->nChainTx) { + if (pindexNew->pprev == nullptr || pindexNew->pprev->nChainTx) { // If pindexNew is the genesis block or all parents are BLOCK_VALID_TRANSACTIONS. std::deque queue; queue.push_back(pindexNew); @@ -2650,7 +2650,7 @@ static bool ReceivedBlockTransactions(const CBlock &block, CValidationState& sta LOCK(cs_nBlockSequenceId); pindex->nSequenceId = nBlockSequenceId++; } - if (chainActive.Tip() == NULL || !setBlockIndexCandidates.value_comp()(pindex, chainActive.Tip())) { + if (chainActive.Tip() == nullptr || !setBlockIndexCandidates.value_comp()(pindex, chainActive.Tip())) { setBlockIndexCandidates.insert(pindex); } std::pair::iterator, std::multimap::iterator> range = mapBlocksUnlinked.equal_range(pindex); @@ -2886,7 +2886,7 @@ std::vector GenerateCoinbaseCommitment(CBlock& block, const CBloc std::vector ret(32, 0x00); if (consensusParams.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout != 0) { if (commitpos == -1) { - uint256 witnessroot = BlockWitnessMerkleRoot(block, NULL); + uint256 witnessroot = BlockWitnessMerkleRoot(block, nullptr); CHash256().Write(witnessroot.begin(), 32).Write(ret.data(), 32).Finalize(witnessroot.begin()); CTxOut out; out.nValue = 0; @@ -2913,7 +2913,7 @@ std::vector GenerateCoinbaseCommitment(CBlock& block, const CBloc * set; UTXO-related validity checks are done in ConnectBlock(). */ static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev, int64_t nAdjustedTime) { - assert(pindexPrev != NULL); + assert(pindexPrev != nullptr); const int nHeight = pindexPrev->nHeight + 1; // Check proof of work if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams)) @@ -2940,7 +2940,7 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationSta static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev) { - const int nHeight = pindexPrev == NULL ? 0 : pindexPrev->nHeight + 1; + const int nHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1; // Start enforcing BIP113 (Median Time Past) using versionbits logic. int nLockTimeFlags = 0; @@ -3025,7 +3025,7 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state // Check for duplicate uint256 hash = block.GetHash(); BlockMap::iterator miSelf = mapBlockIndex.find(hash); - CBlockIndex *pindex = NULL; + CBlockIndex *pindex = nullptr; if (hash != chainparams.GetConsensus().hashGenesisBlock) { if (miSelf != mapBlockIndex.end()) { @@ -3042,7 +3042,7 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state return error("%s: Consensus::CheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); // Get prev block index - CBlockIndex* pindexPrev = NULL; + CBlockIndex* pindexPrev = nullptr; BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); if (mi == mapBlockIndex.end()) return state.DoS(10, error("%s: prev block not found", __func__), 0, "prev-blk-not-found"); @@ -3057,7 +3057,7 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state if (!ContextualCheckBlockHeader(block, state, chainparams.GetConsensus(), pindexPrev, GetAdjustedTime())) return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); } - if (pindex == NULL) + if (pindex == nullptr) pindex = AddToBlockIndex(block); if (ppindex) @@ -3074,7 +3074,7 @@ bool ProcessNewBlockHeaders(const std::vector& headers, CValidatio { LOCK(cs_main); for (const CBlockHeader& header : headers) { - CBlockIndex *pindex = NULL; // Use a temp pindex instead of ppindex to avoid a const_cast + CBlockIndex *pindex = nullptr; // Use a temp pindex instead of ppindex to avoid a const_cast if (!AcceptBlockHeader(header, state, chainparams, &pindex)) { return false; } @@ -3087,7 +3087,7 @@ bool ProcessNewBlockHeaders(const std::vector& headers, CValidatio return true; } -/** Store block on disk. If dbp is non-NULL, the file is known to already reside on disk */ +/** Store block on disk. If dbp is non-nullptr, the file is known to already reside on disk */ static bool AcceptBlock(const std::shared_ptr& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const CDiskBlockPos* dbp, bool* fNewBlock) { const CBlock& block = *pblock; @@ -3095,7 +3095,7 @@ static bool AcceptBlock(const std::shared_ptr& pblock, CValidation if (fNewBlock) *fNewBlock = false; AssertLockHeld(cs_main); - CBlockIndex *pindexDummy = NULL; + CBlockIndex *pindexDummy = nullptr; CBlockIndex *&pindex = ppindex ? *ppindex : pindexDummy; if (!AcceptBlockHeader(block, state, chainparams, &pindex)) @@ -3148,11 +3148,11 @@ static bool AcceptBlock(const std::shared_ptr& pblock, CValidation try { unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); CDiskBlockPos blockPos; - if (dbp != NULL) + if (dbp != nullptr) blockPos = *dbp; - if (!FindBlockPos(state, blockPos, nBlockSize+8, nHeight, block.GetBlockTime(), dbp != NULL)) + if (!FindBlockPos(state, blockPos, nBlockSize+8, nHeight, block.GetBlockTime(), dbp != nullptr)) return error("AcceptBlock(): FindBlockPos failed"); - if (dbp == NULL) + if (dbp == nullptr) if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart())) AbortNode(state, "Failed to write block"); if (!ReceivedBlockTransactions(block, state, pindex, blockPos, chainparams.GetConsensus())) @@ -3170,7 +3170,7 @@ static bool AcceptBlock(const std::shared_ptr& pblock, CValidation bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr pblock, bool fForceProcessing, bool *fNewBlock) { { - CBlockIndex *pindex = NULL; + CBlockIndex *pindex = nullptr; if (fNewBlock) *fNewBlock = false; CValidationState state; // Ensure that CheckBlock() passes before calling AcceptBlock, as @@ -3181,7 +3181,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr& setFilesToPrune, int nManualPr assert(fPruneMode && nManualPruneHeight > 0); LOCK2(cs_main, cs_LastBlockFile); - if (chainActive.Tip() == NULL) + if (chainActive.Tip() == nullptr) return; // last block to prune is the lesser of (user-specified height, MIN_BLOCKS_TO_KEEP from the tip) @@ -3330,7 +3330,7 @@ void PruneBlockFilesManual(int nManualPruneHeight) static void FindFilesToPrune(std::set& setFilesToPrune, uint64_t nPruneAfterHeight) { LOCK2(cs_main, cs_LastBlockFile); - if (chainActive.Tip() == NULL || nPruneTarget == 0) { + if (chainActive.Tip() == nullptr || nPruneTarget == 0) { return; } if ((uint64_t)chainActive.Tip()->nHeight <= nPruneAfterHeight) { @@ -3388,7 +3388,7 @@ bool CheckDiskSpace(uint64_t nAdditionalBytes) static FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly) { if (pos.IsNull()) - return NULL; + return nullptr; fs::path path = GetBlockPosFilename(pos, prefix); fs::create_directories(path.parent_path()); FILE* file = fsbridge::fopen(path, "rb+"); @@ -3396,13 +3396,13 @@ static FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fRe file = fsbridge::fopen(path, "wb+"); if (!file) { LogPrintf("Unable to open file %s\n", path.string()); - return NULL; + return nullptr; } if (pos.nPos) { if (fseek(file, pos.nPos, SEEK_SET)) { LogPrintf("Unable to seek to position %u of %s\n", pos.nPos, path.string()); fclose(file); - return NULL; + return nullptr; } } return file; @@ -3425,7 +3425,7 @@ fs::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix) CBlockIndex * InsertBlockIndex(uint256 hash) { if (hash.IsNull()) - return NULL; + return nullptr; // Return existing BlockMap::iterator mi = mapBlockIndex.find(hash); @@ -3477,13 +3477,13 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) pindex->nChainTx = pindex->nTx; } } - if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->nChainTx || pindex->pprev == NULL)) + if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->nChainTx || pindex->pprev == nullptr)) setBlockIndexCandidates.insert(pindex); if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork)) pindexBestInvalid = pindex; if (pindex->pprev) pindex->BuildSkip(); - if (pindex->IsValid(BLOCK_VALID_TREE) && (pindexBestHeader == NULL || CBlockIndexWorkComparator()(pindexBestHeader, pindex))) + if (pindex->IsValid(BLOCK_VALID_TREE) && (pindexBestHeader == nullptr || CBlockIndexWorkComparator()(pindexBestHeader, pindex))) pindexBestHeader = pindex; } @@ -3581,7 +3581,7 @@ CVerifyDB::~CVerifyDB() bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth) { LOCK(cs_main); - if (chainActive.Tip() == NULL || chainActive.Tip()->pprev == NULL) + if (chainActive.Tip() == nullptr || chainActive.Tip()->pprev == nullptr) return true; // Verify blocks in the best chain @@ -3591,7 +3591,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, LogPrintf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel); CCoinsViewCache coins(coinsview); CBlockIndex* pindexState = chainActive.Tip(); - CBlockIndex* pindexFailure = NULL; + CBlockIndex* pindexFailure = nullptr; int nGoodTransactions = 0; CValidationState state; int reportDone = 0; @@ -3784,7 +3784,7 @@ bool RewindBlockIndex(const CChainParams& params) // of the blockchain). break; } - if (!DisconnectTip(state, params, NULL)) { + if (!DisconnectTip(state, params, nullptr)) { return error("RewindBlockIndex: unable to disconnect block at height %i", pindex->nHeight); } // Occasionally flush state to disk. @@ -3833,7 +3833,7 @@ bool RewindBlockIndex(const CChainParams& params) } } - if (chainActive.Tip() != NULL) { + if (chainActive.Tip() != nullptr) { // We can't prune block index candidates based on our tip if we have // no tip due to chainActive being empty! PruneBlockIndexCandidates(); @@ -3858,9 +3858,9 @@ void UnloadBlockIndex() { LOCK(cs_main); setBlockIndexCandidates.clear(); - chainActive.SetTip(NULL); - pindexBestInvalid = NULL; - pindexBestHeader = NULL; + chainActive.SetTip(nullptr); + pindexBestInvalid = nullptr; + pindexBestHeader = nullptr; mempool.clear(); mapBlocksUnlinked.clear(); vinfoBlockFile.clear(); @@ -3997,7 +3997,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(pblock, state, chainparams, NULL, true, dbp, NULL)) + if (AcceptBlock(pblock, state, chainparams, nullptr, true, dbp, nullptr)) nLoaded++; if (state.IsError()) break; @@ -4031,7 +4031,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB head.ToString()); LOCK(cs_main); CValidationState dummy; - if (AcceptBlock(pblockrecursive, dummy, chainparams, NULL, true, &it->second, NULL)) + if (AcceptBlock(pblockrecursive, dummy, chainparams, nullptr, true, &it->second, nullptr)) { nLoaded++; queue.push_back(pblockrecursive->GetHash()); @@ -4078,35 +4078,35 @@ void static CheckBlockIndex(const Consensus::Params& consensusParams) assert(forward.size() == mapBlockIndex.size()); - std::pair::iterator,std::multimap::iterator> rangeGenesis = forward.equal_range(NULL); + std::pair::iterator,std::multimap::iterator> rangeGenesis = forward.equal_range(nullptr); CBlockIndex *pindex = rangeGenesis.first->second; rangeGenesis.first++; - assert(rangeGenesis.first == rangeGenesis.second); // There is only one index entry with parent NULL. + assert(rangeGenesis.first == rangeGenesis.second); // There is only one index entry with parent nullptr. // Iterate over the entire block tree, using depth-first search. // Along the way, remember whether there are blocks on the path from genesis // block being explored which are the first to have certain properties. size_t nNodes = 0; int nHeight = 0; - CBlockIndex* pindexFirstInvalid = NULL; // Oldest ancestor of pindex which is invalid. - CBlockIndex* pindexFirstMissing = NULL; // Oldest ancestor of pindex which does not have BLOCK_HAVE_DATA. - CBlockIndex* pindexFirstNeverProcessed = NULL; // Oldest ancestor of pindex for which nTx == 0. - CBlockIndex* pindexFirstNotTreeValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_TREE (regardless of being valid or not). - CBlockIndex* pindexFirstNotTransactionsValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_TRANSACTIONS (regardless of being valid or not). - CBlockIndex* pindexFirstNotChainValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_CHAIN (regardless of being valid or not). - CBlockIndex* pindexFirstNotScriptsValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_SCRIPTS (regardless of being valid or not). - while (pindex != NULL) { + CBlockIndex* pindexFirstInvalid = nullptr; // Oldest ancestor of pindex which is invalid. + CBlockIndex* pindexFirstMissing = nullptr; // Oldest ancestor of pindex which does not have BLOCK_HAVE_DATA. + CBlockIndex* pindexFirstNeverProcessed = nullptr; // Oldest ancestor of pindex for which nTx == 0. + CBlockIndex* pindexFirstNotTreeValid = nullptr; // Oldest ancestor of pindex which does not have BLOCK_VALID_TREE (regardless of being valid or not). + CBlockIndex* pindexFirstNotTransactionsValid = nullptr; // Oldest ancestor of pindex which does not have BLOCK_VALID_TRANSACTIONS (regardless of being valid or not). + CBlockIndex* pindexFirstNotChainValid = nullptr; // Oldest ancestor of pindex which does not have BLOCK_VALID_CHAIN (regardless of being valid or not). + CBlockIndex* pindexFirstNotScriptsValid = nullptr; // Oldest ancestor of pindex which does not have BLOCK_VALID_SCRIPTS (regardless of being valid or not). + while (pindex != nullptr) { nNodes++; - if (pindexFirstInvalid == NULL && pindex->nStatus & BLOCK_FAILED_VALID) pindexFirstInvalid = pindex; - if (pindexFirstMissing == NULL && !(pindex->nStatus & BLOCK_HAVE_DATA)) pindexFirstMissing = pindex; - if (pindexFirstNeverProcessed == NULL && pindex->nTx == 0) pindexFirstNeverProcessed = pindex; - if (pindex->pprev != NULL && pindexFirstNotTreeValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TREE) pindexFirstNotTreeValid = pindex; - if (pindex->pprev != NULL && pindexFirstNotTransactionsValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TRANSACTIONS) pindexFirstNotTransactionsValid = pindex; - if (pindex->pprev != NULL && pindexFirstNotChainValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_CHAIN) pindexFirstNotChainValid = pindex; - if (pindex->pprev != NULL && pindexFirstNotScriptsValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS) pindexFirstNotScriptsValid = pindex; + if (pindexFirstInvalid == nullptr && pindex->nStatus & BLOCK_FAILED_VALID) pindexFirstInvalid = pindex; + if (pindexFirstMissing == nullptr && !(pindex->nStatus & BLOCK_HAVE_DATA)) pindexFirstMissing = pindex; + if (pindexFirstNeverProcessed == nullptr && pindex->nTx == 0) pindexFirstNeverProcessed = pindex; + if (pindex->pprev != nullptr && pindexFirstNotTreeValid == nullptr && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TREE) pindexFirstNotTreeValid = pindex; + if (pindex->pprev != nullptr && pindexFirstNotTransactionsValid == nullptr && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TRANSACTIONS) pindexFirstNotTransactionsValid = pindex; + if (pindex->pprev != nullptr && pindexFirstNotChainValid == nullptr && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_CHAIN) pindexFirstNotChainValid = pindex; + if (pindex->pprev != nullptr && pindexFirstNotScriptsValid == nullptr && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS) pindexFirstNotScriptsValid = pindex; // Begin: actual consistency checks. - if (pindex->pprev == NULL) { + if (pindex->pprev == nullptr) { // Genesis block checks. assert(pindex->GetBlockHash() == consensusParams.hashGenesisBlock); // Genesis block's hash must match. assert(pindex == chainActive.Genesis()); // The current active chain's genesis block must be this block. @@ -4125,26 +4125,26 @@ void static CheckBlockIndex(const Consensus::Params& consensusParams) if (pindex->nStatus & BLOCK_HAVE_UNDO) assert(pindex->nStatus & BLOCK_HAVE_DATA); assert(((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS) == (pindex->nTx > 0)); // This is pruning-independent. // All parents having had data (at some point) is equivalent to all parents being VALID_TRANSACTIONS, which is equivalent to nChainTx being set. - assert((pindexFirstNeverProcessed != NULL) == (pindex->nChainTx == 0)); // nChainTx != 0 is used to signal that all parent blocks have been processed (but may have been pruned). - assert((pindexFirstNotTransactionsValid != NULL) == (pindex->nChainTx == 0)); + assert((pindexFirstNeverProcessed != nullptr) == (pindex->nChainTx == 0)); // nChainTx != 0 is used to signal that all parent blocks have been processed (but may have been pruned). + assert((pindexFirstNotTransactionsValid != nullptr) == (pindex->nChainTx == 0)); assert(pindex->nHeight == nHeight); // nHeight must be consistent. - assert(pindex->pprev == NULL || pindex->nChainWork >= pindex->pprev->nChainWork); // For every block except the genesis block, the chainwork must be larger than the parent's. + assert(pindex->pprev == nullptr || pindex->nChainWork >= pindex->pprev->nChainWork); // For every block except the genesis block, the chainwork must be larger than the parent's. assert(nHeight < 2 || (pindex->pskip && (pindex->pskip->nHeight < nHeight))); // The pskip pointer must point back for all but the first 2 blocks. - assert(pindexFirstNotTreeValid == NULL); // All mapBlockIndex entries must at least be TREE valid - if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TREE) assert(pindexFirstNotTreeValid == NULL); // TREE valid implies all parents are TREE valid - if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_CHAIN) assert(pindexFirstNotChainValid == NULL); // CHAIN valid implies all parents are CHAIN valid - if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_SCRIPTS) assert(pindexFirstNotScriptsValid == NULL); // SCRIPTS valid implies all parents are SCRIPTS valid - if (pindexFirstInvalid == NULL) { + assert(pindexFirstNotTreeValid == nullptr); // All mapBlockIndex entries must at least be TREE valid + if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TREE) assert(pindexFirstNotTreeValid == nullptr); // TREE valid implies all parents are TREE valid + if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_CHAIN) assert(pindexFirstNotChainValid == nullptr); // CHAIN valid implies all parents are CHAIN valid + if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_SCRIPTS) assert(pindexFirstNotScriptsValid == nullptr); // SCRIPTS valid implies all parents are SCRIPTS valid + if (pindexFirstInvalid == nullptr) { // Checks for not-invalid blocks. assert((pindex->nStatus & BLOCK_FAILED_MASK) == 0); // The failed mask cannot be set for blocks without invalid parents. } - if (!CBlockIndexWorkComparator()(pindex, chainActive.Tip()) && pindexFirstNeverProcessed == NULL) { - if (pindexFirstInvalid == NULL) { + if (!CBlockIndexWorkComparator()(pindex, chainActive.Tip()) && pindexFirstNeverProcessed == nullptr) { + if (pindexFirstInvalid == nullptr) { // If this block sorts at least as good as the current tip and // is valid and we have all data for its parents, it must be in // setBlockIndexCandidates. chainActive.Tip() must also be there // even if some data has been pruned. - if (pindexFirstMissing == NULL || pindex == chainActive.Tip()) { + if (pindexFirstMissing == nullptr || pindex == chainActive.Tip()) { assert(setBlockIndexCandidates.count(pindex)); } // If some parent is missing, then it could be that this block was in @@ -4165,13 +4165,13 @@ void static CheckBlockIndex(const Consensus::Params& consensusParams) } rangeUnlinked.first++; } - if (pindex->pprev && (pindex->nStatus & BLOCK_HAVE_DATA) && pindexFirstNeverProcessed != NULL && pindexFirstInvalid == NULL) { + if (pindex->pprev && (pindex->nStatus & BLOCK_HAVE_DATA) && pindexFirstNeverProcessed != nullptr && pindexFirstInvalid == nullptr) { // If this block has block data available, some parent was never received, and has no invalid parents, it must be in mapBlocksUnlinked. assert(foundInUnlinked); } if (!(pindex->nStatus & BLOCK_HAVE_DATA)) assert(!foundInUnlinked); // Can't be in mapBlocksUnlinked if we don't HAVE_DATA - if (pindexFirstMissing == NULL) assert(!foundInUnlinked); // We aren't missing data for any parent -- cannot be in mapBlocksUnlinked. - if (pindex->pprev && (pindex->nStatus & BLOCK_HAVE_DATA) && pindexFirstNeverProcessed == NULL && pindexFirstMissing != NULL) { + if (pindexFirstMissing == nullptr) assert(!foundInUnlinked); // We aren't missing data for any parent -- cannot be in mapBlocksUnlinked. + if (pindex->pprev && (pindex->nStatus & BLOCK_HAVE_DATA) && pindexFirstNeverProcessed == nullptr && pindexFirstMissing != nullptr) { // We HAVE_DATA for this block, have received data for all parents at some point, but we're currently missing data for some parent. assert(fHavePruned); // We must have pruned. // This block may have entered mapBlocksUnlinked if: @@ -4183,7 +4183,7 @@ void static CheckBlockIndex(const Consensus::Params& consensusParams) // So if this block is itself better than chainActive.Tip() and it wasn't in // setBlockIndexCandidates, then it must be in mapBlocksUnlinked. if (!CBlockIndexWorkComparator()(pindex, chainActive.Tip()) && setBlockIndexCandidates.count(pindex) == 0) { - if (pindexFirstInvalid == NULL) { + if (pindexFirstInvalid == nullptr) { assert(foundInUnlinked); } } @@ -4204,13 +4204,13 @@ void static CheckBlockIndex(const Consensus::Params& consensusParams) while (pindex) { // We are going to either move to a parent or a sibling of pindex. // If pindex was the first with a certain property, unset the corresponding variable. - if (pindex == pindexFirstInvalid) pindexFirstInvalid = NULL; - if (pindex == pindexFirstMissing) pindexFirstMissing = NULL; - if (pindex == pindexFirstNeverProcessed) pindexFirstNeverProcessed = NULL; - if (pindex == pindexFirstNotTreeValid) pindexFirstNotTreeValid = NULL; - if (pindex == pindexFirstNotTransactionsValid) pindexFirstNotTransactionsValid = NULL; - if (pindex == pindexFirstNotChainValid) pindexFirstNotChainValid = NULL; - if (pindex == pindexFirstNotScriptsValid) pindexFirstNotScriptsValid = NULL; + if (pindex == pindexFirstInvalid) pindexFirstInvalid = nullptr; + if (pindex == pindexFirstMissing) pindexFirstMissing = nullptr; + if (pindex == pindexFirstNeverProcessed) pindexFirstNeverProcessed = nullptr; + if (pindex == pindexFirstNotTreeValid) pindexFirstNotTreeValid = nullptr; + if (pindex == pindexFirstNotTransactionsValid) pindexFirstNotTransactionsValid = nullptr; + if (pindex == pindexFirstNotChainValid) pindexFirstNotChainValid = nullptr; + if (pindex == pindexFirstNotScriptsValid) pindexFirstNotScriptsValid = nullptr; // Find our parent. CBlockIndex* pindexPar = pindex->pprev; // Find which child we just visited. @@ -4307,7 +4307,7 @@ bool LoadMempool(void) CValidationState state; if (nTime + nExpiryTimeout > nNow) { LOCK(cs_main); - AcceptToMemoryPoolWithTime(chainparams, mempool, state, tx, true, NULL, nTime, NULL, false, 0); + AcceptToMemoryPoolWithTime(chainparams, mempool, state, tx, true, nullptr, nTime, nullptr, false, 0); if (state.IsValid()) { ++count; } else { @@ -4383,10 +4383,10 @@ 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) + if (pindex == nullptr) return 0.0; - int64_t nNow = time(NULL); + int64_t nNow = time(nullptr); double fTxTotal; diff --git a/src/validation.h b/src/validation.h index b44b72d2d..edb8eab89 100644 --- a/src/validation.h +++ b/src/validation.h @@ -246,7 +246,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr& block, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex=NULL); +bool ProcessNewBlockHeaders(const std::vector& block, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex=nullptr); /** Check whether enough disk space is available for an incoming block */ bool CheckDiskSpace(uint64_t nAdditionalBytes = 0); @@ -255,7 +255,7 @@ FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly = false); /** Translation to a filesystem path */ fs::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix); /** Import blocks from an external file */ -bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskBlockPos *dbp = NULL); +bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskBlockPos *dbp = nullptr); /** Ensures we have a genesis block in the block tree, possibly writing one to disk. */ bool LoadGenesisBlock(const CChainParams& chainparams); /** Load the block tree and coins database from disk, @@ -300,7 +300,7 @@ void PruneBlockFilesManual(int nManualPruneHeight); /** (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, std::list* plTxnReplaced = NULL, + bool* pfMissingInputs, std::list* plTxnReplaced = nullptr, bool fOverrideMempoolLimit=false, const CAmount nAbsurdFee=0); /** Convert CValidationState to a human-readable message for logging */ @@ -346,7 +346,7 @@ bool TestLockPointValidity(const LockPoints* lp); * * See consensus/consensus.h for flag definitions. */ -bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp = NULL, bool useExistingLockPoints = false); +bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp = nullptr, bool useExistingLockPoints = false); /** * Closure representing one script verification diff --git a/src/versionbits.cpp b/src/versionbits.cpp index 8047e17aa..04a692d82 100644 --- a/src/versionbits.cpp +++ b/src/versionbits.cpp @@ -28,14 +28,14 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* int64_t nTimeTimeout = EndTime(params); // A block's state is always the same as that of the first of its period, so it is computed based on a pindexPrev whose height equals a multiple of nPeriod - 1. - if (pindexPrev != NULL) { + if (pindexPrev != nullptr) { pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod)); } // Walk backwards in steps of nPeriod to find a pindexPrev whose information is known std::vector vToCompute; while (cache.count(pindexPrev) == 0) { - if (pindexPrev == NULL) { + if (pindexPrev == nullptr) { // The genesis block is by definition defined. cache[pindexPrev] = THRESHOLD_DEFINED; break; @@ -112,7 +112,7 @@ BIP9Stats AbstractThresholdConditionChecker::GetStateStatisticsFor(const CBlockI stats.period = Period(params); stats.threshold = Threshold(params); - if (pindex == NULL) + if (pindex == nullptr) return stats; // Find beginning of period @@ -150,12 +150,12 @@ int AbstractThresholdConditionChecker::GetStateSinceHeightFor(const CBlockIndex* // right now pindexPrev points to the block prior to the block that we are computing for, thus: // if we are computing for the last block of a period, then pindexPrev points to the second to last block of the period, and // if we are computing for the first block of a period, then pindexPrev points to the last block of the previous period. - // The parent of the genesis block is represented by NULL. + // The parent of the genesis block is represented by nullptr. pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod)); const CBlockIndex* previousPeriodParent = pindexPrev->GetAncestor(pindexPrev->nHeight - nPeriod); - while (previousPeriodParent != NULL && GetStateFor(previousPeriodParent, params, cache) == initialState) { + while (previousPeriodParent != nullptr && GetStateFor(previousPeriodParent, params, cache) == initialState) { pindexPrev = previousPeriodParent; previousPeriodParent = pindexPrev->GetAncestor(pindexPrev->nHeight - nPeriod); } diff --git a/src/versionbits.h b/src/versionbits.h index f4dfb7151..71dc0c850 100644 --- a/src/versionbits.h +++ b/src/versionbits.h @@ -27,7 +27,7 @@ enum ThresholdState { // A map that gives the state for blocks whose height is a multiple of Period(). // The map is indexed by the block's parent, however, so all keys in the map -// will either be NULL or a block with (height + 1) % Period() == 0. +// will either be nullptr or a block with (height + 1) % Period() == 0. typedef std::map ThresholdConditionCache; struct VBDeploymentInfo { diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index da2d18075..4c06c2c1f 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -47,7 +47,7 @@ void CDBEnv::Reset() fMockDb = false; } -CDBEnv::CDBEnv() : dbenv(NULL) +CDBEnv::CDBEnv() : dbenv(nullptr) { Reset(); } @@ -56,7 +56,7 @@ CDBEnv::~CDBEnv() { EnvShutdown(); delete dbenv; - dbenv = NULL; + dbenv = nullptr; } void CDBEnv::Close() @@ -125,7 +125,7 @@ void CDBEnv::MakeMock() dbenv->set_lk_max_objects(10000); dbenv->set_flags(DB_AUTO_COMMIT, 1); dbenv->log_set_config(DB_LOG_IN_MEMORY, 1); - int ret = dbenv->open(NULL, + int ret = dbenv->open(nullptr, DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | @@ -147,10 +147,10 @@ CDBEnv::VerifyResult CDBEnv::Verify(const std::string& strFile, recoverFunc_type assert(mapFileUseCount.count(strFile) == 0); Db db(dbenv, 0); - int result = db.verify(strFile.c_str(), NULL, NULL, 0); + int result = db.verify(strFile.c_str(), nullptr, nullptr, 0); if (result == 0) return VERIFY_OK; - else if (recoverFunc == NULL) + else if (recoverFunc == nullptr) return RECOVER_FAIL; // Try to recover: @@ -170,7 +170,7 @@ bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*reco int64_t now = GetTime(); newFilename = strprintf("%s.%d.bak", filename, now); - int result = bitdb.dbenv->dbrename(NULL, filename.c_str(), NULL, + int result = bitdb.dbenv->dbrename(nullptr, filename.c_str(), nullptr, newFilename.c_str(), DB_AUTO_COMMIT); if (result == 0) LogPrintf("Renamed %s to %s\n", filename, newFilename); @@ -190,7 +190,7 @@ bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*reco LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size()); std::unique_ptr pdbCopy(new Db(bitdb.dbenv, 0)); - int ret = pdbCopy->open(NULL, // Txn pointer + int ret = pdbCopy->open(nullptr, // Txn pointer filename.c_str(), // Filename "main", // Logical db name DB_BTREE, // Database type @@ -299,7 +299,7 @@ bool CDBEnv::Salvage(const std::string& strFile, bool fAggressive, std::vectormapFileUseCount[strFile]; pdb = env->mapDb[strFile]; - if (pdb == NULL) { + if (pdb == nullptr) { int ret; pdb = new Db(env->dbenv, 0); @@ -392,8 +392,8 @@ CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb throw std::runtime_error(strprintf("CDB: Failed to configure for no temp file backing for database %s", strFile)); } - ret = pdb->open(NULL, // Txn pointer - fMockDb ? NULL : strFile.c_str(), // Filename + ret = pdb->open(nullptr, // Txn pointer + fMockDb ? nullptr : strFile.c_str(), // Filename fMockDb ? strFile.c_str() : "main", // Logical db name DB_BTREE, // Database type nFlags, // Flags @@ -401,7 +401,7 @@ CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb if (ret != 0) { delete pdb; - pdb = NULL; + pdb = nullptr; --env->mapFileUseCount[strFile]; strFile = ""; throw std::runtime_error(strprintf("CDB: Error %d, can't open database %s", ret, strFilename)); @@ -443,8 +443,8 @@ void CDB::Close() return; if (activeTxn) activeTxn->abort(); - activeTxn = NULL; - pdb = NULL; + activeTxn = nullptr; + pdb = nullptr; if (fFlushOnClose) Flush(); @@ -459,12 +459,12 @@ void CDBEnv::CloseDb(const std::string& strFile) { { LOCK(cs_db); - if (mapDb[strFile] != NULL) { + if (mapDb[strFile] != nullptr) { // Close the database handle Db* pdb = mapDb[strFile]; pdb->close(0); delete pdb; - mapDb[strFile] = NULL; + mapDb[strFile] = nullptr; } } } @@ -492,7 +492,7 @@ bool CDB::Rewrite(CWalletDBWrapper& dbw, const char* pszSkip) CDB db(dbw, "r"); Db* pdbCopy = new Db(env->dbenv, 0); - int ret = pdbCopy->open(NULL, // Txn pointer + int ret = pdbCopy->open(nullptr, // Txn pointer strFileRes.c_str(), // Filename "main", // Logical db name DB_BTREE, // Database type @@ -527,7 +527,7 @@ bool CDB::Rewrite(CWalletDBWrapper& dbw, const char* pszSkip) } Dbt datKey(ssKey.data(), ssKey.size()); Dbt datValue(ssValue.data(), ssValue.size()); - int ret2 = pdbCopy->put(NULL, &datKey, &datValue, DB_NOOVERWRITE); + int ret2 = pdbCopy->put(nullptr, &datKey, &datValue, DB_NOOVERWRITE); if (ret2 > 0) fSuccess = false; } @@ -541,10 +541,10 @@ bool CDB::Rewrite(CWalletDBWrapper& dbw, const char* pszSkip) } if (fSuccess) { Db dbA(env->dbenv, 0); - if (dbA.remove(strFile.c_str(), NULL, 0)) + if (dbA.remove(strFile.c_str(), nullptr, 0)) fSuccess = false; Db dbB(env->dbenv, 0); - if (dbB.rename(strFileRes.c_str(), NULL, strFile.c_str(), 0)) + if (dbB.rename(strFileRes.c_str(), nullptr, strFile.c_str(), 0)) fSuccess = false; } if (!fSuccess) diff --git a/src/wallet/db.h b/src/wallet/db.h index 4f3ad0c42..3614e34fb 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -77,10 +77,10 @@ class CDBEnv DbTxn* TxnBegin(int flags = DB_TXN_WRITE_NOSYNC) { - DbTxn* ptxn = NULL; - int ret = dbenv->txn_begin(NULL, &ptxn, flags); + DbTxn* ptxn = nullptr; + int ret = dbenv->txn_begin(nullptr, &ptxn, flags); if (!ptxn || ret != 0) - return NULL; + return nullptr; return ptxn; } }; @@ -191,7 +191,7 @@ class CDB int ret = pdb->get(activeTxn, &datKey, &datValue, 0); memory_cleanse(datKey.get_data(), datKey.get_size()); bool success = false; - if (datValue.get_data() != NULL) { + if (datValue.get_data() != nullptr) { // Unserialize value try { CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK, CLIENT_VERSION); @@ -282,11 +282,11 @@ class CDB Dbc* GetCursor() { if (!pdb) - return NULL; - Dbc* pcursor = NULL; - int ret = pdb->cursor(NULL, &pcursor, 0); + return nullptr; + Dbc* pcursor = nullptr; + int ret = pdb->cursor(nullptr, &pcursor, 0); if (ret != 0) - return NULL; + return nullptr; return pcursor; } @@ -306,7 +306,7 @@ class CDB int ret = pcursor->get(&datKey, &datValue, fFlags); if (ret != 0) return ret; - else if (datKey.get_data() == NULL || datValue.get_data() == NULL) + else if (datKey.get_data() == nullptr || datValue.get_data() == nullptr) return 99999; // Convert to streams @@ -342,7 +342,7 @@ class CDB if (!pdb || !activeTxn) return false; int ret = activeTxn->commit(0); - activeTxn = NULL; + activeTxn = nullptr; return (ret == 0); } @@ -351,7 +351,7 @@ class CDB if (!pdb || !activeTxn) return false; int ret = activeTxn->abort(); - activeTxn = NULL; + activeTxn = nullptr; return (ret == 0); } @@ -366,7 +366,7 @@ class CDB return Write(std::string("version"), nVersion); } - bool static Rewrite(CWalletDBWrapper& dbw, const char* pszSkip = NULL); + bool static Rewrite(CWalletDBWrapper& dbw, const char* pszSkip = nullptr); }; #endif // BITCOIN_WALLET_DB_H diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index a7c229fa7..2c6d93e1a 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1818,8 +1818,8 @@ UniValue listsinceblock(const JSONRPCRequest& request) LOCK2(cs_main, pwallet->cs_wallet); - const CBlockIndex* pindex = NULL; // Block index of the specified block or the common ancestor, if the block provided was in a deactivated chain. - const CBlockIndex* paltindex = NULL; // Block index of the specified block, even if it's in a deactivated chain. + const CBlockIndex* pindex = nullptr; // Block index of the specified block or the common ancestor, if the block provided was in a deactivated chain. + const CBlockIndex* paltindex = nullptr; // Block index of the specified block, even if it's in a deactivated chain. int target_confirms = 1; isminefilter filter = ISMINE_SPENDABLE; @@ -2720,10 +2720,10 @@ UniValue listunspent(const JSONRPCRequest& request) UniValue results(UniValue::VARR); std::vector vecOutputs; - assert(pwallet != NULL); + assert(pwallet != nullptr); LOCK2(cs_main, pwallet->cs_wallet); - pwallet->AvailableCoins(vecOutputs, !include_unsafe, NULL, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, nMinDepth, nMaxDepth); + pwallet->AvailableCoins(vecOutputs, !include_unsafe, nullptr, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, nMinDepth, nMaxDepth); for (const COutput& out : vecOutputs) { CTxDestination address; const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey; diff --git a/src/wallet/rpcwallet.h b/src/wallet/rpcwallet.h index 31e2f6a69..db0808b93 100644 --- a/src/wallet/rpcwallet.h +++ b/src/wallet/rpcwallet.h @@ -14,7 +14,7 @@ void RegisterWalletRPCCommands(CRPCTable &t); * Figures out what wallet, if any, to use for a JSONRPCRequest. * * @param[in] request JSONRPCRequest that wishes to access a wallet - * @return NULL if no wallet should be used, or a pointer to the CWallet + * @return nullptr if no wallet should be used, or a pointer to the CWallet */ CWallet *GetWalletForJSONRPCRequest(const JSONRPCRequest& request); diff --git a/src/wallet/test/crypto_tests.cpp b/src/wallet/test/crypto_tests.cpp index 744063624..98d957b32 100644 --- a/src/wallet/test/crypto_tests.cpp +++ b/src/wallet/test/crypto_tests.cpp @@ -48,7 +48,7 @@ bool OldEncrypt(const CKeyingMaterial& vchPlaintext, std::vector bool fOk = true; EVP_CIPHER_CTX_init(ctx); - if (fOk) fOk = EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, chKey, chIV) != 0; + if (fOk) fOk = EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, chKey, chIV) != 0; if (fOk) fOk = EVP_EncryptUpdate(ctx, &vchCiphertext[0], &nCLen, &vchPlaintext[0], nLen) != 0; if (fOk) fOk = EVP_EncryptFinal_ex(ctx, (&vchCiphertext[0]) + nCLen, &nFLen) != 0; EVP_CIPHER_CTX_cleanup(ctx); @@ -76,7 +76,7 @@ bool OldDecrypt(const std::vector& vchCiphertext, CKeyingMaterial bool fOk = true; EVP_CIPHER_CTX_init(ctx); - if (fOk) fOk = EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, chKey, chIV) != 0; + if (fOk) fOk = EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, chKey, chIV) != 0; if (fOk) fOk = EVP_DecryptUpdate(ctx, &vchPlaintext[0], &nPLen, &vchCiphertext[0], nLen) != 0; if (fOk) fOk = EVP_DecryptFinal_ex(ctx, (&vchPlaintext[0]) + nPLen, &nFLen) != 0; EVP_CIPHER_CTX_cleanup(ctx); diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp index 922fcc8e8..e2f48c45a 100644 --- a/src/wallet/test/wallet_test_fixture.cpp +++ b/src/wallet/test/wallet_test_fixture.cpp @@ -28,7 +28,7 @@ WalletTestingSetup::~WalletTestingSetup() { UnregisterValidationInterface(pwalletMain); delete pwalletMain; - pwalletMain = NULL; + pwalletMain = nullptr; bitdb.Flush(true); bitdb.Reset(); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index d236b1de3..17b101ffe 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -85,7 +85,7 @@ const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const LOCK(cs_wallet); std::map::const_iterator it = mapWallet.find(hash); if (it == mapWallet.end()) - return NULL; + return nullptr; return &(it->second); } @@ -182,10 +182,10 @@ bool CWallet::AddKeyPubKeyWithDB(CWalletDB &walletdb, const CKey& secret, const pwalletdbEncryption = &walletdb; } if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey)) { - if (needsDB) pwalletdbEncryption = NULL; + if (needsDB) pwalletdbEncryption = nullptr; return false; } - if (needsDB) pwalletdbEncryption = NULL; + if (needsDB) pwalletdbEncryption = nullptr; // check if we need to remove from watch-only CScript script; @@ -525,7 +525,7 @@ void CWallet::SyncMetaData(std::pair ran // So: find smallest nOrderPos: int nMinOrderPos = std::numeric_limits::max(); - const CWalletTx* copyFrom = NULL; + const CWalletTx* copyFrom = nullptr; for (TxSpends::iterator it = range.first; it != range.second; ++it) { const uint256& hash = it->second; @@ -640,7 +640,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) pwalletdbEncryption = new CWalletDB(*dbw); if (!pwalletdbEncryption->TxnBegin()) { delete pwalletdbEncryption; - pwalletdbEncryption = NULL; + pwalletdbEncryption = nullptr; return false; } pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey); @@ -665,7 +665,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) } delete pwalletdbEncryption; - pwalletdbEncryption = NULL; + pwalletdbEncryption = nullptr; Lock(); Unlock(strWalletPassphrase); @@ -988,7 +988,7 @@ bool CWallet::LoadToWallet(const CWalletTx& wtxIn) /** * 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 - * pIndex == NULL, then wallet state is not updated in AddToWallet, but + * pIndex == nullptr, 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. @@ -1004,7 +1004,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const CBlockI { AssertLockHeld(cs_wallet); - if (pIndex != NULL) { + if (pIndex != nullptr) { for (const CTxIn& txin : tx.vin) { std::pair range = mapTxSpends.equal_range(txin.prevout); while (range.first != range.second) { @@ -1024,7 +1024,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const CBlockI CWalletTx wtx(this, ptx); // Get merkle branch if transaction was found in a block - if (pIndex != NULL) + if (pIndex != nullptr) wtx.SetMerkleBranch(pIndex, posInBlock); return AddToWallet(wtx, false); @@ -1648,7 +1648,7 @@ bool CWalletTx::RelayWalletTransaction(CConnman* connman) std::set CWalletTx::GetConflicts() const { std::set result; - if (pwallet != NULL) + if (pwallet != nullptr) { uint256 myHash = GetHash(); result = pwallet->GetConflicts(myHash); @@ -1845,7 +1845,7 @@ bool CWalletTx::IsTrusted() const { // Transactions not sent by us: not trusted const CWalletTx* parent = pwallet->GetWalletTx(txin.prevout.hash); - if (parent == NULL) + if (parent == nullptr) return false; const CTxOut& parentOut = parent->tx->vout[txin.prevout.n]; if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE) @@ -3889,11 +3889,11 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx); if (nZapWalletRet != DB_LOAD_OK) { InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile)); - return NULL; + return nullptr; } delete tempWallet; - tempWallet = NULL; + tempWallet = nullptr; } uiInterface.InitMessage(_("Loading wallet...")); @@ -3907,7 +3907,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) { if (nLoadWalletRet == DB_CORRUPT) { InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile)); - return NULL; + return nullptr; } else if (nLoadWalletRet == DB_NONCRITICAL_ERROR) { @@ -3917,16 +3917,16 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) } else if (nLoadWalletRet == DB_TOO_NEW) { InitError(strprintf(_("Error loading %s: Wallet requires newer version of %s"), walletFile, _(PACKAGE_NAME))); - return NULL; + return nullptr; } else if (nLoadWalletRet == DB_NEED_REWRITE) { InitError(strprintf(_("Wallet needed to be rewritten: restart %s to complete"), _(PACKAGE_NAME))); - return NULL; + return nullptr; } else { InitError(strprintf(_("Error loading %s"), walletFile)); - return NULL; + return nullptr; } } @@ -3944,7 +3944,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) if (nMaxVersion < walletInstance->GetVersion()) { InitError(_("Cannot downgrade wallet")); - return NULL; + return nullptr; } walletInstance->SetMaxVersion(nMaxVersion); } @@ -3967,7 +3967,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) walletInstance->SetDefaultKey(newDefaultKey); if (!walletInstance->SetAddressBook(walletInstance->vchDefaultKey.GetID(), "", "receive")) { InitError(_("Cannot write default address") += "\n"); - return NULL; + return nullptr; } } @@ -3977,11 +3977,11 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) bool useHD = GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET); if (walletInstance->IsHDEnabled() && !useHD) { InitError(strprintf(_("Error loading %s: You can't disable HD on an already existing HD wallet"), walletFile)); - return NULL; + return nullptr; } if (!walletInstance->IsHDEnabled() && useHD) { InitError(strprintf(_("Error loading %s: You can't enable HD on an already existing non-HD wallet"), walletFile)); - return NULL; + return nullptr; } } @@ -4010,7 +4010,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) if (pindexRescan != block) { InitError(_("Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)")); - return NULL; + return nullptr; } } @@ -4280,5 +4280,5 @@ int CMerkleTx::GetBlocksToMaturity() const bool CMerkleTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state) { - return ::AcceptToMemoryPool(mempool, state, tx, true, NULL, NULL, false, nAbsurdFee); + return ::AcceptToMemoryPool(mempool, state, tx, true, nullptr, nullptr, false, nAbsurdFee); } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 7ef2e6f1d..a75f1b3fe 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -342,7 +342,7 @@ class CWalletTx : public CMerkleTx CWalletTx() { - Init(NULL); + Init(nullptr); } CWalletTx(const CWallet* pwalletIn, CTransactionRef arg) : CMerkleTx(std::move(arg)) @@ -386,7 +386,7 @@ class CWalletTx : public CMerkleTx template inline void SerializationOp(Stream& s, Operation ser_action) { if (ser_action.ForRead()) - Init(NULL); + Init(nullptr); char fSpent = false; if (!ser_action.ForRead()) @@ -662,7 +662,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface * all coins from coinControl are selected; Never select unconfirmed coins * if they are not ours */ - bool SelectCoins(const std::vector& vAvailableCoins, const CAmount& nTargetValue, std::set& setCoinsRet, CAmount& nValueRet, const CCoinControl *coinControl = NULL) const; + bool SelectCoins(const std::vector& vAvailableCoins, const CAmount& nTargetValue, std::set& setCoinsRet, CAmount& nValueRet, const CCoinControl *coinControl = nullptr) const; CWalletDB *pwalletdbEncryption; @@ -693,7 +693,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface /* Used by TransactionAddedToMemorypool/BlockConnected/Disconnected. * Should be called with pindexBlock and posInBlock if this is for a transaction that is included in a block. */ - void SyncTransaction(const CTransactionRef& tx, const CBlockIndex *pindex = NULL, int posInBlock = 0); + void SyncTransaction(const CTransactionRef& tx, const CBlockIndex *pindex = nullptr, int posInBlock = 0); /* the HD chain data model (external chain counters) */ CHDChain hdChain; @@ -786,7 +786,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface ~CWallet() { delete pwalletdbEncryption; - pwalletdbEncryption = NULL; + pwalletdbEncryption = nullptr; } void SetNull() @@ -794,7 +794,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface nWalletVersion = FEATURE_BASE; nWalletMaxVersion = FEATURE_BASE; nMasterKeyMaxID = 0; - pwalletdbEncryption = NULL; + pwalletdbEncryption = nullptr; nOrderPosNext = 0; nAccountingEntryNumber = 0; nNextResend = 0; @@ -832,7 +832,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface /** * populate vCoins with vector of available COutputs. */ - void AvailableCoins(std::vector& vCoins, bool fOnlySafe=true, const CCoinControl *coinControl = NULL, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t& nMaximumCount = 0, const int& nMinDepth = 0, const int& nMaxDepth = 9999999) const; + void AvailableCoins(std::vector& vCoins, bool fOnlySafe=true, const CCoinControl *coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t& nMaximumCount = 0, const int& nMinDepth = 0, const int& nMaxDepth = 9999999) const; /** * Return list of available coins and locked coins grouped by non-change output address. @@ -921,7 +921,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface * Increment the next transaction order id * @return next transaction order id */ - int64_t IncOrderPosNext(CWalletDB *pwalletdb = NULL); + int64_t IncOrderPosNext(CWalletDB *pwalletdb = nullptr); DBErrors ReorderTransactions(); bool AccountMove(std::string strFrom, std::string strTo, CAmount nAmount, std::string strComment = ""); bool GetAccountPubkey(CPubKey &pubKey, std::string strAccount, bool bForceNew = false); @@ -1049,7 +1049,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool SetDefaultKey(const CPubKey &vchPubKey); //! signify that a particular wallet feature is now used. this may change nWalletVersion and nWalletMaxVersion if those are lower - bool SetMinVersion(enum WalletFeature, CWalletDB* pwalletdbIn = NULL, bool fExplicit = false); + bool SetMinVersion(enum WalletFeature, CWalletDB* pwalletdbIn = nullptr, bool fExplicit = false); //! change which version we're allowed to upgrade to (note that this does not immediately imply upgrading to that format) bool SetMaxVersion(int nVersion); diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 65a28af46..c8dc52626 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -787,7 +787,7 @@ bool CWalletDB::Recover(const std::string& filename, std::string& out_backup_fil { // recover without a key filter callback // results in recovering all record types - return CWalletDB::Recover(filename, NULL, NULL, out_backup_filename); + return CWalletDB::Recover(filename, nullptr, nullptr, out_backup_filename); } bool CWalletDB::RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDataStream ssValue) diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp index c06389805..b637f26fb 100644 --- a/src/zmq/zmqnotificationinterface.cpp +++ b/src/zmq/zmqnotificationinterface.cpp @@ -15,7 +15,7 @@ void zmqError(const char *str) LogPrint(BCLog::ZMQ, "zmq: Error: %s, errno=%s\n", str, zmq_strerror(errno)); } -CZMQNotificationInterface::CZMQNotificationInterface() : pcontext(NULL) +CZMQNotificationInterface::CZMQNotificationInterface() : pcontext(nullptr) { } @@ -31,7 +31,7 @@ CZMQNotificationInterface::~CZMQNotificationInterface() CZMQNotificationInterface* CZMQNotificationInterface::Create() { - CZMQNotificationInterface* notificationInterface = NULL; + CZMQNotificationInterface* notificationInterface = nullptr; std::map factories; std::list notifiers; @@ -62,7 +62,7 @@ CZMQNotificationInterface* CZMQNotificationInterface::Create() if (!notificationInterface->Initialize()) { delete notificationInterface; - notificationInterface = NULL; + notificationInterface = nullptr; } } From 9d5e98ff80479628492329b3b6bdce56d255baa0 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Sun, 30 Jul 2017 21:42:17 +0200 Subject: [PATCH 035/382] Fix typos. --- contrib/linearize/README.md | 2 +- src/chain.h | 2 +- src/qt/test/rpcnestedtests.cpp | 2 +- src/wallet/rpcwallet.cpp | 4 ++-- src/wallet/wallet.cpp | 2 +- test/functional/rawtransactions.py | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/contrib/linearize/README.md b/contrib/linearize/README.md index f2a2ab276..298510698 100644 --- a/contrib/linearize/README.md +++ b/contrib/linearize/README.md @@ -46,7 +46,7 @@ linearize-hashes.py. (Default: `1000*1000*1000 bytes`) * `netmagic`: Network magic number. * `out_of_order_cache_sz`: If out-of-order blocks are being read, the block can -be written to a cache so that the blockchain doesn't have to be seeked again. +be written to a cache so that the blockchain doesn't have to be sought again. This option specifies the cache size. (Default: `100*1000*1000 bytes`) * `rev_hash_bytes`: If true, the block hash list written by linearize-hashes.py will be byte-reversed when read by linearize-data.py. See the linearize-hashes diff --git a/src/chain.h b/src/chain.h index c5304b7d6..593de1917 100644 --- a/src/chain.h +++ b/src/chain.h @@ -216,7 +216,7 @@ class CBlockIndex //! (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. + //! (memory only) Maximum nTime in the chain up to and including this block. unsigned int nTimeMax; void SetNull() diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp index fbad9e544..aecf0d3a7 100644 --- a/src/qt/test/rpcnestedtests.cpp +++ b/src/qt/test/rpcnestedtests.cpp @@ -75,7 +75,7 @@ void RPCNestedTests::rpcNestedTests() (RPCConsole::RPCExecuteCommandLine(result, "createrawtransaction [] {} 0")); //parameter not in brackets are allowed (RPCConsole::RPCExecuteCommandLine(result2, "createrawtransaction([],{},0)")); //parameter in brackets are allowed QVERIFY(result == result2); - (RPCConsole::RPCExecuteCommandLine(result2, "createrawtransaction( [], {} , 0 )")); //whitespace between parametres is allowed + (RPCConsole::RPCExecuteCommandLine(result2, "createrawtransaction( [], {} , 0 )")); //whitespace between parameters is allowed QVERIFY(result == result2); RPCConsole::RPCExecuteCommandLine(result, "getblock(getbestblockhash())[tx][0]", &filtered); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 057379d8d..803db19e1 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1134,7 +1134,7 @@ class Witnessifier : public boost::static_visitor SignatureData sigs; // This check is to make sure that the script we created can actually be solved for and signed by us // if we were to have the private keys. This is just to make sure that the script is valid and that, - // if found in a transaction, we would still accept and relay that transcation. + // if found in a transaction, we would still accept and relay that transaction. if (!ProduceSignature(DummySignatureCreator(pwallet), witscript, sigs) || !VerifyScript(sigs.scriptSig, witscript, &sigs.scriptWitness, MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, DummySignatureCreator(pwallet).Checker())) { return false; @@ -1159,7 +1159,7 @@ class Witnessifier : public boost::static_visitor SignatureData sigs; // This check is to make sure that the script we created can actually be solved for and signed by us // if we were to have the private keys. This is just to make sure that the script is valid and that, - // if found in a transaction, we would still accept and relay that transcation. + // if found in a transaction, we would still accept and relay that transaction. if (!ProduceSignature(DummySignatureCreator(pwallet), witscript, sigs) || !VerifyScript(sigs.scriptSig, witscript, &sigs.scriptWitness, MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, DummySignatureCreator(pwallet).Checker())) { return false; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 18cc3bd02..05d138d2d 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2514,7 +2514,7 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC if (nChangePosInOut != -1) { tx.vout.insert(tx.vout.begin() + nChangePosInOut, wtx.tx->vout[nChangePosInOut]); - // we dont have the normal Create/Commit cycle, and dont want to risk reusing change, + // we don't have the normal Create/Commit cycle, and don't want to risk reusing change, // so just remove the key from the keypool here. reservekey.KeepKey(); } diff --git a/test/functional/rawtransactions.py b/test/functional/rawtransactions.py index 6272fc69b..b6b90d678 100755 --- a/test/functional/rawtransactions.py +++ b/test/functional/rawtransactions.py @@ -2,7 +2,7 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -"""Test the rawtranscation RPCs. +"""Test the rawtransaction RPCs. Test the following RPCs: - createrawtransaction From f4c3d2c9da76489291df72136a1ec80a5c7c712f Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Tue, 8 Aug 2017 20:12:45 +0000 Subject: [PATCH 036/382] Enable disablesafemode by default. Safemode is almost useless as is-- it only triggers in limited cases most of which aren't even concerning. There have been several proposals to remove it. But as a simpler, safer, and more flexible first case, simply deactivate it by default. Anyone who wants it can re-enable and know what they've signed up for. --- src/init.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/init.cpp b/src/init.cpp index ca62d3e7c..6fc8af5fb 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -69,7 +69,7 @@ bool fFeeEstimatesInitialized = false; static const bool DEFAULT_PROXYRANDOMIZE = true; static const bool DEFAULT_REST_ENABLE = false; -static const bool DEFAULT_DISABLE_SAFEMODE = false; +static const bool DEFAULT_DISABLE_SAFEMODE = true; static const bool DEFAULT_STOPAFTERBLOCKIMPORT = false; std::unique_ptr g_connman; From 3f8fa7f58b68a8ed596c62d1edb85a3455a5a724 Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Tue, 8 Aug 2017 21:44:02 -0400 Subject: [PATCH 037/382] Make sure to clean up mapBlockSource if we've already seen the block Credit TheBlueMatt --- src/net_processing.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/net_processing.cpp b/src/net_processing.cpp index a743f04dd..4e63cd1a7 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -2134,9 +2134,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr } bool fNewBlock = false; ProcessNewBlock(chainparams, pblock, true, &fNewBlock); - if (fNewBlock) + if (fNewBlock) { pfrom->nLastBlockTime = GetTime(); - + } else { + LOCK(cs_main); + mapBlockSource.erase(pblock->GetHash()); + } LOCK(cs_main); // hold cs_main for CBlockIndex::IsValid() if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS)) { // Clear download state for this block, which is in @@ -2211,8 +2214,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // Since we requested this block (it was in mapBlocksInFlight), force it to be processed, // even if it would not be a candidate for new tip (missing previous block, chain not long enough, etc) ProcessNewBlock(chainparams, pblock, true, &fNewBlock); - if (fNewBlock) + if (fNewBlock) { pfrom->nLastBlockTime = GetTime(); + } else { + LOCK(cs_main); + mapBlockSource.erase(pblock->GetHash()); + } } } @@ -2390,8 +2397,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr } bool fNewBlock = false; ProcessNewBlock(chainparams, pblock, forceProcessing, &fNewBlock); - if (fNewBlock) + if (fNewBlock) { pfrom->nLastBlockTime = GetTime(); + } else { + LOCK(cs_main); + mapBlockSource.erase(pblock->GetHash()); + } } From 03bc719a85cb4928cb4b43d0bc4142f72cb01b23 Mon Sep 17 00:00:00 2001 From: Karl-Johan Alm Date: Wed, 9 Aug 2017 17:24:15 +0900 Subject: [PATCH 038/382] [wallet] Close DB on error. --- src/wallet/db.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index da2d18075..5bf944ec8 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -101,8 +101,10 @@ bool CDBEnv::Open(const fs::path& pathIn) DB_RECOVER | nEnvFlags, S_IRUSR | S_IWUSR); - if (ret != 0) + if (ret != 0) { + dbenv->close(0); return error("CDBEnv::Open: Error %d opening database environment: %s\n", ret, DbEnv::strerror(ret)); + } fDbEnvInit = true; fMockDb = false; @@ -196,9 +198,9 @@ bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*reco DB_BTREE, // Database type DB_CREATE, // Flags 0); - if (ret > 0) - { + if (ret > 0) { LogPrintf("Cannot create database file %s\n", filename); + pdbCopy->close(0); return false; } @@ -536,8 +538,10 @@ bool CDB::Rewrite(CWalletDBWrapper& dbw, const char* pszSkip) env->CloseDb(strFile); if (pdbCopy->close(0)) fSuccess = false; - delete pdbCopy; + } else { + pdbCopy->close(0); } + delete pdbCopy; } if (fSuccess) { Db dbA(env->dbenv, 0); From 1bcd44223cdc1b584c0cd0863fe9653f540cf856 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Thu, 29 Jun 2017 12:57:45 +0200 Subject: [PATCH 039/382] Remove the virtual specifier for functions with the override specifier `override` guarantees that the function is virtual (in addition to that the function is overriding a virtual function from a base class). --- src/dbwrapper.cpp | 2 +- src/keystore.h | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp index 3626e0177..750966fd3 100644 --- a/src/dbwrapper.cpp +++ b/src/dbwrapper.cpp @@ -19,7 +19,7 @@ class CBitcoinLevelDBLogger : public leveldb::Logger { public: // This code is adapted from posix_logger.h, which is why it is using vsprintf. // Please do not do this in normal code - virtual void Logv(const char * format, va_list ap) override { + void Logv(const char * format, va_list ap) override { if (!LogAcceptCategory(BCLog::LEVELDB)) { return; } diff --git a/src/keystore.h b/src/keystore.h index 965ae0c79..528b6e083 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -97,14 +97,14 @@ class CBasicKeyStore : public CKeyStore } return false; } - virtual bool AddCScript(const CScript& redeemScript) override; - virtual bool HaveCScript(const CScriptID &hash) const override; - virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const override; + bool AddCScript(const CScript& redeemScript) override; + bool HaveCScript(const CScriptID &hash) const override; + bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const override; - virtual bool AddWatchOnly(const CScript &dest) override; - virtual bool RemoveWatchOnly(const CScript &dest) override; - virtual bool HaveWatchOnly(const CScript &dest) const override; - virtual bool HaveWatchOnly() const override; + bool AddWatchOnly(const CScript &dest) override; + bool RemoveWatchOnly(const CScript &dest) override; + bool HaveWatchOnly(const CScript &dest) const override; + bool HaveWatchOnly() const override; }; typedef std::vector > CKeyingMaterial; From 8c2f4b88828b3e40f6cc690261657e66b2653432 Mon Sep 17 00:00:00 2001 From: Jeremy Rubin Date: Mon, 27 Mar 2017 14:25:18 -0400 Subject: [PATCH 040/382] Expose more parallelism with relaxed atomics (suggested in #9938). Fix a test to check the exclusive or of two properties rather than just or. --- src/test/checkqueue_tests.cpp | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp index 6ae0bcadd..c4564b45b 100644 --- a/src/test/checkqueue_tests.cpp +++ b/src/test/checkqueue_tests.cpp @@ -38,7 +38,7 @@ struct FakeCheckCheckCompletion { static std::atomic n_calls; bool operator()() { - ++n_calls; + n_calls.fetch_add(1, std::memory_order_relaxed); return true; } void swap(FakeCheckCheckCompletion& x){}; @@ -88,15 +88,15 @@ struct MemoryCheck { // // Really, copy constructor should be deletable, but CCheckQueue breaks // if it is deleted because of internal push_back. - fake_allocated_memory += b; + fake_allocated_memory.fetch_add(b, std::memory_order_relaxed); }; MemoryCheck(bool b_) : b(b_) { - fake_allocated_memory += b; + fake_allocated_memory.fetch_add(b, std::memory_order_relaxed); }; - ~MemoryCheck(){ - fake_allocated_memory -= b; - + ~MemoryCheck() + { + fake_allocated_memory.fetch_sub(b, std::memory_order_relaxed); }; void swap(MemoryCheck& x) { std::swap(b, x.b); }; }; @@ -117,9 +117,9 @@ struct FrozenCleanupCheck { { if (should_freeze) { std::unique_lock l(m); - nFrozen = 1; + nFrozen.store(1, std::memory_order_relaxed); cv.notify_one(); - cv.wait(l, []{ return nFrozen == 0;}); + cv.wait(l, []{ return nFrozen.load(std::memory_order_relaxed) == 0;}); } } void swap(FrozenCleanupCheck& x){std::swap(should_freeze, x.should_freeze);}; @@ -262,7 +262,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Recovers_From_Failure) control.Add(vChecks); } bool r =control.Wait(); - BOOST_REQUIRE(r || end_fails); + BOOST_REQUIRE(r != end_fails); } } tg.interrupt_all(); @@ -337,7 +337,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Memory) tg.join_all(); } -// Test that a new verification cannot occur until all checks +// Test that a new verification cannot occur until all checks // have been destructed BOOST_AUTO_TEST_CASE(test_CheckQueue_FrozenCleanup) { @@ -361,11 +361,14 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_FrozenCleanup) std::unique_lock l(FrozenCleanupCheck::m); // Wait until the queue has finished all jobs and frozen FrozenCleanupCheck::cv.wait(l, [](){return FrozenCleanupCheck::nFrozen == 1;}); - // Try to get control of the queue a bunch of times - for (auto x = 0; x < 100 && !fails; ++x) { - fails = queue->ControlMutex.try_lock(); - } - // Unfreeze + } + // Try to get control of the queue a bunch of times + for (auto x = 0; x < 100 && !fails; ++x) { + fails = queue->ControlMutex.try_lock(); + } + { + // Unfreeze (we need lock n case of spurious wakeup) + std::unique_lock l(FrozenCleanupCheck::m); FrozenCleanupCheck::nFrozen = 0; } // Awaken frozen destructor From cc5d38f4b826b897dd818bb22c7f5d881718fd35 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Fri, 31 Mar 2017 22:44:04 -0400 Subject: [PATCH 041/382] Add option to attach a python debugger if test fails --- test/functional/test_framework/test_framework.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 8d698a732..e562d1193 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -11,6 +11,7 @@ import logging import optparse import os +import pdb import shutil import subprocess import sys @@ -125,6 +126,8 @@ def main(self): help="Write tested RPC commands into this directory") parser.add_option("--configfile", dest="configfile", help="Location of the test framework config file") + parser.add_option("--pdbonfailure", dest="pdbonfailure", default=False, action="store_true", + help="Attach a python debugger if test fails") self.add_options(parser) (self.options, self.args) = parser.parse_args() @@ -162,6 +165,10 @@ def main(self): except KeyboardInterrupt as e: self.log.warning("Exiting after keyboard interrupt") + if success == TestStatus.FAILED and self.options.pdbonfailure: + print("Testcase failed. Attaching python debugger. Enter ? for help") + pdb.set_trace() + if not self.options.noshutdown: self.log.info("Stopping nodes") if self.nodes: From 095142d1f93f39ad2b88dbe8d40140223a1b3900 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Tue, 18 Jul 2017 15:49:56 -0400 Subject: [PATCH 042/382] [wallet] keypool mark-used and topup This commit adds basic keypool mark-used and topup: - try to topup the keypool on initial load - if a key in the keypool is used, mark all keys before that as used and try to top up --- src/wallet/wallet.cpp | 50 ++++++++++++++++++++++++++++++++++++ src/wallet/wallet.h | 4 +++ test/functional/wallet-hd.py | 10 +++++--- 3 files changed, 60 insertions(+), 4 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index ce345804e..b40dcceda 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -12,6 +12,7 @@ #include "consensus/consensus.h" #include "consensus/validation.h" #include "fs.h" +#include "init.h" #include "key.h" #include "keystore.h" #include "validation.h" @@ -1053,6 +1054,30 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const CBlockI if (fExisted && !fUpdate) return false; if (fExisted || IsMine(tx) || IsFromMe(tx)) { + /* Check if any keys in the wallet keypool that were supposed to be unused + * have appeared in a new transaction. If so, remove those keys from the keypool. + * This can happen when restoring an old wallet backup that does not contain + * the mostly recently created transactions from newer versions of the wallet. + */ + + // loop though all outputs + for (const CTxOut& txout: tx.vout) { + // extract addresses and check if they match with an unused keypool key + std::vector vAffected; + CAffectedKeysVisitor(*this, vAffected).Process(txout.scriptPubKey); + for (const CKeyID &keyid : vAffected) { + std::map::const_iterator mi = m_pool_key_to_index.find(keyid); + if (mi != m_pool_key_to_index.end()) { + LogPrintf("%s: Detected a used keypool key, mark all keypool key up to this key as used\n", __func__); + MarkReserveKeysAsUsed(mi->second); + + if (!TopUpKeyPool()) { + LogPrintf("%s: Topping up keypool failed (locked wallet)\n", __func__); + } + } + } + } + CWalletTx wtx(this, ptx); // Get merkle branch if transaction was found in a block @@ -3611,6 +3636,28 @@ void CReserveKey::ReturnKey() vchPubKey = CPubKey(); } +void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id) +{ + AssertLockHeld(cs_wallet); + bool internal = setInternalKeyPool.count(keypool_id); + if (!internal) assert(setExternalKeyPool.count(keypool_id)); + std::set *setKeyPool = internal ? &setInternalKeyPool : &setExternalKeyPool; + auto it = setKeyPool->begin(); + + CWalletDB walletdb(*dbw); + while (it != std::end(*setKeyPool)) { + const int64_t& index = *(it); + if (index > keypool_id) break; // set*KeyPool is ordered + + CKeyPool keypool; + if (walletdb.ReadPool(index, keypool)) { //TODO: This should be unnecessary + m_pool_key_to_index.erase(keypool.vchPubKey.GetID()); + } + walletdb.ErasePool(index); + it = setKeyPool->erase(it); + } +} + bool CWallet::HasUnusedKeys(int min_keys) const { return setExternalKeyPool.size() >= min_keys && (setInternalKeyPool.size() >= min_keys || !CanSupportFeature(FEATURE_HD_SPLIT)); @@ -3989,6 +4036,9 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) RegisterValidationInterface(walletInstance); + // Try to top up keypool. No-op if the wallet is locked. + walletInstance->TopUpKeyPool(); + CBlockIndex *pindexRescan = chainActive.Genesis(); if (!GetBoolArg("-rescan", false)) { diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 310300126..a9b90fe9a 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -977,6 +977,10 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface void ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey); bool GetKeyFromPool(CPubKey &key, bool internal = false); int64_t GetOldestKeyPoolTime(); + /** + * Marks all keys in the keypool up to and including reserve_key as used. + */ + void MarkReserveKeysAsUsed(int64_t keypool_id); const std::map& GetAllReserveKeys() const { return m_pool_key_to_index; } /** Does the wallet have at least min_keys in the keypool? */ bool HasUnusedKeys(int min_keys) const; diff --git a/test/functional/wallet-hd.py b/test/functional/wallet-hd.py index dfd3dc83c..821575ed1 100755 --- a/test/functional/wallet-hd.py +++ b/test/functional/wallet-hd.py @@ -9,7 +9,6 @@ assert_equal, connect_nodes_bi, ) -import os import shutil @@ -72,10 +71,12 @@ def run_test (self): self.log.info("Restore backup ...") self.stop_node(1) - os.remove(self.options.tmpdir + "/node1/regtest/wallet.dat") + # we need to delete the complete regtest directory + # otherwise node1 would auto-recover all funds in flag the keypool keys as used + shutil.rmtree(tmpdir + "/node1/regtest/blocks") + shutil.rmtree(tmpdir + "/node1/regtest/chainstate") shutil.copyfile(tmpdir + "/hd.bak", tmpdir + "/node1/regtest/wallet.dat") self.nodes[1] = self.start_node(1, self.options.tmpdir, self.extra_args[1]) - #connect_nodes_bi(self.nodes, 0, 1) # Assert that derivation is deterministic hd_add_2 = None @@ -85,11 +86,12 @@ def run_test (self): assert_equal(hd_info_2["hdkeypath"], "m/0'/0'/"+str(_+1)+"'") assert_equal(hd_info_2["hdmasterkeyid"], masterkeyid) assert_equal(hd_add, hd_add_2) + connect_nodes_bi(self.nodes, 0, 1) + self.sync_all() # Needs rescan self.stop_node(1) self.nodes[1] = self.start_node(1, self.options.tmpdir, self.extra_args[1] + ['-rescan']) - #connect_nodes_bi(self.nodes, 0, 1) assert_equal(self.nodes[1].getbalance(), num_hd_adds + 1) # send a tx and make sure its using the internal chain for the changeoutput From d34957e17e8c9740104533aaf4a896e93548c87e Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Wed, 3 May 2017 16:51:51 +0200 Subject: [PATCH 043/382] [wallet] [tests] Add keypool topup functional test --- test/functional/keypool-topup.py | 75 ++++++++++++++++++++++++++++++++ test/functional/test_runner.py | 1 + 2 files changed, 76 insertions(+) create mode 100755 test/functional/keypool-topup.py diff --git a/test/functional/keypool-topup.py b/test/functional/keypool-topup.py new file mode 100755 index 000000000..0e0c0ea74 --- /dev/null +++ b/test/functional/keypool-topup.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test HD Wallet keypool restore function. + +Two nodes. Node1 is under test. Node0 is providing transactions and generating blocks. + +- Start node1, shutdown and backup wallet. +- Generate 110 keys (enough to drain the keypool). Store key 90 (in the initial keypool) and key 110 (beyond the initial keypool). Send funds to key 90 and key 110. +- Stop node1, clear the datadir, move wallet file back into the datadir and restart node1. +- connect node1 to node0. Verify that they sync and node1 receives its funds.""" +import shutil + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + assert_equal, + connect_nodes_bi, + sync_blocks, +) + +class KeypoolRestoreTest(BitcoinTestFramework): + def __init__(self): + super().__init__() + self.setup_clean_chain = True + self.num_nodes = 2 + self.extra_args = [['-usehd=0'], ['-usehd=1', '-keypool=100', '-keypoolmin=20']] + + def run_test(self): + self.tmpdir = self.options.tmpdir + self.nodes[0].generate(101) + + self.log.info("Make backup of wallet") + + self.stop_node(1) + + shutil.copyfile(self.tmpdir + "/node1/regtest/wallet.dat", self.tmpdir + "/wallet.bak") + self.nodes[1] = self.start_node(1, self.tmpdir, self.extra_args[1]) + connect_nodes_bi(self.nodes, 0, 1) + + self.log.info("Generate keys for wallet") + + for _ in range(90): + addr_oldpool = self.nodes[1].getnewaddress() + for _ in range(20): + addr_extpool = self.nodes[1].getnewaddress() + + self.log.info("Send funds to wallet") + + self.nodes[0].sendtoaddress(addr_oldpool, 10) + self.nodes[0].generate(1) + self.nodes[0].sendtoaddress(addr_extpool, 5) + self.nodes[0].generate(1) + sync_blocks(self.nodes) + + self.log.info("Restart node with wallet backup") + + self.stop_node(1) + + shutil.copyfile(self.tmpdir + "/wallet.bak", self.tmpdir + "/node1/regtest/wallet.dat") + + self.log.info("Verify keypool is restored and balance is correct") + + self.nodes[1] = self.start_node(1, self.tmpdir, self.extra_args[1]) + connect_nodes_bi(self.nodes, 0, 1) + self.sync_all() + + assert_equal(self.nodes[1].getbalance(), 15) + assert_equal(self.nodes[1].listtransactions()[0]['category'], "receive") + + # Check that we have marked all keys up to the used keypool key as used + assert_equal(self.nodes[1].validateaddress(self.nodes[1].getnewaddress())['hdkeypath'], "m/0'/0'/111'") + +if __name__ == '__main__': + KeypoolRestoreTest().main() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index a043560ea..23bb41102 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -79,6 +79,7 @@ 'rawtransactions.py', 'reindex.py', # vv Tests less than 30s vv + 'keypool-topup.py', 'zmq_test.py', 'mempool_resurrect_test.py', 'txn_doublespend.py --mineblock', From faa76d1b79cc63177c8e165c9a2dc761aac7ccf4 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Thu, 10 Aug 2017 21:35:29 +0200 Subject: [PATCH 044/382] qa: Fix inv race in example_test --- test/functional/example_test.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/functional/example_test.py b/test/functional/example_test.py index 1ba5f756c..7709524f2 100755 --- a/test/functional/example_test.py +++ b/test/functional/example_test.py @@ -58,6 +58,10 @@ def on_block(self, conn, message): message.block.calc_sha256() self.block_receive_map[message.block.sha256] += 1 + def on_inv(self, conn, message): + """Override the standard on_inv callback""" + pass + def custom_function(): """Do some custom behaviour @@ -198,10 +202,10 @@ def run_test(self): self.log.info("Wait for node2 reach current tip. Test that it has propogated all the blocks to us") + getdata_request = msg_getdata() for block in blocks: - getdata_request = msg_getdata() getdata_request.inv.append(CInv(2, block)) - node2.send_message(getdata_request) + node2.send_message(getdata_request) # wait_until() will loop until a predicate condition is met. Use it to test properties of the # NodeConnCB objects. From 4aa25089b4392a9a17ba5b3a8ea22e893d11d876 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Thu, 10 Aug 2017 20:43:57 +0000 Subject: [PATCH 045/382] Bugfix: Use testnet RequireStandard for -acceptnonstdtxn default --- src/init.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/init.cpp b/src/init.cpp index ca62d3e7c..e3fc3fc92 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -479,7 +479,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageGroup(_("Node relay options:")); if (showDebug) { - strUsage += HelpMessageOpt("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", defaultChainParams->RequireStandard())); + strUsage += HelpMessageOpt("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", !testnetChainParams->RequireStandard())); strUsage += HelpMessageOpt("-incrementalrelayfee=", 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=", strprintf("Fee rate (in %s/kB) used to defined dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE))); } From 85c82b50d1a84fcf82fd3e0692e81aa41470bf84 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 9 Aug 2017 17:48:38 -0700 Subject: [PATCH 046/382] Avoid masking of difficulty adjustment errors by checkpoints Currently difficulty adjustment violations are not reported for chains that branch off before the last checkpoint. Change this by moving the checkpoint check after the difficulty check. --- src/validation.cpp | 42 +++++++++++++++--------------------------- 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/src/validation.cpp b/src/validation.cpp index 405ff356f..28d45195d 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2830,22 +2830,6 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P return true; } -static bool CheckIndexAgainstCheckpoint(const CBlockIndex* pindexPrev, CValidationState& state, const CChainParams& chainparams, const uint256& hash) -{ - if (*pindexPrev->phashBlock == chainparams.GetConsensus().hashGenesisBlock) - return true; - - int nHeight = pindexPrev->nHeight+1; - // Don't accept any forks from the main chain prior to last checkpoint. - // GetLastCheckpoint finds the last checkpoint in MapCheckpoints that's in our - // MapBlockIndex. - CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(chainparams.Checkpoints()); - if (pcheckpoint && nHeight < pcheckpoint->nHeight) - return state.DoS(100, error("%s: forked chain older than last checkpoint (height %d)", __func__, nHeight), REJECT_CHECKPOINT, "bad-fork-prior-to-checkpoint"); - - return true; -} - bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params) { LOCK(cs_main); @@ -2911,14 +2895,26 @@ std::vector GenerateCoinbaseCommitment(CBlock& block, const CBloc /** Context-dependent validity checks. * By "context", we mean only the previous block headers, but not the UTXO * set; UTXO-related validity checks are done in ConnectBlock(). */ -static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev, int64_t nAdjustedTime) +static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& params, const CBlockIndex* pindexPrev, int64_t nAdjustedTime) { assert(pindexPrev != NULL); const int nHeight = pindexPrev->nHeight + 1; + // Check proof of work + const Consensus::Params& consensusParams = params.GetConsensus(); if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams)) return state.DoS(100, false, REJECT_INVALID, "bad-diffbits", false, "incorrect proof of work"); + // Check against checkpoints + if (fCheckpointsEnabled) { + // Don't accept any forks from the main chain prior to last checkpoint. + // GetLastCheckpoint finds the last checkpoint in MapCheckpoints that's in our + // MapBlockIndex. + CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(params.Checkpoints()); + if (pcheckpoint && nHeight < pcheckpoint->nHeight) + return state.DoS(100, error("%s: forked chain older than last checkpoint (height %d)", __func__, nHeight), REJECT_CHECKPOINT, "bad-fork-prior-to-checkpoint"); + } + // Check timestamp against prev if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) return state.Invalid(false, REJECT_INVALID, "time-too-old", "block's timestamp is too early"); @@ -3049,12 +3045,7 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state pindexPrev = (*mi).second; if (pindexPrev->nStatus & BLOCK_FAILED_MASK) return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk"); - - assert(pindexPrev); - if (fCheckpointsEnabled && !CheckIndexAgainstCheckpoint(pindexPrev, state, chainparams, hash)) - return error("%s: CheckIndexAgainstCheckpoint(): %s", __func__, state.GetRejectReason().c_str()); - - if (!ContextualCheckBlockHeader(block, state, chainparams.GetConsensus(), pindexPrev, GetAdjustedTime())) + if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime())) return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); } if (pindex == NULL) @@ -3203,16 +3194,13 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, { AssertLockHeld(cs_main); assert(pindexPrev && pindexPrev == chainActive.Tip()); - if (fCheckpointsEnabled && !CheckIndexAgainstCheckpoint(pindexPrev, state, chainparams, block.GetHash())) - return error("%s: CheckIndexAgainstCheckpoint(): %s", __func__, state.GetRejectReason().c_str()); - CCoinsViewCache viewNew(pcoinsTip); CBlockIndex indexDummy(block); indexDummy.pprev = pindexPrev; indexDummy.nHeight = pindexPrev->nHeight + 1; // NOTE: CheckBlockHeader is called by CheckBlock - if (!ContextualCheckBlockHeader(block, state, chainparams.GetConsensus(), pindexPrev, GetAdjustedTime())) + if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime())) return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, FormatStateMessage(state)); if (!CheckBlock(block, state, chainparams.GetConsensus(), fCheckPOW, fCheckMerkleRoot)) return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state)); From e029c6e709d251809aa04edc08f76a077a2443e7 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Thu, 10 Aug 2017 15:58:25 -0700 Subject: [PATCH 047/382] Only return hex field once in getrawtransaction The hex is already returned in TxToUniv, no need to give it out a second independent time in getrawtransaction itself. --- src/rpc/rawtransaction.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index b967f2dbf..ad6067db4 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -160,13 +160,10 @@ UniValue getrawtransaction(const JSONRPCRequest& request) : "No such mempool transaction. Use -txindex to enable blockchain transaction queries") + ". Use gettransaction for wallet transactions."); - std::string strHex = EncodeHexTx(*tx, RPCSerializationFlags()); - if (!fVerbose) - return strHex; + return EncodeHexTx(*tx, RPCSerializationFlags()); UniValue result(UniValue::VOBJ); - result.push_back(Pair("hex", strHex)); TxToJSON(*tx, hashBlock, result); return result; } From 86279464b4ea207dfb2d978491ce80b849968105 Mon Sep 17 00:00:00 2001 From: Felix Weis Date: Fri, 11 Aug 2017 12:16:55 +0800 Subject: [PATCH 048/382] [RPC] trivial: gettxout no longer shows version of tx Since the switch to a per-txout chainstate db in #10195, the tx version information is no longer stored. Updated `gettxout` rpc help text accordingly. --- src/rpc/blockchain.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index d65e107e3..607262f77 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -962,7 +962,6 @@ UniValue gettxout(const JSONRPCRequest& request) " ,...\n" " ]\n" " },\n" - " \"version\" : n, (numeric) The version\n" " \"coinbase\" : true|false (boolean) Coinbase or not\n" "}\n" From f1bf31186c4e4afcbf1e3aa33c64636d1280c711 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Fri, 11 Aug 2017 11:47:05 -0400 Subject: [PATCH 049/382] [qa] Fix block message processing error in sendheaders.py --- test/functional/sendheaders.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/functional/sendheaders.py b/test/functional/sendheaders.py index 44c357c6d..e47e07fb8 100755 --- a/test/functional/sendheaders.py +++ b/test/functional/sendheaders.py @@ -121,9 +121,6 @@ def on_headers(self, conn, message): message.headers[-1].calc_sha256() self.last_blockhash_announced = message.headers[-1].sha256 - def on_block(self, conn, message): - self.last_message["block"].calc_sha256() - # Test whether the last announcement we received had the # right header or the right inv # inv and headers should be lists of block hashes From 793667af1c31835e0eefcdd283930bb89cfeda8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= Date: Tue, 8 Aug 2017 13:30:04 +0100 Subject: [PATCH 050/382] Improve shutdown process --- src/httpserver.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/httpserver.cpp b/src/httpserver.cpp index ba0125540..adb64a65d 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -481,6 +481,8 @@ void StopHTTPServer() } if (eventBase) { LogPrint(BCLog::HTTP, "Waiting for HTTP event thread to exit\n"); + // Exit the event loop as soon as there are no active events. + event_base_loopexit(eventBase, nullptr); // Give event loop a few seconds to exit (to send back last RPC responses), then break it // Before this was solved with event_base_loopexit, but that didn't work as expected in // at least libevent 2.0.21 and always introduced a delay. In libevent From bdf607e43819a05f155c2cfedd1ecae093149594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= Date: Mon, 7 Aug 2017 15:49:41 +0100 Subject: [PATCH 051/382] test: Add resendwallettransactions functional tests --- test/functional/resendwallettransactions.py | 31 +++++++++++++++++++++ test/functional/test_runner.py | 1 + 2 files changed, 32 insertions(+) create mode 100755 test/functional/resendwallettransactions.py diff --git a/test/functional/resendwallettransactions.py b/test/functional/resendwallettransactions.py new file mode 100755 index 000000000..5059aa106 --- /dev/null +++ b/test/functional/resendwallettransactions.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test resendwallettransactions RPC.""" + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal, assert_raises_jsonrpc + +class ResendWalletTransactionsTest(BitcoinTestFramework): + + def __init__(self): + super().__init__() + self.extra_args = [['--walletbroadcast=false']] + self.num_nodes = 1 + + def run_test(self): + # Should raise RPC_WALLET_ERROR (-4) if walletbroadcast is disabled. + assert_raises_jsonrpc(-4, "Error: Wallet transaction broadcasting is disabled with -walletbroadcast", self.nodes[0].resendwallettransactions) + + # Should return an empty array if there aren't unconfirmed wallet transactions. + self.stop_node(0) + self.nodes[0] = self.start_node(0, self.options.tmpdir) + assert_equal(self.nodes[0].resendwallettransactions(), []) + + # Should return an array with the unconfirmed wallet transaction. + txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1) + assert_equal(self.nodes[0].resendwallettransactions(), [txid]) + +if __name__ == '__main__': + ResendWalletTransactionsTest().main() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index dfd3b8989..4623718fe 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -118,6 +118,7 @@ 'bipdersig-p2p.py', 'bip65-cltv-p2p.py', 'uptime.py', + 'resendwallettransactions.py', ] EXTENDED_SCRIPTS = [ From 08f71c29ea586f41d0ec3ba77f2366c41e861354 Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Tue, 8 Aug 2017 22:51:40 +0000 Subject: [PATCH 052/382] [Trivial] Add a comment on the use of prevector in script. --- src/script/script.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/script/script.h b/src/script/script.h index d16bfd0e0..68b12c143 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -377,6 +377,12 @@ class CScriptNum int64_t m_value; }; +/** + * We use a prevector for the script to reduce the considerable memory overhead + * of vectors in cases where they normally contain a small number of small elements. + * Tests in October 2015 showed use of this reduced dbcache memory usage by 23% + * and made an initial sync 13% faster. + */ typedef prevector<28, unsigned char> CScriptBase; /** Serialized script, used inside transaction inputs and outputs */ From e93ff718c59a21532e8deb57d823b8fae2d87334 Mon Sep 17 00:00:00 2001 From: Charlie Lee Date: Sun, 30 Jul 2017 23:32:47 -0700 Subject: [PATCH 053/382] Add instructions for multi-processor gitian builds --- doc/gitian-building.md | 6 +++++- doc/gitian-building/system_settings.png | Bin 0 -> 76448 bytes doc/release-process.md | 6 +++--- 3 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 doc/gitian-building/system_settings.png diff --git a/doc/gitian-building.md b/doc/gitian-building.md index 9f9afaf04..636686b39 100644 --- a/doc/gitian-building.md +++ b/doc/gitian-building.md @@ -76,7 +76,11 @@ In the VirtualBox GUI click "New" and choose the following parameters in the wiz After creating the VM, we need to configure it. -- Click the `Settings` button, then go to the `Network` tab. Adapter 1 should be attached to `NAT`. +- Click the `Settings` button, then go to `System` tab and `Processor` sub-tab. Increase the number of processors to the number of cores on your machine if you want builds to be faster. + +![](gitian-building/system_settings.png) + +- Go to the `Network` tab. Adapter 1 should be attached to `NAT`. ![](gitian-building/network_settings.png) diff --git a/doc/gitian-building/system_settings.png b/doc/gitian-building/system_settings.png new file mode 100644 index 0000000000000000000000000000000000000000..a5720ef3a345d9a52a64298fd68c3c8402f86b38 GIT binary patch literal 76448 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

gI}JOanh-atZ1Dy|<~gJCmQ!_M9Y)4zQe zdoMjieqJv2&w3YD?@h+lYnSldYgYJ=FUHgUbxe5g|G|c1H;|Q^+hzB4U)On5TEOG< z^+!;t5(=dzs?gL__asLYJ!&u7*C z3-EEb$KTQZT{^DUm16GbZxLTmf%qrZ_PYdSaZS_TB=Z4OM778kt(_x9sF&W<@gQUeBglCqztOG4OFq{krL^TS;L26&$(+z z&#yqHDR16)_Q0)^TVVD3sVI`F5g+t`A3t!OfHET&aRK%iJ#8t1BcpJ9<7{j=Wle>< z0{iBFh6yY8?t+RTW|7gzs?O-V6SZ?%|K`{eY}8H6m?K^Ki~xyN{&Y5-KYk@!nTc z@Hi-x;BGbM?YKbiiVij>*W$l_F^3vG?pQzVFR=P57>cH|V6AdmIn z6cj_JQ^Eei4*bK{Kf=fNF;-7}3yb&PL3OPL7k16V-N!M|Yo*vU@i&;d<_J6B=5VT)G2cVZoHnU*VKZ0JPFF%%*eRpS~LI{$a?=&BT&#{)FpJzVLCl zh7nWOAeS1*LYMjg!ef{NrY+o0@ThBrN0&?cu;-2^L2fzbfAnv->fi%E=Ue#dhb1VG zS0gjb4S)V%G&}=BaO1>Qj9+;Ksv0eB9bG`_{uhFK7i8uY3tiMEcs#mx8e9TXP^+%O z;-6XJ%SAi+v;2te`MO64vXjFweZ(i&dxGktqH<(J`QWX$zoWDW#_iKP@xzimkk`~B z-t8uTwx_P#LvVBy?qA%8ug5Kft4|2ZD@w3`@g$tQ<%CdQ7rgV@dx*-cgr*`3-~WQ% z!!x(x;c$!l4@oUSLBbQf{>E2udlHPh=l1YxlvBfeaC&h>4H`VM?lA+kXs&FZhg;6U zXlSZO+!J^F@b%Z2ICeBHy9OaE+8_T$58mA`7zr_9nDZwq?0@hWg^7WfG

Nbw;$* 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�ez%1= zw;PEr%pbz4!XL*LHXR1vP5Sh;|3D|Kb+>Q%dduEK8Fj%vV_vnw4P!r@^KY|igudix zgVB_p_}Jw)KZVC1?$o?}bM2oUzYd$^Ht{8az}GeAo^-6iA}+jr{fwvbE-&N-$29ce z@RN5fT;Zn)(u|q8f}e}DB(82YEE}Y{ExTJXL5$Ln98%WXaUb zuY1PUXWv>=gS@#hX+C46sI4@iT+NmJhDJ>^Lu*&X)cW%1IXK`Y?X4c*+!g#IWS#T1 z9mapz=lm{fu0@0oXYaOOZ8xvtpPR{@z$&*XuDo2u88}y+2f>L~@0NSNor|rhRjWY3 zJYM<=QE5NZq;KD`&#&`f_r(Es5M=gg6jZrl;3N?$f^tf_Gkw3vz{%_MJNER4$s_ZhNY_Q9A}$^OO+r4xhOm+G6g z{Z5*(|KucD>35cwT+<&qxYO0y_=}8Q4(%QS?;hoNHJ9@#4xB|$OM0*>gq-uFh0Eu! zTFk79ELIMyfFR%5u$E+rpPQmPHof|tqfaPlyo~d*%dqlt+`N;uW$ohq|KaJZ!rI!p zM!nl^ytrE^P~1y#EpEkKix!9A6f4CwP_%_YaS3k0Deh1l0)Y}FI0Sc2y1)N9H@R5J zvsRKdlR3s5?|3IJxMEFSWZ1Bi9XoB#u9{-2o<98iMQRf9;kV)F#ZumQY6&7n1yKKK zH16-LO5*^ZzL5`P^heG^>af_{5E@W*7a%mZcuXPCIMI=#2uxAXN#tZu-S@R-L0nF+GO`eu|~!$&xOW@}Ac>7v`vaZd)ydZ)dyN|(*!YI4^ikB)pbT|J@|<|-s(R{%my6*orN zLYQc_%t$5|+ZT$MyL6+&u@)_YmvzGyalY=+-SVAZXn2h!95Kf@n6qcKb`$qCrm9=dU@ux5_Bced;^Wsz1}zmz56!xOh>*7 zYC-Y?6EujHGWBoVI3Bq3N)9X`m8F8p(y}h-+sVg~rVKVe+ z+Cb9~g{-Lwx)uh3MMhuE4!a-XZ|tww^e=q@pD5?h_+(6;LJ1K{kE*Kk!7ZsS)wiFrddRFNPL&p6068qA-#}b08_-^m za=onb9W-5@VbxPpQ!4=%NAZu_B(9UIKM9=A6~?(U;TMW3cTRUjK42p4Sw{z7O-UwZ zQ$I6EI1uon_qlmJbz~NPuAOqN1o_ne#V7%2Dse{5RUy}q%zgOa>f8L2XSioFC0OB; zE9Ka&%*qZa$ipys*f~O{VIlYB>^jWBl2n+m`@|0tcepW-kWhvTN%WmZ;x!lwPwZI zW)kYoD~0vmO2%39uX`*uehb|04eQ*>s2Dty>#MMAzHUtB71`Hh#0ZFs1nzEv^fJ60 z8s&`u8vgSf04@~N5vwSp^hBEEaSxvt@Jy4wvl_#{cDOfi448&{mj}H(=m)lOW=bB> zn%?|jDOX)4;$1d=uart`-zRW8ZMwn$N4?DmGcgEwn9o*ZxLK2i8p4WBCi@NB?Y;P8 zjk844aYb)WOm1|;N_;!^RMU43-r&VE3URCAH`s=Y6R7HM61TwzEeH*_(6mq#gdLzd z)3pAQ4FIue%y-|8o@KfwgT`fnHxc=irkNa`+P%^(kH&^FSk?rY9hUAqA zAP6=Obc*m3pJ4%Tk$}RJf~OyNB!6)+bLL8_H+u85KS)YaoPLKs-Xi)-n-(8ZUI-xV z2hNdUK$vW9ue0yltnS18k=fQBPvptd%zA=`g5^Xu${&XeFl8E)&N=_*O&r*=jJ@yu z^qaE2f-`qWqd75%xE&6hYnWo^swa!0=i(qoPq`_rgsF)#0(KF}=e^hmUIF2LrUlGn z4F^q4A}gh1F@sWaFW#~+D8CI>NAeZigul(IEVq0c9mEDGAde>mX2;SW2Sw8#w?|w_ z-M$=U5X^_#$R{kv42k5fPih3kdW2=WxMAOV-mSplkrt=6t=?hL0S~vQRAQ&kHY^XTgjlTnmPM@jB1ssyW-(Wz7udRvXsrPhU%8VwO1a+-; zDvcqH2QotXX&;UXr-YZp{E^|K$1;8MUPT;>6PKPkVGLGpt- zJ>Fpcy~se_z=nQ;jY|SXT!(Iz8fiiE8QKeh0XH{NI~joyyUpkm+Z;SeVCQXf?jzSwRZKB`x~i0pCCtA+#N(M ze$5We?+L8CKr!F2 zR57xqT47vgpodOU`zo{5T>u+9E-9yh@>nXkOxTC?s|SY`LeP378j&aVWTB)sDJ$;) zi0lHAbAQ^g)?KDC_7%Xw(K}rGhRCT5rV9w7m_AiWi=!3UM%Jow5AH3#ZDcq2a9kibx{RLE`I3JS zS7+f7p+VW;X%uM4+lJ2jfGIaU{FNQ=@4rjl*Zv+)s@Q5yZG<{EJ;T;ec1 z98qo|Wa^h^W#=ZOyCr*)khyuv{}1!&(^#%QX%B1F3oB|t)cV8ruBo2`g97+(0{81i zgl#q)uQVp|++T;E>);gnmKiK@3p!CR+he3Pry#^=M~Lhlrc7p%n{++JT^9yq%ZqTi z6wj}RkYBYGhnnUcEsrOFb1vgp{G0X(>$^iDm6a)uM}PeKo8Yg)lOCbLKE-BE{=uqF zri#l0cEAQ-yj=56=zn0+r+_iW|2w?s^8WvjA(H?9wD4aB>2@fSKK-52 zmZB6W57reWVbWmom^Yvy1?m8(BmZ}rTYZQC>OTF2kTk`oPyajUVH)>;9Mlk>z`)`P z&+h2fe^iYOrific!MNR76$loRkd7jV>0fO%X}s1qFeqrxr`g)tLM@JI z-n~)tcw3SFzfYM|psu=a6yP=Ax=jrF%M)>Mg#8~+bfK-Kl}wb17gg|$AoZpig%ep? z(zB!NVq1Dg*pBl14zm0NJ(>AtQ`7B_QdCJfbSD|OVy6aublZ{IKXKZ3Z|8`jDQES`8n4(C&uC|tMr}5(AVq}Cp!p7wJ z*I=q`T!J~LoOd7FHzODgQ&&fk2juPwi>6|%pJ4hyV9i0 zytj}EqCZAm=+?lXu_4D_Jcm1mgEFc?Mn!8;54rgLS`2O&cLr@Ow#^LbO0m*5XW^>z zI4IB%za4OCg&pb7(D|wP_^KwuRtu?pOz0{pO?;xtPeNyB{r-3@2I$EN5XUjlehL!c zgh`x@a8S^ylbdp9lxK5mB?tKn)oY=+x9NA_jqWgvteBj{S5Gz>l{9Aygq9>~;)0Or z!|u?q@&2{Y4&0iUtFlou@>2R2RbrSWFmy01;$c-5<6%LtM@J{_0Ru#m6>#}BW+O_J z6JTNtu+0gA&2`qLz-D7&Vi=Aj6lpRLSDTA}>s=pgAvnppnJI}|s_7q=>TFSfQrYbf zsgP%I?Yb~SL(uu$Y!iOTAIt6uTyZ<1o|O;0<&=lq5FDTrLbhavo*jh$$#&M zlVRy$6{WG`b*X_}iUN4K;}qZ93eejvy+u~FV=CXEKUqTZC?@fRjcA@ME&#M*0l44s zZbV~3gDzav?l7%ZhuN={)OQMkgkTn^>>27yKgpJ#^57k*(UDljBD`| zFgpjq|3wRdMZWXJCAed;zO1`oHyuYm^I>#Ko5yQ=QyGBK9_vyvidHgH_JjOYF{gHR z%m#Ocj@d^2_OMeR(0;e}6GtkV0Dydcf;u5XK`m-{J#^Vt(dSel@bU)p^;T*ReP^N# z)CC^sz~v=M^Vl%dya{w^$|5@=xJ!RU`hInEx5o0Jj*|VUWc8Fn0Yn*UGCBPiN~Xwy z`E2*I5w;Ala|JJ612)M%Q|Zk=E`W! z42}-zyk!)3r3MfVW;l-<3|<)VvFEtRr{g8WytMOA^-s3?%|hUNv=WnH5lk6|fv+!f zR#uYJ6Gays9T|yEx;i^sII^>2gbI=Q{LpmaQ(@ty^4Fsw;-9~qI?~6gIsOBdGu|ra z7){Pf!3dkm56{!XEI9otvq_%zt1vw8#ceE+^Ku;}QrUqwQ-H6i?+1a7egU8?2D#Yo zO&vSRp$DnSPSDrV|C0Z;t~a4}__K@%?VA0UvQ|`=x*rXB$2Dc$X`Ggx%a=K4Ib~oB zgnFqQ&c-#?zrASqy2vi3!LJ|n4=s-tQCjilXZ~wC6FENx*H9%0jOTNZKSa!oRedf0 z@!2Ys2=S+sXoF~}tXd+epwmLp98=A_fAhv270mTTt%Uj+W~g)Dr$)T9h>_7Ed!ylR ziKAe#}Hum(@xuB4b^TMg2$wsJ2c-j{8x~P`2WL7gp6_mDgNVO-zDrBV{ zv$@Z|tQPDZdOLj+R_^TEanI(Vay~L`n4TNy3usfOm3rVg&GxM2WH#zM9H@(Nv;2jz zRse+)*zU@Czk6QQalm7kq5V0HSW0gpJ7k{nx|*M-_jb%9wZx@|@|1(6>$w0o^*m>0 z;l$1I5~6IaWQ{uP9y^(??U{x6)4zbgbLEww!N%)Rx5nxk<+y@AskB}vzJ^j)H^^w{ z9DgUly6>y%m47mH0*di6s7cX`G6fxov~j>6z})?d__;dBY~Wt*+;M5yD5?m};G!d3 zC3!;?c>cKD{HGxV-eqfaZ~e-Os8080>!Wk;Z+f~99gDtR?k~&k2a~F(n_OYBJ3ICj zCJ;QQg(@c0tb>v4;-#xg!obKF;dcP7uyHWMDO&zi@|!84PVwfK`U=mgEVcIN9P+(t zaL+KLy)v8u&(UlmP1zjCdcHXkD_r|Pow`VUZ#EgwWKen%9!Om7DO7q$Hvl5)`uM7A zfsrzM#eN1{?*xwk3T$&YQL63 z<}Bq+0pfW4b|8POg?2vd2-~z8;cQlw`0g%c)eqkYBh2gXh8#vyi{3Dr-zieu_R1{M z>c_MU_2dT?b2WO$}6_4uoT(xtNM zghzOC$6ThGA0k5ysdMw>fMf8oz2Mz6=FPrJy=5WsC#rH-$O?mAMeQst{T-{HOouc9sTHy$D0Oaek^?x-cc z|MPwZj#`%uci`Xt*$n+s7;OZ2Ij!PT!M^V&97PUXYz~jdO*kRrtCng3ijS~E?^DjGf3d{ax)Uy;oxccr)D zQKUS=M{zIZaL6%Z zJwEFT>^Iraa`E}a3iocMS)^fO=@1Wl(*fwEYauyK%w?P2C9zb0Jyr`=m!Wu3@rzc@- ztWEAGt%=#^g69`cw^y}%P z$pvEQse8WKO{7!A@ZRMqgmSq(#AyLy_LT6W6~(kQ8cDwEF-dQ8uJ-iwd>}Ry6}_Bd zNh|~@t+h^?ot`afix=Ay+kG{dj6Ax+e>W3p{QXo&xpz|P!`h3tnKN@@oT?TZ8Z)jY zIhPY-{C9_GcRo;FC7L@sBk`3js6Y!k|8{&JI(|?s!QnUrZyTZ2iDU{v)kyHK zs1cquR1_&N6g_NME^;u87E)zWXurVZ?zklbuF}<7S(o3cs2G(P0fcPRdbYgm8EWci z`nU9WQ;Ru9H~QrFwR8-?fh>EfI5nc;MCfspqbwZ}qvmutDZL5bp`tsUko8HA->!j@ zKJ%>+et|E9Ufw*ji&8Y>-~v4%8pW%!q_kJAv{H3V{*XCTf`Qh%_{Y+|2#j*-fNNRA z2JF}_#_5M6ncqkxZ`#E5m)>+mHi9CYpOx$UdWO1zO(SgwRaG$asO&;-3iq=hC6X_m z!`DJT+wSFU$PKy~b3)$b&+Gko-B&Pq+AKRx0KGIi~fG) zW%CWfJ(+GJ+DIp$@upF@)K-7dYB_m%Mu`%o&4mbo`a(l7v|X9Nhc_yTbnV0< zq-2X=oRdXL(Up^P7NzkCMY~h|cE8c;lhYlenzrmrzIzM!icYQLgTBqj*BfyQuB3ND z1*VwO38TTfq@R?%I6dmMdEtkHbHRpz0H67mT9m2N=$!oZkIe5V?UOO9oT2Tpo zxm(S`$j^e$XX)9f%vnE=x$e|^Clp)b_K_9Iie}|8rZQe6%|BXD>L+txeBSSO75b!a zEv#R;{H3SoxpRzBLYw;ZJKtXF(zi=BR$mB;yE>_}h*0~`lSSoCu9O>5tAdk>tBq_l zD6gMe!F7_q))3;yyb(E!vE*Ge9a3HToW;{c3KlFZ{kEH@?{{ltE!;~vaniYqn1YO` z$;imG!4^6)Rg~9q9@g zQd~^K>wAk)8^!Ax!;G_+`I%}Ve)qA*bG1Zmf;g=c+R&v4Lq6^(yb%o(4q_fBL#|qR%G8Wc5;)I3Pb;U%xOISsGEb{*z4B z1NRblPsi&m;STH732UEy@3&nQcj6WzLd+6(2JqXmF5FHl=V6mIde%vOSuV(NdnK9; zgb^<$rJ|LLCO8rbp8hD&UBvomo=AV(6}OCm`|JC)De}+Ckj7dh0wcS6z^Y=hLOP8g z9%A;16ppj$?1a%&X%@Rbc`KlPUy?pQ*_oB#QE(6|XgNEI>Ovnrdq^K_7*7_y6C!8K ztLfnZJUo5Q*G?z5TL(iL8nT0qjJ~JLLZTSCy3~v;N3o}ot25s}h(F|0Bc~MU)-S1# zr9HkqGcsXBN_o^x3?6SlaVGTAN@GMlZS!H9i+Zbd@p@)Erk6oyEI@YPxE$6G><0!UtP$epL% z4gyJWdgj+$6Yps0bF)PpeE?0f#s-(48yiH_%^UUCI_k!py|=@mnf`89p^$w2ku5!# zx>E4{PyZi&%PwT8=_F-n)5MGh z4%H^2Cj;vHM+PpMyH~1b7FWM!=S4z%)h#TD&h1k*?2%ak7mw)#GYbo&T?IYrvDpm` z4R)iX=#L{yb4Voe5t#UcP}U`8kWtQ2EPZlbEDN6&c<&DQ(!by>1>!Quq+A`q=11jC zCBp5ZUr2z^Jx*x(8?xi3W7`e>aaD|^VE#@$6WRgDv`YP)UOPwU;#@fomqBi={eu7C z3&#y0=Q#YLq+rR9vVQGH;_;`o*O~JJMGbmOfxG9G1O@s@ScbI&5bebFj1b=XZ1)Z1 zEAI_QyB5w5xQQ8S-5enOb@%)cO^G9C6lR& z0ga%;s)|)^5)b1ui⋘SfMD2)tOyXV^-kG_Yhk^T;r0@@;cemQ)s{Q?)gQxe#Gv| zgBajQ)mo!}&1~1n-?e^UEHzklVdkT&)QYb<;&^T9)^a!S0Y_X&NyJAu*i$G~Bd%f5 z#b?t7@rs}EEE*W-agcKWsE za9YUg4F=TL)qyg3XQmn}w+yxYgFp5QvW%VAFfq7OcBJoXVu%S{-_BJ=*Za(O;E@0; zD??z$es{b3*HVSKf;|rm#&7ZTZ2K46{W(U!3BgnLN1MXmC$JT>2-fk~wzo2uC1ZOX z^@I8Ee5mA&F4y0VGz@zj#5BLRdl64&J(Njnm+4&W8Urrb*>^~Fs2ApXD0n#@IPpUz zWjua1UUzeIo8~gFIi%~mE~C7r>iXhi=I8`!vYEE1;RC(FpO#2|3%#Den(sdrW9~1e zA$n!t$`6@NK~cwr7V&OV^A|ooLs8ZcuP9VPG(IeCc}+80SP{X&M^shV86n1oy#x*k z^>?BWAJz(blkmn&gdw4r3w&!%C2#Dgov%gP_T1lT+hKRVDv+fzf(^(fNUkPd$U>D| zHN;K471Oh_(qiaf7_;;JH++vRUmk$i8t;wWBmH}-An=&wH4}nduBG79W6lwGp;qcL zO6Y26-q?OD`7yZ)4GpKJ!ow(6b;EelSZ(}!?CiAeoK$vBPO#j9i$U7}KR^Fma0nsD zi`S9C$=BjRZZLcPk2)=g^XF^poFVdMY znOqQuqm%CFy@gO8$EhK47kY6s+G3{EGs_$S!$WhJEyEteN&hJiZ`%h!28bfV7%7={ zAS^)*q_&a4(bYG$@-2uvq4Rv4BP&XSJSbK`!0A@^=4P68)TQq`Vdi%}QZ2Fk+fC1O z2O3fl6p_io==gC>)w-F5W~Q!q9nmkvD8#d{d`NZMtRhmZo%k&^IMrmrXvay(I_sb~ zLq==D=p;sp?cXh71qb&h`Il>` zeK&d1=ROjdnJn^RN-Y0Qm&*1>914=O>a_}#xY|G3zaYw2jpbu&E!=Qn8`-RO6Qh6; zquSn=)#ez_!e z1@rMe4e<`&{k$&UBZZkgUKSUs?|Q=d#HGa(bHrMgTTQkqyxDpVRs%_l}1VaXrW-)jZKiq<+@NqF($q9CR;jXxd0Y*cfysGWzyW`x=K#k9#k`;an0y z+C*xY)bewA4eKGfN&h4n{wC>C{{dBpml5UdoOZ77H8TI)8mgI85js+#y&F_x^j zc7KDIxOdKP&^3WyGJkmhRa23OTnN8vsr}SFy|^&Yjg}ad;&6a(yO=T4T68J`GXrjJ>*V5w95$sfVFDCiM%q^NgvFrGq2@Ay0Z?*ki{zx&= zuqE1){iD7PnJhv8)<<~qm2ZT5h;Q^HJo-Lh^f#>g8b&*$`g>{q=<~N`6xF!XDx0~! zJa^qjvh3JQ`WVrSSx^)-RLa+)**&X z--zvo_&6Bu>Yn7LuXednCBvi%!tZo4s?iHq&iAyn?3Wkrv5Fq?nJ4eZO`nyG9QLnR zF%hN=BMK!J_n2wTibtUgD^N$j!Ar3&sFq>|F~9H5CnuJpkGNl-E=O3-?@>+Mw3Mq* zmA4YfIe);zUg9fM+JRivQdYJZO+6r)#|lONCGCA+r<$YUUWULgv@9k~wc)gmS?w0G zr!N#-8~k_B50tKN=zx`lTW?ypmi;{9jsMajG>zPjp}o6b1~>GJ6(``P#20 zed2_=4j)lk_(GZfdn9PB-f>~X$0X9^xjuV}%cq3q49AU|BWs8NJ^i=KO2Mf`;UqH1 zGu8B{f`*Y-RGb*tbzgaJqTKkrC$v+et;@R!5m2=>!wzS9E8;h@#x0*wv8hJ;b1lqv z_{v*O^M-$o(27)~*>Q@7KkuZSA?L_*HJn8S)nvni+8l)d}oY_q)=X8d`~V8%*fS^?sqLY5UGRutC^|Xynk< z#^oD63~Tad&w;`_t8m9svi{e-j8vl7iZYnH+4kpeyphRk*2%m-TzS4-!4}56g3|P zUKWz>Y9-MyQdNe$s-FHbQsGkmEG|}~A3l7%0RiDDIw(H_Jfz^c1)t%J30881TU4Re zz(tTd?$BDx3y+G`%$MNWm%g!0KK-q)4;J;JYS^rQiXnnYs19r5e4x8N^rsoe@&SPo z*lOIV#o^#4e_0j!6?N(gAM#jjhQ`&OsivGayJp!B=SrJ)#eax^&Nsy*eU;y;dLTAw zzZ@WCSfD_%!Qub$rnW&1O9!Qd7hGIEjrJe7745W0#ku*(;uH?#mWn(E{;2?I^rfyp zp`vB_QlHrm0hJg&!5yf9v0!Jt?Cjnm@jJt2gP9GmQ1StxPHq=Z{RMpdM8Vwlf=onz zYFEbbJa9@dH?qM)tZp9V+aqUe8Nw`i-^TY)igEqE zs%-Wi^ZTpl?UYJOqEi&Q3s;MIAx` zTPnzb+Z7o*XeO4O`DWYOGF1=E1#k50bRhUPHZbgX)>}i(1d5l$1zHDUH6^q4&JTE+g-gM8cnV z{?p9#RMQE=xcUlU#e3P{Vlt+0Sq(f3L*s_GXNcbq56tbiW3TSlT&iDH_ekTsMhQ{g ztr#AD_iNXS8MHZA+3jve(rv)s$^L|hq^1O+AjjskdO7O*kA*l$LxMI#=Uh-3~ptfsZXQp(8$TsYf`I-8tJ)NTMxPE$3rVMP}0Evp<&9@1wW?yZ{y(?pt8 z-pcIr;>y|RQRX1=-T*i0<=#s|~Opevfa zjXJHxBvsS2S&&emQD6k&s=djx%9mj4s8-Os)i-}a39VgGDU=_JJi1r>uvClca+Mss z;0Y=jPytYPTCx4r@^skypZg7p=YT5mRrC+g4q*> zEF%!`c!ezy7S}xXoW#bt_t)Akulsm~oz_f?I=rndL zA97|8xE7FN>Dk(5N&oM3&|fWva#sV2^&bClADqL(t5G{ zD3|;5n{P(b;p+xTqZ2#M4DyIrC?M!;&wXF#m=RnfgQW<4KZL;*suc@og3 zeKUvvH`T4UXr-x%Aj(+Q=88MQwzwZPo?7!@jR4EEhB5(gQG}+GQx>Gx1J^047ptT} z&^T-INY9Gz!$9?=lU}+qHH$F7g4j-YJDG+{=TpEDdZ0xYCl6`@c=Qpf`Qg%0DS(mf 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 H_RD_(VGmy2 literal 0 HcmV?d00001 diff --git a/doc/release-process.md b/doc/release-process.md index 5a99b726f..f429b4bbd 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -111,16 +111,16 @@ The gbuild invocations below DO NOT DO THIS by default. ### Build and sign Bitcoin Core for Linux, Windows, and OS X: pushd ./gitian-builder - ./bin/gbuild --memory 3000 --commit bitcoin=v${VERSION} ../bitcoin/contrib/gitian-descriptors/gitian-linux.yml + ./bin/gbuild --num-make 2 --memory 3000 --commit bitcoin=v${VERSION} ../bitcoin/contrib/gitian-descriptors/gitian-linux.yml ./bin/gsign --signer $SIGNER --release ${VERSION}-linux --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-linux.yml mv build/out/bitcoin-*.tar.gz build/out/src/bitcoin-*.tar.gz ../ - ./bin/gbuild --memory 3000 --commit bitcoin=v${VERSION} ../bitcoin/contrib/gitian-descriptors/gitian-win.yml + ./bin/gbuild --num-make 2 --memory 3000 --commit bitcoin=v${VERSION} ../bitcoin/contrib/gitian-descriptors/gitian-win.yml ./bin/gsign --signer $SIGNER --release ${VERSION}-win-unsigned --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-win.yml mv build/out/bitcoin-*-win-unsigned.tar.gz inputs/bitcoin-win-unsigned.tar.gz mv build/out/bitcoin-*.zip build/out/bitcoin-*.exe ../ - ./bin/gbuild --memory 3000 --commit bitcoin=v${VERSION} ../bitcoin/contrib/gitian-descriptors/gitian-osx.yml + ./bin/gbuild --num-make 2 --memory 3000 --commit bitcoin=v${VERSION} ../bitcoin/contrib/gitian-descriptors/gitian-osx.yml ./bin/gsign --signer $SIGNER --release ${VERSION}-osx-unsigned --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-osx.yml mv build/out/bitcoin-*-osx-unsigned.tar.gz inputs/bitcoin-osx-unsigned.tar.gz mv build/out/bitcoin-*.tar.gz build/out/bitcoin-*.dmg ../ From 1227be30ecff2bdf9f88213f356e1b4a2b28543a Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 14 Aug 2017 16:45:58 +0200 Subject: [PATCH 054/382] doc: Update release notes from wiki Update release notes from wiki, and fill in authors list from git. Additional credits: - Awemany (for #10854) - Gregory Maxwell (release notes writing) - John Newbery (release notes writing) - Kibbled Jive Elk Zoo (for https://github.com/bitcoin/bitcoin/pull/10177#issuecomment-309244097) - Luke Dashjr (release notes writing) - Marco Falke (release notes writing) - Pieter Wuille (release notes writing) - Rusty Russell (release notes writing) - tintinweb (for early-announcing miniupnp CVE-2017-8798, forgot this for 0.14.2) Tree-SHA512: 8024eb761fcac4bb7f16ba3a9db376508f1f1bcf8a89cfb5e2928ad384675d3e912cada6ffef7d5aac181a965ebb8b823f6a63d9e976c1be753ec8eb9a8b9ef5 --- doc/release-notes.md | 315 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 298 insertions(+), 17 deletions(-) diff --git a/doc/release-notes.md b/doc/release-notes.md index a13ede2dd..9398a3f8c 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -1,9 +1,9 @@ (note: this is a temporary file, to be added-to by anybody, and moved to release-notes at release time) -Bitcoin Core version *version* is now available from: +Bitcoin Core version *0.15.0* is now available from: - + This is a new major version release, including new features, various bugfixes and performance improvements, as well as updated translations. @@ -16,16 +16,39 @@ To receive security and update notifications, please subscribe to: +How to Upgrade +============== + +If you are running an older version, shut it down. Wait until it has completely +shut down (which might take a few minutes for older versions), then run the +installer (on Windows) or just copy over /Applications/Bitcoin-Qt (on Mac) +or `bitcoind`/`bitcoin-qt` (on Linux). + +The first time you run version 0.15.0, your chainstate database will be converted to a +new format, which will take anywhere from a few minutes to half an hour, +depending on the speed of your machine. + +Note that the block database format also changed in version 0.8.0 and there is no +automatic upgrade code from before version 0.8 to version 0.15.0. Upgrading +directly from 0.7.x and earlier without redownloading the blockchain is not supported. +However, as usual, old wallet versions are still supported. + +Downgrading warning +------------------- + +The chainstate database for this release is not compatible with previous +releases, so if you run 0.15 and then decide to switch back to any +older version, you will need to run the old release with the `-reindex-chainstate` +option to rebuild the chainstate data structures in the old format. + +If your node has pruning enabled, this will entail re-downloading and +processing the entire blockchain. + Compatibility ============== Bitcoin Core is extensively tested on multiple operating systems using -the Linux kernel, macOS 10.8+, and Windows Vista and later. - -Microsoft ended support for Windows XP on [April 8th, 2014](https://www.microsoft.com/en-us/WindowsForBusiness/end-of-xp-support). -No attempt is made to prevent installing or running the software on Windows XP, you -can still do so at your own risk but be aware that there are known instabilities. -Please do not report issues about Windows XP to the issue tracker. +the Linux kernel, macOS 10.8+, and Windows Vista and later. Windows XP is not supported. Bitcoin Core should also work on most other Unix-like systems but is not frequently tested on them. @@ -33,25 +56,184 @@ frequently tested on them. Notable changes =============== +Performance Improvements +------------------------ + +Version 0.15 contains a number of significant performance improvements, which make +Initial Block Download, startup, transaction and block validation much faster: + +- The chainstate database (which is used for tracking UTXOs) has been changed + from a per-transaction model to a per-output model (See [PR 10195](https://github.com/bitcoin/bitcoin/pull/10195)). Advantages of this model + are that it: + - avoids the CPU overhead of deserializing and serializing the unused outputs; + - has more predictable memory usage; + - uses simpler code; + - is adaptable to various future cache flushing strategies. + + As a result, validating the blockchain during Initial Block Download (IBD) and reindex + is ~30-40% faster, uses 10-20% less memory, and flushes to disk far less frequently. + The only downside is that the on-disk database is 15% larger. During the conversion from the previous format + a few extra gigabytes may be used. +- Earlier versions experienced a spike in memory usage while flushing UTXO updates to disk. + As a result, only half of the available memory was actually used as cache, and the other half was + reserved to accommodate flushing. This is no longer the case (See [PR 10148](https://github.com/bitcoin/bitcoin/pull/10148)), and the entirety of + the available cache (see `-dbcache`) is now actually used as cache. This reduces the flushing + frequency by a factor 2 or more. +- In previous versions, signature validation for transactions has been cached when the + transaction is accepted to the mempool. Version 0.15 extends this to cache the entire script + validity (See [PR 10192](https://github.com/bitcoin/bitcoin/pull/10192)). This means that if a transaction in a block has already been accepted to the + mempool, the scriptSig does not need to be re-evaluated. Empirical tests show that + this results in new block validation being 40-50% faster. +- LevelDB has been upgraded to version 1.20 (See [PR 10544](https://github.com/bitcoin/bitcoin/pull/10544)). This version contains hardware acceleration for CRC + on architectures supporting SSE 4.2. As a result, synchronization and block validation are now faster. +- SHA256 hashing has been optimized for architectures supporting SSE 4 (See [PR 10182](https://github.com/bitcoin/bitcoin/pull/10182)). SHA256 is around + 50% faster on supported hardware, which results in around 5% faster IBD and block + validation. In version 0.15, SHA256 hardware optimization is disabled in release builds by + default, but can be enabled by using `--enable-experimental-asm` when building. +- Refill of the keypool no longer flushes the wallet between each key which resulted in a ~20x speedup in creating a new wallet. Part of this speedup was used to increase the default keypool to 1000 keys to make recovery more robust. (See [PR 10831](https://github.com/bitcoin/bitcoin/pull/10831)). + +Fee Estimation Improvements +--------------------------- + +Fee estimation has been significantly improved in version 0.15, with more accurate fee estimates used by the wallet and a wider range of options for advanced users of the `estimatesmartfee` and `estimaterawfee` RPCs (See [PR 10199](https://github.com/bitcoin/bitcoin/pull/10199)). + +### Changes to internal logic and wallet behavior + +- Internally, estimates are now tracked on 3 different time horizons. This allows for longer targets and means estimates adjust more quickly to changes in conditions. +- Estimates can now be *conservative* or *economical*. *Conservative* estimates use longer time horizons to produce an estimate which is less susceptible to rapid changes in fee conditions. *Economical* estimates use shorter time horizons and will be more affected by short-term changes in fee conditions. Economical estimates may be considerably lower during periods of low transaction activity (for example over weekends), but may result in transactions being unconfirmed if prevailing fees increase rapidly. +- By default, the wallet will use conservative fee estimates to increase the reliability of transactions being confirmed within the desired target. For transactions that are marked as replaceable, the wallet will use an economical estimate by default, since the fee can be 'bumped' if the fee conditions change rapidly (See [PR 10589](https://github.com/bitcoin/bitcoin/pull/10589)). +- Estimates can now be made for confirmation targets up to 1008 blocks (one week). +- More data on historical fee rates is stored, leading to more precise fee estimates. +- Transactions which leave the mempool due to eviction or other non-confirmed reasons are now taken into account by the fee estimation logic, leading to more accurate fee estimates. +- The fee estimation logic will make sure enough data has been gathered to return a meaningful estimate. If there is insufficient data, a fallback default fee is used. + +### Changes to fee estimate RPCs + +- The `estimatefee` RPC is now deprecated in favor of using only `estimatesmartfee` (which is the implementation used by the GUI) +- The `estimatesmartfee` RPC interface has been changed (See [PR 10707](https://github.com/bitcoin/bitcoin/pull/10707)): + - The `nblocks` argument has been renamed to `conf_target` (to be consistent with other RPC methods). + - An `estimate_mode` argument has been added. This argument takes one of the following strings: `CONSERVATIVE`, `ECONOMICAL` or `UNSET` (which defaults to `CONSERVATIVE`). + - The RPC return object now contains an `errors` member, which returns errors encountered during processing. + - If Bitcoin Core has not been running for long enough and has not seen enough blocks or transactions to produce an accurate fee estimation, an error will be returned (previously a value of -1 was used to indicate an error, which could be confused for a feerate). +- A new `estimaterawfee` RPC is added to provide raw fee data. External clients can query and use this data in their own fee estimation logic. + +Multi-wallet support +-------------------- + +Bitcoin Core now supports loading multiple, separate wallets (See [PR 8694](https://github.com/bitcoin/bitcoin/pull/8694), [PR 10849](https://github.com/bitcoin/bitcoin/pull/10849)). The wallets are completely separated, with individual balances, keys and received transactions. + +Multi-wallet is enabled by using more than one `-wallet` argument when starting Bitcoin, either on the command line or in the Bitcoin config file. + +**In Bitcoin-Qt, only the first wallet will be displayed and accessible for creating and signing transactions.** GUI selectable multiple wallets will be supported in a future version. However, even in 0.15 other loaded wallets will continue to remain synchronized to the node's current tip in the background. This can be useful if running a pruned node, since loading a wallet where the most recent sync is beyond the pruned height results in having to download and revalidate the whole blockchain. Continuing to synchronize all wallets in the background avoids this problem. + +Bitcoin Core 0.15.0 contains the following changes to the RPC interface and `bitcoin-cli` for multi-wallet: + +* When running Bitcoin Core with a single wallet, there are **no** changes to the RPC interface or `bitcoin-cli`. All RPC calls and `bitcoin-cli` commands continue to work as before. +* When running Bitcoin Core with multi-wallet, all *node-level* RPC methods continue to work as before. HTTP RPC requests should be send to the normal `:/` endpoint, and `bitcoin-cli` commands should be run as before. A *node-level* RPC method is any method which does not require access to the wallet. +* When running Bitcoin Core with multi-wallet, *wallet-level* RPC methods must specify the wallet for which they're intended in every request. HTTP RPC requests should be send to the `:/wallet//` endpoint, for example `127.0.0.1:8332/wallet/wallet1.dat/`. `bitcoin-cli` commands should be run with a `-rpcwallet` option, for example `bitcoin-cli -rpcwallet=wallet1.dat getbalance`. +* A new *node-level* `listwallets` RPC method is added to display which wallets are currently loaded. The names returned by this method are the same as those used in the HTTP endpoint and for the `rpcwallet` argument. + +Note that while multi-wallet is now fully supported, the RPC multi-wallet interface should be considered unstable for version 0.15.0, and there may backwards-incompatible changes in future versions. + +Replace-by-fee control in the GUI +--------------------------------- + +Bitcoin Core has supported creating opt-in replace-by-fee (RBF) transactions +since version 0.12.0, and since version 0.14.0 has included a `bumpfee` RPC method to +replace unconfirmed opt-in RBF transactions with a new transaction that pays +a higher fee. + +In version 0.15, creating an opt-in RBF transaction and replacing the unconfirmed +transaction with a higher-fee transaction are both supported in the GUI (See [PR 9592](https://github.com/bitcoin/bitcoin/pull/9592)). + +Removal of Coin Age Priority +---------------------------- + +In previous versions of Bitcoin Core, a portion of each block could be reserved for transactions based on the age and value of UTXOs they spent. This concept (Coin Age Priority) is a policy choice by miners, and there are no consensus rules around the inclusion of Coin Age Priority transactions in blocks. In practice, only a few miners continue to use Coin Age Priority for transaction selection in blocks. Bitcoin Core 0.15 removes all remaining support for Coin Age Priority (See [PR 9602](https://github.com/bitcoin/bitcoin/pull/9602)). This has the following implications: + +- The concept of *free transactions* has been removed. High Coin Age Priority transactions would previously be allowed to be relayed even if they didn't attach a miner fee. This is no longer possible since there is no concept of Coin Age Priority. The `-limitfreerelay` and `-relaypriority` options which controlled relay of free transactions have therefore been removed. +- The `-sendfreetransactions` option has been removed, since almost all miners do not include transactions which do not attach a transaction fee. +- The `-blockprioritysize` option has been removed. +- The `estimatepriority` and `estimatesmartpriority` RPCs have been removed. +- The `getmempoolancestors`, `getmempooldescendants`, `getmempooolentry` and `getrawmempool` RPCs no longer return `startingpriority` and `currentpriority`. +- The `prioritisetransaction` RPC no longer takes a `priority_delta` argument, which is replaced by a `dummy` argument for backwards compatibility with clients using positional arguments. The RPC is still used to change the apparent fee-rate of the transaction by using the `fee_delta` argument. +- `-minrelaytxfee` can now be set to 0. If `minrelaytxfee` is set, then fees smaller than `minrelaytxfee` (per kB) are rejected from relaying, mining and transaction creation. This defaults to 1000 satoshi/kB. +- The `-printpriority` option has been updated to only output the fee rate and hash of transactions included in a block by the mining code. + +Mempool Persistence Across Restarts +----------------------------------- + +Version 0.14 introduced mempool persistence across restarts (the mempool is saved to a `mempool.dat` file in the data directory prior to shutdown and restores the mempool when the node is restarted). Version 0.15 allows this feature to be switched on or off using the `-persistmempool` command-line option (See [PR 9966](https://github.com/bitcoin/bitcoin/pull/9966)). By default, the option is set to true, and the mempool is saved on shutdown and reloaded on startup. If set to false, the `mempool.dat` file will not be loaded on startup or saved on shutdown. + +New RPC methods +--------------- + +Version 0.15 introduces several new RPC methods: + +- `abortrescan` stops current wallet rescan, e.g. when triggered by an `importprivkey` call (See [PR 10208](https://github.com/bitcoin/bitcoin/pull/10208)). +- `combinerawtransaction` accepts a JSON array of raw transactions and combines them into a single raw transaction (See [PR 10571](https://github.com/bitcoin/bitcoin/pull/10571)). +- `estimaterawfee` returns raw fee data so that customized logic can be implemented to analyze the data and calculate estimates. See [Fee Estimation Improvements](#fee-estimation-improvements) for full details on changes to the fee estimation logic and interface. +- `getchaintxstats` returns statistics about the total number and rate of transactions + in the chain (See [PR 9733](https://github.com/bitcoin/bitcoin/pull/9733)). +- `listwallets` lists wallets which are currently loaded. See the *Multi-wallet* section + of these release notes for full details (See [Multi-wallet support](#multi-wallet-support)). +- `uptime` returns the total runtime of the `bitcoind` server since its last start (See [PR 10400](https://github.com/bitcoin/bitcoin/pull/10400)). + Low-level RPC changes --------------------- +- When using Bitcoin Core in multi-wallet mode, RPC requests for wallet methods must specify + the wallet that they're intended for. See [Multi-wallet support](#multi-wallet-support) for full details. + - The new database model no longer stores information about transaction - versions of unspent outputs. This means that: + versions of unspent outputs (See [Performance improvements](#performance-improvements)). This means that: - The `gettxout` RPC no longer has a `version` field in the response. - The `gettxoutsetinfo` RPC reports `hash_serialized_2` instead of `hash_serialized`, which does not commit to the transaction versions of unspent outputs, but does commit to the height and coinbase information. - - The `gettxoutsetinfo` response now contains `disk_size` and `bogosize` instead of - `bytes_serialized`. The first is a more accurate estimate of actual disk usage, but - is not deterministic. The second is unrelated to disk usage, but is a - database-independent metric of UTXO set size: it counts every UTXO entry as 50 + the - length of its scriptPubKey. - The `getutxos` REST path no longer reports the `txvers` field in JSON format, and always reports 0 for transaction versions in the binary format +- The `estimatefee` RPC is deprecated. Clients should switch to using the `estimatesmartfee` RPC, which returns better fee estimates. See [Fee Estimation Improvements](#fee-estimation-improvements) for full details on changes to the fee estimation logic and interface. + +- The `gettxoutsetinfo` response now contains `disk_size` and `bogosize` instead of + `bytes_serialized`. The first is a more accurate estimate of actual disk usage, but + is not deterministic. The second is unrelated to disk usage, but is a + database-independent metric of UTXO set size: it counts every UTXO entry as 50 + the + length of its scriptPubKey (See [PR 10426](https://github.com/bitcoin/bitcoin/pull/10426)). + +- `signrawtransaction` can no longer be used to combine multiple transactions into a single transaction. Instead, use the new `combinerawtransaction` RPC (See [PR 10571](https://github.com/bitcoin/bitcoin/pull/10571)). + +- `fundrawtransaction` no longer accepts a `reserveChangeKey` option. This option used to allow RPC users to fund a raw transaction using an key from the keypool for the change address without removing it from the available keys in the keypool. The key could then be re-used for a `getnewaddress` call, which could potentially result in confusing or dangerous behaviour (See [PR 10784](https://github.com/bitcoin/bitcoin/pull/10784)). + +- `estimatepriority` and `estimatesmartpriority` have been removed. See [Removal of Coin Age Priority](#removal-of-coin-age-priority). + +- The `listunspent` RPC now takes a `query_options` argument (see [PR 8952](https://github.com/bitcoin/bitcoin/pull/8952)), which is a JSON object + containing one or more of the following members: + - `minimumAmount` - a number specifying the minimum value of each UTXO + - `maximumAmount` - a number specifying the maximum value of each UTXO + - `maximumCount` - a number specifying the minimum number of UTXOs + - `minimumSumAmount` - a number specifying the minimum sum value of all UTXOs + +- The `getmempoolancestors`, `getmempooldescendants`, `getmempooolentry` and `getrawmempool` RPCs no longer return `startingpriority` and `currentpriority`. See [Removal of Coin Age Priority](#removal-of-coin-age-priority). + +- The `dumpwallet` RPC now returns the full absolute path to the dumped wallet. (it + used to return no value, even if successful (See [PR 9740](https://github.com/bitcoin/bitcoin/pull/9740)). + +- In the `getpeerinfo` RPC, the return object for each peer now returns an `addrbind` member, which contains the ip address and port of the connection to the peer. This is in addition to the `addrlocal` member which contains the ip address and port of the local node as reported by the peer (See [PR 10478](https://github.com/bitcoin/bitcoin/pull/10478)). + +- The `disconnectnode` RPC can now disconnect a node specified by node ID (as well as by IP address/port). To disconnect a node based on node ID, call the RPC with the new `nodeid` argument (See [PR 10143](https://github.com/bitcoin/bitcoin/pull/10143)). + +- The second argument in `prioritisetransaction` has been renamed from `priority_delta` to `dummy` since Bitcoin Core no longer has a concept of coin age priority. The `dummy` argument has no functional effect, but is retained for positional argument compatibility. See [Removal of Coin Age Priority](#removal-of-coin-age-priority). + +- The `resendwallettransactions` RPC throws an error if the `-walletbroadcast` option is set to false (See [PR 10995](https://github.com/bitcoin/bitcoin/pull/10995)). + +- The second argument in the `submitblock` RPC argument has been renamed from `parameters` to `dummy`. This argument never had any effect, and the renaming is simply to communicate this fact to the user (See [PR 10191](https://github.com/bitcoin/bitcoin/pull/10191)) + (Clients should, however, use positional arguments for `submitblock` in order to be compatible with BIP 22.) + +- The `verbose` argument of `getblock` has been renamed to `verbosity` and now takes an integer from 0-2. Verbose level 0 is equivalent to `verbose=false`. Verbose level 1 is equivalent to `verbose=true`. Verbose level 2 will give the full transaction details of each transaction in the output as given by `getrawtransaction`. The old behavior of using the `verbose` named argument and a boolean value is still maintained for compatibility. -- Error codes have been updated to be more accurate for the following error cases: +- Error codes have been updated to be more accurate for the following error cases (See [PR 9853](https://github.com/bitcoin/bitcoin/pull/9853)): - `getblock` now returns RPC_MISC_ERROR if the block can't be found on disk (for example if the block has been pruned). Previously returned RPC_INTERNAL_ERROR. - `pruneblockchain` now returns RPC_MISC_ERROR if the blocks cannot be pruned @@ -64,13 +246,13 @@ Low-level RPC changes or subnet is invalid. Previously returned RPC_CLIENT_NODE_ALREADY_ADDED. - `setban` now returns RPC_CLIENT_INVALID_IP_OR_SUBNET if the user tries to unban a node that has not previously been banned. Previously returned RPC_MISC_ERROR. - - `removeprunedfunds` now returns RPC_WALLET_ERROR if bitcoind is unable to remove + - `removeprunedfunds` now returns RPC_WALLET_ERROR if `bitcoind` is unable to remove the transaction. Previously returned RPC_INTERNAL_ERROR. - `removeprunedfunds` now returns RPC_INVALID_PARAMETER if the transaction does not exist in the wallet. Previously returned RPC_INTERNAL_ERROR. - `fundrawtransaction` now returns RPC_INVALID_ADDRESS_OR_KEY if an invalid change address is provided. Previously returned RPC_INVALID_PARAMETER. - - `fundrawtransaction` now returns RPC_WALLET_ERROR if bitcoind is unable to create + - `fundrawtransaction` now returns RPC_WALLET_ERROR if `bitcoind` is unable to create the transaction. The error message provides further details. Previously returned RPC_INTERNAL_ERROR. - `bumpfee` now returns RPC_INVALID_PARAMETER if the provided transaction has @@ -102,5 +284,104 @@ Credits Thanks to everyone who directly contributed to this release: +- ロハン ダル +- Ahmad Kazi +- aideca +- Akio Nakamura +- Alex Morcos +- Allan Doensen +- Andres G. Aragoneses +- Andrew Chow +- Awemany +- Bob McElrath +- Brian McMichael +- BtcDrak +- Charlie Lee +- Chris Gavin +- Chris Stewart +- Cory Fields +- CryptAxe +- Dag Robole +- Daniel Aleksandersen +- Daniel Cousens +- darksh1ne +- Dimitris Tsapakidis +- Eric Shaw Jr +- Evan Klitzke +- fanquake +- Felix Weis +- flack +- Greg Griffith +- Gregory Maxwell +- Gregory Sanders +- gubatron +- Ian Kelling +- Jack Grigg +- James Evans +- James Hilliard +- Jameson Lopp +- Jeremy Rubin +- Jimmy Song +- João Barbosa +- Johnathan Corgan +- John Newbery +- Jonas Schnelli +- jonnynewbs +- Jorge Timón +- Kalle Alm +- Karl-Johan Alm +- Kewde +- keystrike +- KibbledJiveElkZoo +- Kibbled Jive Elk Zoo +- kirit93 +- kobake +- Kyle Honeycutt +- Lawrence Nahum +- Luke Dashjr +- Marco Falke +- Marcos Mayorga +- Marijn Stollenga +- Mario Dian +- Mark Friedenbach +- Marko Bencun +- Masahiko Hyuga +- Matt Corallo +- Matthew Zipkin +- Matthias Grundmann +- Michael Goldstein +- Michael Rotarius +- Mikerah +- Mike van Rossum +- Mitchell Cash +- NicolasDorier +- Nicolas Dorier +- Patrick Strateman +- Pavel Janík +- Pavlos Antoniou +- Pavol Rusnak +- Pedro Branco +- Peter Todd +- Pieter Wuille +- practicalswift +- René Nyffenegger +- Ricardo Velhote +- romanornr +- Russell Yanofsky +- Rusty Russell +- Ryan Havar +- shaolinfry +- Shigeya Suzuki +- Simone Madeo +- Spencer Lievens +- Steven D. Lander +- Suhas Daftuar +- Takashi Mitsuta +- Thomas Snider +- Timothy Redaelli +- tintinweb +- tnaka +- Warren Togami +- Wladimir J. van der Laan As well as everyone that helped translating on [Transifex](https://www.transifex.com/projects/p/bitcoin/). From bb81e1735575e1c00e808326735168cc9810c174 Mon Sep 17 00:00:00 2001 From: Marko Bencun Date: Tue, 1 Aug 2017 21:17:40 +0200 Subject: [PATCH 055/382] scripted-diff: stop using the gArgs wrappers They were temporary additions to ease the transition. -BEGIN VERIFY SCRIPT- find src/ -name "*.cpp" ! -wholename "src/util.h" ! -wholename "src/util.cpp" | xargs perl -i -pe 's/(? [params] " + strprintf(_("Send command to %s"), _(PACKAGE_NAME)) + "\n" + " bitcoin-cli [options] -named [name=value] ... " + strprintf(_("Send command to %s (with named arguments)"), _(PACKAGE_NAME)) + "\n" + @@ -100,11 +100,11 @@ static int AppInitRPC(int argc, char* argv[]) return EXIT_SUCCESS; } if (!fs::is_directory(GetDataDir(false))) { - fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", GetArg("-datadir", "").c_str()); + fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str()); return EXIT_FAILURE; } try { - ReadConfigFile(GetArg("-conf", BITCOIN_CONF_FILENAME)); + gArgs.ReadConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)); } catch (const std::exception& e) { fprintf(stderr,"Error reading configuration file: %s\n", e.what()); return EXIT_FAILURE; @@ -116,7 +116,7 @@ static int AppInitRPC(int argc, char* argv[]) fprintf(stderr, "Error: %s\n", e.what()); return EXIT_FAILURE; } - if (GetBoolArg("-rpcssl", false)) + if (gArgs.GetBoolArg("-rpcssl", false)) { fprintf(stderr, "Error: SSL mode for RPC (-rpcssl) is no longer supported.\n"); return EXIT_FAILURE; @@ -198,15 +198,15 @@ UniValue CallRPC(const std::string& strMethod, const UniValue& params) // 2. port in -rpcconnect (ie following : in ipv4 or ]: in ipv6) // 3. default port for chain int port = BaseParams().RPCPort(); - SplitHostPort(GetArg("-rpcconnect", DEFAULT_RPCCONNECT), port, host); - port = GetArg("-rpcport", port); + SplitHostPort(gArgs.GetArg("-rpcconnect", DEFAULT_RPCCONNECT), port, host); + port = gArgs.GetArg("-rpcport", port); // Obtain event base raii_event_base base = obtain_event_base(); // Synchronously look up hostname raii_evhttp_connection evcon = obtain_evhttp_connection_base(base.get(), host, port); - evhttp_connection_set_timeout(evcon.get(), GetArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT)); + evhttp_connection_set_timeout(evcon.get(), gArgs.GetArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT)); HTTPReply response; raii_evhttp_request req = obtain_evhttp_request(http_request_done, (void*)&response); @@ -218,16 +218,16 @@ UniValue CallRPC(const std::string& strMethod, const UniValue& params) // Get credentials std::string strRPCUserColonPass; - if (GetArg("-rpcpassword", "") == "") { + if (gArgs.GetArg("-rpcpassword", "") == "") { // Try fall back to cookie-based authentication if no password is provided if (!GetAuthCookie(&strRPCUserColonPass)) { throw std::runtime_error(strprintf( _("Could not locate RPC credentials. No authentication cookie could be found, and no rpcpassword is set in the configuration file (%s)"), - GetConfigFile(GetArg("-conf", BITCOIN_CONF_FILENAME)).string().c_str())); + GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)).string().c_str())); } } else { - strRPCUserColonPass = GetArg("-rpcuser", "") + ":" + GetArg("-rpcpassword", ""); + strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", ""); } struct evkeyvalq* output_headers = evhttp_request_get_output_headers(req.get()); @@ -244,7 +244,7 @@ UniValue CallRPC(const std::string& strMethod, const UniValue& params) // check if we should use a special wallet endpoint std::string endpoint = "/"; - std::string walletName = GetArg("-rpcwallet", ""); + std::string walletName = gArgs.GetArg("-rpcwallet", ""); if (!walletName.empty()) { char *encodedURI = evhttp_uriencode(walletName.c_str(), walletName.size(), false); if (encodedURI) { @@ -294,7 +294,7 @@ int CommandLineRPC(int argc, char *argv[]) argv++; } std::vector args = std::vector(&argv[1], &argv[argc]); - if (GetBoolArg("-stdin", false)) { + if (gArgs.GetBoolArg("-stdin", false)) { // Read one arg per line from stdin and append std::string line; while (std::getline(std::cin,line)) @@ -306,14 +306,14 @@ int CommandLineRPC(int argc, char *argv[]) args.erase(args.begin()); // Remove trailing method name from arguments vector UniValue params; - if(GetBoolArg("-named", DEFAULT_NAMED)) { + if(gArgs.GetBoolArg("-named", DEFAULT_NAMED)) { params = RPCConvertNamedValues(strMethod, args); } else { params = RPCConvertValues(strMethod, args); } // Execute and handle connection failures with -rpcwait - const bool fWait = GetBoolArg("-rpcwait", false); + const bool fWait = gArgs.GetBoolArg("-rpcwait", false); do { try { const UniValue reply = CallRPC(strMethod, params); diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index e5ea9f322..cff90d13a 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -39,7 +39,7 @@ static int AppInitRawTx(int argc, char* argv[]) // // Parameters // - ParseParameters(argc, argv); + gArgs.ParseParameters(argc, argv); // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause) try { @@ -49,9 +49,9 @@ static int AppInitRawTx(int argc, char* argv[]) return EXIT_FAILURE; } - fCreateBlank = GetBoolArg("-create", false); + fCreateBlank = gArgs.GetBoolArg("-create", false); - if (argc<2 || IsArgSet("-?") || IsArgSet("-h") || IsArgSet("-help")) + if (argc<2 || gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") || gArgs.IsArgSet("-help")) { // First part of help message is specific to this utility std::string strUsage = strprintf(_("%s bitcoin-tx utility version"), _(PACKAGE_NAME)) + " " + FormatFullVersion() + "\n\n" + @@ -737,9 +737,9 @@ static void OutputTxHex(const CTransaction& tx) static void OutputTx(const CTransaction& tx) { - if (GetBoolArg("-json", false)) + if (gArgs.GetBoolArg("-json", false)) OutputTxJSON(tx); - else if (GetBoolArg("-txid", false)) + else if (gArgs.GetBoolArg("-txid", false)) OutputTxHash(tx); else OutputTxHex(tx); diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index eaef7a903..543eba0e6 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -71,14 +71,14 @@ bool AppInit(int argc, char* argv[]) // Parameters // // If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main() - ParseParameters(argc, argv); + gArgs.ParseParameters(argc, argv); // Process help and version before taking care about datadir - if (IsArgSet("-?") || IsArgSet("-h") || IsArgSet("-help") || IsArgSet("-version")) + if (gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") || gArgs.IsArgSet("-help") || gArgs.IsArgSet("-version")) { std::string strUsage = strprintf(_("%s Daemon"), _(PACKAGE_NAME)) + " " + _("version") + " " + FormatFullVersion() + "\n"; - if (IsArgSet("-version")) + if (gArgs.IsArgSet("-version")) { strUsage += FormatParagraph(LicenseInfo()); } @@ -98,12 +98,12 @@ bool AppInit(int argc, char* argv[]) { if (!fs::is_directory(GetDataDir(false))) { - fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", GetArg("-datadir", "").c_str()); + fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str()); return false; } try { - ReadConfigFile(GetArg("-conf", BITCOIN_CONF_FILENAME)); + gArgs.ReadConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)); } catch (const std::exception& e) { fprintf(stderr,"Error reading configuration file: %s\n", e.what()); return false; @@ -125,7 +125,7 @@ bool AppInit(int argc, char* argv[]) } // -server defaults to true for bitcoind but not for the GUI so do this here - SoftSetBoolArg("-server", true); + gArgs.SoftSetBoolArg("-server", true); // Set this early so that parameter interactions go to console InitLogging(); InitParameterInteraction(); @@ -144,7 +144,7 @@ bool AppInit(int argc, char* argv[]) // InitError will have been called with detailed error, which ends up on console exit(EXIT_FAILURE); } - if (GetBoolArg("-daemon", false)) + if (gArgs.GetBoolArg("-daemon", false)) { #if HAVE_DECL_DAEMON fprintf(stdout, "Bitcoin server starting\n"); diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index 43c9a13c5..224c9eb0e 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -89,8 +89,8 @@ void SelectBaseParams(const std::string& chain) std::string ChainNameFromCommandLine() { - bool fRegTest = GetBoolArg("-regtest", false); - bool fTestNet = GetBoolArg("-testnet", false); + bool fRegTest = gArgs.GetBoolArg("-regtest", false); + bool fTestNet = gArgs.GetBoolArg("-testnet", false); if (fTestNet && fRegTest) throw std::runtime_error("Invalid combination of -regtest and -testnet."); diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp index 27767f90b..3e72c5bb9 100644 --- a/src/dbwrapper.cpp +++ b/src/dbwrapper.cpp @@ -115,7 +115,7 @@ CDBWrapper::CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory, bo dbwrapper_private::HandleError(status); LogPrintf("Opened LevelDB successfully\n"); - if (GetBoolArg("-forcecompactdb", false)) { + if (gArgs.GetBoolArg("-forcecompactdb", false)) { LogPrintf("Starting database compaction of %s\n", path.string()); pdb->CompactRange(nullptr, nullptr); LogPrintf("Finished database compaction of %s\n", path.string()); diff --git a/src/httprpc.cpp b/src/httprpc.cpp index 69c3e3f49..dd219c729 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -210,7 +210,7 @@ static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &) static bool InitRPCAuthentication() { - if (GetArg("-rpcpassword", "") == "") + if (gArgs.GetArg("-rpcpassword", "") == "") { LogPrintf("No rpcpassword set - using random cookie authentication\n"); if (!GenerateAuthCookie(&strRPCUserColonPass)) { @@ -221,7 +221,7 @@ static bool InitRPCAuthentication() } } else { LogPrintf("Config options rpcuser and rpcpassword will soon be deprecated. Locally-run instances may remove rpcuser to use cookie-based auth, or may be replaced with rpcauth. Please see share/rpcuser for rpcauth auth generation.\n"); - strRPCUserColonPass = GetArg("-rpcuser", "") + ":" + GetArg("-rpcpassword", ""); + strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", ""); } return true; } diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 5adfca409..86b37f79b 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -309,14 +309,14 @@ static bool ThreadHTTP(struct event_base* base, struct evhttp* http) /** Bind HTTP server to specified addresses */ static bool HTTPBindAddresses(struct evhttp* http) { - int defaultPort = GetArg("-rpcport", BaseParams().RPCPort()); + int defaultPort = gArgs.GetArg("-rpcport", BaseParams().RPCPort()); std::vector > endpoints; // Determine what addresses to bind to - if (!IsArgSet("-rpcallowip")) { // Default to loopback if not allowing external IPs + if (!gArgs.IsArgSet("-rpcallowip")) { // Default to loopback if not allowing external IPs endpoints.push_back(std::make_pair("::1", defaultPort)); endpoints.push_back(std::make_pair("127.0.0.1", defaultPort)); - if (IsArgSet("-rpcbind")) { + if (gArgs.IsArgSet("-rpcbind")) { LogPrintf("WARNING: option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect\n"); } } else if (gArgs.IsArgSet("-rpcbind")) { // Specific bind address @@ -369,7 +369,7 @@ bool InitHTTPServer() if (!InitHTTPAllowList()) return false; - if (GetBoolArg("-rpcssl", false)) { + if (gArgs.GetBoolArg("-rpcssl", false)) { uiInterface.ThreadSafeMessageBox( "SSL mode for RPC (-rpcssl) is no longer supported.", "", CClientUIInterface::MSG_ERROR); @@ -401,7 +401,7 @@ bool InitHTTPServer() return false; } - evhttp_set_timeout(http, GetArg("-rpcservertimeout", DEFAULT_HTTP_SERVER_TIMEOUT)); + evhttp_set_timeout(http, gArgs.GetArg("-rpcservertimeout", DEFAULT_HTTP_SERVER_TIMEOUT)); evhttp_set_max_headers_size(http, MAX_HEADERS_SIZE); evhttp_set_max_body_size(http, MAX_SIZE); evhttp_set_gencb(http, http_request_cb, nullptr); @@ -412,7 +412,7 @@ bool InitHTTPServer() } LogPrint(BCLog::HTTP, "Initialized HTTP server\n"); - int workQueueDepth = std::max((long)GetArg("-rpcworkqueue", DEFAULT_HTTP_WORKQUEUE), 1L); + int workQueueDepth = std::max((long)gArgs.GetArg("-rpcworkqueue", DEFAULT_HTTP_WORKQUEUE), 1L); LogPrintf("HTTP: creating work queue of depth %d\n", workQueueDepth); workQueue = new WorkQueue(workQueueDepth); @@ -442,7 +442,7 @@ std::future threadResult; bool StartHTTPServer() { LogPrint(BCLog::HTTP, "Starting HTTP server\n"); - int rpcThreads = std::max((long)GetArg("-rpcthreads", DEFAULT_HTTP_THREADS), 1L); + int rpcThreads = std::max((long)gArgs.GetArg("-rpcthreads", DEFAULT_HTTP_THREADS), 1L); LogPrintf("HTTP: starting %d worker threads\n", rpcThreads); std::packaged_task task(ThreadHTTP); threadResult = task.get_future(); diff --git a/src/init.cpp b/src/init.cpp index cddb42c0e..d79c2967b 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -199,7 +199,7 @@ void Shutdown() StopTorControl(); UnregisterNodeSignals(GetNodeSignals()); - if (fDumpMempoolLater && GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) { + if (fDumpMempoolLater && gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) { DumpMempool(); } @@ -321,7 +321,7 @@ void OnRPCPreCommand(const CRPCCommand& cmd) { // Observe safe mode std::string strWarning = GetWarnings("rpc"); - if (strWarning != "" && !GetBoolArg("-disablesafemode", DEFAULT_DISABLE_SAFEMODE) && + if (strWarning != "" && !gArgs.GetBoolArg("-disablesafemode", DEFAULT_DISABLE_SAFEMODE) && !cmd.okSafeMode) throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, std::string("Safe mode: ") + strWarning); } @@ -332,7 +332,7 @@ std::string HelpMessage(HelpMessageMode mode) const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET); const auto defaultChainParams = CreateChainParams(CBaseChainParams::MAIN); const auto testnetChainParams = CreateChainParams(CBaseChainParams::TESTNET); - const bool showDebug = GetBoolArg("-help-debug", false); + const bool showDebug = gArgs.GetBoolArg("-help-debug", false); // 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. @@ -546,7 +546,7 @@ static void BlockNotifyCallback(bool initialSync, const CBlockIndex *pBlockIndex if (initialSync || !pBlockIndex) return; - std::string strCmd = GetArg("-blocknotify", ""); + std::string strCmd = gArgs.GetArg("-blocknotify", ""); boost::replace_all(strCmd, "%s", pBlockIndex->GetBlockHash().GetHex()); boost::thread t(runCommand, strCmd); // thread runs free @@ -683,12 +683,12 @@ void ThreadImport(std::vector vImportFiles) StartShutdown(); } - if (GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) { + if (gArgs.GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) { LogPrintf("Stopping after block import\n"); StartShutdown(); } } // End scope of CImportingNow - if (GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) { + if (gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) { LoadMempool(); fDumpMempoolLater = !fRequestShutdown; } @@ -727,7 +727,7 @@ bool AppInitServers(boost::thread_group& threadGroup) return false; if (!StartHTTPRPC()) return false; - if (GetBoolArg("-rest", DEFAULT_REST_ENABLE) && !StartREST()) + if (gArgs.GetBoolArg("-rest", DEFAULT_REST_ENABLE) && !StartREST()) return false; if (!StartHTTPServer()) return false; @@ -739,61 +739,61 @@ void InitParameterInteraction() { // when specifying an explicit binding address, you want to listen on it // even when -connect or -proxy is specified - if (IsArgSet("-bind")) { - if (SoftSetBoolArg("-listen", true)) + if (gArgs.IsArgSet("-bind")) { + if (gArgs.SoftSetBoolArg("-listen", true)) LogPrintf("%s: parameter interaction: -bind set -> setting -listen=1\n", __func__); } - if (IsArgSet("-whitebind")) { - if (SoftSetBoolArg("-listen", true)) + if (gArgs.IsArgSet("-whitebind")) { + if (gArgs.SoftSetBoolArg("-listen", true)) LogPrintf("%s: parameter interaction: -whitebind set -> setting -listen=1\n", __func__); } if (gArgs.IsArgSet("-connect")) { // when only connecting to trusted nodes, do not seed via DNS, or listen by default - if (SoftSetBoolArg("-dnsseed", false)) + if (gArgs.SoftSetBoolArg("-dnsseed", false)) LogPrintf("%s: parameter interaction: -connect set -> setting -dnsseed=0\n", __func__); - if (SoftSetBoolArg("-listen", false)) + if (gArgs.SoftSetBoolArg("-listen", false)) LogPrintf("%s: parameter interaction: -connect set -> setting -listen=0\n", __func__); } - if (IsArgSet("-proxy")) { + if (gArgs.IsArgSet("-proxy")) { // to protect privacy, do not listen by default if a default proxy server is specified - if (SoftSetBoolArg("-listen", false)) + if (gArgs.SoftSetBoolArg("-listen", false)) LogPrintf("%s: parameter interaction: -proxy set -> setting -listen=0\n", __func__); // to protect privacy, do not use UPNP when a proxy is set. The user may still specify -listen=1 // to listen locally, so don't rely on this happening through -listen below. - if (SoftSetBoolArg("-upnp", false)) + if (gArgs.SoftSetBoolArg("-upnp", false)) LogPrintf("%s: parameter interaction: -proxy set -> setting -upnp=0\n", __func__); // to protect privacy, do not discover addresses by default - if (SoftSetBoolArg("-discover", false)) + if (gArgs.SoftSetBoolArg("-discover", false)) LogPrintf("%s: parameter interaction: -proxy set -> setting -discover=0\n", __func__); } - if (!GetBoolArg("-listen", DEFAULT_LISTEN)) { + if (!gArgs.GetBoolArg("-listen", DEFAULT_LISTEN)) { // do not map ports or try to retrieve public IP when not listening (pointless) - if (SoftSetBoolArg("-upnp", false)) + if (gArgs.SoftSetBoolArg("-upnp", false)) LogPrintf("%s: parameter interaction: -listen=0 -> setting -upnp=0\n", __func__); - if (SoftSetBoolArg("-discover", false)) + if (gArgs.SoftSetBoolArg("-discover", false)) LogPrintf("%s: parameter interaction: -listen=0 -> setting -discover=0\n", __func__); - if (SoftSetBoolArg("-listenonion", false)) + if (gArgs.SoftSetBoolArg("-listenonion", false)) LogPrintf("%s: parameter interaction: -listen=0 -> setting -listenonion=0\n", __func__); } - if (IsArgSet("-externalip")) { + if (gArgs.IsArgSet("-externalip")) { // if an explicit public IP is specified, do not try to find others - if (SoftSetBoolArg("-discover", false)) + if (gArgs.SoftSetBoolArg("-discover", false)) LogPrintf("%s: parameter interaction: -externalip set -> setting -discover=0\n", __func__); } // disable whitelistrelay in blocksonly mode - if (GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)) { - if (SoftSetBoolArg("-whitelistrelay", false)) + if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)) { + if (gArgs.SoftSetBoolArg("-whitelistrelay", false)) LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -whitelistrelay=0\n", __func__); } // Forcing relay from whitelisted hosts implies we will accept relays from them in the first place. - if (GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) { - if (SoftSetBoolArg("-whitelistrelay", true)) + if (gArgs.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) { + if (gArgs.SoftSetBoolArg("-whitelistrelay", true)) LogPrintf("%s: parameter interaction: -whitelistforcerelay=1 -> setting -whitelistrelay=1\n", __func__); } } @@ -805,10 +805,10 @@ static std::string ResolveErrMsg(const char * const optname, const std::string& void InitLogging() { - fPrintToConsole = GetBoolArg("-printtoconsole", false); - fLogTimestamps = GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS); - fLogTimeMicros = GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS); - fLogIPs = GetBoolArg("-logips", DEFAULT_LOGIPS); + fPrintToConsole = gArgs.GetBoolArg("-printtoconsole", false); + fLogTimestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS); + fLogTimeMicros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS); + fLogIPs = gArgs.GetBoolArg("-logips", DEFAULT_LOGIPS); LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); LogPrintf("Bitcoin version %s\n", FormatFullVersion()); @@ -865,7 +865,7 @@ bool AppInitBasicSetup() return InitError("Initializing networking failed"); #ifndef WIN32 - if (!GetBoolArg("-sysperms", false)) { + if (!gArgs.GetBoolArg("-sysperms", false)) { umask(077); } @@ -893,8 +893,8 @@ bool AppInitParameterInteraction() // also see: InitParameterInteraction() // if using block pruning, then disallow txindex - if (GetArg("-prune", 0)) { - if (GetBoolArg("-txindex", DEFAULT_TXINDEX)) + if (gArgs.GetArg("-prune", 0)) { + if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)) return InitError(_("Prune mode is incompatible with -txindex.")); } @@ -906,7 +906,7 @@ bool AppInitParameterInteraction() // Make sure enough file descriptors are available int nBind = std::max(nUserBind, size_t(1)); - nUserMaxConnections = GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS); + nUserMaxConnections = gArgs.GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS); nMaxConnections = std::max(nUserMaxConnections, 0); // Trim requested connection counts, to fit into system limitations @@ -947,55 +947,55 @@ bool AppInitParameterInteraction() } // Check for -debugnet - if (GetBoolArg("-debugnet", false)) + if (gArgs.GetBoolArg("-debugnet", false)) InitWarning(_("Unsupported argument -debugnet ignored, use -debug=net.")); // Check for -socks - as this is a privacy risk to continue, exit here - if (IsArgSet("-socks")) + if (gArgs.IsArgSet("-socks")) return InitError(_("Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported.")); // Check for -tor - as this is a privacy risk to continue, exit here - if (GetBoolArg("-tor", false)) + if (gArgs.GetBoolArg("-tor", false)) return InitError(_("Unsupported argument -tor found, use -onion.")); - if (GetBoolArg("-benchmark", false)) + if (gArgs.GetBoolArg("-benchmark", false)) InitWarning(_("Unsupported argument -benchmark ignored, use -debug=bench.")); - if (GetBoolArg("-whitelistalwaysrelay", false)) + if (gArgs.GetBoolArg("-whitelistalwaysrelay", false)) InitWarning(_("Unsupported argument -whitelistalwaysrelay ignored, use -whitelistrelay and/or -whitelistforcerelay.")); - if (IsArgSet("-blockminsize")) + if (gArgs.IsArgSet("-blockminsize")) InitWarning("Unsupported argument -blockminsize ignored."); // Checkmempool and checkblockindex default to true in regtest mode - int ratio = std::min(std::max(GetArg("-checkmempool", chainparams.DefaultConsistencyChecks() ? 1 : 0), 0), 1000000); + int ratio = std::min(std::max(gArgs.GetArg("-checkmempool", chainparams.DefaultConsistencyChecks() ? 1 : 0), 0), 1000000); if (ratio != 0) { mempool.setSanityCheck(1.0 / ratio); } - fCheckBlockIndex = GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks()); - fCheckpointsEnabled = GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_ENABLED); + fCheckBlockIndex = gArgs.GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks()); + fCheckpointsEnabled = gArgs.GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_ENABLED); - hashAssumeValid = uint256S(GetArg("-assumevalid", chainparams.GetConsensus().defaultAssumeValid.GetHex())); + hashAssumeValid = uint256S(gArgs.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; + int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; + int64_t nMempoolSizeMin = gArgs.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 minimum 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")) + if (gArgs.IsArgSet("-incrementalrelayfee")) { CAmount n = 0; - if (!ParseMoney(GetArg("-incrementalrelayfee", ""), n)) - return InitError(AmountErrMsg("incrementalrelayfee", GetArg("-incrementalrelayfee", ""))); + if (!ParseMoney(gArgs.GetArg("-incrementalrelayfee", ""), n)) + return InitError(AmountErrMsg("incrementalrelayfee", gArgs.GetArg("-incrementalrelayfee", ""))); incrementalRelayFee = CFeeRate(n); } // -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency - nScriptCheckThreads = GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS); + nScriptCheckThreads = gArgs.GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS); if (nScriptCheckThreads <= 0) nScriptCheckThreads += GetNumCores(); if (nScriptCheckThreads <= 1) @@ -1004,7 +1004,7 @@ bool AppInitParameterInteraction() nScriptCheckThreads = MAX_SCRIPTCHECK_THREADS; // block pruning; get the amount of disk space (in MiB) to allot for block & undo files - int64_t nPruneArg = GetArg("-prune", 0); + int64_t nPruneArg = gArgs.GetArg("-prune", 0); if (nPruneArg < 0) { return InitError(_("Prune cannot be configured with a negative value.")); } @@ -1026,14 +1026,14 @@ bool AppInitParameterInteraction() RegisterWalletRPCCommands(tableRPC); #endif - nConnectTimeout = GetArg("-timeout", DEFAULT_CONNECT_TIMEOUT); + nConnectTimeout = gArgs.GetArg("-timeout", DEFAULT_CONNECT_TIMEOUT); if (nConnectTimeout <= 0) nConnectTimeout = DEFAULT_CONNECT_TIMEOUT; - if (IsArgSet("-minrelaytxfee")) { + if (gArgs.IsArgSet("-minrelaytxfee")) { CAmount n = 0; - if (!ParseMoney(GetArg("-minrelaytxfee", ""), n)) { - return InitError(AmountErrMsg("minrelaytxfee", GetArg("-minrelaytxfee", ""))); + if (!ParseMoney(gArgs.GetArg("-minrelaytxfee", ""), n)) { + return InitError(AmountErrMsg("minrelaytxfee", gArgs.GetArg("-minrelaytxfee", ""))); } // High fee check is done afterward in CWallet::ParameterInteraction() ::minRelayTxFee = CFeeRate(n); @@ -1045,55 +1045,55 @@ bool AppInitParameterInteraction() // 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")) + if (gArgs.IsArgSet("-blockmintxfee")) { CAmount n = 0; - if (!ParseMoney(GetArg("-blockmintxfee", ""), n)) - return InitError(AmountErrMsg("blockmintxfee", GetArg("-blockmintxfee", ""))); + if (!ParseMoney(gArgs.GetArg("-blockmintxfee", ""), n)) + return InitError(AmountErrMsg("blockmintxfee", gArgs.GetArg("-blockmintxfee", ""))); } // Feerate used to define dust. Shouldn't be changed lightly as old // implementations may inadvertently create non-standard transactions - if (IsArgSet("-dustrelayfee")) + if (gArgs.IsArgSet("-dustrelayfee")) { CAmount n = 0; - if (!ParseMoney(GetArg("-dustrelayfee", ""), n) || 0 == n) - return InitError(AmountErrMsg("dustrelayfee", GetArg("-dustrelayfee", ""))); + if (!ParseMoney(gArgs.GetArg("-dustrelayfee", ""), n) || 0 == n) + return InitError(AmountErrMsg("dustrelayfee", gArgs.GetArg("-dustrelayfee", ""))); dustRelayFee = CFeeRate(n); } - fRequireStandard = !GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard()); + fRequireStandard = !gArgs.GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard()); if (chainparams.RequireStandard() && !fRequireStandard) return InitError(strprintf("acceptnonstdtxn is not currently supported for %s chain", chainparams.NetworkIDString())); - nBytesPerSigOp = GetArg("-bytespersigop", nBytesPerSigOp); + nBytesPerSigOp = gArgs.GetArg("-bytespersigop", nBytesPerSigOp); #ifdef ENABLE_WALLET if (!CWallet::ParameterInteraction()) return false; #endif - fIsBareMultisigStd = GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG); - fAcceptDatacarrier = GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER); - nMaxDatacarrierBytes = GetArg("-datacarriersize", nMaxDatacarrierBytes); + fIsBareMultisigStd = gArgs.GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG); + fAcceptDatacarrier = gArgs.GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER); + nMaxDatacarrierBytes = gArgs.GetArg("-datacarriersize", nMaxDatacarrierBytes); // Option to startup with mocktime set (used for regression testing): - SetMockTime(GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op + SetMockTime(gArgs.GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op - if (GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS)) + if (gArgs.GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS)) nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM); - if (GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) < 0) + if (gArgs.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) < 0) return InitError("rpcserialversion must be non-negative."); - if (GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) > 1) + if (gArgs.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) > 1) return InitError("unknown rpcserialversion requested."); - nMaxTipAge = GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE); + nMaxTipAge = gArgs.GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE); - fEnableReplacement = GetBoolArg("-mempoolreplacement", DEFAULT_ENABLE_REPLACEMENT); - if ((!fEnableReplacement) && IsArgSet("-mempoolreplacement")) { + fEnableReplacement = gArgs.GetBoolArg("-mempoolreplacement", DEFAULT_ENABLE_REPLACEMENT); + if ((!fEnableReplacement) && gArgs.IsArgSet("-mempoolreplacement")) { // Minimal effort at forwards compatibility - std::string strReplacementModeList = GetArg("-mempoolreplacement", ""); // default is impossible + std::string strReplacementModeList = gArgs.GetArg("-mempoolreplacement", ""); // default is impossible std::vector vstrReplacementModes; boost::split(vstrReplacementModes, strReplacementModeList, boost::is_any_of(",")); fEnableReplacement = (std::find(vstrReplacementModes.begin(), vstrReplacementModes.end(), "fee") != vstrReplacementModes.end()); @@ -1198,7 +1198,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) #ifndef WIN32 CreatePidFile(GetPidFile(), getpid()); #endif - if (GetBoolArg("-shrinkdebugfile", logCategories == BCLog::NONE)) { + if (gArgs.GetBoolArg("-shrinkdebugfile", logCategories == BCLog::NONE)) { // 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(); @@ -1211,7 +1211,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) LogPrintf("Startup time: %s\n", DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime())); LogPrintf("Default data directory %s\n", GetDefaultDataDir().string()); LogPrintf("Using data directory %s\n", GetDataDir().string()); - LogPrintf("Using config file %s\n", GetConfigFile(GetArg("-conf", BITCOIN_CONF_FILENAME)).string()); + LogPrintf("Using config file %s\n", GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)).string()); LogPrintf("Using at most %i automatic connections (%i file descriptors available)\n", nMaxConnections, nFD); InitSignatureCache(); @@ -1234,7 +1234,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) * that the server is there and will be ready later). Warmup mode will * be disabled when initialisation is finished. */ - if (GetBoolArg("-server", false)) + if (gArgs.GetBoolArg("-server", false)) { uiInterface.InitMessage.connect(SetRPCWarmupStatus); if (!AppInitServers(threadGroup)) @@ -1291,12 +1291,12 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) } // Check for host lookup allowed before parsing any network related parameters - fNameLookup = GetBoolArg("-dns", DEFAULT_NAME_LOOKUP); + fNameLookup = gArgs.GetBoolArg("-dns", DEFAULT_NAME_LOOKUP); - bool proxyRandomize = GetBoolArg("-proxyrandomize", DEFAULT_PROXYRANDOMIZE); + bool proxyRandomize = gArgs.GetBoolArg("-proxyrandomize", DEFAULT_PROXYRANDOMIZE); // -proxy sets a proxy for all outgoing network traffic // -noproxy (or -proxy=0) as well as the empty string can be used to not set a proxy, this is the default - std::string proxyArg = GetArg("-proxy", ""); + std::string proxyArg = gArgs.GetArg("-proxy", ""); SetLimited(NET_TOR); if (proxyArg != "" && proxyArg != "0") { CService proxyAddr; @@ -1318,7 +1318,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) // -onion can be used to set only a proxy for .onion, or override normal proxy for .onion addresses // -noonion (or -onion=0) disables connecting to .onion entirely // An empty string is used to not override the onion proxy (in which case it defaults to -proxy set above, or none) - std::string onionArg = GetArg("-onion", ""); + std::string onionArg = gArgs.GetArg("-onion", ""); if (onionArg != "") { if (onionArg == "0") { // Handle -noonion/-onion=0 SetLimited(NET_TOR); // set onions as unreachable @@ -1336,9 +1336,9 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) } // see Step 2: parameter interactions for more information about these - fListen = GetBoolArg("-listen", DEFAULT_LISTEN); - fDiscover = GetBoolArg("-discover", true); - fRelayTxes = !GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY); + fListen = gArgs.GetBoolArg("-listen", DEFAULT_LISTEN); + fDiscover = gArgs.GetBoolArg("-discover", true); + fRelayTxes = !gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY); for (const std::string& strAddr : gArgs.GetArgs("-externalip")) { CService addrLocal; @@ -1358,27 +1358,27 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) uint64_t nMaxOutboundLimit = 0; //unlimited unless -maxuploadtarget is set uint64_t nMaxOutboundTimeframe = MAX_UPLOAD_TIMEFRAME; - if (IsArgSet("-maxuploadtarget")) { - nMaxOutboundLimit = GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET)*1024*1024; + if (gArgs.IsArgSet("-maxuploadtarget")) { + nMaxOutboundLimit = gArgs.GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET)*1024*1024; } // ********************************************************* Step 7: load block chain - fReindex = GetBoolArg("-reindex", false); - bool fReindexChainState = GetBoolArg("-reindex-chainstate", false); + fReindex = gArgs.GetBoolArg("-reindex", false); + bool fReindexChainState = gArgs.GetBoolArg("-reindex-chainstate", false); // cache size calculations - int64_t nTotalCache = (GetArg("-dbcache", nDefaultDbCache) << 20); + int64_t nTotalCache = (gArgs.GetArg("-dbcache", nDefaultDbCache) << 20); nTotalCache = std::max(nTotalCache, nMinDbCache << 20); // total cache cannot be less than nMinDbCache nTotalCache = std::min(nTotalCache, nMaxDbCache << 20); // total cache cannot be greater than nMaxDbcache int64_t nBlockTreeDBCache = nTotalCache / 8; - nBlockTreeDBCache = std::min(nBlockTreeDBCache, (GetBoolArg("-txindex", DEFAULT_TXINDEX) ? nMaxBlockDBAndTxIndexCache : nMaxBlockDBCache) << 20); + nBlockTreeDBCache = std::min(nBlockTreeDBCache, (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX) ? nMaxBlockDBAndTxIndexCache : nMaxBlockDBCache) << 20); nTotalCache -= nBlockTreeDBCache; int64_t nCoinDBCache = std::min(nTotalCache / 2, (nTotalCache / 4) + (1 << 23)); // use 25%-50% of the remainder for disk cache nCoinDBCache = std::min(nCoinDBCache, nMaxCoinsDBCache << 20); // cap total coins db cache nTotalCache -= nCoinDBCache; nCoinCacheUsage = nTotalCache; // the rest goes to in-memory cache - int64_t nMempoolSizeMax = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; + int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; LogPrintf("Cache configuration:\n"); LogPrintf("* Using %.1fMiB for block index database\n", nBlockTreeDBCache * (1.0 / 1024 / 1024)); LogPrintf("* Using %.1fMiB for chain state database\n", nCoinDBCache * (1.0 / 1024 / 1024)); @@ -1427,7 +1427,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?")); // Check for changed -txindex state - if (fTxIndex != GetBoolArg("-txindex", DEFAULT_TXINDEX)) { + if (fTxIndex != gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)) { strLoadError = _("You need to rebuild the database using -reindex-chainstate to change -txindex"); break; } @@ -1493,7 +1493,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) if (!is_coinsview_empty) { uiInterface.InitMessage(_("Verifying blocks...")); - if (fHavePruned && GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) { + if (fHavePruned && gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) { LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks", MIN_BLOCKS_TO_KEEP); } @@ -1510,8 +1510,8 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) } } - if (!CVerifyDB().VerifyDB(chainparams, pcoinsdbview, GetArg("-checklevel", DEFAULT_CHECKLEVEL), - GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) { + if (!CVerifyDB().VerifyDB(chainparams, pcoinsdbview, gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL), + gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) { strLoadError = _("Corrupted block database detected"); break; } @@ -1610,7 +1610,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) fHaveGenesis = true; } - if (IsArgSet("-blocknotify")) + if (gArgs.IsArgSet("-blocknotify")) uiInterface.NotifyBlockTip.connect(BlockNotifyCallback); std::vector vImportFiles; @@ -1634,13 +1634,13 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) //// debug print LogPrintf("mapBlockIndex.size() = %u\n", mapBlockIndex.size()); LogPrintf("nBestHeight = %d\n", chainActive.Height()); - if (GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) + if (gArgs.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) StartTorControl(threadGroup, scheduler); Discover(threadGroup); // Map ports with UPnP - MapPort(GetBoolArg("-upnp", DEFAULT_UPNP)); + MapPort(gArgs.GetBoolArg("-upnp", DEFAULT_UPNP)); CConnman::Options connOptions; connOptions.nLocalServices = nLocalServices; @@ -1651,8 +1651,8 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) connOptions.nMaxFeeler = 1; connOptions.nBestHeight = chainActive.Height(); connOptions.uiInterface = &uiInterface; - connOptions.nSendBufferMaxSize = 1000*GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER); - connOptions.nReceiveFloodSize = 1000*GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER); + connOptions.nSendBufferMaxSize = 1000*gArgs.GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER); + connOptions.nReceiveFloodSize = 1000*gArgs.GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER); connOptions.nMaxOutboundTimeframe = nMaxOutboundTimeframe; connOptions.nMaxOutboundLimit = nMaxOutboundLimit; diff --git a/src/miner.cpp b/src/miner.cpp index 79016bfd3..403d3d4e4 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -88,20 +88,20 @@ static BlockAssembler::Options DefaultOptions(const CChainParams& params) options.nBlockMaxWeight = DEFAULT_BLOCK_MAX_WEIGHT; options.nBlockMaxSize = DEFAULT_BLOCK_MAX_SIZE; bool fWeightSet = false; - if (IsArgSet("-blockmaxweight")) { - options.nBlockMaxWeight = GetArg("-blockmaxweight", DEFAULT_BLOCK_MAX_WEIGHT); + if (gArgs.IsArgSet("-blockmaxweight")) { + options.nBlockMaxWeight = gArgs.GetArg("-blockmaxweight", DEFAULT_BLOCK_MAX_WEIGHT); options.nBlockMaxSize = MAX_BLOCK_SERIALIZED_SIZE; fWeightSet = true; } - if (IsArgSet("-blockmaxsize")) { - options.nBlockMaxSize = GetArg("-blockmaxsize", DEFAULT_BLOCK_MAX_SIZE); + if (gArgs.IsArgSet("-blockmaxsize")) { + options.nBlockMaxSize = gArgs.GetArg("-blockmaxsize", DEFAULT_BLOCK_MAX_SIZE); if (!fWeightSet) { options.nBlockMaxWeight = options.nBlockMaxSize * WITNESS_SCALE_FACTOR; } } - if (IsArgSet("-blockmintxfee")) { + if (gArgs.IsArgSet("-blockmintxfee")) { CAmount n = 0; - ParseMoney(GetArg("-blockmintxfee", ""), n); + ParseMoney(gArgs.GetArg("-blockmintxfee", ""), n); options.blockMinFeeRate = CFeeRate(n); } else { options.blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE); @@ -151,7 +151,7 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc // -regtest only: allow overriding block.nVersion with // -blockversion=N to test forking scenarios if (chainparams.MineBlocksOnDemand()) - pblock->nVersion = GetArg("-blockversion", pblock->nVersion); + pblock->nVersion = gArgs.GetArg("-blockversion", pblock->nVersion); pblock->nTime = GetAdjustedTime(); const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast(); @@ -272,7 +272,7 @@ void BlockAssembler::AddToBlock(CTxMemPool::txiter iter) nFees += iter->GetFee(); inBlock.insert(iter); - bool fPrintPriority = GetBoolArg("-printpriority", DEFAULT_PRINTPRIORITY); + bool fPrintPriority = gArgs.GetBoolArg("-printpriority", DEFAULT_PRINTPRIORITY); if (fPrintPriority) { LogPrintf("fee %s txid %s\n", CFeeRate(iter->GetModifiedFee(), iter->GetTxSize()).ToString(), diff --git a/src/net.cpp b/src/net.cpp index 0ed219606..599a6128f 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -101,7 +101,7 @@ void CConnman::AddOneShot(const std::string& strDest) unsigned short GetListenPort() { - return (unsigned short)(GetArg("-port", Params().GetDefaultPort())); + return (unsigned short)(gArgs.GetArg("-port", Params().GetDefaultPort())); } // find 'best' local address for a particular peer @@ -514,7 +514,7 @@ void CConnman::Ban(const CSubNet& subNet, const BanReason &banReason, int64_t ba banEntry.banReason = banReason; if (bantimeoffset <= 0) { - bantimeoffset = GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME); + bantimeoffset = gArgs.GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME); sinceUnixEpoch = false; } banEntry.nBanUntil = (sinceUnixEpoch ? 0 : GetTime() )+bantimeoffset; @@ -1575,7 +1575,7 @@ void CConnman::ThreadDNSAddressSeed() // creating fewer identifying DNS requests, reduces trust by giving seeds // less influence on the network topology, and reduces traffic to the seeds. if ((addrman.size() > 0) && - (!GetBoolArg("-forcednsseed", DEFAULT_FORCEDNSSEED))) { + (!gArgs.GetBoolArg("-forcednsseed", DEFAULT_FORCEDNSSEED))) { if (!interruptNet.sleep_for(std::chrono::seconds(11))) return; @@ -2336,7 +2336,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions) // Send and receive from sockets, accept connections threadSocketHandler = std::thread(&TraceThread >, "net", std::function(std::bind(&CConnman::ThreadSocketHandler, this))); - if (!GetBoolArg("-dnsseed", true)) + if (!gArgs.GetBoolArg("-dnsseed", true)) LogPrintf("DNS seeding disabled\n"); else threadDNSAddressSeed = std::thread(&TraceThread >, "dnsseed", std::function(std::bind(&CConnman::ThreadDNSAddressSeed, this))); diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 766618130..e09b56a63 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -581,7 +581,7 @@ void UnregisterNodeSignals(CNodeSignals& nodeSignals) void AddToCompactExtraTransactions(const CTransactionRef& tx) { - size_t max_extra_txn = GetArg("-blockreconstructionextratxn", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN); + size_t max_extra_txn = gArgs.GetArg("-blockreconstructionextratxn", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN); if (max_extra_txn <= 0) return; if (!vExtraTxnForCompact.size()) @@ -704,7 +704,7 @@ void Misbehaving(NodeId pnode, int howmuch) return; state->nMisbehavior += howmuch; - int banscore = GetArg("-banscore", DEFAULT_BANSCORE_THRESHOLD); + int banscore = gArgs.GetArg("-banscore", DEFAULT_BANSCORE_THRESHOLD); if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore) { LogPrintf("%s: %s peer=%d (%d -> %d) BAN THRESHOLD EXCEEDED\n", __func__, state->name, pnode, state->nMisbehavior-howmuch, state->nMisbehavior); @@ -1175,7 +1175,7 @@ inline void static SendBlockTransactions(const CBlock& block, const BlockTransac bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman& connman, const std::atomic& interruptMsgProc) { LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->GetId()); - if (IsArgSet("-dropmessagestest") && GetRand(GetArg("-dropmessagestest", 0)) == 0) + if (gArgs.IsArgSet("-dropmessagestest") && GetRand(gArgs.GetArg("-dropmessagestest", 0)) == 0) { LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n"); return true; @@ -1541,7 +1541,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr bool fBlocksOnly = !fRelayTxes; // Allow whitelisted peers to send data other than blocks in blocks only mode if whitelistrelay is true - if (pfrom->fWhitelisted && GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)) + if (pfrom->fWhitelisted && gArgs.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)) fBlocksOnly = false; LOCK(cs_main); @@ -1782,7 +1782,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr { // Stop processing the transaction early if // We are in blocks only mode and peer is either not whitelisted or whitelistrelay is off - if (!fRelayTxes && (!pfrom->fWhitelisted || !GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY))) + if (!fRelayTxes && (!pfrom->fWhitelisted || !gArgs.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY))) { LogPrint(BCLog::NET, "transaction sent in violation of protocol peer=%d\n", pfrom->GetId()); return true; @@ -1901,7 +1901,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr AddOrphanTx(ptx, pfrom->GetId()); // DoS prevention: do not allow mapOrphanTransactions to grow unbounded - unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS)); + unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, gArgs.GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS)); unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx); if (nEvicted > 0) { LogPrint(BCLog::MEMPOOL, "mapOrphan overflow, removed %u tx\n", nEvicted); @@ -1926,7 +1926,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr AddToCompactExtraTransactions(ptx); } - if (pfrom->fWhitelisted && GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) { + if (pfrom->fWhitelisted && gArgs.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) { // Always relay transactions received from whitelisted peers, even // if they were already in the mempool or rejected from it due // to policy, allowing the node to function as a gateway for @@ -3297,9 +3297,9 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic& interr // Message: feefilter // // We don't want white listed peers to filter txs to us if we have -whitelistforcerelay - if (pto->nVersion >= FEEFILTER_VERSION && GetBoolArg("-feefilter", DEFAULT_FEEFILTER) && - !(pto->fWhitelisted && GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY))) { - CAmount currentFilter = mempool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK(); + if (pto->nVersion >= FEEFILTER_VERSION && gArgs.GetBoolArg("-feefilter", DEFAULT_FEEFILTER) && + !(pto->fWhitelisted && gArgs.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY))) { + CAmount currentFilter = mempool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK(); int64_t timeNow = GetTimeMicros(); if (timeNow > pto->nextSendTimeFeeFilter) { static CFeeRate default_feerate(DEFAULT_MIN_RELAY_TX_FEE); diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index b6cfdf888..2b6bd354a 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -106,7 +106,7 @@ static QString GetLangTerritory() if(!lang_territory_qsettings.isEmpty()) lang_territory = lang_territory_qsettings; // 3) -lang command line argument - lang_territory = QString::fromStdString(GetArg("-lang", lang_territory.toStdString())); + lang_territory = QString::fromStdString(gArgs.GetArg("-lang", lang_territory.toStdString())); return lang_territory; } @@ -345,7 +345,7 @@ BitcoinApplication::BitcoinApplication(int &argc, char **argv): // This must be done inside the BitcoinApplication constructor, or after it, because // PlatformStyle::instantiate requires a QApplication std::string platformName; - platformName = GetArg("-uiplatform", BitcoinGUI::DEFAULT_UIPLATFORM); + platformName = gArgs.GetArg("-uiplatform", BitcoinGUI::DEFAULT_UIPLATFORM); platformStyle = PlatformStyle::instantiate(QString::fromStdString(platformName)); if (!platformStyle) // Fall back to "other" if specified name not found platformStyle = PlatformStyle::instantiate("other"); @@ -498,7 +498,7 @@ void BitcoinApplication::initializeResult(bool success) #endif // If -min option passed, start window minimized. - if(GetBoolArg("-min", false)) + if(gArgs.GetBoolArg("-min", false)) { window->showMinimized(); } @@ -550,7 +550,7 @@ int main(int argc, char *argv[]) /// 1. Parse command-line options. These take precedence over anything else. // Command-line options take precedence: - ParseParameters(argc, argv); + gArgs.ParseParameters(argc, argv); // Do not refer to data directory yet, this can be overridden by Intro::pickDataDirectory @@ -606,9 +606,9 @@ int main(int argc, char *argv[]) // Show help message immediately after parsing command-line options (for "-lang") and setting locale, // but before showing splash screen. - if (IsArgSet("-?") || IsArgSet("-h") || IsArgSet("-help") || IsArgSet("-version")) + if (gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") || gArgs.IsArgSet("-help") || gArgs.IsArgSet("-version")) { - HelpMessageDialog help(nullptr, IsArgSet("-version")); + HelpMessageDialog help(nullptr, gArgs.IsArgSet("-version")); help.showOrPrint(); return EXIT_SUCCESS; } @@ -623,11 +623,11 @@ int main(int argc, char *argv[]) if (!fs::is_directory(GetDataDir(false))) { QMessageBox::critical(0, QObject::tr(PACKAGE_NAME), - QObject::tr("Error: Specified data directory \"%1\" does not exist.").arg(QString::fromStdString(GetArg("-datadir", "")))); + QObject::tr("Error: Specified data directory \"%1\" does not exist.").arg(QString::fromStdString(gArgs.GetArg("-datadir", "")))); return EXIT_FAILURE; } try { - ReadConfigFile(GetArg("-conf", BITCOIN_CONF_FILENAME)); + gArgs.ReadConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)); } catch (const std::exception& e) { QMessageBox::critical(0, QObject::tr(PACKAGE_NAME), QObject::tr("Error: Cannot parse configuration file: %1. Only use key=value syntax.").arg(e.what())); @@ -691,12 +691,12 @@ int main(int argc, char *argv[]) // Allow parameter interaction before we create the options model app.parameterSetup(); // Load GUI settings from QSettings - app.createOptionsModel(IsArgSet("-resetguisettings")); + app.createOptionsModel(gArgs.IsArgSet("-resetguisettings")); // Subscribe to global signals from core uiInterface.InitMessage.connect(InitMessage); - if (GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !GetBoolArg("-min", false)) + if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !gArgs.GetBoolArg("-min", false)) app.createSplashScreen(networkStyle.data()); int rv = EXIT_SUCCESS; diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 28ffda998..f3c5daebe 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -651,7 +651,7 @@ bool SetStartOnSystemStartup(bool fAutoStart) // Start client minimized QString strArgs = "-min"; // Set -testnet /-regtest options - strArgs += QString::fromStdString(strprintf(" -testnet=%d -regtest=%d", GetBoolArg("-testnet", false), GetBoolArg("-regtest", false))); + strArgs += QString::fromStdString(strprintf(" -testnet=%d -regtest=%d", gArgs.GetBoolArg("-testnet", false), gArgs.GetBoolArg("-regtest", false))); #ifdef UNICODE boost::scoped_array args(new TCHAR[strArgs.length() + 1]); @@ -760,7 +760,7 @@ bool SetStartOnSystemStartup(bool fAutoStart) optionFile << "Name=Bitcoin\n"; else optionFile << strprintf("Name=Bitcoin (%s)\n", chain); - optionFile << "Exec=" << pszExePath << strprintf(" -min -testnet=%d -regtest=%d\n", GetBoolArg("-testnet", false), GetBoolArg("-regtest", false)); + optionFile << "Exec=" << pszExePath << strprintf(" -min -testnet=%d -regtest=%d\n", gArgs.GetBoolArg("-testnet", false), gArgs.GetBoolArg("-regtest", false)); optionFile << "Terminal=false\n"; optionFile << "Hidden=false\n"; optionFile.close(); diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index 231a71575..72809942f 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -131,7 +131,7 @@ Intro::Intro(QWidget *parent) : ); ui->lblExplanation2->setText(ui->lblExplanation2->text().arg(tr(PACKAGE_NAME))); - uint64_t pruneTarget = std::max(0, GetArg("-prune", 0)); + uint64_t pruneTarget = std::max(0, gArgs.GetArg("-prune", 0)); requiredSpace = BLOCK_CHAIN_SIZE; QString storageRequiresMsg = tr("At least %1 GB of data will be stored in this directory, and it will grow over time."); if (pruneTarget) { @@ -191,14 +191,14 @@ bool Intro::pickDataDirectory() QSettings settings; /* If data directory provided on command line, no need to look at settings or show a picking dialog */ - if(!GetArg("-datadir", "").empty()) + if(!gArgs.GetArg("-datadir", "").empty()) return true; /* 1) Default data directory for operating system */ QString dataDir = getDefaultDataDirectory(); /* 2) Allow QSettings to override default dir */ dataDir = settings.value("strDataDir", dataDir).toString(); - if(!fs::exists(GUIUtil::qstringToBoostPath(dataDir)) || GetBoolArg("-choosedatadir", DEFAULT_CHOOSE_DATADIR) || settings.value("fReset", false).toBool() || GetBoolArg("-resetguisettings", false)) + if(!fs::exists(GUIUtil::qstringToBoostPath(dataDir)) || gArgs.GetBoolArg("-choosedatadir", DEFAULT_CHOOSE_DATADIR) || settings.value("fReset", false).toBool() || gArgs.GetBoolArg("-resetguisettings", false)) { /* If current default data directory does not exist, let the user choose one */ Intro intro; @@ -231,7 +231,7 @@ bool Intro::pickDataDirectory() * (to be consistent with bitcoind behavior) */ if(dataDir != getDefaultDataDirectory()) - SoftSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting + gArgs.SoftSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting return true; } diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index d6e740ee9..77efef3e3 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -36,7 +36,7 @@ OptionsModel::OptionsModel(QObject *parent, bool resetSettings) : void OptionsModel::addOverriddenOption(const std::string &option) { - strOverriddenByCommandLine += QString::fromStdString(option) + "=" + QString::fromStdString(GetArg(option, "")) + " "; + strOverriddenByCommandLine += QString::fromStdString(option) + "=" + QString::fromStdString(gArgs.GetArg(option, "")) + " "; } // Writes all missing QSettings with their default values @@ -86,18 +86,18 @@ void OptionsModel::Init(bool resetSettings) // // If setting doesn't exist create it with defaults. // - // If SoftSetArg() or SoftSetBoolArg() return false we were overridden + // If gArgs.SoftSetArg() or gArgs.SoftSetBoolArg() return false we were overridden // by command-line and show this in the UI. // Main if (!settings.contains("nDatabaseCache")) settings.setValue("nDatabaseCache", (qint64)nDefaultDbCache); - if (!SoftSetArg("-dbcache", settings.value("nDatabaseCache").toString().toStdString())) + if (!gArgs.SoftSetArg("-dbcache", settings.value("nDatabaseCache").toString().toStdString())) addOverriddenOption("-dbcache"); if (!settings.contains("nThreadsScriptVerif")) settings.setValue("nThreadsScriptVerif", DEFAULT_SCRIPTCHECK_THREADS); - if (!SoftSetArg("-par", settings.value("nThreadsScriptVerif").toString().toStdString())) + if (!gArgs.SoftSetArg("-par", settings.value("nThreadsScriptVerif").toString().toStdString())) addOverriddenOption("-par"); if (!settings.contains("strDataDir")) @@ -107,19 +107,19 @@ void OptionsModel::Init(bool resetSettings) #ifdef ENABLE_WALLET if (!settings.contains("bSpendZeroConfChange")) settings.setValue("bSpendZeroConfChange", true); - if (!SoftSetBoolArg("-spendzeroconfchange", settings.value("bSpendZeroConfChange").toBool())) + if (!gArgs.SoftSetBoolArg("-spendzeroconfchange", settings.value("bSpendZeroConfChange").toBool())) addOverriddenOption("-spendzeroconfchange"); #endif // Network if (!settings.contains("fUseUPnP")) settings.setValue("fUseUPnP", DEFAULT_UPNP); - if (!SoftSetBoolArg("-upnp", settings.value("fUseUPnP").toBool())) + if (!gArgs.SoftSetBoolArg("-upnp", settings.value("fUseUPnP").toBool())) addOverriddenOption("-upnp"); if (!settings.contains("fListen")) settings.setValue("fListen", DEFAULT_LISTEN); - if (!SoftSetBoolArg("-listen", settings.value("fListen").toBool())) + if (!gArgs.SoftSetBoolArg("-listen", settings.value("fListen").toBool())) addOverriddenOption("-listen"); if (!settings.contains("fUseProxy")) @@ -127,9 +127,9 @@ void OptionsModel::Init(bool resetSettings) if (!settings.contains("addrProxy")) settings.setValue("addrProxy", "127.0.0.1:9050"); // Only try to set -proxy, if user has enabled fUseProxy - if (settings.value("fUseProxy").toBool() && !SoftSetArg("-proxy", settings.value("addrProxy").toString().toStdString())) + if (settings.value("fUseProxy").toBool() && !gArgs.SoftSetArg("-proxy", settings.value("addrProxy").toString().toStdString())) addOverriddenOption("-proxy"); - else if(!settings.value("fUseProxy").toBool() && !GetArg("-proxy", "").empty()) + else if(!settings.value("fUseProxy").toBool() && !gArgs.GetArg("-proxy", "").empty()) addOverriddenOption("-proxy"); if (!settings.contains("fUseSeparateProxyTor")) @@ -137,15 +137,15 @@ void OptionsModel::Init(bool resetSettings) if (!settings.contains("addrSeparateProxyTor")) settings.setValue("addrSeparateProxyTor", "127.0.0.1:9050"); // Only try to set -onion, if user has enabled fUseSeparateProxyTor - if (settings.value("fUseSeparateProxyTor").toBool() && !SoftSetArg("-onion", settings.value("addrSeparateProxyTor").toString().toStdString())) + if (settings.value("fUseSeparateProxyTor").toBool() && !gArgs.SoftSetArg("-onion", settings.value("addrSeparateProxyTor").toString().toStdString())) addOverriddenOption("-onion"); - else if(!settings.value("fUseSeparateProxyTor").toBool() && !GetArg("-onion", "").empty()) + else if(!settings.value("fUseSeparateProxyTor").toBool() && !gArgs.GetArg("-onion", "").empty()) addOverriddenOption("-onion"); // Display if (!settings.contains("language")) settings.setValue("language", ""); - if (!SoftSetArg("-lang", settings.value("language").toString().toStdString())) + if (!gArgs.SoftSetArg("-lang", settings.value("language").toString().toStdString())) addOverriddenOption("-lang"); language = settings.value("language").toString(); diff --git a/src/qt/paymentrequestplus.cpp b/src/qt/paymentrequestplus.cpp index 897762b1a..0a5cb9866 100644 --- a/src/qt/paymentrequestplus.cpp +++ b/src/qt/paymentrequestplus.cpp @@ -145,7 +145,7 @@ bool PaymentRequestPlus::getMerchant(X509_STORE* certStore, QString& merchant) c int error = X509_STORE_CTX_get_error(store_ctx); // For testing payment requests, we allow self signed root certs! // This option is just shown in the UI options, if -help-debug is enabled. - if (!(error == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT && GetBoolArg("-allowselfsignedrootcertificates", DEFAULT_SELFSIGNED_ROOTCERTS))) { + if (!(error == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT && gArgs.GetBoolArg("-allowselfsignedrootcertificates", DEFAULT_SELFSIGNED_ROOTCERTS))) { throw SSLVerifyError(X509_verify_cert_error_string(error)); } else { qDebug() << "PaymentRequestPlus::getMerchant: Allowing self signed root certificate, because -allowselfsignedrootcertificates is true."; diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index c5e2b78c5..67fe32018 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -122,7 +122,7 @@ void PaymentServer::LoadRootCAs(X509_STORE* _store) // Note: use "-system-" default here so that users can pass -rootcertificates="" // and get 'I don't like X.509 certificates, don't trust anybody' behavior: - QString certFile = QString::fromStdString(GetArg("-rootcertificates", "-system-")); + QString certFile = QString::fromStdString(gArgs.GetArg("-rootcertificates", "-system-")); // Empty store if (certFile.isEmpty()) { diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp index fbad9e544..e2abfb374 100644 --- a/src/qt/test/rpcnestedtests.cpp +++ b/src/qt/test/rpcnestedtests.cpp @@ -41,7 +41,7 @@ void RPCNestedTests::rpcNestedTests() std::string path = QDir::tempPath().toStdString() + "/" + strprintf("test_bitcoin_qt_%lu_%i", (unsigned long)GetTime(), (int)(GetRand(100000))); QDir dir(QString::fromStdString(path)); dir.mkpath("."); - ForceSetArg("-datadir", path); + gArgs.ForceSetArg("-datadir", path); //mempool.setSanityCheck(1.0); TestingSetup test; diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp index c9b344fbd..5d8c23d13 100644 --- a/src/qt/utilitydialog.cpp +++ b/src/qt/utilitydialog.cpp @@ -78,7 +78,7 @@ HelpMessageDialog::HelpMessageDialog(QWidget *parent, bool about) : cursor.insertBlock(); std::string strUsage = HelpMessage(HMM_BITCOIN_QT); - const bool showDebug = GetBoolArg("-help-debug", false); + const bool showDebug = gArgs.GetBoolArg("-help-debug", false); strUsage += HelpMessageGroup(tr("UI Options:").toStdString()); if (showDebug) { strUsage += HelpMessageOpt("-allowselfsignedrootcertificates", strprintf("Allow self signed root certificates (default: %u)", DEFAULT_SELFSIGNED_ROOTCERTS)); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index ba0e1da0c..3ca726d0f 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -739,7 +739,7 @@ bool WalletModel::bumpFee(uint256 hash) bool WalletModel::isWalletEnabled() { - return !GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET); + return !gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET); } bool WalletModel::hdEnabled() const diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 701608e13..24e0405a8 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1018,8 +1018,8 @@ UniValue gettxout(const JSONRPCRequest& request) UniValue verifychain(const JSONRPCRequest& request) { - int nCheckLevel = GetArg("-checklevel", DEFAULT_CHECKLEVEL); - int nCheckDepth = GetArg("-checkblocks", DEFAULT_CHECKBLOCKS); + int nCheckLevel = gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL); + int nCheckDepth = gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS); if (request.fHelp || request.params.size() > 2) throw std::runtime_error( "verifychain ( checklevel nblocks )\n" @@ -1324,7 +1324,7 @@ UniValue mempoolInfoToJSON() ret.push_back(Pair("size", (int64_t) mempool.size())); ret.push_back(Pair("bytes", (int64_t) mempool.GetTotalTxSize())); ret.push_back(Pair("usage", (int64_t) mempool.DynamicMemoryUsage())); - size_t maxmempool = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; + size_t maxmempool = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; ret.push_back(Pair("maxmempool", (int64_t) maxmempool)); ret.push_back(Pair("mempoolminfee", ValueFromAmount(mempool.GetMinFee(maxmempool).GetFeePerK()))); diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp index 823a5775f..db0626b5e 100644 --- a/src/rpc/protocol.cpp +++ b/src/rpc/protocol.cpp @@ -68,7 +68,7 @@ static const std::string COOKIEAUTH_FILE = ".cookie"; fs::path GetAuthCookieFile() { - fs::path path(GetArg("-rpccookiefile", COOKIEAUTH_FILE)); + fs::path path(gArgs.GetArg("-rpccookiefile", COOKIEAUTH_FILE)); if (!path.is_complete()) path = GetDataDir() / path; return path; } diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 1799c8e98..9ad8d228f 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -552,7 +552,7 @@ void RPCRunLater(const std::string& name, std::function func, int64_ int RPCSerializationFlags() { int flag = 0; - if (GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) == 0) + if (gArgs.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) == 0) flag |= SERIALIZE_TRANSACTION_NO_WITNESS; return flag; } diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp index ceb573b2e..4cc7afa2f 100644 --- a/src/script/sigcache.cpp +++ b/src/script/sigcache.cpp @@ -74,7 +74,7 @@ void InitSignatureCache() { // nMaxCacheSize is unsigned. If -maxsigcachesize is set to zero, // setup_bytes creates the minimum possible cache (2 elements). - size_t nMaxCacheSize = std::min(std::max((int64_t)0, GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) / 2), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20); + size_t nMaxCacheSize = std::min(std::max((int64_t)0, gArgs.GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) / 2), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20); size_t nElems = signatureCache.setup_bytes(nMaxCacheSize); LogPrintf("Using %zu MiB out of %zu/2 requested for signature cache, able to store %zu elements\n", (nElems*sizeof(uint256)) >>20, (nMaxCacheSize*2)>>20, nElems); diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp index 4a284517a..ffbeeb7d9 100644 --- a/src/test/DoS_tests.cpp +++ b/src/test/DoS_tests.cpp @@ -78,7 +78,7 @@ BOOST_AUTO_TEST_CASE(DoS_banscore) std::atomic interruptDummy(false); connman->ClearBanned(); - ForceSetArg("-banscore", "111"); // because 11 is my favorite number + gArgs.ForceSetArg("-banscore", "111"); // because 11 is my favorite number CAddress addr1(ip(0xa0b0c001), NODE_NONE); CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 3, 1, CAddress(), "", true); dummyNode1.SetSendVersion(PROTOCOL_VERSION); @@ -94,7 +94,7 @@ BOOST_AUTO_TEST_CASE(DoS_banscore) Misbehaving(dummyNode1.GetId(), 1); SendMessages(&dummyNode1, *connman, interruptDummy); BOOST_CHECK(connman->IsBanned(addr1)); - ForceSetArg("-banscore", std::to_string(DEFAULT_BANSCORE_THRESHOLD)); + gArgs.ForceSetArg("-banscore", std::to_string(DEFAULT_BANSCORE_THRESHOLD)); } BOOST_AUTO_TEST_CASE(DoS_bantime) diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp index c79675f5a..18a7e5993 100644 --- a/src/test/getarg_tests.cpp +++ b/src/test/getarg_tests.cpp @@ -27,135 +27,135 @@ static void ResetArgs(const std::string& strArg) for (std::string& s : vecArg) vecChar.push_back(s.c_str()); - ParseParameters(vecChar.size(), &vecChar[0]); + gArgs.ParseParameters(vecChar.size(), &vecChar[0]); } BOOST_AUTO_TEST_CASE(boolarg) { ResetArgs("-foo"); - BOOST_CHECK(GetBoolArg("-foo", false)); - BOOST_CHECK(GetBoolArg("-foo", true)); + BOOST_CHECK(gArgs.GetBoolArg("-foo", false)); + BOOST_CHECK(gArgs.GetBoolArg("-foo", true)); - BOOST_CHECK(!GetBoolArg("-fo", false)); - BOOST_CHECK(GetBoolArg("-fo", true)); + BOOST_CHECK(!gArgs.GetBoolArg("-fo", false)); + BOOST_CHECK(gArgs.GetBoolArg("-fo", true)); - BOOST_CHECK(!GetBoolArg("-fooo", false)); - BOOST_CHECK(GetBoolArg("-fooo", true)); + BOOST_CHECK(!gArgs.GetBoolArg("-fooo", false)); + BOOST_CHECK(gArgs.GetBoolArg("-fooo", true)); ResetArgs("-foo=0"); - BOOST_CHECK(!GetBoolArg("-foo", false)); - BOOST_CHECK(!GetBoolArg("-foo", true)); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", false)); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", true)); ResetArgs("-foo=1"); - BOOST_CHECK(GetBoolArg("-foo", false)); - BOOST_CHECK(GetBoolArg("-foo", true)); + BOOST_CHECK(gArgs.GetBoolArg("-foo", false)); + BOOST_CHECK(gArgs.GetBoolArg("-foo", true)); // New 0.6 feature: auto-map -nosomething to !-something: ResetArgs("-nofoo"); - BOOST_CHECK(!GetBoolArg("-foo", false)); - BOOST_CHECK(!GetBoolArg("-foo", true)); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", false)); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", true)); ResetArgs("-nofoo=1"); - BOOST_CHECK(!GetBoolArg("-foo", false)); - BOOST_CHECK(!GetBoolArg("-foo", true)); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", false)); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", true)); ResetArgs("-foo -nofoo"); // -nofoo should win - BOOST_CHECK(!GetBoolArg("-foo", false)); - BOOST_CHECK(!GetBoolArg("-foo", true)); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", false)); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", true)); ResetArgs("-foo=1 -nofoo=1"); // -nofoo should win - BOOST_CHECK(!GetBoolArg("-foo", false)); - BOOST_CHECK(!GetBoolArg("-foo", true)); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", false)); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", true)); ResetArgs("-foo=0 -nofoo=0"); // -nofoo=0 should win - BOOST_CHECK(GetBoolArg("-foo", false)); - BOOST_CHECK(GetBoolArg("-foo", true)); + BOOST_CHECK(gArgs.GetBoolArg("-foo", false)); + BOOST_CHECK(gArgs.GetBoolArg("-foo", true)); // New 0.6 feature: treat -- same as -: ResetArgs("--foo=1"); - BOOST_CHECK(GetBoolArg("-foo", false)); - BOOST_CHECK(GetBoolArg("-foo", true)); + BOOST_CHECK(gArgs.GetBoolArg("-foo", false)); + BOOST_CHECK(gArgs.GetBoolArg("-foo", true)); ResetArgs("--nofoo=1"); - BOOST_CHECK(!GetBoolArg("-foo", false)); - BOOST_CHECK(!GetBoolArg("-foo", true)); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", false)); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", true)); } BOOST_AUTO_TEST_CASE(stringarg) { ResetArgs(""); - BOOST_CHECK_EQUAL(GetArg("-foo", ""), ""); - BOOST_CHECK_EQUAL(GetArg("-foo", "eleven"), "eleven"); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), ""); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), "eleven"); ResetArgs("-foo -bar"); - BOOST_CHECK_EQUAL(GetArg("-foo", ""), ""); - BOOST_CHECK_EQUAL(GetArg("-foo", "eleven"), ""); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), ""); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), ""); ResetArgs("-foo="); - BOOST_CHECK_EQUAL(GetArg("-foo", ""), ""); - BOOST_CHECK_EQUAL(GetArg("-foo", "eleven"), ""); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), ""); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), ""); ResetArgs("-foo=11"); - BOOST_CHECK_EQUAL(GetArg("-foo", ""), "11"); - BOOST_CHECK_EQUAL(GetArg("-foo", "eleven"), "11"); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "11"); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), "11"); ResetArgs("-foo=eleven"); - BOOST_CHECK_EQUAL(GetArg("-foo", ""), "eleven"); - BOOST_CHECK_EQUAL(GetArg("-foo", "eleven"), "eleven"); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "eleven"); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), "eleven"); } BOOST_AUTO_TEST_CASE(intarg) { ResetArgs(""); - BOOST_CHECK_EQUAL(GetArg("-foo", 11), 11); - BOOST_CHECK_EQUAL(GetArg("-foo", 0), 0); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 11), 11); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 0), 0); ResetArgs("-foo -bar"); - BOOST_CHECK_EQUAL(GetArg("-foo", 11), 0); - BOOST_CHECK_EQUAL(GetArg("-bar", 11), 0); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 11), 0); + BOOST_CHECK_EQUAL(gArgs.GetArg("-bar", 11), 0); ResetArgs("-foo=11 -bar=12"); - BOOST_CHECK_EQUAL(GetArg("-foo", 0), 11); - BOOST_CHECK_EQUAL(GetArg("-bar", 11), 12); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 0), 11); + BOOST_CHECK_EQUAL(gArgs.GetArg("-bar", 11), 12); ResetArgs("-foo=NaN -bar=NotANumber"); - BOOST_CHECK_EQUAL(GetArg("-foo", 1), 0); - BOOST_CHECK_EQUAL(GetArg("-bar", 11), 0); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 1), 0); + BOOST_CHECK_EQUAL(gArgs.GetArg("-bar", 11), 0); } BOOST_AUTO_TEST_CASE(doubledash) { ResetArgs("--foo"); - BOOST_CHECK_EQUAL(GetBoolArg("-foo", false), true); + BOOST_CHECK_EQUAL(gArgs.GetBoolArg("-foo", false), true); ResetArgs("--foo=verbose --bar=1"); - BOOST_CHECK_EQUAL(GetArg("-foo", ""), "verbose"); - BOOST_CHECK_EQUAL(GetArg("-bar", 0), 1); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "verbose"); + BOOST_CHECK_EQUAL(gArgs.GetArg("-bar", 0), 1); } BOOST_AUTO_TEST_CASE(boolargno) { ResetArgs("-nofoo"); - BOOST_CHECK(!GetBoolArg("-foo", true)); - BOOST_CHECK(!GetBoolArg("-foo", false)); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", true)); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", false)); ResetArgs("-nofoo=1"); - BOOST_CHECK(!GetBoolArg("-foo", true)); - BOOST_CHECK(!GetBoolArg("-foo", false)); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", true)); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", false)); ResetArgs("-nofoo=0"); - BOOST_CHECK(GetBoolArg("-foo", true)); - BOOST_CHECK(GetBoolArg("-foo", false)); + BOOST_CHECK(gArgs.GetBoolArg("-foo", true)); + BOOST_CHECK(gArgs.GetBoolArg("-foo", false)); ResetArgs("-foo --nofoo"); // --nofoo should win - BOOST_CHECK(!GetBoolArg("-foo", true)); - BOOST_CHECK(!GetBoolArg("-foo", false)); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", true)); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", false)); ResetArgs("-nofoo -foo"); // foo always wins: - BOOST_CHECK(GetBoolArg("-foo", true)); - BOOST_CHECK(GetBoolArg("-foo", false)); + BOOST_CHECK(gArgs.GetBoolArg("-foo", true)); + BOOST_CHECK(gArgs.GetBoolArg("-foo", false)); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp index 095d86834..31b05d868 100644 --- a/src/test/net_tests.cpp +++ b/src/test/net_tests.cpp @@ -80,7 +80,7 @@ BOOST_AUTO_TEST_CASE(cnode_listen_port) BOOST_CHECK(port == Params().GetDefaultPort()); // test set port unsigned short altPort = 12345; - SoftSetArg("-port", std::to_string(altPort)); + gArgs.SoftSetArg("-port", std::to_string(altPort)); port = GetListenPort(); BOOST_CHECK(port == altPort); } diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 72d7f1b19..94ec7c03f 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -63,7 +63,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha ClearDatadirCache(); pathTemp = GetTempPath() / strprintf("test_bitcoin_%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(100000))); fs::create_directories(pathTemp); - ForceSetArg("-datadir", pathTemp.string()); + gArgs.ForceSetArg("-datadir", pathTemp.string()); // Note that because we don't bother running a scheduler thread here, // callbacks via CValidationInterface are unreliable, but that's OK, diff --git a/src/timedata.cpp b/src/timedata.cpp index 099ed7f04..5113bb60d 100644 --- a/src/timedata.cpp +++ b/src/timedata.cpp @@ -81,7 +81,7 @@ void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample) int64_t nMedian = vTimeOffsets.median(); std::vector vSorted = vTimeOffsets.sorted(); // Only let other nodes change our time by so much - if (abs64(nMedian) <= std::max(0, GetArg("-maxtimeadjustment", DEFAULT_MAX_TIME_ADJUSTMENT))) + if (abs64(nMedian) <= std::max(0, gArgs.GetArg("-maxtimeadjustment", DEFAULT_MAX_TIME_ADJUSTMENT))) { nTimeOffset = nMedian; } diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index 57c22537b..e8bc3e5e7 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -525,7 +525,7 @@ void TorController::auth_cb(TorControlConnection& _conn, const TorControlReply& // Now that we know Tor is running setup the proxy for onion addresses // if -onion isn't set to something else. - if (GetArg("-onion", "") == "") { + if (gArgs.GetArg("-onion", "") == "") { CService resolved(LookupNumeric("127.0.0.1", 9050)); proxyType addrOnion = proxyType(resolved, true); SetProxy(NET_TOR, addrOnion); @@ -642,7 +642,7 @@ void TorController::protocolinfo_cb(TorControlConnection& _conn, const TorContro * cookie: hex-encoded ~/.tor/control_auth_cookie * password: "password" */ - std::string torpassword = GetArg("-torpassword", ""); + std::string torpassword = gArgs.GetArg("-torpassword", ""); if (!torpassword.empty()) { if (methods.count("HASHEDPASSWORD")) { LogPrint(BCLog::TOR, "tor: Using HASHEDPASSWORD authentication\n"); @@ -735,7 +735,7 @@ static boost::thread torControlThread; static void TorControlThread() { - TorController ctrl(gBase, GetArg("-torcontrol", DEFAULT_TOR_CONTROL)); + TorController ctrl(gBase, gArgs.GetArg("-torcontrol", DEFAULT_TOR_CONTROL)); event_base_dispatch(gBase); } diff --git a/src/txdb.cpp b/src/txdb.cpp index 7f7eb6567..4c1b04cd9 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -85,8 +85,8 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { CDBBatch batch(db); size_t count = 0; size_t changed = 0; - size_t batch_size = (size_t)GetArg("-dbbatchsize", nDefaultDbBatchSize); - int crash_simulate = GetArg("-dbcrashratio", 0); + size_t batch_size = (size_t)gArgs.GetArg("-dbbatchsize", nDefaultDbBatchSize); + int crash_simulate = gArgs.GetArg("-dbcrashratio", 0); assert(!hashBlock.IsNull()); uint256 old_tip = GetBestBlock(); diff --git a/src/validation.cpp b/src/validation.cpp index f21700c67..8bd23a0f1 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -398,7 +398,7 @@ void UpdateMempoolForReorg(DisconnectedBlockTransactions &disconnectpool, bool f // We also need to remove any now-immature transactions mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); // Re-limit mempool size, in case we added any transactions - LimitMempoolSize(mempool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); + LimitMempoolSize(mempool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); } // Used to avoid mempool polluting consensus critical paths if CCoinsViewMempool @@ -456,7 +456,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool // Reject transactions with witness before segregated witness activates (override with -prematurewitness) bool witnessEnabled = IsWitnessEnabled(chainActive.Tip(), chainparams.GetConsensus()); - if (!GetBoolArg("-prematurewitness", false) && tx.HasWitness() && !witnessEnabled) { + if (!gArgs.GetBoolArg("-prematurewitness", false) && tx.HasWitness() && !witnessEnabled) { return state.DoS(0, false, REJECT_NONSTANDARD, "no-witness-yet", true); } @@ -611,7 +611,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool return state.DoS(0, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops", false, strprintf("%d", nSigOpsCost)); - CAmount mempoolRejectFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize); + CAmount mempoolRejectFee = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize); if (mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) { return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nFees, mempoolRejectFee)); } @@ -628,10 +628,10 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool // Calculate in-mempool ancestors, up to a limit. CTxMemPool::setEntries setAncestors; - size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); - size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000; - size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); - size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000; + size_t nLimitAncestors = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); + size_t nLimitAncestorSize = gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000; + size_t nLimitDescendants = gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); + size_t nLimitDescendantSize = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000; std::string errString; if (!pool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { return state.DoS(0, false, REJECT_NONSTANDARD, "too-long-mempool-chain", false, errString); @@ -783,7 +783,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS; if (!chainparams.RequireStandard()) { - scriptVerifyFlags = GetArg("-promiscuousmempoolflags", scriptVerifyFlags); + scriptVerifyFlags = gArgs.GetArg("-promiscuousmempoolflags", scriptVerifyFlags); } // Check against previous transactions @@ -860,7 +860,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool // trim mempool and check if tx was trimmed if (!fOverrideMempoolLimit) { - LimitMempoolSize(pool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); + LimitMempoolSize(pool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); if (!pool.exists(hash)) return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool full"); } @@ -1061,7 +1061,7 @@ CBlockIndex *pindexBestForkTip = nullptr, *pindexBestForkBase = nullptr; static void AlertNotify(const std::string& strMessage) { uiInterface.NotifyAlertChanged(); - std::string strCmd = GetArg("-alertnotify", ""); + std::string strCmd = gArgs.GetArg("-alertnotify", ""); if (strCmd.empty()) return; // Alert text should be plain ascii coming from a trusted source, but to @@ -1216,7 +1216,7 @@ static uint256 scriptExecutionCacheNonce(GetRandHash()); void InitScriptExecutionCache() { // nMaxCacheSize is unsigned. If -maxsigcachesize is set to zero, // setup_bytes creates the minimum possible cache (2 elements). - size_t nMaxCacheSize = std::min(std::max((int64_t)0, GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) / 2), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20); + size_t nMaxCacheSize = std::min(std::max((int64_t)0, gArgs.GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) / 2), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20); size_t nElems = scriptExecutionCache.setup_bytes(nMaxCacheSize); LogPrintf("Using %zu MiB out of %zu/2 requested for script execution cache, able to store %zu elements\n", (nElems*sizeof(uint256)) >>20, (nMaxCacheSize*2)>>20, nElems); @@ -1900,7 +1900,7 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState & if (nLastSetChain == 0) { nLastSetChain = nNow; } - int64_t nMempoolSizeMax = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; + int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; int64_t cacheSize = pcoinsTip->DynamicMemoryUsage(); int64_t nTotalSpace = nCoinCacheUsage + std::max(nMempoolSizeMax - nMempoolUsage, 0); // The cache is large and we're within 10% and 10 MiB of the limit, but we have time now (not in the middle of a block processing). @@ -2421,7 +2421,7 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, CBlockIndex *pindexMostWork = nullptr; CBlockIndex *pindexNewTip = nullptr; - int nStopAtHeight = GetArg("-stopatheight", DEFAULT_STOPATHEIGHT); + int nStopAtHeight = gArgs.GetArg("-stopatheight", DEFAULT_STOPATHEIGHT); do { boost::this_thread::interruption_point(); if (ShutdownRequested()) @@ -3892,7 +3892,7 @@ bool LoadBlockIndex(const CChainParams& chainparams) LogPrintf("Initializing databases...\n"); // Use the provided setting for -txindex in the new database - fTxIndex = GetBoolArg("-txindex", DEFAULT_TXINDEX); + fTxIndex = gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX); pblocktree->WriteFlag("txindex", fTxIndex); } return true; @@ -4263,7 +4263,7 @@ static const uint64_t MEMPOOL_DUMP_VERSION = 1; bool LoadMempool(void) { const CChainParams& chainparams = Params(); - int64_t nExpiryTimeout = GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60; + int64_t nExpiryTimeout = gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60; FILE* filestr = fsbridge::fopen(GetDataDir() / "mempool.dat", "rb"); CAutoFile file(filestr, SER_DISK, CLIENT_VERSION); if (file.IsNull()) { diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 4c06c2c1f..b12d46e40 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -78,7 +78,7 @@ bool CDBEnv::Open(const fs::path& pathIn) LogPrintf("CDBEnv::Open: LogDir=%s ErrorFile=%s\n", pathLogDir.string(), pathErrorFile.string()); unsigned int nEnvFlags = 0; - if (GetBoolArg("-privdb", DEFAULT_WALLET_PRIVDB)) + if (gArgs.GetBoolArg("-privdb", DEFAULT_WALLET_PRIVDB)) nEnvFlags |= DB_PRIVATE; dbenv->set_lg_dir(pathLogDir.string().c_str()); @@ -429,7 +429,7 @@ void CDB::Flush() if (fReadOnly) nMinutes = 1; - env->dbenv->txn_checkpoint(nMinutes ? GetArg("-dblogsize", DEFAULT_WALLET_DBLOGSIZE) * 1024 : 0, nMinutes, 0); + env->dbenv->txn_checkpoint(nMinutes ? gArgs.GetArg("-dblogsize", DEFAULT_WALLET_DBLOGSIZE) * 1024 : 0, nMinutes, 0); } void CWalletDBWrapper::IncrementUpdateCounter() diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index 4bfd8726a..c1ea2b629 100644 --- a/src/wallet/feebumper.cpp +++ b/src/wallet/feebumper.cpp @@ -193,7 +193,7 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, const CCoin // 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); + CFeeRate minMempoolFeeRate = mempool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000); if (nNewFeeRate.GetFeePerK() < minMempoolFeeRate.GetFeePerK()) { vErrors.push_back(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()))); currentResult = BumpFeeResult::WALLET_ERROR; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 8ef33f248..a6176c348 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1197,7 +1197,7 @@ UniValue addwitnessaddress(const JSONRPCRequest& request) { LOCK(cs_main); - if (!IsWitnessEnabled(chainActive.Tip(), Params().GetConsensus()) && !GetBoolArg("-walletprematurewitness", false)) { + if (!IsWitnessEnabled(chainActive.Tip(), Params().GetConsensus()) && !gArgs.GetBoolArg("-walletprematurewitness", false)) { throw JSONRPCError(RPC_WALLET_ERROR, "Segregated witness not enabled on network"); } } @@ -3202,7 +3202,7 @@ static const CRPCCommand commands[] = void RegisterWalletRPCCommands(CRPCTable &t) { - if (GetBoolArg("-disablewallet", false)) + if (gArgs.GetBoolArg("-disablewallet", false)) return; for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index c152b9d69..599e74149 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -496,7 +496,7 @@ void CWallet::Flush(bool shutdown) bool CWallet::Verify() { - if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) + if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) return true; uiInterface.InitMessage(_("Verifying wallet(s)...")); @@ -528,7 +528,7 @@ bool CWallet::Verify() return InitError(strError); } - if (GetBoolArg("-salvagewallet", false)) { + if (gArgs.GetBoolArg("-salvagewallet", false)) { // Recover readable keypairs: CWallet dummyWallet; std::string backup_filename; @@ -986,7 +986,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED); // notify an external script when a wallet transaction comes in or is updated - std::string strCmd = GetArg("-walletnotify", ""); + std::string strCmd = gArgs.GetArg("-walletnotify", ""); if ( !strCmd.empty()) { @@ -2501,8 +2501,8 @@ bool CWallet::SelectCoins(const std::vector& vAvailableCoins, const CAm ++it; } - size_t nMaxChainLength = std::min(GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT), GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT)); - bool fRejectLongChains = GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS); + size_t nMaxChainLength = std::min(gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT), gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT)); + bool fRejectLongChains = gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS); bool res = nTargetValue <= nValueFromPresetInputs || SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 6, 0, vCoins, setCoinsRet, nValueRet) || @@ -2937,15 +2937,15 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, CWalletT } } - if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { + if (gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { // Lastly, ensure this tx will pass the mempool's chain limits LockPoints lp; CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, false, 0, lp); CTxMemPool::setEntries setAncestors; - size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); - size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000; - size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); - size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000; + size_t nLimitAncestors = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); + size_t nLimitAncestorSize = gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000; + size_t nLimitDescendants = gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); + size_t nLimitDescendantSize = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000; std::string errString; if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { strFailReason = _("Transaction has too long of a mempool chain"); @@ -3072,7 +3072,7 @@ CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, const CCoinControl& coin_c if (feeCalc) feeCalc->reason = FeeReason::FALLBACK; } // Obey mempool min fee when using smart fee estimation - CAmount min_mempool_fee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nTxBytes); + CAmount min_mempool_fee = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nTxBytes); if (fee_needed < min_mempool_fee) { fee_needed = min_mempool_fee; if (feeCalc) feeCalc->reason = FeeReason::MEMPOOL_MIN; @@ -3307,7 +3307,7 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize) if (kpSize > 0) nTargetSize = kpSize; else - nTargetSize = std::max(GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 0); + nTargetSize = std::max(gArgs.GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 0); // count amount of available keys (internal, external) // make sure the keypool of external and internal keys fits the user selected target (-keypool) @@ -3933,7 +3933,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) // needed to restore wallet transaction meta data after -zapwallettxes std::vector vWtx; - if (GetBoolArg("-zapwallettxes", false)) { + if (gArgs.GetBoolArg("-zapwallettxes", false)) { uiInterface.InitMessage(_("Zapping all transactions from wallet...")); std::unique_ptr dbw(new CWalletDBWrapper(&bitdb, walletFile)); @@ -3982,9 +3982,9 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) } } - if (GetBoolArg("-upgradewallet", fFirstRun)) + if (gArgs.GetBoolArg("-upgradewallet", fFirstRun)) { - int nMaxVersion = GetArg("-upgradewallet", 0); + int nMaxVersion = gArgs.GetArg("-upgradewallet", 0); if (nMaxVersion == 0) // the -upgradewallet without argument case { LogPrintf("Performing wallet upgrade to %i\n", FEATURE_LATEST); @@ -4004,7 +4004,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) if (fFirstRun) { // Create new keyUser and set as default key - if (GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET) && !walletInstance->IsHDEnabled()) { + if (gArgs.GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET) && !walletInstance->IsHDEnabled()) { // ensure this wallet.dat can only be opened by clients supporting HD with chain split walletInstance->SetMinVersion(FEATURE_HD_SPLIT); @@ -4025,8 +4025,8 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) walletInstance->SetBestChain(chainActive.GetLocator()); } - else if (IsArgSet("-usehd")) { - bool useHD = GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET); + else if (gArgs.IsArgSet("-usehd")) { + bool useHD = gArgs.GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET); if (walletInstance->IsHDEnabled() && !useHD) { InitError(strprintf(_("Error loading %s: You can't disable HD on an already existing HD wallet"), walletFile)); return nullptr; @@ -4045,7 +4045,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) walletInstance->TopUpKeyPool(); CBlockIndex *pindexRescan = chainActive.Genesis(); - if (!GetBoolArg("-rescan", false)) + if (!gArgs.GetBoolArg("-rescan", false)) { CWalletDB walletdb(*walletInstance->dbw); CBlockLocator locator; @@ -4085,7 +4085,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) walletInstance->dbw->IncrementUpdateCounter(); // Restore wallet transaction metadata after -zapwallettxes=1 - if (GetBoolArg("-zapwallettxes", false) && GetArg("-zapwallettxes", "1") != "2") + if (gArgs.GetBoolArg("-zapwallettxes", false) && gArgs.GetArg("-zapwallettxes", "1") != "2") { CWalletDB walletdb(*walletInstance->dbw); @@ -4109,7 +4109,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) } } } - walletInstance->SetBroadcastTransactions(GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST)); + walletInstance->SetBroadcastTransactions(gArgs.GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST)); { LOCK(walletInstance->cs_wallet); @@ -4123,7 +4123,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) bool CWallet::InitLoadWallet() { - if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { + if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { LogPrintf("Wallet disabled!\n"); return true; } @@ -4155,29 +4155,29 @@ void CWallet::postInitProcess(CScheduler& scheduler) bool CWallet::ParameterInteraction() { - SoftSetArg("-wallet", DEFAULT_WALLET_DAT); + gArgs.SoftSetArg("-wallet", DEFAULT_WALLET_DAT); const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1; - if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) + if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) return true; - if (GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && SoftSetBoolArg("-walletbroadcast", false)) { + if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && gArgs.SoftSetBoolArg("-walletbroadcast", false)) { LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\n", __func__); } - if (GetBoolArg("-salvagewallet", false)) { + if (gArgs.GetBoolArg("-salvagewallet", false)) { if (is_multiwallet) { return InitError(strprintf("%s is only allowed with a single wallet file", "-salvagewallet")); } // Rewrite just private keys: rescan to find transactions - if (SoftSetBoolArg("-rescan", true)) { + if (gArgs.SoftSetBoolArg("-rescan", true)) { LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting -rescan=1\n", __func__); } } - int zapwallettxes = GetArg("-zapwallettxes", 0); + int zapwallettxes = gArgs.GetArg("-zapwallettxes", 0); // -zapwallettxes implies dropping the mempool on startup - if (zapwallettxes != 0 && SoftSetBoolArg("-persistmempool", false)) { + if (zapwallettxes != 0 && gArgs.SoftSetBoolArg("-persistmempool", false)) { LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting -persistmempool=0\n", __func__, zapwallettxes); } @@ -4186,61 +4186,61 @@ bool CWallet::ParameterInteraction() if (is_multiwallet) { return InitError(strprintf("%s is only allowed with a single wallet file", "-zapwallettxes")); } - if (SoftSetBoolArg("-rescan", true)) { + if (gArgs.SoftSetBoolArg("-rescan", true)) { LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting -rescan=1\n", __func__, zapwallettxes); } } if (is_multiwallet) { - if (GetBoolArg("-upgradewallet", false)) { + if (gArgs.GetBoolArg("-upgradewallet", false)) { return InitError(strprintf("%s is only allowed with a single wallet file", "-upgradewallet")); } } - if (GetBoolArg("-sysperms", false)) + if (gArgs.GetBoolArg("-sysperms", false)) return InitError("-sysperms is not allowed in combination with enabled wallet functionality"); - if (GetArg("-prune", 0) && GetBoolArg("-rescan", false)) + if (gArgs.GetArg("-prune", 0) && gArgs.GetBoolArg("-rescan", false)) return InitError(_("Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.")); if (::minRelayTxFee.GetFeePerK() > HIGH_TX_FEE_PER_KB) InitWarning(AmountHighWarn("-minrelaytxfee") + " " + _("The wallet will avoid paying less than the minimum relay fee.")); - if (IsArgSet("-mintxfee")) + if (gArgs.IsArgSet("-mintxfee")) { CAmount n = 0; - if (!ParseMoney(GetArg("-mintxfee", ""), n) || 0 == n) - return InitError(AmountErrMsg("mintxfee", GetArg("-mintxfee", ""))); + if (!ParseMoney(gArgs.GetArg("-mintxfee", ""), n) || 0 == n) + return InitError(AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", ""))); if (n > HIGH_TX_FEE_PER_KB) InitWarning(AmountHighWarn("-mintxfee") + " " + _("This is the minimum transaction fee you pay on every transaction.")); CWallet::minTxFee = CFeeRate(n); } - if (IsArgSet("-fallbackfee")) + if (gArgs.IsArgSet("-fallbackfee")) { CAmount nFeePerK = 0; - if (!ParseMoney(GetArg("-fallbackfee", ""), nFeePerK)) - return InitError(strprintf(_("Invalid amount for -fallbackfee=: '%s'"), GetArg("-fallbackfee", ""))); + if (!ParseMoney(gArgs.GetArg("-fallbackfee", ""), nFeePerK)) + return InitError(strprintf(_("Invalid amount for -fallbackfee=: '%s'"), gArgs.GetArg("-fallbackfee", ""))); if (nFeePerK > HIGH_TX_FEE_PER_KB) InitWarning(AmountHighWarn("-fallbackfee") + " " + _("This is the transaction fee you may pay when fee estimates are not available.")); CWallet::fallbackFee = CFeeRate(nFeePerK); } - if (IsArgSet("-discardfee")) + if (gArgs.IsArgSet("-discardfee")) { CAmount nFeePerK = 0; - if (!ParseMoney(GetArg("-discardfee", ""), nFeePerK)) - return InitError(strprintf(_("Invalid amount for -discardfee=: '%s'"), GetArg("-discardfee", ""))); + if (!ParseMoney(gArgs.GetArg("-discardfee", ""), nFeePerK)) + return InitError(strprintf(_("Invalid amount for -discardfee=: '%s'"), gArgs.GetArg("-discardfee", ""))); if (nFeePerK > HIGH_TX_FEE_PER_KB) InitWarning(AmountHighWarn("-discardfee") + " " + _("This is the transaction fee you may discard if change is smaller than dust at this level")); CWallet::m_discard_rate = CFeeRate(nFeePerK); } - if (IsArgSet("-paytxfee")) + if (gArgs.IsArgSet("-paytxfee")) { CAmount nFeePerK = 0; - if (!ParseMoney(GetArg("-paytxfee", ""), nFeePerK)) - return InitError(AmountErrMsg("paytxfee", GetArg("-paytxfee", ""))); + if (!ParseMoney(gArgs.GetArg("-paytxfee", ""), nFeePerK)) + return InitError(AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", ""))); if (nFeePerK > HIGH_TX_FEE_PER_KB) InitWarning(AmountHighWarn("-paytxfee") + " " + _("This is the transaction fee you will pay if you send a transaction.")); @@ -4249,26 +4249,26 @@ bool CWallet::ParameterInteraction() if (payTxFee < ::minRelayTxFee) { return InitError(strprintf(_("Invalid amount for -paytxfee=: '%s' (must be at least %s)"), - GetArg("-paytxfee", ""), ::minRelayTxFee.ToString())); + gArgs.GetArg("-paytxfee", ""), ::minRelayTxFee.ToString())); } } - if (IsArgSet("-maxtxfee")) + if (gArgs.IsArgSet("-maxtxfee")) { CAmount nMaxFee = 0; - if (!ParseMoney(GetArg("-maxtxfee", ""), nMaxFee)) - return InitError(AmountErrMsg("maxtxfee", GetArg("-maxtxfee", ""))); + if (!ParseMoney(gArgs.GetArg("-maxtxfee", ""), nMaxFee)) + return InitError(AmountErrMsg("maxtxfee", gArgs.GetArg("-maxtxfee", ""))); if (nMaxFee > HIGH_MAX_TX_FEE) InitWarning(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction.")); maxTxFee = nMaxFee; if (CFeeRate(maxTxFee, 1000) < ::minRelayTxFee) { return InitError(strprintf(_("Invalid amount for -maxtxfee=: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"), - GetArg("-maxtxfee", ""), ::minRelayTxFee.ToString())); + gArgs.GetArg("-maxtxfee", ""), ::minRelayTxFee.ToString())); } } - nTxConfirmTarget = GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET); - bSpendZeroConfChange = GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); - fWalletRbf = GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF); + nTxConfirmTarget = gArgs.GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET); + bSpendZeroConfChange = gArgs.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); + fWalletRbf = gArgs.GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF); return true; } diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index c8dc52626..72c22d225 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -573,7 +573,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) fNoncriticalErrors = true; // ... but do warn the user there is something wrong. if (strType == "tx") // Rescan if there is a bad transaction record: - SoftSetBoolArg("-rescan", true); + gArgs.SoftSetBoolArg("-rescan", true); } } if (!strErr.empty()) @@ -751,7 +751,7 @@ void MaybeCompactWalletDB() if (fOneThread.exchange(true)) { return; } - if (!GetBoolArg("-flushwallet", DEFAULT_FLUSHWALLET)) { + if (!gArgs.GetBoolArg("-flushwallet", DEFAULT_FLUSHWALLET)) { return; } diff --git a/src/warnings.cpp b/src/warnings.cpp index 75ccfeb11..d4e33b701 100644 --- a/src/warnings.cpp +++ b/src/warnings.cpp @@ -51,7 +51,7 @@ std::string GetWarnings(const std::string& strFor) strGUI = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications"); } - if (GetBoolArg("-testsafemode", DEFAULT_TESTSAFEMODE)) + if (gArgs.GetBoolArg("-testsafemode", DEFAULT_TESTSAFEMODE)) strStatusBar = strRPC = strGUI = "testsafemode enabled"; // Misc warnings like out of disk space and clock is wrong diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp index b637f26fb..c410cc269 100644 --- a/src/zmq/zmqnotificationinterface.cpp +++ b/src/zmq/zmqnotificationinterface.cpp @@ -43,10 +43,10 @@ CZMQNotificationInterface* CZMQNotificationInterface::Create() for (std::map::const_iterator i=factories.begin(); i!=factories.end(); ++i) { std::string arg("-zmq" + i->first); - if (IsArgSet(arg)) + if (gArgs.IsArgSet(arg)) { CZMQNotifierFactory factory = i->second; - std::string address = GetArg(arg, ""); + std::string address = gArgs.GetArg(arg, ""); CZMQAbstractNotifier *notifier = factory(); notifier->SetType(i->first); notifier->SetAddress(address); From fcbde9091e7cd644237010f16f27052ac3b0d1ad Mon Sep 17 00:00:00 2001 From: Marko Bencun Date: Thu, 15 Jun 2017 22:45:55 +0200 Subject: [PATCH 056/382] remove unused gArgs wrappers --- src/util.cpp | 6 +++--- src/util.h | 46 ---------------------------------------------- 2 files changed, 3 insertions(+), 49 deletions(-) diff --git a/src/util.cpp b/src/util.cpp index fc75b7ef8..ba563478f 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -564,8 +564,8 @@ const fs::path &GetDataDir(bool fNetSpecific) if (!path.empty()) return path; - if (IsArgSet("-datadir")) { - path = fs::system_complete(GetArg("-datadir", "")); + if (gArgs.IsArgSet("-datadir")) { + path = fs::system_complete(gArgs.GetArg("-datadir", "")); if (!fs::is_directory(path)) { path = ""; return path; @@ -627,7 +627,7 @@ void ArgsManager::ReadConfigFile(const std::string& confPath) #ifndef WIN32 fs::path GetPidFile() { - fs::path pathPidFile(GetArg("-pid", BITCOIN_PID_FILENAME)); + fs::path pathPidFile(gArgs.GetArg("-pid", BITCOIN_PID_FILENAME)); if (!pathPidFile.is_complete()) pathPidFile = GetDataDir() / pathPidFile; return pathPidFile; } diff --git a/src/util.h b/src/util.h index b01606cb8..ead3d3d93 100644 --- a/src/util.h +++ b/src/util.h @@ -263,52 +263,6 @@ class ArgsManager extern ArgsManager gArgs; -// wrappers using the global ArgsManager: -static inline void ParseParameters(int argc, const char*const argv[]) -{ - gArgs.ParseParameters(argc, argv); -} - -static inline void ReadConfigFile(const std::string& confPath) -{ - gArgs.ReadConfigFile(confPath); -} - -static inline bool SoftSetArg(const std::string& strArg, const std::string& strValue) -{ - return gArgs.SoftSetArg(strArg, strValue); -} - -static inline void ForceSetArg(const std::string& strArg, const std::string& strValue) -{ - gArgs.ForceSetArg(strArg, strValue); -} - -static inline bool IsArgSet(const std::string& strArg) -{ - return gArgs.IsArgSet(strArg); -} - -static inline std::string GetArg(const std::string& strArg, const std::string& strDefault) -{ - return gArgs.GetArg(strArg, strDefault); -} - -static inline int64_t GetArg(const std::string& strArg, int64_t nDefault) -{ - return gArgs.GetArg(strArg, nDefault); -} - -static inline bool GetBoolArg(const std::string& strArg, bool fDefault) -{ - return gArgs.GetBoolArg(strArg, fDefault); -} - -static inline bool SoftSetBoolArg(const std::string& strArg, bool fValue) -{ - return gArgs.SoftSetBoolArg(strArg, fValue); -} - /** * Format a string to be used as group of options in help messages * From d97fe2016cc7739929aac5c44de5037163b17ad0 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Wed, 2 Aug 2017 07:19:28 -0400 Subject: [PATCH 057/382] Move some static functions out of wallet.h/cpp This commit just moves a few function declarations and updates callers. Function bodies are moved in two followup MOVEONLY commits. This change is desirable because wallet.h/cpp are monolithic and hard to navigate, so pulling things out and grouping together pieces of related functionality should improve the organization. Another proximate motivation is the wallet process separation work in https://github.com/bitcoin/bitcoin/pull/10973, where (at least initially) parameter parsing and fee estimation are still done in the main process rather than the wallet process, and having functions that run in different processes scrambled up throughout wallet.cpp is unnecessarily confusing. --- src/Makefile.am | 4 ++++ src/init.cpp | 11 ++++++----- src/qt/coincontroldialog.cpp | 3 ++- src/qt/optionsdialog.cpp | 4 ---- src/qt/sendcoinsdialog.cpp | 10 +++++----- src/wallet/feebumper.cpp | 5 +++-- src/wallet/fees.cpp | 13 +++++++++++++ src/wallet/fees.h | 34 ++++++++++++++++++++++++++++++++++ src/wallet/init.cpp | 12 ++++++++++++ src/wallet/init.h | 25 +++++++++++++++++++++++++ src/wallet/rpcwallet.cpp | 3 ++- src/wallet/wallet.cpp | 21 +++++++++++---------- src/wallet/wallet.h | 22 ---------------------- 13 files changed, 117 insertions(+), 50 deletions(-) create mode 100644 src/wallet/fees.cpp create mode 100644 src/wallet/fees.h create mode 100644 src/wallet/init.cpp create mode 100644 src/wallet/init.h diff --git a/src/Makefile.am b/src/Makefile.am index f7abab482..dea656869 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -162,6 +162,8 @@ BITCOIN_CORE_H = \ wallet/crypter.h \ wallet/db.h \ wallet/feebumper.h \ + wallet/fees.h \ + wallet/init.h \ wallet/rpcwallet.h \ wallet/wallet.h \ wallet/walletdb.h \ @@ -239,6 +241,8 @@ libbitcoin_wallet_a_SOURCES = \ wallet/crypter.cpp \ wallet/db.cpp \ wallet/feebumper.cpp \ + wallet/fees.cpp \ + wallet/init.cpp \ wallet/rpcdump.cpp \ wallet/rpcwallet.cpp \ wallet/wallet.cpp \ diff --git a/src/init.cpp b/src/init.cpp index d79c2967b..c6ddefbfc 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -43,6 +43,7 @@ #include "utilmoneystr.h" #include "validationinterface.h" #ifdef ENABLE_WALLET +#include "wallet/init.h" #include "wallet/wallet.h" #endif #include "warnings.h" @@ -420,7 +421,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-maxuploadtarget=", strprintf(_("Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d)"), DEFAULT_MAX_UPLOAD_TARGET)); #ifdef ENABLE_WALLET - strUsage += CWallet::GetWalletHelpString(showDebug); + strUsage += GetWalletHelpString(showDebug); #endif #if ENABLE_ZMQ @@ -1035,7 +1036,7 @@ bool AppInitParameterInteraction() if (!ParseMoney(gArgs.GetArg("-minrelaytxfee", ""), n)) { return InitError(AmountErrMsg("minrelaytxfee", gArgs.GetArg("-minrelaytxfee", ""))); } - // High fee check is done afterward in CWallet::ParameterInteraction() + // High fee check is done afterward in WalletParameterInteraction() ::minRelayTxFee = CFeeRate(n); } else if (incrementalRelayFee > ::minRelayTxFee) { // Allow only setting incrementalRelayFee to control both @@ -1068,7 +1069,7 @@ bool AppInitParameterInteraction() nBytesPerSigOp = gArgs.GetArg("-bytespersigop", nBytesPerSigOp); #ifdef ENABLE_WALLET - if (!CWallet::ParameterInteraction()) + if (!WalletParameterInteraction()) return false; #endif @@ -1245,7 +1246,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) // ********************************************************* Step 5: verify wallet database integrity #ifdef ENABLE_WALLET - if (!CWallet::Verify()) + if (!WalletVerify()) return false; #endif // ********************************************************* Step 6: network initialization @@ -1566,7 +1567,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) // ********************************************************* Step 8: load wallet #ifdef ENABLE_WALLET - if (!CWallet::InitLoadWallet()) + if (!InitLoadWallet()) return false; #else LogPrintf("No wallet support compiled in!\n"); diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index f3ee0fbe3..562c36179 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -18,6 +18,7 @@ #include "policy/fees.h" #include "policy/policy.h" #include "validation.h" // For mempool +#include "wallet/fees.h" #include "wallet/wallet.h" #include @@ -510,7 +511,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) nBytes -= 34; // Fee - nPayFee = CWallet::GetMinimumFee(nBytes, *coinControl, ::mempool, ::feeEstimator, nullptr /* FeeCalculation */); + nPayFee = GetMinimumFee(nBytes, *coinControl, ::mempool, ::feeEstimator, nullptr /* FeeCalculation */); if (nPayAmount > 0) { diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index b80b6541d..6f2f2f37c 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -17,10 +17,6 @@ #include "netbase.h" #include "txdb.h" // for -dbcache defaults -#ifdef ENABLE_WALLET -#include "wallet/wallet.h" // for CWallet::GetRequiredFee() -#endif - #include #include #include diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index a01886c3e..625e43574 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -22,7 +22,7 @@ #include "ui_interface.h" #include "txmempool.h" #include "policy/fees.h" -#include "wallet/wallet.h" +#include "wallet/fees.h" #include #include @@ -185,7 +185,7 @@ void SendCoinsDialog::setModel(WalletModel *_model) connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels())); connect(ui->optInRBF, SIGNAL(stateChanged(int)), this, SLOT(updateSmartFeeLabel())); connect(ui->optInRBF, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels())); - ui->customFee->setSingleStep(CWallet::GetRequiredFee(1000)); + ui->customFee->setSingleStep(GetRequiredFee(1000)); updateFeeSectionControls(); updateMinFeeLabel(); updateSmartFeeLabel(); @@ -610,7 +610,7 @@ void SendCoinsDialog::on_buttonMinimizeFee_clicked() void SendCoinsDialog::setMinimumFee() { ui->radioCustomPerKilobyte->setChecked(true); - ui->customFee->setValue(CWallet::GetRequiredFee(1000)); + ui->customFee->setValue(GetRequiredFee(1000)); } void SendCoinsDialog::updateFeeSectionControls() @@ -643,7 +643,7 @@ void SendCoinsDialog::updateMinFeeLabel() { if (model && model->getOptionsModel()) ui->checkBoxMinimumFee->setText(tr("Pay only the required fee of %1").arg( - BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), CWallet::GetRequiredFee(1000)) + "/kB") + BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), GetRequiredFee(1000)) + "/kB") ); } @@ -668,7 +668,7 @@ void SendCoinsDialog::updateSmartFeeLabel() updateCoinControlState(coin_control); coin_control.m_feerate.reset(); // Explicitly use only fee estimation rate for smart fee labels FeeCalculation feeCalc; - CFeeRate feeRate = CFeeRate(CWallet::GetMinimumFee(1000, coin_control, ::mempool, ::feeEstimator, &feeCalc)); + CFeeRate feeRate = CFeeRate(GetMinimumFee(1000, coin_control, ::mempool, ::feeEstimator, &feeCalc)); ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), feeRate.GetFeePerK()) + "/kB"); diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index c1ea2b629..285b0099c 100644 --- a/src/wallet/feebumper.cpp +++ b/src/wallet/feebumper.cpp @@ -5,6 +5,7 @@ #include "consensus/validation.h" #include "wallet/coincontrol.h" #include "wallet/feebumper.h" +#include "wallet/fees.h" #include "wallet/wallet.h" #include "policy/fees.h" #include "policy/policy.h" @@ -156,7 +157,7 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, const CCoin currentResult = BumpFeeResult::INVALID_PARAMETER; return; } - CAmount requiredFee = CWallet::GetRequiredFee(maxNewTxSize); + CAmount requiredFee = GetRequiredFee(maxNewTxSize); if (totalFee < requiredFee) { vErrors.push_back(strprintf("Insufficient totalFee (cannot be less than required fee %s)", FormatMoney(requiredFee))); @@ -166,7 +167,7 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, const CCoin nNewFee = totalFee; nNewFeeRate = CFeeRate(totalFee, maxNewTxSize); } else { - nNewFee = CWallet::GetMinimumFee(maxNewTxSize, coin_control, mempool, ::feeEstimator, nullptr /* FeeCalculation */); + nNewFee = GetMinimumFee(maxNewTxSize, coin_control, mempool, ::feeEstimator, nullptr /* FeeCalculation */); nNewFeeRate = CFeeRate(nNewFee, maxNewTxSize); // New fee rate must be at least old rate + minimum incremental relay rate diff --git a/src/wallet/fees.cpp b/src/wallet/fees.cpp new file mode 100644 index 000000000..3a9e68354 --- /dev/null +++ b/src/wallet/fees.cpp @@ -0,0 +1,13 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "wallet/fees.h" + +#include "policy/policy.h" +#include "txmempool.h" +#include "util.h" +#include "validation.h" +#include "wallet/coincontrol.h" +#include "wallet/wallet.h" diff --git a/src/wallet/fees.h b/src/wallet/fees.h new file mode 100644 index 000000000..7b8a7dc86 --- /dev/null +++ b/src/wallet/fees.h @@ -0,0 +1,34 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_WALLET_FEES_H +#define BITCOIN_WALLET_FEES_H + +#include "amount.h" + +class CBlockPolicyEstimator; +class CCoinControl; +class CFeeRate; +class CTxMemPool; +struct FeeCalculation; + +/** + * Return the minimum required fee taking into account the + * floating relay fee and user set minimum transaction fee + */ +CAmount GetRequiredFee(unsigned int nTxBytes); + +/** + * Estimate the minimum fee considering user set parameters + * and the required fee + */ +CAmount GetMinimumFee(unsigned int nTxBytes, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc); + +/** + * Return the maximum feerate for discarding change. + */ +CFeeRate GetDiscardRate(const CBlockPolicyEstimator& estimator); + +#endif // BITCOIN_WALLET_FEES_H diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp new file mode 100644 index 000000000..094667892 --- /dev/null +++ b/src/wallet/init.cpp @@ -0,0 +1,12 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "wallet/init.h" + +#include "net.h" +#include "util.h" +#include "utilmoneystr.h" +#include "validation.h" +#include "wallet/wallet.h" diff --git a/src/wallet/init.h b/src/wallet/init.h new file mode 100644 index 000000000..fa2251506 --- /dev/null +++ b/src/wallet/init.h @@ -0,0 +1,25 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_WALLET_INIT_H +#define BITCOIN_WALLET_INIT_H + +#include + +//! Return the wallets help message. +std::string GetWalletHelpString(bool showDebug); + +//! Wallets parameter interaction +bool WalletParameterInteraction(); + +//! Responsible for reading and validating the -wallet arguments and verifying the wallet database. +// This function will perform salvage on the wallet if requested, as long as only one wallet is +// being loaded (CWallet::ParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet). +bool WalletVerify(); + +//! Load wallet databases. +bool InitLoadWallet(); + +#endif // BITCOIN_WALLET_INIT_H diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index a6176c348..30b8c8260 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -8,7 +8,6 @@ #include "chain.h" #include "consensus/validation.h" #include "core_io.h" -#include "init.h" #include "httpserver.h" #include "validation.h" #include "net.h" @@ -27,6 +26,8 @@ #include "wallet/wallet.h" #include "wallet/walletdb.h" +#include // For StartShutdown + #include #include diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 599e74149..a9b615275 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -30,6 +30,7 @@ #include "util.h" #include "ui_interface.h" #include "utilmoneystr.h" +#include "wallet/fees.h" #include @@ -494,7 +495,7 @@ void CWallet::Flush(bool shutdown) dbw->Flush(shutdown); } -bool CWallet::Verify() +bool WalletVerify() { if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) return true; @@ -2599,7 +2600,7 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC return true; } -static CFeeRate GetDiscardRate(const CBlockPolicyEstimator& estimator) +CFeeRate GetDiscardRate(const CBlockPolicyEstimator& estimator) { unsigned int highest_target = estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE); CFeeRate discard_rate = estimator.estimateSmartFee(highest_target, nullptr /* FeeCalculation */, false /* conservative */); @@ -3031,12 +3032,12 @@ bool CWallet::AddAccountingEntry(const CAccountingEntry& acentry, CWalletDB *pwa return true; } -CAmount CWallet::GetRequiredFee(unsigned int nTxBytes) +CAmount GetRequiredFee(unsigned int nTxBytes) { - return std::max(minTxFee.GetFee(nTxBytes), ::minRelayTxFee.GetFee(nTxBytes)); + return std::max(CWallet::minTxFee.GetFee(nTxBytes), ::minRelayTxFee.GetFee(nTxBytes)); } -CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc) +CAmount GetMinimumFee(unsigned int nTxBytes, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc) { /* User control of how to calculate fee uses the following parameter precedence: 1. coin_control.m_feerate @@ -3068,7 +3069,7 @@ CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, const CCoinControl& coin_c fee_needed = estimator.estimateSmartFee(target, feeCalc, conservative_estimate).GetFee(nTxBytes); if (fee_needed == 0) { // if we don't have enough data for estimateSmartFee, then use fallbackFee - fee_needed = fallbackFee.GetFee(nTxBytes); + fee_needed = CWallet::fallbackFee.GetFee(nTxBytes); if (feeCalc) feeCalc->reason = FeeReason::FALLBACK; } // Obey mempool min fee when using smart fee estimation @@ -3888,7 +3889,7 @@ std::vector CWallet::GetDestValues(const std::string& prefix) const return values; } -std::string CWallet::GetWalletHelpString(bool showDebug) +std::string GetWalletHelpString(bool showDebug) { std::string strUsage = HelpMessageGroup(_("Wallet options:")); strUsage += HelpMessageOpt("-disablewallet", _("Do not load the wallet and disable wallet RPC calls")); @@ -4121,7 +4122,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) return walletInstance; } -bool CWallet::InitLoadWallet() +bool InitLoadWallet() { if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { LogPrintf("Wallet disabled!\n"); @@ -4129,7 +4130,7 @@ bool CWallet::InitLoadWallet() } for (const std::string& walletFile : gArgs.GetArgs("-wallet")) { - CWallet * const pwallet = CreateWalletFromFile(walletFile); + CWallet * const pwallet = CWallet::CreateWalletFromFile(walletFile); if (!pwallet) { return false; } @@ -4153,7 +4154,7 @@ void CWallet::postInitProcess(CScheduler& scheduler) } } -bool CWallet::ParameterInteraction() +bool WalletParameterInteraction() { gArgs.SoftSetArg("-wallet", DEFAULT_WALLET_DAT); const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index f97a99d82..bceeb12fb 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -960,16 +960,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface static CFeeRate minTxFee; static CFeeRate fallbackFee; static CFeeRate m_discard_rate; - /** - * Estimate the minimum fee considering user set parameters - * and the required fee - */ - static CAmount GetMinimumFee(unsigned int nTxBytes, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc); - /** - * Return the minimum required fee taking into account the - * floating relay fee and user set minimum transaction fee - */ - static CAmount GetRequiredFee(unsigned int nTxBytes); bool NewKeyPool(); size_t KeypoolCountExternalKeys(); @@ -1060,11 +1050,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface //! Flush wallet (bitdb flush) void Flush(bool shutdown=false); - //! Responsible for reading and validating the -wallet arguments and verifying the wallet database. - // This function will perform salvage on the wallet if requested, as long as only one wallet is - // being loaded (CWallet::ParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet). - static bool Verify(); - /** * Address book entry changed. * @note called with lock cs_wallet held. @@ -1101,12 +1086,8 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface /** 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); - /* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */ static CWallet* CreateWalletFromFile(const std::string walletFile); - static bool InitLoadWallet(); /** * Wallet post-init setup @@ -1114,9 +1095,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface */ void postInitProcess(CScheduler& scheduler); - /* Wallets parameter interaction */ - static bool ParameterInteraction(); - bool BackupWallet(const std::string& strDest); /* Set the HD chain model (chain child index counters) */ From e7fe3208a83c170e50407d703525f1b4cbf337a3 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Wed, 2 Aug 2017 07:44:05 -0400 Subject: [PATCH 058/382] MOVEONLY: Fee functions wallet/wallet.cpp -> wallet/fees.cpp --- src/wallet/fees.cpp | 76 +++++++++++++++++++++++++++++++++++++++++++ src/wallet/wallet.cpp | 76 ------------------------------------------- 2 files changed, 76 insertions(+), 76 deletions(-) diff --git a/src/wallet/fees.cpp b/src/wallet/fees.cpp index 3a9e68354..76eeeeda0 100644 --- a/src/wallet/fees.cpp +++ b/src/wallet/fees.cpp @@ -11,3 +11,79 @@ #include "validation.h" #include "wallet/coincontrol.h" #include "wallet/wallet.h" + + +CAmount GetRequiredFee(unsigned int nTxBytes) +{ + return std::max(CWallet::minTxFee.GetFee(nTxBytes), ::minRelayTxFee.GetFee(nTxBytes)); +} + + +CAmount GetMinimumFee(unsigned int nTxBytes, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc) +{ + /* User control of how to calculate fee uses the following parameter precedence: + 1. coin_control.m_feerate + 2. coin_control.m_confirm_target + 3. payTxFee (user-set global variable) + 4. nTxConfirmTarget (user-set global variable) + The first parameter that is set is used. + */ + CAmount fee_needed; + if (coin_control.m_feerate) { // 1. + fee_needed = coin_control.m_feerate->GetFee(nTxBytes); + if (feeCalc) feeCalc->reason = FeeReason::PAYTXFEE; + // Allow to override automatic min/max check over coin control instance + if (coin_control.fOverrideFeeRate) return fee_needed; + } + else if (!coin_control.m_confirm_target && ::payTxFee != CFeeRate(0)) { // 3. TODO: remove magic value of 0 for global payTxFee + fee_needed = ::payTxFee.GetFee(nTxBytes); + if (feeCalc) feeCalc->reason = FeeReason::PAYTXFEE; + } + else { // 2. or 4. + // We will use smart fee estimation + unsigned int target = coin_control.m_confirm_target ? *coin_control.m_confirm_target : ::nTxConfirmTarget; + // By default estimates are economical iff we are signaling opt-in-RBF + bool conservative_estimate = !coin_control.signalRbf; + // Allow to override the default fee estimate mode over the CoinControl instance + if (coin_control.m_fee_mode == FeeEstimateMode::CONSERVATIVE) conservative_estimate = true; + else if (coin_control.m_fee_mode == FeeEstimateMode::ECONOMICAL) conservative_estimate = false; + + fee_needed = estimator.estimateSmartFee(target, feeCalc, conservative_estimate).GetFee(nTxBytes); + if (fee_needed == 0) { + // if we don't have enough data for estimateSmartFee, then use fallbackFee + fee_needed = CWallet::fallbackFee.GetFee(nTxBytes); + if (feeCalc) feeCalc->reason = FeeReason::FALLBACK; + } + // Obey mempool min fee when using smart fee estimation + CAmount min_mempool_fee = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nTxBytes); + if (fee_needed < min_mempool_fee) { + fee_needed = min_mempool_fee; + if (feeCalc) feeCalc->reason = FeeReason::MEMPOOL_MIN; + } + } + + // prevent user from paying a fee below minRelayTxFee or minTxFee + CAmount required_fee = GetRequiredFee(nTxBytes); + if (required_fee > fee_needed) { + fee_needed = required_fee; + if (feeCalc) feeCalc->reason = FeeReason::REQUIRED; + } + // But always obey the maximum + if (fee_needed > maxTxFee) { + fee_needed = maxTxFee; + if (feeCalc) feeCalc->reason = FeeReason::MAXTXFEE; + } + return fee_needed; +} + + +CFeeRate GetDiscardRate(const CBlockPolicyEstimator& estimator) +{ + unsigned int highest_target = estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE); + CFeeRate discard_rate = estimator.estimateSmartFee(highest_target, nullptr /* FeeCalculation */, false /* conservative */); + // Don't let discard_rate be greater than longest possible fee estimate if we get a valid fee estimate + discard_rate = (discard_rate == CFeeRate(0)) ? CWallet::m_discard_rate : std::min(discard_rate, CWallet::m_discard_rate); + // Discard rate must be at least dustRelayFee + discard_rate = std::max(discard_rate, ::dustRelayFee); + return discard_rate; +} diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index a9b615275..1f12d03b5 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2600,17 +2600,6 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC return true; } -CFeeRate GetDiscardRate(const CBlockPolicyEstimator& estimator) -{ - unsigned int highest_target = estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE); - CFeeRate discard_rate = estimator.estimateSmartFee(highest_target, nullptr /* FeeCalculation */, false /* conservative */); - // Don't let discard_rate be greater than longest possible fee estimate if we get a valid fee estimate - discard_rate = (discard_rate == CFeeRate(0)) ? CWallet::m_discard_rate : std::min(discard_rate, CWallet::m_discard_rate); - // Discard rate must be at least dustRelayFee - discard_rate = std::max(discard_rate, ::dustRelayFee); - return discard_rate; -} - bool CWallet::CreateTransaction(const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign) { @@ -3032,71 +3021,6 @@ bool CWallet::AddAccountingEntry(const CAccountingEntry& acentry, CWalletDB *pwa return true; } -CAmount GetRequiredFee(unsigned int nTxBytes) -{ - return std::max(CWallet::minTxFee.GetFee(nTxBytes), ::minRelayTxFee.GetFee(nTxBytes)); -} - -CAmount GetMinimumFee(unsigned int nTxBytes, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc) -{ - /* User control of how to calculate fee uses the following parameter precedence: - 1. coin_control.m_feerate - 2. coin_control.m_confirm_target - 3. payTxFee (user-set global variable) - 4. nTxConfirmTarget (user-set global variable) - The first parameter that is set is used. - */ - CAmount fee_needed; - if (coin_control.m_feerate) { // 1. - fee_needed = coin_control.m_feerate->GetFee(nTxBytes); - if (feeCalc) feeCalc->reason = FeeReason::PAYTXFEE; - // Allow to override automatic min/max check over coin control instance - if (coin_control.fOverrideFeeRate) return fee_needed; - } - else if (!coin_control.m_confirm_target && ::payTxFee != CFeeRate(0)) { // 3. TODO: remove magic value of 0 for global payTxFee - fee_needed = ::payTxFee.GetFee(nTxBytes); - if (feeCalc) feeCalc->reason = FeeReason::PAYTXFEE; - } - else { // 2. or 4. - // We will use smart fee estimation - unsigned int target = coin_control.m_confirm_target ? *coin_control.m_confirm_target : ::nTxConfirmTarget; - // By default estimates are economical iff we are signaling opt-in-RBF - bool conservative_estimate = !coin_control.signalRbf; - // Allow to override the default fee estimate mode over the CoinControl instance - if (coin_control.m_fee_mode == FeeEstimateMode::CONSERVATIVE) conservative_estimate = true; - else if (coin_control.m_fee_mode == FeeEstimateMode::ECONOMICAL) conservative_estimate = false; - - fee_needed = estimator.estimateSmartFee(target, feeCalc, conservative_estimate).GetFee(nTxBytes); - if (fee_needed == 0) { - // if we don't have enough data for estimateSmartFee, then use fallbackFee - fee_needed = CWallet::fallbackFee.GetFee(nTxBytes); - if (feeCalc) feeCalc->reason = FeeReason::FALLBACK; - } - // Obey mempool min fee when using smart fee estimation - CAmount min_mempool_fee = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nTxBytes); - if (fee_needed < min_mempool_fee) { - fee_needed = min_mempool_fee; - if (feeCalc) feeCalc->reason = FeeReason::MEMPOOL_MIN; - } - } - - // prevent user from paying a fee below minRelayTxFee or minTxFee - CAmount required_fee = GetRequiredFee(nTxBytes); - if (required_fee > fee_needed) { - fee_needed = required_fee; - if (feeCalc) feeCalc->reason = FeeReason::REQUIRED; - } - // But always obey the maximum - if (fee_needed > maxTxFee) { - fee_needed = maxTxFee; - if (feeCalc) feeCalc->reason = FeeReason::MAXTXFEE; - } - return fee_needed; -} - - - - DBErrors CWallet::LoadWallet(bool& fFirstRunRet) { fFirstRunRet = false; From f01103c1e0a204fc7f40a06755f6c3adb5480cf8 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Wed, 2 Aug 2017 07:48:52 -0400 Subject: [PATCH 059/382] MOVEONLY: Init functions wallet/wallet.cpp -> wallet/init.cpp --- src/wallet/init.cpp | 235 ++++++++++++++++++++++++++++++++++++++++++ src/wallet/wallet.cpp | 235 ------------------------------------------ 2 files changed, 235 insertions(+), 235 deletions(-) diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index 094667892..18365b1b7 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -10,3 +10,238 @@ #include "utilmoneystr.h" #include "validation.h" #include "wallet/wallet.h" + +std::string GetWalletHelpString(bool showDebug) +{ + std::string strUsage = HelpMessageGroup(_("Wallet options:")); + strUsage += HelpMessageOpt("-disablewallet", _("Do not load the wallet and disable wallet RPC calls")); + strUsage += HelpMessageOpt("-keypool=", strprintf(_("Set key pool size to (default: %u)"), DEFAULT_KEYPOOL_SIZE)); + strUsage += HelpMessageOpt("-fallbackfee=", strprintf(_("A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s)"), + CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE))); + strUsage += HelpMessageOpt("-discardfee=", strprintf(_("The fee rate (in %s/kB) that indicates your tolerance for discarding change by adding it to the fee (default: %s). " + "Note: An output is discarded if it is dust at this rate, but we will always discard up to the dust relay fee and a discard fee above that is limited by the fee estimate for the longest target"), + CURRENCY_UNIT, FormatMoney(DEFAULT_DISCARD_FEE))); + strUsage += HelpMessageOpt("-mintxfee=", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)"), + CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE))); + strUsage += HelpMessageOpt("-paytxfee=", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"), + CURRENCY_UNIT, FormatMoney(payTxFee.GetFeePerK()))); + strUsage += HelpMessageOpt("-rescan", _("Rescan the block chain for missing wallet transactions on startup")); + strUsage += HelpMessageOpt("-salvagewallet", _("Attempt to recover private keys from a corrupt wallet on startup")); + strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE)); + strUsage += HelpMessageOpt("-txconfirmtarget=", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), DEFAULT_TX_CONFIRM_TARGET)); + strUsage += HelpMessageOpt("-usehd", _("Use hierarchical deterministic key generation (HD) after BIP32. Only has effect during wallet creation/first start") + " " + strprintf(_("(default: %u)"), DEFAULT_USE_HD_WALLET)); + strUsage += HelpMessageOpt("-walletrbf", strprintf(_("Send transactions with full-RBF opt-in enabled (default: %u)"), DEFAULT_WALLET_RBF)); + strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format on startup")); + strUsage += HelpMessageOpt("-wallet=", _("Specify wallet file (within data directory)") + " " + strprintf(_("(default: %s)"), DEFAULT_WALLET_DAT)); + strUsage += HelpMessageOpt("-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %u)"), DEFAULT_WALLETBROADCAST)); + strUsage += HelpMessageOpt("-walletnotify=", _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)")); + strUsage += HelpMessageOpt("-zapwallettxes=", _("Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup") + + " " + _("(1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)")); + + if (showDebug) + { + strUsage += HelpMessageGroup(_("Wallet debugging/testing options:")); + + strUsage += HelpMessageOpt("-dblogsize=", strprintf("Flush wallet database activity from memory to disk log every megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE)); + strUsage += HelpMessageOpt("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET)); + strUsage += HelpMessageOpt("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", DEFAULT_WALLET_PRIVDB)); + strUsage += HelpMessageOpt("-walletrejectlongchains", strprintf(_("Wallet will not create transactions that violate mempool chain limits (default: %u)"), DEFAULT_WALLET_REJECT_LONG_CHAINS)); + } + + return strUsage; +} + +bool WalletParameterInteraction() +{ + gArgs.SoftSetArg("-wallet", DEFAULT_WALLET_DAT); + const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1; + + if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) + return true; + + if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && gArgs.SoftSetBoolArg("-walletbroadcast", false)) { + LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\n", __func__); + } + + if (gArgs.GetBoolArg("-salvagewallet", false)) { + if (is_multiwallet) { + return InitError(strprintf("%s is only allowed with a single wallet file", "-salvagewallet")); + } + // Rewrite just private keys: rescan to find transactions + if (gArgs.SoftSetBoolArg("-rescan", true)) { + LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting -rescan=1\n", __func__); + } + } + + int zapwallettxes = gArgs.GetArg("-zapwallettxes", 0); + // -zapwallettxes implies dropping the mempool on startup + if (zapwallettxes != 0 && gArgs.SoftSetBoolArg("-persistmempool", false)) { + LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting -persistmempool=0\n", __func__, zapwallettxes); + } + + // -zapwallettxes implies a rescan + if (zapwallettxes != 0) { + if (is_multiwallet) { + return InitError(strprintf("%s is only allowed with a single wallet file", "-zapwallettxes")); + } + if (gArgs.SoftSetBoolArg("-rescan", true)) { + LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting -rescan=1\n", __func__, zapwallettxes); + } + } + + if (is_multiwallet) { + if (gArgs.GetBoolArg("-upgradewallet", false)) { + return InitError(strprintf("%s is only allowed with a single wallet file", "-upgradewallet")); + } + } + + if (gArgs.GetBoolArg("-sysperms", false)) + return InitError("-sysperms is not allowed in combination with enabled wallet functionality"); + if (gArgs.GetArg("-prune", 0) && gArgs.GetBoolArg("-rescan", false)) + return InitError(_("Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.")); + + if (::minRelayTxFee.GetFeePerK() > HIGH_TX_FEE_PER_KB) + InitWarning(AmountHighWarn("-minrelaytxfee") + " " + + _("The wallet will avoid paying less than the minimum relay fee.")); + + if (gArgs.IsArgSet("-mintxfee")) + { + CAmount n = 0; + if (!ParseMoney(gArgs.GetArg("-mintxfee", ""), n) || 0 == n) + return InitError(AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", ""))); + if (n > HIGH_TX_FEE_PER_KB) + InitWarning(AmountHighWarn("-mintxfee") + " " + + _("This is the minimum transaction fee you pay on every transaction.")); + CWallet::minTxFee = CFeeRate(n); + } + if (gArgs.IsArgSet("-fallbackfee")) + { + CAmount nFeePerK = 0; + if (!ParseMoney(gArgs.GetArg("-fallbackfee", ""), nFeePerK)) + return InitError(strprintf(_("Invalid amount for -fallbackfee=: '%s'"), gArgs.GetArg("-fallbackfee", ""))); + if (nFeePerK > HIGH_TX_FEE_PER_KB) + InitWarning(AmountHighWarn("-fallbackfee") + " " + + _("This is the transaction fee you may pay when fee estimates are not available.")); + CWallet::fallbackFee = CFeeRate(nFeePerK); + } + if (gArgs.IsArgSet("-discardfee")) + { + CAmount nFeePerK = 0; + if (!ParseMoney(gArgs.GetArg("-discardfee", ""), nFeePerK)) + return InitError(strprintf(_("Invalid amount for -discardfee=: '%s'"), gArgs.GetArg("-discardfee", ""))); + if (nFeePerK > HIGH_TX_FEE_PER_KB) + InitWarning(AmountHighWarn("-discardfee") + " " + + _("This is the transaction fee you may discard if change is smaller than dust at this level")); + CWallet::m_discard_rate = CFeeRate(nFeePerK); + } + if (gArgs.IsArgSet("-paytxfee")) + { + CAmount nFeePerK = 0; + if (!ParseMoney(gArgs.GetArg("-paytxfee", ""), nFeePerK)) + return InitError(AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", ""))); + if (nFeePerK > HIGH_TX_FEE_PER_KB) + InitWarning(AmountHighWarn("-paytxfee") + " " + + _("This is the transaction fee you will pay if you send a transaction.")); + + payTxFee = CFeeRate(nFeePerK, 1000); + if (payTxFee < ::minRelayTxFee) + { + return InitError(strprintf(_("Invalid amount for -paytxfee=: '%s' (must be at least %s)"), + gArgs.GetArg("-paytxfee", ""), ::minRelayTxFee.ToString())); + } + } + if (gArgs.IsArgSet("-maxtxfee")) + { + CAmount nMaxFee = 0; + if (!ParseMoney(gArgs.GetArg("-maxtxfee", ""), nMaxFee)) + return InitError(AmountErrMsg("maxtxfee", gArgs.GetArg("-maxtxfee", ""))); + if (nMaxFee > HIGH_MAX_TX_FEE) + InitWarning(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction.")); + maxTxFee = nMaxFee; + if (CFeeRate(maxTxFee, 1000) < ::minRelayTxFee) + { + return InitError(strprintf(_("Invalid amount for -maxtxfee=: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"), + gArgs.GetArg("-maxtxfee", ""), ::minRelayTxFee.ToString())); + } + } + nTxConfirmTarget = gArgs.GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET); + bSpendZeroConfChange = gArgs.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); + fWalletRbf = gArgs.GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF); + + return true; +} + +bool WalletVerify() +{ + if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) + return true; + + uiInterface.InitMessage(_("Verifying wallet(s)...")); + + // Keep track of each wallet absolute path to detect duplicates. + std::set wallet_paths; + + for (const std::string& walletFile : gArgs.GetArgs("-wallet")) { + if (boost::filesystem::path(walletFile).filename() != walletFile) { + return InitError(strprintf(_("Error loading wallet %s. -wallet parameter must only specify a filename (not a path)."), walletFile)); + } + + if (SanitizeString(walletFile, SAFE_CHARS_FILENAME) != walletFile) { + return InitError(strprintf(_("Error loading wallet %s. Invalid characters in -wallet filename."), walletFile)); + } + + fs::path wallet_path = fs::absolute(walletFile, GetDataDir()); + + if (fs::exists(wallet_path) && (!fs::is_regular_file(wallet_path) || fs::is_symlink(wallet_path))) { + return InitError(strprintf(_("Error loading wallet %s. -wallet filename must be a regular file."), walletFile)); + } + + if (!wallet_paths.insert(wallet_path).second) { + return InitError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified."), walletFile)); + } + + std::string strError; + if (!CWalletDB::VerifyEnvironment(walletFile, GetDataDir().string(), strError)) { + return InitError(strError); + } + + if (gArgs.GetBoolArg("-salvagewallet", false)) { + // Recover readable keypairs: + CWallet dummyWallet; + std::string backup_filename; + if (!CWalletDB::Recover(walletFile, (void *)&dummyWallet, CWalletDB::RecoverKeysOnlyFilter, backup_filename)) { + return false; + } + } + + std::string strWarning; + bool dbV = CWalletDB::VerifyDatabaseFile(walletFile, GetDataDir().string(), strWarning, strError); + if (!strWarning.empty()) { + InitWarning(strWarning); + } + if (!dbV) { + InitError(strError); + return false; + } + } + + return true; +} + +bool InitLoadWallet() +{ + if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { + LogPrintf("Wallet disabled!\n"); + return true; + } + + for (const std::string& walletFile : gArgs.GetArgs("-wallet")) { + CWallet * const pwallet = CWallet::CreateWalletFromFile(walletFile); + if (!pwallet) { + return false; + } + vpwallets.push_back(pwallet); + } + + return true; +} diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 1f12d03b5..e2f12fa02 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -495,63 +495,6 @@ void CWallet::Flush(bool shutdown) dbw->Flush(shutdown); } -bool WalletVerify() -{ - if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) - return true; - - uiInterface.InitMessage(_("Verifying wallet(s)...")); - - // Keep track of each wallet absolute path to detect duplicates. - std::set wallet_paths; - - for (const std::string& walletFile : gArgs.GetArgs("-wallet")) { - if (boost::filesystem::path(walletFile).filename() != walletFile) { - return InitError(strprintf(_("Error loading wallet %s. -wallet parameter must only specify a filename (not a path)."), walletFile)); - } - - if (SanitizeString(walletFile, SAFE_CHARS_FILENAME) != walletFile) { - return InitError(strprintf(_("Error loading wallet %s. Invalid characters in -wallet filename."), walletFile)); - } - - fs::path wallet_path = fs::absolute(walletFile, GetDataDir()); - - if (fs::exists(wallet_path) && (!fs::is_regular_file(wallet_path) || fs::is_symlink(wallet_path))) { - return InitError(strprintf(_("Error loading wallet %s. -wallet filename must be a regular file."), walletFile)); - } - - if (!wallet_paths.insert(wallet_path).second) { - return InitError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified."), walletFile)); - } - - std::string strError; - if (!CWalletDB::VerifyEnvironment(walletFile, GetDataDir().string(), strError)) { - return InitError(strError); - } - - if (gArgs.GetBoolArg("-salvagewallet", false)) { - // Recover readable keypairs: - CWallet dummyWallet; - std::string backup_filename; - if (!CWalletDB::Recover(walletFile, (void *)&dummyWallet, CWalletDB::RecoverKeysOnlyFilter, backup_filename)) { - return false; - } - } - - std::string strWarning; - bool dbV = CWalletDB::VerifyDatabaseFile(walletFile, GetDataDir().string(), strWarning, strError); - if (!strWarning.empty()) { - InitWarning(strWarning); - } - if (!dbV) { - InitError(strError); - return false; - } - } - - return true; -} - void CWallet::SyncMetaData(std::pair range) { // We want all the wallet transactions in range to have the same metadata as @@ -3813,46 +3756,6 @@ std::vector CWallet::GetDestValues(const std::string& prefix) const return values; } -std::string GetWalletHelpString(bool showDebug) -{ - std::string strUsage = HelpMessageGroup(_("Wallet options:")); - strUsage += HelpMessageOpt("-disablewallet", _("Do not load the wallet and disable wallet RPC calls")); - strUsage += HelpMessageOpt("-keypool=", strprintf(_("Set key pool size to (default: %u)"), DEFAULT_KEYPOOL_SIZE)); - strUsage += HelpMessageOpt("-fallbackfee=", strprintf(_("A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s)"), - CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE))); - strUsage += HelpMessageOpt("-discardfee=", strprintf(_("The fee rate (in %s/kB) that indicates your tolerance for discarding change by adding it to the fee (default: %s). " - "Note: An output is discarded if it is dust at this rate, but we will always discard up to the dust relay fee and a discard fee above that is limited by the fee estimate for the longest target"), - CURRENCY_UNIT, FormatMoney(DEFAULT_DISCARD_FEE))); - strUsage += HelpMessageOpt("-mintxfee=", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)"), - CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE))); - strUsage += HelpMessageOpt("-paytxfee=", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"), - CURRENCY_UNIT, FormatMoney(payTxFee.GetFeePerK()))); - strUsage += HelpMessageOpt("-rescan", _("Rescan the block chain for missing wallet transactions on startup")); - strUsage += HelpMessageOpt("-salvagewallet", _("Attempt to recover private keys from a corrupt wallet on startup")); - strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE)); - strUsage += HelpMessageOpt("-txconfirmtarget=", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), DEFAULT_TX_CONFIRM_TARGET)); - strUsage += HelpMessageOpt("-usehd", _("Use hierarchical deterministic key generation (HD) after BIP32. Only has effect during wallet creation/first start") + " " + strprintf(_("(default: %u)"), DEFAULT_USE_HD_WALLET)); - strUsage += HelpMessageOpt("-walletrbf", strprintf(_("Send transactions with full-RBF opt-in enabled (default: %u)"), DEFAULT_WALLET_RBF)); - strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format on startup")); - strUsage += HelpMessageOpt("-wallet=", _("Specify wallet file (within data directory)") + " " + strprintf(_("(default: %s)"), DEFAULT_WALLET_DAT)); - strUsage += HelpMessageOpt("-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %u)"), DEFAULT_WALLETBROADCAST)); - strUsage += HelpMessageOpt("-walletnotify=", _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)")); - strUsage += HelpMessageOpt("-zapwallettxes=", _("Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup") + - " " + _("(1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)")); - - if (showDebug) - { - strUsage += HelpMessageGroup(_("Wallet debugging/testing options:")); - - strUsage += HelpMessageOpt("-dblogsize=", strprintf("Flush wallet database activity from memory to disk log every megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE)); - strUsage += HelpMessageOpt("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET)); - strUsage += HelpMessageOpt("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", DEFAULT_WALLET_PRIVDB)); - strUsage += HelpMessageOpt("-walletrejectlongchains", strprintf(_("Wallet will not create transactions that violate mempool chain limits (default: %u)"), DEFAULT_WALLET_REJECT_LONG_CHAINS)); - } - - return strUsage; -} - CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) { // needed to restore wallet transaction meta data after -zapwallettxes @@ -4046,24 +3949,6 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) return walletInstance; } -bool InitLoadWallet() -{ - if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { - LogPrintf("Wallet disabled!\n"); - return true; - } - - for (const std::string& walletFile : gArgs.GetArgs("-wallet")) { - CWallet * const pwallet = CWallet::CreateWalletFromFile(walletFile); - if (!pwallet) { - return false; - } - vpwallets.push_back(pwallet); - } - - return true; -} - std::atomic CWallet::fFlushScheduled(false); void CWallet::postInitProcess(CScheduler& scheduler) @@ -4078,126 +3963,6 @@ void CWallet::postInitProcess(CScheduler& scheduler) } } -bool WalletParameterInteraction() -{ - gArgs.SoftSetArg("-wallet", DEFAULT_WALLET_DAT); - const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1; - - if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) - return true; - - if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && gArgs.SoftSetBoolArg("-walletbroadcast", false)) { - LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\n", __func__); - } - - if (gArgs.GetBoolArg("-salvagewallet", false)) { - if (is_multiwallet) { - return InitError(strprintf("%s is only allowed with a single wallet file", "-salvagewallet")); - } - // Rewrite just private keys: rescan to find transactions - if (gArgs.SoftSetBoolArg("-rescan", true)) { - LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting -rescan=1\n", __func__); - } - } - - int zapwallettxes = gArgs.GetArg("-zapwallettxes", 0); - // -zapwallettxes implies dropping the mempool on startup - if (zapwallettxes != 0 && gArgs.SoftSetBoolArg("-persistmempool", false)) { - LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting -persistmempool=0\n", __func__, zapwallettxes); - } - - // -zapwallettxes implies a rescan - if (zapwallettxes != 0) { - if (is_multiwallet) { - return InitError(strprintf("%s is only allowed with a single wallet file", "-zapwallettxes")); - } - if (gArgs.SoftSetBoolArg("-rescan", true)) { - LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting -rescan=1\n", __func__, zapwallettxes); - } - } - - if (is_multiwallet) { - if (gArgs.GetBoolArg("-upgradewallet", false)) { - return InitError(strprintf("%s is only allowed with a single wallet file", "-upgradewallet")); - } - } - - if (gArgs.GetBoolArg("-sysperms", false)) - return InitError("-sysperms is not allowed in combination with enabled wallet functionality"); - if (gArgs.GetArg("-prune", 0) && gArgs.GetBoolArg("-rescan", false)) - return InitError(_("Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.")); - - if (::minRelayTxFee.GetFeePerK() > HIGH_TX_FEE_PER_KB) - InitWarning(AmountHighWarn("-minrelaytxfee") + " " + - _("The wallet will avoid paying less than the minimum relay fee.")); - - if (gArgs.IsArgSet("-mintxfee")) - { - CAmount n = 0; - if (!ParseMoney(gArgs.GetArg("-mintxfee", ""), n) || 0 == n) - return InitError(AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", ""))); - if (n > HIGH_TX_FEE_PER_KB) - InitWarning(AmountHighWarn("-mintxfee") + " " + - _("This is the minimum transaction fee you pay on every transaction.")); - CWallet::minTxFee = CFeeRate(n); - } - if (gArgs.IsArgSet("-fallbackfee")) - { - CAmount nFeePerK = 0; - if (!ParseMoney(gArgs.GetArg("-fallbackfee", ""), nFeePerK)) - return InitError(strprintf(_("Invalid amount for -fallbackfee=: '%s'"), gArgs.GetArg("-fallbackfee", ""))); - if (nFeePerK > HIGH_TX_FEE_PER_KB) - InitWarning(AmountHighWarn("-fallbackfee") + " " + - _("This is the transaction fee you may pay when fee estimates are not available.")); - CWallet::fallbackFee = CFeeRate(nFeePerK); - } - if (gArgs.IsArgSet("-discardfee")) - { - CAmount nFeePerK = 0; - if (!ParseMoney(gArgs.GetArg("-discardfee", ""), nFeePerK)) - return InitError(strprintf(_("Invalid amount for -discardfee=: '%s'"), gArgs.GetArg("-discardfee", ""))); - if (nFeePerK > HIGH_TX_FEE_PER_KB) - InitWarning(AmountHighWarn("-discardfee") + " " + - _("This is the transaction fee you may discard if change is smaller than dust at this level")); - CWallet::m_discard_rate = CFeeRate(nFeePerK); - } - if (gArgs.IsArgSet("-paytxfee")) - { - CAmount nFeePerK = 0; - if (!ParseMoney(gArgs.GetArg("-paytxfee", ""), nFeePerK)) - return InitError(AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", ""))); - if (nFeePerK > HIGH_TX_FEE_PER_KB) - InitWarning(AmountHighWarn("-paytxfee") + " " + - _("This is the transaction fee you will pay if you send a transaction.")); - - payTxFee = CFeeRate(nFeePerK, 1000); - if (payTxFee < ::minRelayTxFee) - { - return InitError(strprintf(_("Invalid amount for -paytxfee=: '%s' (must be at least %s)"), - gArgs.GetArg("-paytxfee", ""), ::minRelayTxFee.ToString())); - } - } - if (gArgs.IsArgSet("-maxtxfee")) - { - CAmount nMaxFee = 0; - if (!ParseMoney(gArgs.GetArg("-maxtxfee", ""), nMaxFee)) - return InitError(AmountErrMsg("maxtxfee", gArgs.GetArg("-maxtxfee", ""))); - if (nMaxFee > HIGH_MAX_TX_FEE) - InitWarning(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction.")); - maxTxFee = nMaxFee; - if (CFeeRate(maxTxFee, 1000) < ::minRelayTxFee) - { - return InitError(strprintf(_("Invalid amount for -maxtxfee=: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"), - gArgs.GetArg("-maxtxfee", ""), ::minRelayTxFee.ToString())); - } - } - nTxConfirmTarget = gArgs.GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET); - bSpendZeroConfChange = gArgs.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); - fWalletRbf = gArgs.GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF); - - return true; -} - bool CWallet::BackupWallet(const std::string& strDest) { return dbw->Backup(strDest); From f6283b4719d740a91a84b354951c2dc169779d34 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 17 Feb 2017 17:26:01 +0100 Subject: [PATCH 060/382] build: bump version to 0.15.99 Now that 0.15 branch has been split off, master is 0.15.99 (pre-0.16). Also clean out release notes. Tree-SHA512: 160f712fae7bfc51e49e36a27ab01f5c243b79a19a70312df95f9bc5cb8067c70aa88911f741fd1625bee549a70655eaea24d98f6049d98c3c14ee1b3143f4cb --- configure.ac | 2 +- doc/release-notes.md | 326 +------------------------------------------ 2 files changed, 3 insertions(+), 325 deletions(-) diff --git a/configure.ac b/configure.ac index fd1396fc9..1c530f8ff 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N) AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 0) -define(_CLIENT_VERSION_MINOR, 14) +define(_CLIENT_VERSION_MINOR, 15) define(_CLIENT_VERSION_REVISION, 99) define(_CLIENT_VERSION_BUILD, 0) define(_CLIENT_VERSION_IS_RELEASE, false) diff --git a/doc/release-notes.md b/doc/release-notes.md index 9398a3f8c..aa1d1bea1 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -1,9 +1,9 @@ (note: this is a temporary file, to be added-to by anybody, and moved to release-notes at release time) -Bitcoin Core version *0.15.0* is now available from: +Bitcoin Core version *version* is now available from: - + This is a new major version release, including new features, various bugfixes and performance improvements, as well as updated translations. @@ -56,332 +56,10 @@ frequently tested on them. Notable changes =============== -Performance Improvements ------------------------- - -Version 0.15 contains a number of significant performance improvements, which make -Initial Block Download, startup, transaction and block validation much faster: - -- The chainstate database (which is used for tracking UTXOs) has been changed - from a per-transaction model to a per-output model (See [PR 10195](https://github.com/bitcoin/bitcoin/pull/10195)). Advantages of this model - are that it: - - avoids the CPU overhead of deserializing and serializing the unused outputs; - - has more predictable memory usage; - - uses simpler code; - - is adaptable to various future cache flushing strategies. - - As a result, validating the blockchain during Initial Block Download (IBD) and reindex - is ~30-40% faster, uses 10-20% less memory, and flushes to disk far less frequently. - The only downside is that the on-disk database is 15% larger. During the conversion from the previous format - a few extra gigabytes may be used. -- Earlier versions experienced a spike in memory usage while flushing UTXO updates to disk. - As a result, only half of the available memory was actually used as cache, and the other half was - reserved to accommodate flushing. This is no longer the case (See [PR 10148](https://github.com/bitcoin/bitcoin/pull/10148)), and the entirety of - the available cache (see `-dbcache`) is now actually used as cache. This reduces the flushing - frequency by a factor 2 or more. -- In previous versions, signature validation for transactions has been cached when the - transaction is accepted to the mempool. Version 0.15 extends this to cache the entire script - validity (See [PR 10192](https://github.com/bitcoin/bitcoin/pull/10192)). This means that if a transaction in a block has already been accepted to the - mempool, the scriptSig does not need to be re-evaluated. Empirical tests show that - this results in new block validation being 40-50% faster. -- LevelDB has been upgraded to version 1.20 (See [PR 10544](https://github.com/bitcoin/bitcoin/pull/10544)). This version contains hardware acceleration for CRC - on architectures supporting SSE 4.2. As a result, synchronization and block validation are now faster. -- SHA256 hashing has been optimized for architectures supporting SSE 4 (See [PR 10182](https://github.com/bitcoin/bitcoin/pull/10182)). SHA256 is around - 50% faster on supported hardware, which results in around 5% faster IBD and block - validation. In version 0.15, SHA256 hardware optimization is disabled in release builds by - default, but can be enabled by using `--enable-experimental-asm` when building. -- Refill of the keypool no longer flushes the wallet between each key which resulted in a ~20x speedup in creating a new wallet. Part of this speedup was used to increase the default keypool to 1000 keys to make recovery more robust. (See [PR 10831](https://github.com/bitcoin/bitcoin/pull/10831)). - -Fee Estimation Improvements ---------------------------- - -Fee estimation has been significantly improved in version 0.15, with more accurate fee estimates used by the wallet and a wider range of options for advanced users of the `estimatesmartfee` and `estimaterawfee` RPCs (See [PR 10199](https://github.com/bitcoin/bitcoin/pull/10199)). - -### Changes to internal logic and wallet behavior - -- Internally, estimates are now tracked on 3 different time horizons. This allows for longer targets and means estimates adjust more quickly to changes in conditions. -- Estimates can now be *conservative* or *economical*. *Conservative* estimates use longer time horizons to produce an estimate which is less susceptible to rapid changes in fee conditions. *Economical* estimates use shorter time horizons and will be more affected by short-term changes in fee conditions. Economical estimates may be considerably lower during periods of low transaction activity (for example over weekends), but may result in transactions being unconfirmed if prevailing fees increase rapidly. -- By default, the wallet will use conservative fee estimates to increase the reliability of transactions being confirmed within the desired target. For transactions that are marked as replaceable, the wallet will use an economical estimate by default, since the fee can be 'bumped' if the fee conditions change rapidly (See [PR 10589](https://github.com/bitcoin/bitcoin/pull/10589)). -- Estimates can now be made for confirmation targets up to 1008 blocks (one week). -- More data on historical fee rates is stored, leading to more precise fee estimates. -- Transactions which leave the mempool due to eviction or other non-confirmed reasons are now taken into account by the fee estimation logic, leading to more accurate fee estimates. -- The fee estimation logic will make sure enough data has been gathered to return a meaningful estimate. If there is insufficient data, a fallback default fee is used. - -### Changes to fee estimate RPCs - -- The `estimatefee` RPC is now deprecated in favor of using only `estimatesmartfee` (which is the implementation used by the GUI) -- The `estimatesmartfee` RPC interface has been changed (See [PR 10707](https://github.com/bitcoin/bitcoin/pull/10707)): - - The `nblocks` argument has been renamed to `conf_target` (to be consistent with other RPC methods). - - An `estimate_mode` argument has been added. This argument takes one of the following strings: `CONSERVATIVE`, `ECONOMICAL` or `UNSET` (which defaults to `CONSERVATIVE`). - - The RPC return object now contains an `errors` member, which returns errors encountered during processing. - - If Bitcoin Core has not been running for long enough and has not seen enough blocks or transactions to produce an accurate fee estimation, an error will be returned (previously a value of -1 was used to indicate an error, which could be confused for a feerate). -- A new `estimaterawfee` RPC is added to provide raw fee data. External clients can query and use this data in their own fee estimation logic. - -Multi-wallet support --------------------- - -Bitcoin Core now supports loading multiple, separate wallets (See [PR 8694](https://github.com/bitcoin/bitcoin/pull/8694), [PR 10849](https://github.com/bitcoin/bitcoin/pull/10849)). The wallets are completely separated, with individual balances, keys and received transactions. - -Multi-wallet is enabled by using more than one `-wallet` argument when starting Bitcoin, either on the command line or in the Bitcoin config file. - -**In Bitcoin-Qt, only the first wallet will be displayed and accessible for creating and signing transactions.** GUI selectable multiple wallets will be supported in a future version. However, even in 0.15 other loaded wallets will continue to remain synchronized to the node's current tip in the background. This can be useful if running a pruned node, since loading a wallet where the most recent sync is beyond the pruned height results in having to download and revalidate the whole blockchain. Continuing to synchronize all wallets in the background avoids this problem. - -Bitcoin Core 0.15.0 contains the following changes to the RPC interface and `bitcoin-cli` for multi-wallet: - -* When running Bitcoin Core with a single wallet, there are **no** changes to the RPC interface or `bitcoin-cli`. All RPC calls and `bitcoin-cli` commands continue to work as before. -* When running Bitcoin Core with multi-wallet, all *node-level* RPC methods continue to work as before. HTTP RPC requests should be send to the normal `:/` endpoint, and `bitcoin-cli` commands should be run as before. A *node-level* RPC method is any method which does not require access to the wallet. -* When running Bitcoin Core with multi-wallet, *wallet-level* RPC methods must specify the wallet for which they're intended in every request. HTTP RPC requests should be send to the `:/wallet//` endpoint, for example `127.0.0.1:8332/wallet/wallet1.dat/`. `bitcoin-cli` commands should be run with a `-rpcwallet` option, for example `bitcoin-cli -rpcwallet=wallet1.dat getbalance`. -* A new *node-level* `listwallets` RPC method is added to display which wallets are currently loaded. The names returned by this method are the same as those used in the HTTP endpoint and for the `rpcwallet` argument. - -Note that while multi-wallet is now fully supported, the RPC multi-wallet interface should be considered unstable for version 0.15.0, and there may backwards-incompatible changes in future versions. - -Replace-by-fee control in the GUI ---------------------------------- - -Bitcoin Core has supported creating opt-in replace-by-fee (RBF) transactions -since version 0.12.0, and since version 0.14.0 has included a `bumpfee` RPC method to -replace unconfirmed opt-in RBF transactions with a new transaction that pays -a higher fee. - -In version 0.15, creating an opt-in RBF transaction and replacing the unconfirmed -transaction with a higher-fee transaction are both supported in the GUI (See [PR 9592](https://github.com/bitcoin/bitcoin/pull/9592)). - -Removal of Coin Age Priority ----------------------------- - -In previous versions of Bitcoin Core, a portion of each block could be reserved for transactions based on the age and value of UTXOs they spent. This concept (Coin Age Priority) is a policy choice by miners, and there are no consensus rules around the inclusion of Coin Age Priority transactions in blocks. In practice, only a few miners continue to use Coin Age Priority for transaction selection in blocks. Bitcoin Core 0.15 removes all remaining support for Coin Age Priority (See [PR 9602](https://github.com/bitcoin/bitcoin/pull/9602)). This has the following implications: - -- The concept of *free transactions* has been removed. High Coin Age Priority transactions would previously be allowed to be relayed even if they didn't attach a miner fee. This is no longer possible since there is no concept of Coin Age Priority. The `-limitfreerelay` and `-relaypriority` options which controlled relay of free transactions have therefore been removed. -- The `-sendfreetransactions` option has been removed, since almost all miners do not include transactions which do not attach a transaction fee. -- The `-blockprioritysize` option has been removed. -- The `estimatepriority` and `estimatesmartpriority` RPCs have been removed. -- The `getmempoolancestors`, `getmempooldescendants`, `getmempooolentry` and `getrawmempool` RPCs no longer return `startingpriority` and `currentpriority`. -- The `prioritisetransaction` RPC no longer takes a `priority_delta` argument, which is replaced by a `dummy` argument for backwards compatibility with clients using positional arguments. The RPC is still used to change the apparent fee-rate of the transaction by using the `fee_delta` argument. -- `-minrelaytxfee` can now be set to 0. If `minrelaytxfee` is set, then fees smaller than `minrelaytxfee` (per kB) are rejected from relaying, mining and transaction creation. This defaults to 1000 satoshi/kB. -- The `-printpriority` option has been updated to only output the fee rate and hash of transactions included in a block by the mining code. - -Mempool Persistence Across Restarts ------------------------------------ - -Version 0.14 introduced mempool persistence across restarts (the mempool is saved to a `mempool.dat` file in the data directory prior to shutdown and restores the mempool when the node is restarted). Version 0.15 allows this feature to be switched on or off using the `-persistmempool` command-line option (See [PR 9966](https://github.com/bitcoin/bitcoin/pull/9966)). By default, the option is set to true, and the mempool is saved on shutdown and reloaded on startup. If set to false, the `mempool.dat` file will not be loaded on startup or saved on shutdown. - -New RPC methods ---------------- - -Version 0.15 introduces several new RPC methods: - -- `abortrescan` stops current wallet rescan, e.g. when triggered by an `importprivkey` call (See [PR 10208](https://github.com/bitcoin/bitcoin/pull/10208)). -- `combinerawtransaction` accepts a JSON array of raw transactions and combines them into a single raw transaction (See [PR 10571](https://github.com/bitcoin/bitcoin/pull/10571)). -- `estimaterawfee` returns raw fee data so that customized logic can be implemented to analyze the data and calculate estimates. See [Fee Estimation Improvements](#fee-estimation-improvements) for full details on changes to the fee estimation logic and interface. -- `getchaintxstats` returns statistics about the total number and rate of transactions - in the chain (See [PR 9733](https://github.com/bitcoin/bitcoin/pull/9733)). -- `listwallets` lists wallets which are currently loaded. See the *Multi-wallet* section - of these release notes for full details (See [Multi-wallet support](#multi-wallet-support)). -- `uptime` returns the total runtime of the `bitcoind` server since its last start (See [PR 10400](https://github.com/bitcoin/bitcoin/pull/10400)). - -Low-level RPC changes ---------------------- - -- When using Bitcoin Core in multi-wallet mode, RPC requests for wallet methods must specify - the wallet that they're intended for. See [Multi-wallet support](#multi-wallet-support) for full details. - -- The new database model no longer stores information about transaction - versions of unspent outputs (See [Performance improvements](#performance-improvements)). This means that: - - The `gettxout` RPC no longer has a `version` field in the response. - - The `gettxoutsetinfo` RPC reports `hash_serialized_2` instead of `hash_serialized`, - which does not commit to the transaction versions of unspent outputs, but does - commit to the height and coinbase information. - - The `getutxos` REST path no longer reports the `txvers` field in JSON format, - and always reports 0 for transaction versions in the binary format - -- The `estimatefee` RPC is deprecated. Clients should switch to using the `estimatesmartfee` RPC, which returns better fee estimates. See [Fee Estimation Improvements](#fee-estimation-improvements) for full details on changes to the fee estimation logic and interface. - -- The `gettxoutsetinfo` response now contains `disk_size` and `bogosize` instead of - `bytes_serialized`. The first is a more accurate estimate of actual disk usage, but - is not deterministic. The second is unrelated to disk usage, but is a - database-independent metric of UTXO set size: it counts every UTXO entry as 50 + the - length of its scriptPubKey (See [PR 10426](https://github.com/bitcoin/bitcoin/pull/10426)). - -- `signrawtransaction` can no longer be used to combine multiple transactions into a single transaction. Instead, use the new `combinerawtransaction` RPC (See [PR 10571](https://github.com/bitcoin/bitcoin/pull/10571)). - -- `fundrawtransaction` no longer accepts a `reserveChangeKey` option. This option used to allow RPC users to fund a raw transaction using an key from the keypool for the change address without removing it from the available keys in the keypool. The key could then be re-used for a `getnewaddress` call, which could potentially result in confusing or dangerous behaviour (See [PR 10784](https://github.com/bitcoin/bitcoin/pull/10784)). - -- `estimatepriority` and `estimatesmartpriority` have been removed. See [Removal of Coin Age Priority](#removal-of-coin-age-priority). - -- The `listunspent` RPC now takes a `query_options` argument (see [PR 8952](https://github.com/bitcoin/bitcoin/pull/8952)), which is a JSON object - containing one or more of the following members: - - `minimumAmount` - a number specifying the minimum value of each UTXO - - `maximumAmount` - a number specifying the maximum value of each UTXO - - `maximumCount` - a number specifying the minimum number of UTXOs - - `minimumSumAmount` - a number specifying the minimum sum value of all UTXOs - -- The `getmempoolancestors`, `getmempooldescendants`, `getmempooolentry` and `getrawmempool` RPCs no longer return `startingpriority` and `currentpriority`. See [Removal of Coin Age Priority](#removal-of-coin-age-priority). - -- The `dumpwallet` RPC now returns the full absolute path to the dumped wallet. (it - used to return no value, even if successful (See [PR 9740](https://github.com/bitcoin/bitcoin/pull/9740)). - -- In the `getpeerinfo` RPC, the return object for each peer now returns an `addrbind` member, which contains the ip address and port of the connection to the peer. This is in addition to the `addrlocal` member which contains the ip address and port of the local node as reported by the peer (See [PR 10478](https://github.com/bitcoin/bitcoin/pull/10478)). - -- The `disconnectnode` RPC can now disconnect a node specified by node ID (as well as by IP address/port). To disconnect a node based on node ID, call the RPC with the new `nodeid` argument (See [PR 10143](https://github.com/bitcoin/bitcoin/pull/10143)). - -- The second argument in `prioritisetransaction` has been renamed from `priority_delta` to `dummy` since Bitcoin Core no longer has a concept of coin age priority. The `dummy` argument has no functional effect, but is retained for positional argument compatibility. See [Removal of Coin Age Priority](#removal-of-coin-age-priority). - -- The `resendwallettransactions` RPC throws an error if the `-walletbroadcast` option is set to false (See [PR 10995](https://github.com/bitcoin/bitcoin/pull/10995)). - -- The second argument in the `submitblock` RPC argument has been renamed from `parameters` to `dummy`. This argument never had any effect, and the renaming is simply to communicate this fact to the user (See [PR 10191](https://github.com/bitcoin/bitcoin/pull/10191)) - (Clients should, however, use positional arguments for `submitblock` in order to be compatible with BIP 22.) - -- The `verbose` argument of `getblock` has been renamed to `verbosity` and now takes an integer from 0-2. Verbose level 0 is equivalent to `verbose=false`. Verbose level 1 is equivalent to `verbose=true`. Verbose level 2 will give the full transaction details of each transaction in the output as given by `getrawtransaction`. The old behavior of using the `verbose` named argument and a boolean value is still maintained for compatibility. - -- Error codes have been updated to be more accurate for the following error cases (See [PR 9853](https://github.com/bitcoin/bitcoin/pull/9853)): - - `getblock` now returns RPC_MISC_ERROR if the block can't be found on disk (for - example if the block has been pruned). Previously returned RPC_INTERNAL_ERROR. - - `pruneblockchain` now returns RPC_MISC_ERROR if the blocks cannot be pruned - because the node is not in pruned mode. Previously returned RPC_METHOD_NOT_FOUND. - - `pruneblockchain` now returns RPC_INVALID_PARAMETER if the blocks cannot be pruned - because the supplied timestamp is too late. Previously returned RPC_INTERNAL_ERROR. - - `pruneblockchain` now returns RPC_MISC_ERROR if the blocks cannot be pruned - because the blockchain is too short. Previously returned RPC_INTERNAL_ERROR. - - `setban` now returns RPC_CLIENT_INVALID_IP_OR_SUBNET if the supplied IP address - or subnet is invalid. Previously returned RPC_CLIENT_NODE_ALREADY_ADDED. - - `setban` now returns RPC_CLIENT_INVALID_IP_OR_SUBNET if the user tries to unban - a node that has not previously been banned. Previously returned RPC_MISC_ERROR. - - `removeprunedfunds` now returns RPC_WALLET_ERROR if `bitcoind` is unable to remove - the transaction. Previously returned RPC_INTERNAL_ERROR. - - `removeprunedfunds` now returns RPC_INVALID_PARAMETER if the transaction does not - exist in the wallet. Previously returned RPC_INTERNAL_ERROR. - - `fundrawtransaction` now returns RPC_INVALID_ADDRESS_OR_KEY if an invalid change - address is provided. Previously returned RPC_INVALID_PARAMETER. - - `fundrawtransaction` now returns RPC_WALLET_ERROR if `bitcoind` is unable to create - the transaction. The error message provides further details. Previously returned - RPC_INTERNAL_ERROR. - - `bumpfee` now returns RPC_INVALID_PARAMETER if the provided transaction has - descendants in the wallet. Previously returned RPC_MISC_ERROR. - - `bumpfee` now returns RPC_INVALID_PARAMETER if the provided transaction has - descendants in the mempool. Previously returned RPC_MISC_ERROR. - - `bumpfee` now returns RPC_WALLET_ERROR if the provided transaction has - has been mined or conflicts with a mined transaction. Previously returned - RPC_INVALID_ADDRESS_OR_KEY. - - `bumpfee` now returns RPC_WALLET_ERROR if the provided transaction is not - BIP 125 replaceable. Previously returned RPC_INVALID_ADDRESS_OR_KEY. - - `bumpfee` now returns RPC_WALLET_ERROR if the provided transaction has already - been bumped by a different transaction. Previously returned RPC_INVALID_REQUEST. - - `bumpfee` now returns RPC_WALLET_ERROR if the provided transaction contains - inputs which don't belong to this wallet. Previously returned RPC_INVALID_ADDRESS_OR_KEY. - - `bumpfee` now returns RPC_WALLET_ERROR if the provided transaction has multiple change - outputs. Previously returned RPC_MISC_ERROR. - - `bumpfee` now returns RPC_WALLET_ERROR if the provided transaction has no change - output. Previously returned RPC_MISC_ERROR. - - `bumpfee` now returns RPC_WALLET_ERROR if the fee is too high. Previously returned - RPC_MISC_ERROR. - - `bumpfee` now returns RPC_WALLET_ERROR if the fee is too low. Previously returned - RPC_MISC_ERROR. - - `bumpfee` now returns RPC_WALLET_ERROR if the change output is too small to bump the - fee. Previously returned RPC_MISC_ERROR. - Credits ======= Thanks to everyone who directly contributed to this release: -- ロハン ダル -- Ahmad Kazi -- aideca -- Akio Nakamura -- Alex Morcos -- Allan Doensen -- Andres G. Aragoneses -- Andrew Chow -- Awemany -- Bob McElrath -- Brian McMichael -- BtcDrak -- Charlie Lee -- Chris Gavin -- Chris Stewart -- Cory Fields -- CryptAxe -- Dag Robole -- Daniel Aleksandersen -- Daniel Cousens -- darksh1ne -- Dimitris Tsapakidis -- Eric Shaw Jr -- Evan Klitzke -- fanquake -- Felix Weis -- flack -- Greg Griffith -- Gregory Maxwell -- Gregory Sanders -- gubatron -- Ian Kelling -- Jack Grigg -- James Evans -- James Hilliard -- Jameson Lopp -- Jeremy Rubin -- Jimmy Song -- João Barbosa -- Johnathan Corgan -- John Newbery -- Jonas Schnelli -- jonnynewbs -- Jorge Timón -- Kalle Alm -- Karl-Johan Alm -- Kewde -- keystrike -- KibbledJiveElkZoo -- Kibbled Jive Elk Zoo -- kirit93 -- kobake -- Kyle Honeycutt -- Lawrence Nahum -- Luke Dashjr -- Marco Falke -- Marcos Mayorga -- Marijn Stollenga -- Mario Dian -- Mark Friedenbach -- Marko Bencun -- Masahiko Hyuga -- Matt Corallo -- Matthew Zipkin -- Matthias Grundmann -- Michael Goldstein -- Michael Rotarius -- Mikerah -- Mike van Rossum -- Mitchell Cash -- NicolasDorier -- Nicolas Dorier -- Patrick Strateman -- Pavel Janík -- Pavlos Antoniou -- Pavol Rusnak -- Pedro Branco -- Peter Todd -- Pieter Wuille -- practicalswift -- René Nyffenegger -- Ricardo Velhote -- romanornr -- Russell Yanofsky -- Rusty Russell -- Ryan Havar -- shaolinfry -- Shigeya Suzuki -- Simone Madeo -- Spencer Lievens -- Steven D. Lander -- Suhas Daftuar -- Takashi Mitsuta -- Thomas Snider -- Timothy Redaelli -- tintinweb -- tnaka -- Warren Togami -- Wladimir J. van der Laan As well as everyone that helped translating on [Transifex](https://www.transifex.com/projects/p/bitcoin/). From 140de14a12228cf7af44f59f40a937c2910a0158 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 14 Aug 2017 17:37:50 +0200 Subject: [PATCH 061/382] gitian: bump descriptors for master Tree-SHA512: dc56ab285ea3fd293794341d7e2f8452730d3efb59f793112d4e1b036a051f9d221a7e577a460b426ecfb1578558203fa6a432efc62e6cabc534059719a2b437 --- contrib/gitian-descriptors/gitian-linux.yml | 2 +- contrib/gitian-descriptors/gitian-osx.yml | 2 +- contrib/gitian-descriptors/gitian-win.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml index 3da8510cf..0569c6a77 100644 --- a/contrib/gitian-descriptors/gitian-linux.yml +++ b/contrib/gitian-descriptors/gitian-linux.yml @@ -1,5 +1,5 @@ --- -name: "bitcoin-linux-0.15" +name: "bitcoin-linux-0.16" enable_cache: true suites: - "trusty" diff --git a/contrib/gitian-descriptors/gitian-osx.yml b/contrib/gitian-descriptors/gitian-osx.yml index 206db7c19..d31517358 100644 --- a/contrib/gitian-descriptors/gitian-osx.yml +++ b/contrib/gitian-descriptors/gitian-osx.yml @@ -1,5 +1,5 @@ --- -name: "bitcoin-osx-0.15" +name: "bitcoin-osx-0.16" enable_cache: true suites: - "trusty" diff --git a/contrib/gitian-descriptors/gitian-win.yml b/contrib/gitian-descriptors/gitian-win.yml index 1d4d70494..aba46df26 100644 --- a/contrib/gitian-descriptors/gitian-win.yml +++ b/contrib/gitian-descriptors/gitian-win.yml @@ -1,5 +1,5 @@ --- -name: "bitcoin-win-0.15" +name: "bitcoin-win-0.16" enable_cache: true suites: - "trusty" From 6fb8f5f17c079a7d32c855eae313c740e5f9e6be Mon Sep 17 00:00:00 2001 From: practicalswift Date: Thu, 27 Jul 2017 01:09:05 +0200 Subject: [PATCH 062/382] Check that -blocknotify command is non-empty before executing To make BlockNotifyCallback(...) (-blocknotify) consistent with: * AlertNotify(...) (-alertnotify) * AddToWallet(...) (-walletnotify) --- src/init.cpp | 7 ++++--- src/wallet/wallet.cpp | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index d79c2967b..873a87c00 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -547,9 +547,10 @@ static void BlockNotifyCallback(bool initialSync, const CBlockIndex *pBlockIndex return; std::string strCmd = gArgs.GetArg("-blocknotify", ""); - - boost::replace_all(strCmd, "%s", pBlockIndex->GetBlockHash().GetHex()); - boost::thread t(runCommand, strCmd); // thread runs free + if (!strCmd.empty()) { + boost::replace_all(strCmd, "%s", pBlockIndex->GetBlockHash().GetHex()); + boost::thread t(runCommand, strCmd); // thread runs free + } } static bool fHaveGenesis = false; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 599e74149..41ecec9b6 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -988,7 +988,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) // notify an external script when a wallet transaction comes in or is updated std::string strCmd = gArgs.GetArg("-walletnotify", ""); - if ( !strCmd.empty()) + if (!strCmd.empty()) { boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex()); boost::thread t(runCommand, strCmd); // thread runs free From cffe85f975413441b8fbc5bda82fd2c9d75476f5 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Thu, 27 Jul 2017 11:35:01 +0200 Subject: [PATCH 063/382] Skip sys::system(...) call in case of empty command --- src/util.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util.cpp b/src/util.cpp index ba563478f..be76f2696 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -812,6 +812,7 @@ fs::path GetSpecialFolderPath(int nFolder, bool fCreate) void runCommand(const std::string& strCommand) { + if (strCommand.empty()) return; int nErr = ::system(strCommand.c_str()); if (nErr) LogPrintf("runCommand error: system(%s) returned %d\n", strCommand, nErr); From a897d0e37a02d29907c3a3f0f6536a26a155751d Mon Sep 17 00:00:00 2001 From: practicalswift Date: Fri, 11 Aug 2017 09:24:26 +0200 Subject: [PATCH 064/382] tests: Remove OldSetKeyFromPassphrase/OldEncrypt/OldDecrypt --- src/wallet/test/crypto_tests.cpp | 133 +++---------------------------- 1 file changed, 11 insertions(+), 122 deletions(-) diff --git a/src/wallet/test/crypto_tests.cpp b/src/wallet/test/crypto_tests.cpp index 98d957b32..cbd74b6f9 100644 --- a/src/wallet/test/crypto_tests.cpp +++ b/src/wallet/test/crypto_tests.cpp @@ -9,86 +9,9 @@ #include #include -#include -#include BOOST_FIXTURE_TEST_SUITE(wallet_crypto, BasicTestingSetup) -bool OldSetKeyFromPassphrase(const SecureString& strKeyData, const std::vector& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod, unsigned char* chKey, unsigned char* chIV) -{ - if (nRounds < 1 || chSalt.size() != WALLET_CRYPTO_SALT_SIZE) - return false; - - int i = 0; - if (nDerivationMethod == 0) - i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha512(), &chSalt[0], - (unsigned char *)&strKeyData[0], strKeyData.size(), nRounds, chKey, chIV); - - if (i != (int)WALLET_CRYPTO_KEY_SIZE) - { - memory_cleanse(chKey, WALLET_CRYPTO_KEY_SIZE); - memory_cleanse(chIV, WALLET_CRYPTO_IV_SIZE); - return false; - } - return true; -} - -bool OldEncrypt(const CKeyingMaterial& vchPlaintext, std::vector &vchCiphertext, const unsigned char chKey[32], const unsigned char chIV[16]) -{ - // max ciphertext len for a n bytes of plaintext is - // n + AES_BLOCK_SIZE - 1 bytes - int nLen = vchPlaintext.size(); - int nCLen = nLen + AES_BLOCK_SIZE, nFLen = 0; - vchCiphertext = std::vector (nCLen); - - EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); - - if (!ctx) return false; - - bool fOk = true; - - EVP_CIPHER_CTX_init(ctx); - if (fOk) fOk = EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, chKey, chIV) != 0; - if (fOk) fOk = EVP_EncryptUpdate(ctx, &vchCiphertext[0], &nCLen, &vchPlaintext[0], nLen) != 0; - if (fOk) fOk = EVP_EncryptFinal_ex(ctx, (&vchCiphertext[0]) + nCLen, &nFLen) != 0; - EVP_CIPHER_CTX_cleanup(ctx); - - EVP_CIPHER_CTX_free(ctx); - - if (!fOk) return false; - - vchCiphertext.resize(nCLen + nFLen); - return true; -} - -bool OldDecrypt(const std::vector& vchCiphertext, CKeyingMaterial& vchPlaintext, const unsigned char chKey[32], const unsigned char chIV[16]) -{ - // plaintext will always be equal to or lesser than length of ciphertext - int nLen = vchCiphertext.size(); - int nPLen = nLen, nFLen = 0; - - vchPlaintext = CKeyingMaterial(nPLen); - - EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); - - if (!ctx) return false; - - bool fOk = true; - - EVP_CIPHER_CTX_init(ctx); - if (fOk) fOk = EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, chKey, chIV) != 0; - if (fOk) fOk = EVP_DecryptUpdate(ctx, &vchPlaintext[0], &nPLen, &vchCiphertext[0], nLen) != 0; - if (fOk) fOk = EVP_DecryptFinal_ex(ctx, (&vchPlaintext[0]) + nPLen, &nFLen) != 0; - EVP_CIPHER_CTX_cleanup(ctx); - - EVP_CIPHER_CTX_free(ctx); - - if (!fOk) return false; - - vchPlaintext.resize(nPLen + nFLen); - return true; -} - class TestCrypter { public: @@ -96,25 +19,15 @@ static void TestPassphraseSingle(const std::vector& vchSalt, cons const std::vector& correctKey = std::vector(), const std::vector& correctIV=std::vector()) { - unsigned char chKey[WALLET_CRYPTO_KEY_SIZE]; - unsigned char chIV[WALLET_CRYPTO_IV_SIZE]; - CCrypter crypt; crypt.SetKeyFromPassphrase(passphrase, vchSalt, rounds, 0); - OldSetKeyFromPassphrase(passphrase, vchSalt, rounds, 0, chKey, chIV); - - BOOST_CHECK_MESSAGE(memcmp(chKey, crypt.vchKey.data(), crypt.vchKey.size()) == 0, \ - HexStr(chKey, chKey+sizeof(chKey)) + std::string(" != ") + HexStr(crypt.vchKey)); - BOOST_CHECK_MESSAGE(memcmp(chIV, crypt.vchIV.data(), crypt.vchIV.size()) == 0, \ - HexStr(chIV, chIV+sizeof(chIV)) + std::string(" != ") + HexStr(crypt.vchIV)); - if(!correctKey.empty()) - BOOST_CHECK_MESSAGE(memcmp(chKey, &correctKey[0], sizeof(chKey)) == 0, \ - HexStr(chKey, chKey+sizeof(chKey)) + std::string(" != ") + HexStr(correctKey.begin(), correctKey.end())); + BOOST_CHECK_MESSAGE(memcmp(crypt.vchKey.data(), correctKey.data(), crypt.vchKey.size()) == 0, \ + HexStr(crypt.vchKey.begin(), crypt.vchKey.end()) + std::string(" != ") + HexStr(correctKey.begin(), correctKey.end())); if(!correctIV.empty()) - BOOST_CHECK_MESSAGE(memcmp(chIV, &correctIV[0], sizeof(chIV)) == 0, - HexStr(chIV, chIV+sizeof(chIV)) + std::string(" != ") + HexStr(correctIV.begin(), correctIV.end())); + BOOST_CHECK_MESSAGE(memcmp(crypt.vchIV.data(), correctIV.data(), crypt.vchIV.size()) == 0, + HexStr(crypt.vchIV.begin(), crypt.vchIV.end()) + std::string(" != ") + HexStr(correctIV.begin(), correctIV.end())); } static void TestPassphrase(const std::vector& vchSalt, const SecureString& passphrase, uint32_t rounds, @@ -126,50 +39,26 @@ static void TestPassphrase(const std::vector& vchSalt, const Secu TestPassphraseSingle(vchSalt, SecureString(i, passphrase.end()), rounds); } - static void TestDecrypt(const CCrypter& crypt, const std::vector& vchCiphertext, \ const std::vector& vchPlaintext = std::vector()) { - CKeyingMaterial vchDecrypted1; - CKeyingMaterial vchDecrypted2; - int result1, result2; - result1 = crypt.Decrypt(vchCiphertext, vchDecrypted1); - result2 = OldDecrypt(vchCiphertext, vchDecrypted2, crypt.vchKey.data(), crypt.vchIV.data()); - BOOST_CHECK(result1 == result2); - - // These two should be equal. However, OpenSSL 1.0.1j introduced a change - // that would zero all padding except for the last byte for failed decrypts. - // This behavior was reverted for 1.0.1k. - if (vchDecrypted1 != vchDecrypted2 && vchDecrypted1.size() >= AES_BLOCK_SIZE && SSLeay() == 0x100010afL) - { - for(CKeyingMaterial::iterator it = vchDecrypted1.end() - AES_BLOCK_SIZE; it != vchDecrypted1.end() - 1; it++) - *it = 0; - } - - BOOST_CHECK_MESSAGE(vchDecrypted1 == vchDecrypted2, HexStr(vchDecrypted1.begin(), vchDecrypted1.end()) + " != " + HexStr(vchDecrypted2.begin(), vchDecrypted2.end())); - + CKeyingMaterial vchDecrypted; + crypt.Decrypt(vchCiphertext, vchDecrypted); if (vchPlaintext.size()) - BOOST_CHECK(CKeyingMaterial(vchPlaintext.begin(), vchPlaintext.end()) == vchDecrypted2); + BOOST_CHECK(CKeyingMaterial(vchPlaintext.begin(), vchPlaintext.end()) == vchDecrypted); } static void TestEncryptSingle(const CCrypter& crypt, const CKeyingMaterial& vchPlaintext, const std::vector& vchCiphertextCorrect = std::vector()) { - std::vector vchCiphertext1; - std::vector vchCiphertext2; - int result1 = crypt.Encrypt(vchPlaintext, vchCiphertext1); - - int result2 = OldEncrypt(vchPlaintext, vchCiphertext2, crypt.vchKey.data(), crypt.vchIV.data()); - BOOST_CHECK(result1 == result2); - BOOST_CHECK(vchCiphertext1 == vchCiphertext2); + std::vector vchCiphertext; + crypt.Encrypt(vchPlaintext, vchCiphertext); if (!vchCiphertextCorrect.empty()) - BOOST_CHECK(vchCiphertext2 == vchCiphertextCorrect); + BOOST_CHECK(vchCiphertext == vchCiphertextCorrect); const std::vector vchPlaintext2(vchPlaintext.begin(), vchPlaintext.end()); - - if(vchCiphertext1 == vchCiphertext2) - TestDecrypt(crypt, vchCiphertext1, vchPlaintext2); + TestDecrypt(crypt, vchCiphertext, vchPlaintext2); } static void TestEncrypt(const CCrypter& crypt, const std::vector& vchPlaintextIn, \ From c06755f5cf0a774813d37f100655ed1974b4fcf1 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Tue, 8 Aug 2017 17:37:11 +0200 Subject: [PATCH 065/382] wallet: Fix memory leak when loading a corrupted wallet file --- src/wallet/wallet.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 599e74149..3f1de5714 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3937,15 +3937,12 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) uiInterface.InitMessage(_("Zapping all transactions from wallet...")); std::unique_ptr dbw(new CWalletDBWrapper(&bitdb, walletFile)); - CWallet *tempWallet = new CWallet(std::move(dbw)); + std::unique_ptr tempWallet(new CWallet(std::move(dbw))); DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx); if (nZapWalletRet != DB_LOAD_OK) { InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile)); return nullptr; } - - delete tempWallet; - tempWallet = nullptr; } uiInterface.InitMessage(_("Loading wallet...")); From 8f2f1e0458d263dc9b51caad0adc0246e3580114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= Date: Sun, 13 Aug 2017 15:04:57 +0100 Subject: [PATCH 066/382] wallet: Avoid second mapWallet lookup --- src/qt/walletmodel.cpp | 7 +++--- src/wallet/feebumper.cpp | 9 ++++---- src/wallet/rpcwallet.cpp | 10 ++++---- src/wallet/wallet.cpp | 49 ++++++++++++++++++++++++---------------- 4 files changed, 44 insertions(+), 31 deletions(-) diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index ba0e1da0c..d29b5c92a 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -577,10 +577,11 @@ void WalletModel::getOutputs(const std::vector& vOutpoints, std::vect LOCK2(cs_main, wallet->cs_wallet); for (const COutPoint& outpoint : vOutpoints) { - if (!wallet->mapWallet.count(outpoint.hash)) continue; - int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain(); + auto it = wallet->mapWallet.find(outpoint.hash); + if (it == wallet->mapWallet.end()) continue; + int nDepth = it->second.GetDepthInMainChain(); if (nDepth < 0) continue; - COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true /* spendable */, true /* solvable */, true /* safe */); + COutput out(&it->second, outpoint.n, nDepth, true /* spendable */, true /* solvable */, true /* safe */); vOutputs.push_back(out); } } diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index 4bfd8726a..5e84597f0 100644 --- a/src/wallet/feebumper.cpp +++ b/src/wallet/feebumper.cpp @@ -76,12 +76,12 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, const CCoin vErrors.clear(); bumpedTxid.SetNull(); AssertLockHeld(pWallet->cs_wallet); - if (!pWallet->mapWallet.count(txid)) { + auto it = pWallet->mapWallet.find(txid); + if (it == pWallet->mapWallet.end()) { vErrors.push_back("Invalid or non-wallet transaction id"); currentResult = BumpFeeResult::INVALID_ADDRESS_OR_KEY; return; } - auto it = pWallet->mapWallet.find(txid); const CWalletTx& wtx = it->second; if (!preconditionChecks(pWallet, wtx)) { @@ -241,12 +241,13 @@ bool CFeeBumper::commit(CWallet *pWallet) if (!vErrors.empty() || currentResult != BumpFeeResult::OK) { return false; } - if (txid.IsNull() || !pWallet->mapWallet.count(txid)) { + auto it = txid.IsNull() ? pWallet->mapWallet.end() : pWallet->mapWallet.find(txid); + if (it == pWallet->mapWallet.end()) { vErrors.push_back("Invalid or non-wallet transaction id"); currentResult = BumpFeeResult::MISC_ERROR; return false; } - CWalletTx& oldWtx = pWallet->mapWallet[txid]; + CWalletTx& oldWtx = it->second; // make sure the transaction still has no descendants and hasn't been mined in the meantime if (!preconditionChecks(pWallet, oldWtx)) { diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 057379d8d..e04b6f7ca 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1874,10 +1874,11 @@ UniValue listsinceblock(const JSONRPCRequest& request) throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); } for (const CTransactionRef& tx : block.vtx) { - if (pwallet->mapWallet.count(tx->GetHash()) > 0) { + auto it = pwallet->mapWallet.find(tx->GetHash()); + if (it != pwallet->mapWallet.end()) { // We want all transactions regardless of confirmation count to appear here, // even negative confirmation ones, hence the big negative. - ListTransactions(pwallet, pwallet->mapWallet[tx->GetHash()], "*", -100000000, true, removed, filter); + ListTransactions(pwallet, it->second, "*", -100000000, true, removed, filter); } } paltindex = paltindex->pprev; @@ -1957,10 +1958,11 @@ UniValue gettransaction(const JSONRPCRequest& request) filter = filter | ISMINE_WATCH_ONLY; UniValue entry(UniValue::VOBJ); - if (!pwallet->mapWallet.count(hash)) { + auto it = pwallet->mapWallet.find(hash); + if (it == pwallet->mapWallet.end()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); } - const CWalletTx& wtx = pwallet->mapWallet[hash]; + const CWalletTx& wtx = it->second; CAmount nCredit = wtx.GetCredit(filter); CAmount nDebit = wtx.GetDebit(filter); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 18cc3bd02..d839c9a82 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -590,8 +590,9 @@ void CWallet::AddToSpends(const COutPoint& outpoint, const uint256& wtxid) void CWallet::AddToSpends(const uint256& wtxid) { - assert(mapWallet.count(wtxid)); - CWalletTx& thisTx = mapWallet[wtxid]; + auto it = mapWallet.find(wtxid); + assert(it != mapWallet.end()); + CWalletTx& thisTx = it->second; if (thisTx.IsCoinBase()) // Coinbases don't spend anything! return; @@ -974,8 +975,9 @@ bool CWallet::LoadToWallet(const CWalletTx& wtxIn) wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0))); AddToSpends(hash); for (const CTxIn& txin : wtx.tx->vin) { - if (mapWallet.count(txin.prevout.hash)) { - CWalletTx& prevtx = mapWallet[txin.prevout.hash]; + auto it = mapWallet.find(txin.prevout.hash); + if (it != mapWallet.end()) { + CWalletTx& prevtx = it->second; if (prevtx.nIndex == -1 && !prevtx.hashUnset()) { MarkConflicted(prevtx.hashBlock, wtx.GetHash()); } @@ -1050,8 +1052,9 @@ bool CWallet::AbandonTransaction(const uint256& hashTx) std::set done; // Can't mark abandoned if confirmed or in mempool - assert(mapWallet.count(hashTx)); - CWalletTx& origtx = mapWallet[hashTx]; + auto it = mapWallet.find(hashTx); + assert(it != mapWallet.end()); + CWalletTx& origtx = it->second; if (origtx.GetDepthInMainChain() > 0 || origtx.InMempool()) { return false; } @@ -1062,8 +1065,9 @@ bool CWallet::AbandonTransaction(const uint256& hashTx) uint256 now = *todo.begin(); todo.erase(now); done.insert(now); - assert(mapWallet.count(now)); - CWalletTx& wtx = mapWallet[now]; + auto it = mapWallet.find(now); + assert(it != mapWallet.end()); + CWalletTx& wtx = it->second; int currentconfirm = wtx.GetDepthInMainChain(); // If the orig tx was not in block, none of its spends can be assert(currentconfirm <= 0); @@ -1088,8 +1092,10 @@ bool CWallet::AbandonTransaction(const uint256& hashTx) // available of the outputs it spends. So force those to be recomputed for (const CTxIn& txin : wtx.tx->vin) { - if (mapWallet.count(txin.prevout.hash)) - mapWallet[txin.prevout.hash].MarkDirty(); + auto it = mapWallet.find(txin.prevout.hash); + if (it != mapWallet.end()) { + it->second.MarkDirty(); + } } } } @@ -1127,8 +1133,9 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx) uint256 now = *todo.begin(); todo.erase(now); done.insert(now); - assert(mapWallet.count(now)); - CWalletTx& wtx = mapWallet[now]; + auto it = mapWallet.find(now); + assert(it != mapWallet.end()); + CWalletTx& wtx = it->second; int currentconfirm = wtx.GetDepthInMainChain(); if (conflictconfirms < currentconfirm) { // Block is 'more conflicted' than current confirm; update. @@ -1147,10 +1154,11 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx) } // If a transaction changes 'conflicted' state, that changes the balance // available of the outputs it spends. So force those to be recomputed - for (const CTxIn& txin : wtx.tx->vin) - { - if (mapWallet.count(txin.prevout.hash)) - mapWallet[txin.prevout.hash].MarkDirty(); + for (const CTxIn& txin : wtx.tx->vin) { + auto it = mapWallet.find(txin.prevout.hash); + if (it != mapWallet.end()) { + it->second.MarkDirty(); + } } } } @@ -1165,10 +1173,11 @@ void CWallet::SyncTransaction(const CTransactionRef& ptx, const CBlockIndex *pin // If a transaction changes 'conflicted' state, that changes the balance // available of the outputs it spends. So force those to be // recomputed, also: - for (const CTxIn& txin : tx.vin) - { - if (mapWallet.count(txin.prevout.hash)) - mapWallet[txin.prevout.hash].MarkDirty(); + for (const CTxIn& txin : tx.vin) { + auto it = mapWallet.find(txin.prevout.hash); + if (it != mapWallet.end()) { + it->second.MarkDirty(); + } } } From e666efcdba527a58175f9de3357dd19bb5880178 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Mon, 14 Aug 2017 19:38:18 -0400 Subject: [PATCH 067/382] Get rid of redundant RPC params.size() checks No change in behavior. --- src/rpc/blockchain.cpp | 4 ++-- src/rpc/mining.cpp | 2 +- src/rpc/misc.cpp | 6 +++--- src/rpc/net.cpp | 8 ++++---- src/rpc/rawtransaction.cpp | 10 +++++----- src/wallet/rpcwallet.cpp | 36 ++++++++++++++++++------------------ 6 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 24e0405a8..cc06cee6f 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1489,11 +1489,11 @@ UniValue getchaintxstats(const JSONRPCRequest& request) const CBlockIndex* pindex; int blockcount = 30 * 24 * 60 * 60 / Params().GetConsensus().nPowTargetSpacing; // By default: 1 month - if (request.params.size() > 0 && !request.params[0].isNull()) { + if (!request.params[0].isNull()) { blockcount = request.params[0].get_int(); } - bool havehash = request.params.size() > 1 && !request.params[1].isNull(); + bool havehash = !request.params[1].isNull(); uint256 hash; if (havehash) { hash = uint256S(request.params[1].get_str()); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index f498b5c8e..a6950899f 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -842,7 +842,7 @@ UniValue estimatesmartfee(const JSONRPCRequest& request) RPCTypeCheckArgument(request.params[0], UniValue::VNUM); unsigned int conf_target = ParseConfirmTarget(request.params[0]); bool conservative = true; - if (request.params.size() > 1 && !request.params[1].isNull()) { + if (!request.params[1].isNull()) { FeeEstimateMode fee_mode; if (!FeeModeFromString(request.params[1].get_str(), fee_mode)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter"); diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index efff4a99a..6917f4b34 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -552,7 +552,7 @@ UniValue getmemoryinfo(const JSONRPCRequest& request) + HelpExampleRpc("getmemoryinfo", "") ); - std::string mode = (request.params.size() < 1 || request.params[0].isNull()) ? "stats" : request.params[0].get_str(); + std::string mode = request.params[0].isNull() ? "stats" : request.params[0].get_str(); if (mode == "stats") { UniValue obj(UniValue::VOBJ); obj.push_back(Pair("locked", RPCLockedMemoryInfo())); @@ -603,11 +603,11 @@ UniValue logging(const JSONRPCRequest& request) } uint32_t originalLogCategories = logCategories; - if (request.params.size() > 0 && request.params[0].isArray()) { + if (request.params[0].isArray()) { logCategories |= getCategoryMask(request.params[0]); } - if (request.params.size() > 1 && request.params[1].isArray()) { + if (request.params[1].isArray()) { logCategories &= ~getCategoryMask(request.params[1]); } diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index e463a4eda..e6b210a19 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -258,7 +258,7 @@ UniValue disconnectnode(const JSONRPCRequest& request) bool success; const UniValue &address_arg = request.params[0]; - const UniValue &id_arg = request.params.size() < 2 ? NullUniValue : request.params[1]; + const UniValue &id_arg = request.params[1]; if (!address_arg.isNull() && id_arg.isNull()) { /* handle disconnect-by-address */ @@ -311,7 +311,7 @@ UniValue getaddednodeinfo(const JSONRPCRequest& request) std::vector vInfo = g_connman->GetAddedNodeInfo(); - if (request.params.size() == 1 && !request.params[0].isNull()) { + if (!request.params[0].isNull()) { bool found = false; for (const AddedNodeInfo& info : vInfo) { if (info.strAddedNode == request.params[0].get_str()) { @@ -534,11 +534,11 @@ UniValue setban(const JSONRPCRequest& request) throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: IP/Subnet already banned"); int64_t banTime = 0; //use standard bantime if not specified - if (request.params.size() >= 3 && !request.params[2].isNull()) + if (!request.params[2].isNull()) banTime = request.params[2].get_int64(); bool absolute = false; - if (request.params.size() == 4 && request.params[3].isTrue()) + if (request.params[3].isTrue()) absolute = true; isSubnet ? g_connman->Ban(subNet, BanReasonManuallyAdded, banTime, absolute) : g_connman->Ban(netAddr, BanReasonManuallyAdded, banTime, absolute); diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 810051185..9d9fef664 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -339,14 +339,14 @@ UniValue createrawtransaction(const JSONRPCRequest& request) CMutableTransaction rawTx; - if (request.params.size() > 2 && !request.params[2].isNull()) { + if (!request.params[2].isNull()) { int64_t nLockTime = request.params[2].get_int64(); if (nLockTime < 0 || nLockTime > std::numeric_limits::max()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range"); rawTx.nLockTime = nLockTime; } - bool rbfOptIn = request.params.size() > 3 ? request.params[3].isTrue() : false; + bool rbfOptIn = request.params[3].isTrue(); for (unsigned int idx = 0; idx < inputs.size(); idx++) { const UniValue& input = inputs[idx]; @@ -735,7 +735,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request) bool fGivenKeys = false; CBasicKeyStore tempKeystore; - if (request.params.size() > 2 && !request.params[2].isNull()) { + if (!request.params[2].isNull()) { fGivenKeys = true; UniValue keys = request.params[2].get_array(); for (unsigned int idx = 0; idx < keys.size(); idx++) { @@ -757,7 +757,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request) #endif // Add previous txouts given in the RPC call: - if (request.params.size() > 1 && !request.params[1].isNull()) { + if (!request.params[1].isNull()) { UniValue prevTxs = request.params[1].get_array(); for (unsigned int idx = 0; idx < prevTxs.size(); idx++) { const UniValue& p = prevTxs[idx]; @@ -828,7 +828,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request) #endif int nHashType = SIGHASH_ALL; - if (request.params.size() > 3 && !request.params[3].isNull()) { + if (!request.params[3].isNull()) { static std::map mapSigHashValues = { {std::string("ALL"), int(SIGHASH_ALL)}, {std::string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY)}, diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index a6176c348..00cb99ada 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -462,26 +462,26 @@ UniValue sendtoaddress(const JSONRPCRequest& request) // Wallet comments CWalletTx wtx; - if (request.params.size() > 2 && !request.params[2].isNull() && !request.params[2].get_str().empty()) + if (!request.params[2].isNull() && !request.params[2].get_str().empty()) wtx.mapValue["comment"] = request.params[2].get_str(); - if (request.params.size() > 3 && !request.params[3].isNull() && !request.params[3].get_str().empty()) + if (!request.params[3].isNull() && !request.params[3].get_str().empty()) wtx.mapValue["to"] = request.params[3].get_str(); bool fSubtractFeeFromAmount = false; - if (request.params.size() > 4 && !request.params[4].isNull()) { + if (!request.params[4].isNull()) { fSubtractFeeFromAmount = request.params[4].get_bool(); } CCoinControl coin_control; - if (request.params.size() > 5 && !request.params[5].isNull()) { + if (!request.params[5].isNull()) { coin_control.signalRbf = request.params[5].get_bool(); } - if (request.params.size() > 6 && !request.params[6].isNull()) { + if (!request.params[6].isNull()) { coin_control.m_confirm_target = ParseConfirmTarget(request.params[6]); } - if (request.params.size() > 7 && !request.params[7].isNull()) { + if (!request.params[7].isNull()) { if (!FeeModeFromString(request.params[7].get_str(), coin_control.m_fee_mode)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter"); } @@ -904,9 +904,9 @@ UniValue sendfrom(const JSONRPCRequest& request) CWalletTx wtx; wtx.strFromAccount = strAccount; - if (request.params.size() > 4 && !request.params[4].isNull() && !request.params[4].get_str().empty()) + if (!request.params[4].isNull() && !request.params[4].get_str().empty()) wtx.mapValue["comment"] = request.params[4].get_str(); - if (request.params.size() > 5 && !request.params[5].isNull() && !request.params[5].get_str().empty()) + if (!request.params[5].isNull() && !request.params[5].get_str().empty()) wtx.mapValue["to"] = request.params[5].get_str(); EnsureWalletIsUnlocked(pwallet); @@ -986,23 +986,23 @@ UniValue sendmany(const JSONRPCRequest& request) CWalletTx wtx; wtx.strFromAccount = strAccount; - if (request.params.size() > 3 && !request.params[3].isNull() && !request.params[3].get_str().empty()) + if (!request.params[3].isNull() && !request.params[3].get_str().empty()) wtx.mapValue["comment"] = request.params[3].get_str(); UniValue subtractFeeFromAmount(UniValue::VARR); - if (request.params.size() > 4 && !request.params[4].isNull()) + if (!request.params[4].isNull()) subtractFeeFromAmount = request.params[4].get_array(); CCoinControl coin_control; - if (request.params.size() > 5 && !request.params[5].isNull()) { + if (!request.params[5].isNull()) { coin_control.signalRbf = request.params[5].get_bool(); } - if (request.params.size() > 6 && !request.params[6].isNull()) { + if (!request.params[6].isNull()) { coin_control.m_confirm_target = ParseConfirmTarget(request.params[6]); } - if (request.params.size() > 7 && !request.params[7].isNull()) { + if (!request.params[7].isNull()) { if (!FeeModeFromString(request.params[7].get_str(), coin_control.m_fee_mode)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter"); } @@ -2670,19 +2670,19 @@ UniValue listunspent(const JSONRPCRequest& request) ); int nMinDepth = 1; - if (request.params.size() > 0 && !request.params[0].isNull()) { + if (!request.params[0].isNull()) { RPCTypeCheckArgument(request.params[0], UniValue::VNUM); nMinDepth = request.params[0].get_int(); } int nMaxDepth = 9999999; - if (request.params.size() > 1 && !request.params[1].isNull()) { + if (!request.params[1].isNull()) { RPCTypeCheckArgument(request.params[1], UniValue::VNUM); nMaxDepth = request.params[1].get_int(); } std::set setAddress; - if (request.params.size() > 2 && !request.params[2].isNull()) { + if (!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++) { @@ -2697,7 +2697,7 @@ UniValue listunspent(const JSONRPCRequest& request) } bool include_unsafe = true; - if (request.params.size() > 3 && !request.params[3].isNull()) { + if (!request.params[3].isNull()) { RPCTypeCheckArgument(request.params[3], UniValue::VBOOL); include_unsafe = request.params[3].get_bool(); } @@ -3112,7 +3112,7 @@ UniValue generate(const JSONRPCRequest& request) int num_generate = request.params[0].get_int(); uint64_t max_tries = 1000000; - if (request.params.size() > 1 && !request.params[1].isNull()) { + if (!request.params[1].isNull()) { max_tries = request.params[1].get_int(); } From e067673f4ea7a74b7251282b48ea9ca57416533a Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Mon, 14 Aug 2017 19:44:02 -0400 Subject: [PATCH 068/382] Avoid treating null RPC arguments different from missing arguments This changes RPC methods to treat null arguments the same as missing arguments, instead of throwing type errors. Specifically: - `getbalance` method now returns the wallet balance when the `account` param is null instead of throwing a type error (same as when parameter is missing). It is still an error to supply `minconf` or `watchonly` options when the account is null. - `addnode` and `setban` methods now return help text instead of type errors if `command` params are null (same as when params are missing). - `sendrawtransaction`, `setaccount`, `movecmd`, `sendfrom`, `addmultisigaddress`, `listaccounts`, `lockunspent` methods accept null default values where missing values were previously allowed, and treat them the same. --- src/rpc/net.cpp | 4 ++-- src/rpc/rawtransaction.cpp | 2 +- src/wallet/rpcwallet.cpp | 25 ++++++++++++------------- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index e6b210a19..f19b96824 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -193,7 +193,7 @@ UniValue getpeerinfo(const JSONRPCRequest& request) UniValue addnode(const JSONRPCRequest& request) { std::string strCommand; - if (request.params.size() == 2) + if (!request.params[1].isNull()) strCommand = request.params[1].get_str(); if (request.fHelp || request.params.size() != 2 || (strCommand != "onetry" && strCommand != "add" && strCommand != "remove")) @@ -490,7 +490,7 @@ UniValue getnetworkinfo(const JSONRPCRequest& request) UniValue setban(const JSONRPCRequest& request) { std::string strCommand; - if (request.params.size() >= 2) + if (!request.params[1].isNull()) strCommand = request.params[1].get_str(); if (request.fHelp || request.params.size() < 2 || (strCommand != "add" && strCommand != "remove")) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 9d9fef664..10fc9c162 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -922,7 +922,7 @@ UniValue sendrawtransaction(const JSONRPCRequest& request) const uint256& hashTx = tx->GetHash(); CAmount nMaxRawTxFee = maxTxFee; - if (request.params.size() > 1 && request.params[1].get_bool()) + if (!request.params[1].isNull() && request.params[1].get_bool()) nMaxRawTxFee = 0; CCoinsViewCache &view = *pcoinsTip; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 00cb99ada..275173aa4 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -280,7 +280,7 @@ UniValue setaccount(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); std::string strAccount; - if (request.params.size() > 1) + if (!request.params[1].isNull()) strAccount = AccountFromValue(request.params[1]); // Only add the account if the address is yours. @@ -768,7 +768,7 @@ UniValue getbalance(const JSONRPCRequest& request) LOCK2(cs_main, pwallet->cs_wallet); - if (request.params.size() == 0) + if (request.params[0].isNull() && request.params[1].isNull() && request.params[2].isNull()) return ValueFromAmount(pwallet->GetBalance()); const std::string& account_param = request.params[0].get_str(); @@ -838,11 +838,11 @@ UniValue movecmd(const JSONRPCRequest& request) CAmount nAmount = AmountFromValue(request.params[2]); if (nAmount <= 0) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); - if (request.params.size() > 3) + if (!request.params[3].isNull()) // unused parameter, used to be nMinDepth, keep type-checking it though (void)request.params[3].get_int(); std::string strComment; - if (request.params.size() > 4) + if (!request.params[4].isNull()) strComment = request.params[4].get_str(); if (!pwallet->AccountMove(strFrom, strTo, nAmount, strComment)) { @@ -899,7 +899,7 @@ UniValue sendfrom(const JSONRPCRequest& request) if (nAmount <= 0) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); int nMinDepth = 1; - if (request.params.size() > 3) + if (!request.params[3].isNull()) nMinDepth = request.params[3].get_int(); CWalletTx wtx; @@ -1105,7 +1105,7 @@ UniValue addmultisigaddress(const JSONRPCRequest& request) LOCK2(cs_main, pwallet->cs_wallet); std::string strAccount; - if (request.params.size() > 2) + if (!request.params[2].isNull()) strAccount = AccountFromValue(request.params[2]); // Construct using pay-to-script-hash: @@ -1711,10 +1711,10 @@ UniValue listaccounts(const JSONRPCRequest& request) LOCK2(cs_main, pwallet->cs_wallet); int nMinDepth = 1; - if (request.params.size() > 0) + if (!request.params[0].isNull()) nMinDepth = request.params[0].get_int(); isminefilter includeWatchonly = ISMINE_SPENDABLE; - if(request.params.size() > 1) + if(!request.params[1].isNull()) if(request.params[1].get_bool()) includeWatchonly = includeWatchonly | ISMINE_WATCH_ONLY; @@ -2361,19 +2361,18 @@ UniValue lockunspent(const JSONRPCRequest& request) LOCK2(cs_main, pwallet->cs_wallet); - if (request.params.size() == 1) - RPCTypeCheck(request.params, {UniValue::VBOOL}); - else - RPCTypeCheck(request.params, {UniValue::VBOOL, UniValue::VARR}); + RPCTypeCheckArgument(request.params[0], UniValue::VBOOL); bool fUnlock = request.params[0].get_bool(); - if (request.params.size() == 1) { + if (request.params[1].isNull()) { if (fUnlock) pwallet->UnlockAllCoins(); return true; } + RPCTypeCheckArgument(request.params[1], UniValue::VARR); + UniValue outputs = request.params[1].get_array(); for (unsigned int idx = 0; idx < outputs.size(); idx++) { const UniValue& output = outputs[idx]; From fd5d71ec4b931a44b524012bf550a61025b9fb3b Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Mon, 14 Aug 2017 23:32:38 -0400 Subject: [PATCH 069/382] Update developer notes after params.size() cleanup --- doc/developer-notes.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/doc/developer-notes.md b/doc/developer-notes.md index d783a7a8a..e999ad3d3 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -570,16 +570,14 @@ A few guidelines for introducing and reviewing new RPC interfaces: is specified as-is in BIP22. - Missing arguments and 'null' should be treated the same: as default values. If there is no - default value, both cases should fail in the same way. + default value, both cases should fail in the same way. The easiest way to follow this + guideline is detect unspecified arguments with `params[x].isNull()` instead of + `params.size() <= x`. The former returns true if the argument is either null or missing, + while the latter returns true if is missing, and false if it is null. - *Rationale*: Avoids surprises when switching to name-based arguments. Missing name-based arguments are passed as 'null'. - - *Exception*: Many legacy exceptions to this exist, one of the worst ones is - `getbalance` which follows a completely different code path based on the - number of arguments. We are still in the process of cleaning these up. Do not introduce - new ones. - - Try not to overload methods on argument type. E.g. don't make `getblock(true)` and `getblock("hash")` do different things. From 7897338918dac072e788b8ab2919d4559f311bef Mon Sep 17 00:00:00 2001 From: John Newbery Date: Fri, 2 Jun 2017 14:30:36 -0400 Subject: [PATCH 070/382] [tests] Introduce TestNode TestNode is a class responsible for all state related to a bitcoind node under test. It stores local state, is responsible for tracking the bitcoind process and delegates unrecognised messages to the RPC connection. This commit changes start_nodes and stop_nodes to start and stop the bitcoind nodes in parallel, making test setup and teardown much faster. --- test/functional/blockchain.py | 4 +- test/functional/bumpfee.py | 3 +- test/functional/fundrawtransaction.py | 3 +- test/functional/getblocktemplate_longpoll.py | 2 +- test/functional/keypool.py | 3 +- test/functional/multiwallet.py | 12 +- test/functional/rpcbind_test.py | 4 +- .../test_framework/test_framework.py | 115 ++++++--------- test/functional/test_framework/test_node.py | 134 ++++++++++++++++++ test/functional/test_framework/util.py | 4 +- test/functional/wallet-dump.py | 3 +- test/functional/wallet-encryption.py | 3 +- 12 files changed, 193 insertions(+), 97 deletions(-) create mode 100755 test/functional/test_framework/test_node.py diff --git a/test/functional/blockchain.py b/test/functional/blockchain.py index a7034e6bc..0812e1b0d 100755 --- a/test/functional/blockchain.py +++ b/test/functional/blockchain.py @@ -139,13 +139,13 @@ def _test_stopatheight(self): self.nodes[0].generate(6) assert_equal(self.nodes[0].getblockcount(), 206) self.log.debug('Node should not stop at this height') - assert_raises(subprocess.TimeoutExpired, lambda: self.bitcoind_processes[0].wait(timeout=3)) + assert_raises(subprocess.TimeoutExpired, lambda: self.nodes[0].process.wait(timeout=3)) try: self.nodes[0].generate(1) except (ConnectionError, http.client.BadStatusLine): pass # The node already shut down before response self.log.debug('Node should stop at this height...') - self.bitcoind_processes[0].wait(timeout=BITCOIND_PROC_WAIT_TIMEOUT) + self.nodes[0].process.wait(timeout=BITCOIND_PROC_WAIT_TIMEOUT) self.nodes[0] = self.start_node(0, self.options.tmpdir) assert_equal(self.nodes[0].getblockcount(), 207) diff --git a/test/functional/bumpfee.py b/test/functional/bumpfee.py index 9237f0924..3c488f609 100755 --- a/test/functional/bumpfee.py +++ b/test/functional/bumpfee.py @@ -41,8 +41,7 @@ def setup_network(self, split=False): self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, extra_args) # Encrypt wallet for test_locked_wallet_fails test - self.nodes[1].encryptwallet(WALLET_PASSPHRASE) - self.bitcoind_processes[1].wait() + self.nodes[1].node_encrypt_wallet(WALLET_PASSPHRASE) self.nodes[1] = self.start_node(1, self.options.tmpdir, extra_args[1]) self.nodes[1].walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT) diff --git a/test/functional/fundrawtransaction.py b/test/functional/fundrawtransaction.py index e52e77391..f2f4efcf2 100755 --- a/test/functional/fundrawtransaction.py +++ b/test/functional/fundrawtransaction.py @@ -451,8 +451,7 @@ def run_test(self): self.stop_node(0) self.stop_node(2) self.stop_node(3) - self.nodes[1].encryptwallet("test") - self.bitcoind_processes[1].wait(timeout=BITCOIND_PROC_WAIT_TIMEOUT) + self.nodes[1].node_encrypt_wallet("test") self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir) # This test is not meant to test fee estimation and we'd like diff --git a/test/functional/getblocktemplate_longpoll.py b/test/functional/getblocktemplate_longpoll.py index bbe1dda5f..cca30e268 100755 --- a/test/functional/getblocktemplate_longpoll.py +++ b/test/functional/getblocktemplate_longpoll.py @@ -17,7 +17,7 @@ def __init__(self, node): self.longpollid = templat['longpollid'] # create a new connection to the node, we can't use the same # connection from two threads - self.node = get_rpc_proxy(node.url, 1, timeout=600) + self.node = get_rpc_proxy(node.url, 1, timeout=600, coveragedir=node.coverage_dir) def run(self): self.node.getblocktemplate({'longpollid':self.longpollid}) diff --git a/test/functional/keypool.py b/test/functional/keypool.py index e8be55991..3e7bb0ee0 100755 --- a/test/functional/keypool.py +++ b/test/functional/keypool.py @@ -17,8 +17,7 @@ def run_test(self): assert(addr_before_encrypting_data['hdmasterkeyid'] == wallet_info_old['hdmasterkeyid']) # Encrypt wallet and wait to terminate - nodes[0].encryptwallet('test') - self.bitcoind_processes[0].wait() + nodes[0].node_encrypt_wallet('test') # Restart node 0 nodes[0] = self.start_node(0, self.options.tmpdir) # Keep creating keys diff --git a/test/functional/multiwallet.py b/test/functional/multiwallet.py index 5679f4050..fc6e8e325 100755 --- a/test/functional/multiwallet.py +++ b/test/functional/multiwallet.py @@ -35,11 +35,15 @@ def run_test(self): self.nodes[0] = self.start_node(0, self.options.tmpdir, self.extra_args[0]) - w1 = self.nodes[0] / "wallet/w1" + w1 = self.nodes[0].get_wallet_rpc("w1") + w2 = self.nodes[0].get_wallet_rpc("w2") + w3 = self.nodes[0].get_wallet_rpc("w3") + wallet_bad = self.nodes[0].get_wallet_rpc("bad") + w1.generate(1) # accessing invalid wallet fails - assert_raises_jsonrpc(-18, "Requested wallet does not exist or is not loaded", (self.nodes[0] / "wallet/bad").getwalletinfo) + assert_raises_jsonrpc(-18, "Requested wallet does not exist or is not loaded", wallet_bad.getwalletinfo) # accessing wallet RPC without using wallet endpoint fails assert_raises_jsonrpc(-19, "Wallet file not specified", self.nodes[0].getwalletinfo) @@ -50,14 +54,12 @@ def run_test(self): w1_name = w1_info['walletname'] assert_equal(w1_name, "w1") - # check w1 wallet balance - w2 = self.nodes[0] / "wallet/w2" + # check w2 wallet balance w2_info = w2.getwalletinfo() assert_equal(w2_info['immature_balance'], 0) w2_name = w2_info['walletname'] assert_equal(w2_name, "w2") - w3 = self.nodes[0] / "wallet/w3" w3_name = w3.getwalletinfo()['walletname'] assert_equal(w3_name, "w3") diff --git a/test/functional/rpcbind_test.py b/test/functional/rpcbind_test.py index 951685aa7..20808207b 100755 --- a/test/functional/rpcbind_test.py +++ b/test/functional/rpcbind_test.py @@ -37,7 +37,7 @@ def run_bind_test(self, allow_ips, connect_to, addresses, expected): base_args += ['-rpcallowip=' + x for x in allow_ips] binds = ['-rpcbind='+addr for addr in addresses] self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, [base_args + binds], connect_to) - pid = self.bitcoind_processes[0].pid + pid = self.nodes[0].process.pid assert_equal(set(get_bind_addrs(pid)), set(expected)) self.stop_nodes() @@ -49,7 +49,7 @@ def run_allowip_test(self, allow_ips, rpchost, rpcport): base_args = ['-disablewallet', '-nolisten'] + ['-rpcallowip='+x for x in allow_ips] self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, [base_args]) # connect to node through non-loopback interface - node = get_rpc_proxy(rpc_url(get_datadir_path(self.options.tmpdir, 0), 0, "%s:%d" % (rpchost, rpcport)), 0) + node = get_rpc_proxy(rpc_url(get_datadir_path(self.options.tmpdir, 0), 0, "%s:%d" % (rpchost, rpcport)), 0, coveragedir=self.options.coveragedir) node.getnetworkinfo() self.stop_nodes() diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 8d698a732..d8a8b56f9 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -5,14 +5,11 @@ """Base class for RPC testing.""" from collections import deque -import errno from enum import Enum -import http.client import logging import optparse import os import shutil -import subprocess import sys import tempfile import time @@ -20,6 +17,7 @@ from .authproxy import JSONRPCException from . import coverage +from .test_node import TestNode from .util import ( MAX_NODES, PortSeed, @@ -27,12 +25,9 @@ check_json_precision, connect_nodes_bi, disconnect_nodes, - get_rpc_proxy, initialize_datadir, - get_datadir_path, log_filename, p2p_port, - rpc_url, set_node_times, sync_blocks, sync_mempools, @@ -69,7 +64,6 @@ def __init__(self): self.num_nodes = 4 self.setup_clean_chain = False self.nodes = [] - self.bitcoind_processes = {} self.mocktime = 0 def add_options(self, parser): @@ -206,64 +200,62 @@ def main(self): def start_node(self, i, dirname, extra_args=None, rpchost=None, timewait=None, binary=None, stderr=None): """Start a bitcoind and return RPC connection to it""" - datadir = os.path.join(dirname, "node" + str(i)) + if extra_args is None: + extra_args = [] if binary is None: binary = os.getenv("BITCOIND", "bitcoind") - args = [binary, "-datadir=" + datadir, "-server", "-keypool=1", "-discover=0", "-rest", "-logtimemicros", "-debug", "-debugexclude=libevent", "-debugexclude=leveldb", "-mocktime=" + str(self.mocktime), "-uacomment=testnode%d" % i] - if extra_args is not None: - args.extend(extra_args) - self.bitcoind_processes[i] = subprocess.Popen(args, stderr=stderr) - self.log.debug("initialize_chain: bitcoind started, waiting for RPC to come up") - self._wait_for_bitcoind_start(self.bitcoind_processes[i], datadir, i, rpchost) - self.log.debug("initialize_chain: RPC successfully started") - proxy = get_rpc_proxy(rpc_url(datadir, i, rpchost), i, timeout=timewait) + node = TestNode(i, dirname, extra_args, rpchost, timewait, binary, stderr, self.mocktime, coverage_dir=self.options.coveragedir) + node.start() + node.wait_for_rpc_connection() - if self.options.coveragedir: - coverage.write_all_rpc_commands(self.options.coveragedir, proxy) + if self.options.coveragedir is not None: + coverage.write_all_rpc_commands(self.options.coveragedir, node.rpc) - return proxy + return node def start_nodes(self, num_nodes, dirname, extra_args=None, rpchost=None, timewait=None, binary=None): """Start multiple bitcoinds, return RPC connections to them""" if extra_args is None: - extra_args = [None] * num_nodes + extra_args = [[]] * num_nodes if binary is None: binary = [None] * num_nodes assert_equal(len(extra_args), num_nodes) assert_equal(len(binary), num_nodes) - rpcs = [] + nodes = [] try: for i in range(num_nodes): - rpcs.append(self.start_node(i, dirname, extra_args[i], rpchost, timewait=timewait, binary=binary[i])) + nodes.append(TestNode(i, dirname, extra_args[i], rpchost, timewait=timewait, binary=binary[i], stderr=None, mocktime=self.mocktime, coverage_dir=self.options.coveragedir)) + nodes[i].start() + for node in nodes: + node.wait_for_rpc_connection() except: # If one node failed to start, stop the others - # TODO: abusing self.nodes in this way is a little hacky. - # Eventually we should do a better job of tracking nodes - self.nodes.extend(rpcs) self.stop_nodes() - self.nodes = [] raise - return rpcs + + if self.options.coveragedir is not None: + for node in nodes: + coverage.write_all_rpc_commands(self.options.coveragedir, node.rpc) + + return nodes def stop_node(self, i): """Stop a bitcoind test node""" - - self.log.debug("Stopping node %d" % i) - try: - self.nodes[i].stop() - except http.client.CannotSendRequest as e: - self.log.exception("Unable to stop node") - return_code = self.bitcoind_processes[i].wait(timeout=BITCOIND_PROC_WAIT_TIMEOUT) - del self.bitcoind_processes[i] - assert_equal(return_code, 0) + self.nodes[i].stop_node() + while not self.nodes[i].is_node_stopped(): + time.sleep(0.1) def stop_nodes(self): """Stop multiple bitcoind test nodes""" + for node in self.nodes: + # Issue RPC to stop nodes + node.stop_node() - for i in range(len(self.nodes)): - self.stop_node(i) - assert not self.bitcoind_processes.values() # All connections must be gone now + for node in self.nodes: + # Wait for nodes to stop + while not node.is_node_stopped(): + time.sleep(0.1) def assert_start_raises_init_error(self, i, dirname, extra_args=None, expected_msg=None): with tempfile.SpooledTemporaryFile(max_size=2**16) as log_stderr: @@ -272,6 +264,8 @@ def assert_start_raises_init_error(self, i, dirname, extra_args=None, expected_m self.stop_node(i) except Exception as e: assert 'bitcoind exited' in str(e) # node must have shutdown + self.nodes[i].running = False + self.nodes[i].process = None if expected_msg is not None: log_stderr.seek(0) stderr = log_stderr.read().decode('utf-8') @@ -285,7 +279,7 @@ def assert_start_raises_init_error(self, i, dirname, extra_args=None, expected_m raise AssertionError(assert_msg) def wait_for_node_exit(self, i, timeout): - self.bitcoind_processes[i].wait(timeout) + self.nodes[i].process.wait(timeout) def split_network(self): """ @@ -382,18 +376,13 @@ def _initialize_chain(self, test_dir, num_nodes, cachedir): args = [os.getenv("BITCOIND", "bitcoind"), "-server", "-keypool=1", "-datadir=" + datadir, "-discover=0"] if i > 0: args.append("-connect=127.0.0.1:" + str(p2p_port(0))) - self.bitcoind_processes[i] = subprocess.Popen(args) - self.log.debug("initialize_chain: bitcoind started, waiting for RPC to come up") - self._wait_for_bitcoind_start(self.bitcoind_processes[i], datadir, i) - self.log.debug("initialize_chain: RPC successfully started") + self.nodes.append(TestNode(i, cachedir, extra_args=[], rpchost=None, timewait=None, binary=None, stderr=None, mocktime=self.mocktime, coverage_dir=None)) + self.nodes[i].args = args + self.nodes[i].start() - self.nodes = [] - for i in range(MAX_NODES): - try: - self.nodes.append(get_rpc_proxy(rpc_url(get_datadir_path(cachedir, i), i), i)) - except: - self.log.exception("Error connecting to node %d" % i) - sys.exit(1) + # Wait for RPC connections to be ready + for node in self.nodes: + node.wait_for_rpc_connection() # Create a 200-block-long chain; each of the 4 first nodes # gets 25 mature blocks and 25 immature. @@ -437,30 +426,6 @@ def _initialize_chain_clean(self, test_dir, num_nodes): for i in range(num_nodes): initialize_datadir(test_dir, i) - def _wait_for_bitcoind_start(self, process, datadir, i, rpchost=None): - """Wait for bitcoind to start. - - This means that RPC is accessible and fully initialized. - Raise an exception if bitcoind exits during initialization.""" - while True: - if process.poll() is not None: - raise Exception('bitcoind exited with status %i during initialization' % process.returncode) - try: - # Check if .cookie file to be created - rpc = get_rpc_proxy(rpc_url(datadir, i, rpchost), i, coveragedir=self.options.coveragedir) - rpc.getblockcount() - break # break out of loop on success - except IOError as e: - if e.errno != errno.ECONNREFUSED: # Port not yet open? - raise # unknown IO error - except JSONRPCException as e: # Initialization phase - if e.error['code'] != -28: # RPC in warmup? - raise # unknown JSON RPC exception - except ValueError as e: # cookie file not found and no rpcuser or rpcassword. bitcoind still starting - if "No RPC credentials" not in str(e): - raise - time.sleep(0.25) - class ComparisonTestFramework(BitcoinTestFramework): """Test framework for doing p2p comparison testing diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py new file mode 100755 index 000000000..66f89d43f --- /dev/null +++ b/test/functional/test_framework/test_node.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Class for bitcoind node under test""" + +import errno +import http.client +import logging +import os +import subprocess +import time + +from .util import ( + assert_equal, + get_rpc_proxy, + rpc_url, +) +from .authproxy import JSONRPCException + +class TestNode(): + """A class for representing a bitcoind node under test. + + This class contains: + + - state about the node (whether it's running, etc) + - a Python subprocess.Popen object representing the running process + - an RPC connection to the node + + To make things easier for the test writer, a bit of magic is happening under the covers. + Any unrecognised messages will be dispatched to the RPC connection.""" + + def __init__(self, i, dirname, extra_args, rpchost, timewait, binary, stderr, mocktime, coverage_dir): + self.index = i + self.datadir = os.path.join(dirname, "node" + str(i)) + self.rpchost = rpchost + self.rpc_timeout = timewait + if binary is None: + self.binary = os.getenv("BITCOIND", "bitcoind") + else: + self.binary = binary + self.stderr = stderr + self.coverage_dir = coverage_dir + # Most callers will just need to add extra args to the standard list below. For those callers that need more flexibity, they can just set the args property directly. + self.extra_args = extra_args + self.args = [self.binary, "-datadir=" + self.datadir, "-server", "-keypool=1", "-discover=0", "-rest", "-logtimemicros", "-debug", "-debugexclude=libevent", "-debugexclude=leveldb", "-mocktime=" + str(mocktime), "-uacomment=testnode%d" % i] + + self.running = False + self.process = None + self.rpc_connected = False + self.rpc = None + self.url = None + self.log = logging.getLogger('TestFramework.node%d' % i) + + def __getattr__(self, *args, **kwargs): + """Dispatches any unrecognised messages to the RPC connection.""" + assert self.rpc_connected and self.rpc is not None, "Error: no RPC connection" + return self.rpc.__getattr__(*args, **kwargs) + + def start(self): + """Start the node.""" + self.process = subprocess.Popen(self.args + self.extra_args, stderr=self.stderr) + self.running = True + self.log.debug("bitcoind started, waiting for RPC to come up") + + def wait_for_rpc_connection(self): + """Sets up an RPC connection to the bitcoind process. Returns False if unable to connect.""" + + # Wait for up to 10 seconds for the RPC server to respond + for _ in range(40): + assert not self.process.poll(), "bitcoind exited with status %i during initialization" % self.process.returncode + try: + self.rpc = get_rpc_proxy(rpc_url(self.datadir, self.index, self.rpchost), self.index, coveragedir=self.coverage_dir) + self.rpc.getblockcount() + # If the call to getblockcount() succeeds then the RPC connection is up + self.rpc_connected = True + self.url = self.rpc.url + self.log.debug("RPC successfully started") + return + except IOError as e: + if e.errno != errno.ECONNREFUSED: # Port not yet open? + raise # unknown IO error + except JSONRPCException as e: # Initialization phase + if e.error['code'] != -28: # RPC in warmup? + raise # unknown JSON RPC exception + except ValueError as e: # cookie file not found and no rpcuser or rpcassword. bitcoind still starting + if "No RPC credentials" not in str(e): + raise + time.sleep(0.25) + raise AssertionError("Unable to connect to bitcoind") + + def get_wallet_rpc(self, wallet_name): + assert self.rpc_connected + assert self.rpc + wallet_path = "wallet/%s" % wallet_name + return self.rpc / wallet_path + + def stop_node(self): + """Stop the node.""" + if not self.running: + return + self.log.debug("Stopping node") + try: + self.stop() + except http.client.CannotSendRequest: + self.log.exception("Unable to stop node.") + + def is_node_stopped(self): + """Checks whether the node has stopped. + + Returns True if the node has stopped. False otherwise. + This method is responsible for freeing resources (self.process).""" + if not self.running: + return True + return_code = self.process.poll() + if return_code is not None: + # process has stopped. Assert that it didn't return an error code. + assert_equal(return_code, 0) + self.running = False + self.process = None + self.log.debug("Node stopped") + return True + return False + + def node_encrypt_wallet(self, passphrase): + """"Encrypts the wallet. + + This causes bitcoind to shutdown, so this method takes + care of cleaning up resources.""" + self.encryptwallet(passphrase) + while not self.is_node_stopped(): + time.sleep(0.1) + self.rpc = None + self.rpc_connected = False diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index acca72aa8..4098fd861 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -204,7 +204,7 @@ def rpc_port(n): return PORT_MIN + PORT_RANGE + n + (MAX_NODES * PortSeed.n) % (PORT_RANGE - 1 - MAX_NODES) def rpc_url(datadir, i, rpchost=None): - rpc_u, rpc_p = get_auth_cookie(datadir, i) + rpc_u, rpc_p = get_auth_cookie(datadir) host = '127.0.0.1' port = rpc_port(i) if rpchost: @@ -232,7 +232,7 @@ def initialize_datadir(dirname, n): def get_datadir_path(dirname, n): return os.path.join(dirname, "node" + str(n)) -def get_auth_cookie(datadir, n): +def get_auth_cookie(datadir): user = None password = None if os.path.isfile(os.path.join(datadir, "bitcoin.conf")): diff --git a/test/functional/wallet-dump.py b/test/functional/wallet-dump.py index 569cc46e6..61ad00330 100755 --- a/test/functional/wallet-dump.py +++ b/test/functional/wallet-dump.py @@ -94,8 +94,7 @@ def run_test (self): assert_equal(found_addr_rsv, 90*2) # 90 keys plus 100% internal keys #encrypt wallet, restart, unlock and dump - self.nodes[0].encryptwallet('test') - self.bitcoind_processes[0].wait() + self.nodes[0].node_encrypt_wallet('test') self.nodes[0] = self.start_node(0, self.options.tmpdir, self.extra_args[0]) self.nodes[0].walletpassphrase('test', 10) # Should be a no-op: diff --git a/test/functional/wallet-encryption.py b/test/functional/wallet-encryption.py index ba72918fe..8fea4140d 100755 --- a/test/functional/wallet-encryption.py +++ b/test/functional/wallet-encryption.py @@ -30,8 +30,7 @@ def run_test(self): assert_equal(len(privkey), 52) # Encrypt the wallet - self.nodes[0].encryptwallet(passphrase) - self.bitcoind_processes[0].wait(timeout=BITCOIND_PROC_WAIT_TIMEOUT) + self.nodes[0].node_encrypt_wallet(passphrase) self.nodes[0] = self.start_node(0, self.options.tmpdir) # Test that the wallet is encrypted From 844b73e486ca146f3f9f3060e878814752c25fd5 Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Tue, 15 Aug 2017 10:27:20 -0700 Subject: [PATCH 071/382] disable jni in builds --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 1c530f8ff..e5ed93894 100644 --- a/configure.ac +++ b/configure.ac @@ -1261,7 +1261,7 @@ if test x$need_bundled_univalue = xyes; then AC_CONFIG_SUBDIRS([src/univalue]) fi -ac_configure_args="${ac_configure_args} --disable-shared --with-pic --with-bignum=no --enable-module-recovery" +ac_configure_args="${ac_configure_args} --disable-shared --with-pic --with-bignum=no --enable-module-recovery --disable-jni" AC_CONFIG_SUBDIRS([src/secp256k1]) AC_OUTPUT From 745d2e315f39d7591c0ea9e772a19e3cd9b51b09 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Tue, 15 Aug 2017 15:47:27 -0400 Subject: [PATCH 072/382] Clean up getbalance RPC parameter handling Only change in behavior is that unsupported combinations of parameters now trigger more specific error messages instead of the vague "JSON value is not a string as expected" error. --- src/wallet/rpcwallet.cpp | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 275173aa4..06a63e18e 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -768,18 +768,31 @@ UniValue getbalance(const JSONRPCRequest& request) LOCK2(cs_main, pwallet->cs_wallet); - if (request.params[0].isNull() && request.params[1].isNull() && request.params[2].isNull()) - return ValueFromAmount(pwallet->GetBalance()); + const UniValue& account_value = request.params[0]; + const UniValue& minconf = request.params[1]; + const UniValue& include_watchonly = request.params[2]; + + if (account_value.isNull()) { + if (!minconf.isNull()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, + "getbalance minconf option is only currently supported if an account is specified"); + } + if (!include_watchonly.isNull()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, + "getbalance include_watchonly option is only currently supported if an account is specified"); + } + return ValueFromAmount(pwallet->GetBalance()); + } - const std::string& account_param = request.params[0].get_str(); + const std::string& account_param = account_value.get_str(); const std::string* account = account_param != "*" ? &account_param : nullptr; int nMinDepth = 1; - if (!request.params[1].isNull()) - nMinDepth = request.params[1].get_int(); + if (!minconf.isNull()) + nMinDepth = minconf.get_int(); isminefilter filter = ISMINE_SPENDABLE; - if(!request.params[2].isNull()) - if(request.params[2].get_bool()) + if(!include_watchonly.isNull()) + if(include_watchonly.get_bool()) filter = filter | ISMINE_WATCH_ONLY; return ValueFromAmount(pwallet->GetLegacyBalance(filter, nMinDepth, account)); From b23549f6e677a8e22953568704eac7ea0c2c1289 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Tue, 11 Jul 2017 13:01:44 -0400 Subject: [PATCH 073/382] [tests] add TestNodeCLI class for calling bitcoin-cli for a node --- test/functional/test_framework/test_node.py | 29 +++++++++++++++++++++ test/functional/test_runner.py | 1 + 2 files changed, 30 insertions(+) diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 66f89d43f..d3227f43c 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -4,8 +4,10 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Class for bitcoind node under test""" +import decimal import errno import http.client +import json import logging import os import subprocess @@ -45,6 +47,8 @@ def __init__(self, i, dirname, extra_args, rpchost, timewait, binary, stderr, mo self.extra_args = extra_args self.args = [self.binary, "-datadir=" + self.datadir, "-server", "-keypool=1", "-discover=0", "-rest", "-logtimemicros", "-debug", "-debugexclude=libevent", "-debugexclude=leveldb", "-mocktime=" + str(mocktime), "-uacomment=testnode%d" % i] + self.cli = TestNodeCLI(os.getenv("BITCOINCLI", "bitcoin-cli"), self.datadir) + self.running = False self.process = None self.rpc_connected = False @@ -132,3 +136,28 @@ def node_encrypt_wallet(self, passphrase): time.sleep(0.1) self.rpc = None self.rpc_connected = False + +class TestNodeCLI(): + """Interface to bitcoin-cli for an individual node""" + + def __init__(self, binary, datadir): + self.binary = binary + self.datadir = datadir + + def __getattr__(self, command): + def dispatcher(*args, **kwargs): + return self.send_cli(command, *args, **kwargs) + return dispatcher + + def send_cli(self, command, *args, **kwargs): + """Run bitcoin-cli command. Deserializes returned string as python object.""" + + pos_args = [str(arg) for arg in args] + named_args = [str(key) + "=" + str(value) for (key, value) in kwargs.items()] + assert not (pos_args and named_args), "Cannot use positional arguments and named arguments in the same bitcoin-cli call" + p_args = [self.binary, "-datadir=" + self.datadir] + if named_args: + p_args += ["-named"] + p_args += [command] + pos_args + named_args + cli_output = subprocess.check_output(p_args, universal_newlines=True) + return json.loads(cli_output, parse_float=decimal.Decimal) diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 93f180555..01236b607 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -279,6 +279,7 @@ def run_tests(test_list, src_dir, build_dir, exeext, tmpdir, jobs=1, enable_cove #Set env vars if "BITCOIND" not in os.environ: os.environ["BITCOIND"] = build_dir + '/src/bitcoind' + exeext + os.environ["BITCOINCLI"] = build_dir + '/src/bitcoin-cli' + exeext tests_dir = src_dir + '/test/functional/' From c6ec4358a797b7a11283238a0cf0b4531def9e92 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Tue, 11 Jul 2017 13:02:01 -0400 Subject: [PATCH 074/382] [tests] Add bitcoin_cli.py test script --- test/functional/bitcoin_cli.py | 26 ++++++++++++++++++++++++++ test/functional/test_runner.py | 1 + 2 files changed, 27 insertions(+) create mode 100755 test/functional/bitcoin_cli.py diff --git a/test/functional/bitcoin_cli.py b/test/functional/bitcoin_cli.py new file mode 100755 index 000000000..103320209 --- /dev/null +++ b/test/functional/bitcoin_cli.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test bitcoin-cli""" +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal + +class TestBitcoinCli(BitcoinTestFramework): + + def __init__(self): + super().__init__() + self.setup_clean_chain = True + self.num_nodes = 1 + + def run_test(self): + """Main test logic""" + + self.log.info("Compare responses from getinfo RPC and `bitcoin-cli getinfo`") + cli_get_info = self.nodes[0].cli.getinfo() + rpc_get_info = self.nodes[0].getinfo() + + assert_equal(cli_get_info, rpc_get_info) + +if __name__ == '__main__': + TestBitcoinCli().main() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 01236b607..fae4f66d7 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -81,6 +81,7 @@ # vv Tests less than 30s vv 'keypool-topup.py', 'zmq_test.py', + 'bitcoin_cli.py', 'mempool_resurrect_test.py', 'txn_doublespend.py --mineblock', 'txn_clone.py', From e53615b443894fb96d5eb885b3812776c1b1033b Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Fri, 28 Jul 2017 17:00:49 -0700 Subject: [PATCH 075/382] Remove vchDefaultKey and have better first run detection Removes vchDefaultKey which was only used for first run detection. Improves wallet first run detection by checking to see if any keys were read from the database. This will now also check for a valid defaultkey for backwards compatibility reasons and to check for any corruption. Keys will stil be generated on the first one, but there won't be any shown in the address book as was previously done. --- src/wallet/crypter.h | 2 +- src/wallet/wallet.cpp | 26 ++++++++------------------ src/wallet/wallet.h | 4 ---- src/wallet/walletdb.cpp | 17 +++++++++-------- src/wallet/walletdb.h | 2 -- test/functional/keypool-topup.py | 2 +- test/functional/wallet-hd.py | 8 ++++---- 7 files changed, 23 insertions(+), 38 deletions(-) diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h index 1dc44e424..f1e8a2565 100644 --- a/src/wallet/crypter.h +++ b/src/wallet/crypter.h @@ -113,7 +113,6 @@ friend class wallet_crypto::TestCrypter; // for test access to chKey/chIV class CCryptoKeyStore : public CBasicKeyStore { private: - CryptedKeyMap mapCryptedKeys; CKeyingMaterial vMasterKey; @@ -131,6 +130,7 @@ class CCryptoKeyStore : public CBasicKeyStore bool EncryptKeys(CKeyingMaterial& vMasterKeyIn); bool Unlock(const CKeyingMaterial& vMasterKeyIn); + CryptedKeyMap mapCryptedKeys; public: CCryptoKeyStore() : fUseCrypto(false), fDecryptionThoroughlyChecked(false) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 599e74149..ea436b413 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3114,9 +3114,11 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet) } } + // This wallet is in its first run if all of these are empty + fFirstRunRet = mapKeys.empty() && mapCryptedKeys.empty() && mapWatchKeys.empty() && setWatchOnly.empty() && mapScripts.empty(); + if (nLoadWalletRet != DB_LOAD_OK) return nLoadWalletRet; - fFirstRunRet = !vchDefaultKey.IsValid(); uiInterface.LoadWallet(this); @@ -3126,7 +3128,6 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet) DBErrors CWallet::ZapSelectTx(std::vector& vHashIn, std::vector& vHashOut) { AssertLockHeld(cs_wallet); // mapWallet - vchDefaultKey = CPubKey(); DBErrors nZapSelectTxRet = CWalletDB(*dbw,"cr+").ZapSelectTx(vHashIn, vHashOut); for (uint256 hash : vHashOut) mapWallet.erase(hash); @@ -3155,7 +3156,6 @@ DBErrors CWallet::ZapSelectTx(std::vector& vHashIn, std::vector& vWtx) { - vchDefaultKey = CPubKey(); DBErrors nZapWalletTxRet = CWalletDB(*dbw,"cr+").ZapWalletTx(vWtx); if (nZapWalletTxRet == DB_NEED_REWRITE) { @@ -3231,14 +3231,6 @@ const std::string& CWallet::GetAccountName(const CScript& scriptPubKey) const return DEFAULT_ACCOUNT_NAME; } -bool CWallet::SetDefaultKey(const CPubKey &vchPubKey) -{ - if (!CWalletDB(*dbw).WriteDefaultKey(vchPubKey)) - return false; - vchDefaultKey = vchPubKey; - return true; -} - /** * Mark old keypool keys as used, * and generate all new keys @@ -4014,13 +4006,11 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) if (!walletInstance->SetHDMasterKey(masterPubKey)) throw std::runtime_error(std::string(__func__) + ": Storing master key failed"); } - CPubKey newDefaultKey; - if (walletInstance->GetKeyFromPool(newDefaultKey, false)) { - walletInstance->SetDefaultKey(newDefaultKey); - if (!walletInstance->SetAddressBook(walletInstance->vchDefaultKey.GetID(), "", "receive")) { - InitError(_("Cannot write default address") += "\n"); - return nullptr; - } + + // Top up the keypool + if (!walletInstance->TopUpKeyPool()) { + InitError(_("Unable to generate initial keys") += "\n"); + return NULL; } walletInstance->SetBestChain(chainActive.GetLocator()); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index f97a99d82..d79edbb11 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -807,8 +807,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface std::map mapAddressBook; - CPubKey vchDefaultKey; - std::set setLockedCoins; const CWalletTx* GetWalletTx(const uint256& hash) const; @@ -1040,8 +1038,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface return setInternalKeyPool.size() + setExternalKeyPool.size(); } - bool SetDefaultKey(const CPubKey &vchPubKey); - //! signify that a particular wallet feature is now used. this may change nWalletVersion and nWalletMaxVersion if those are lower bool SetMinVersion(enum WalletFeature, CWalletDB* pwalletdbIn = nullptr, bool fExplicit = false); diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 72c22d225..102f3d3a0 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -130,11 +130,6 @@ bool CWalletDB::WriteOrderPosNext(int64_t nOrderPosNext) return WriteIC(std::string("orderposnext"), nOrderPosNext); } -bool CWalletDB::WriteDefaultKey(const CPubKey& vchPubKey) -{ - return WriteIC(std::string("defaultkey"), vchPubKey); -} - bool CWalletDB::ReadPool(int64_t nPool, CKeyPool& keypool) { return batch.Read(std::make_pair(std::string("pool"), nPool), keypool); @@ -452,7 +447,14 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, } else if (strType == "defaultkey") { - ssValue >> pwallet->vchDefaultKey; + // We don't want or need the default key, but if there is one set, + // we want to make sure that it is valid so that we can detect corruption + CPubKey vchPubKey; + ssValue >> vchPubKey; + if (!vchPubKey.IsValid()) { + strErr = "Error reading wallet database: Default Key corrupt"; + return false; + } } else if (strType == "pool") { @@ -522,7 +524,6 @@ bool CWalletDB::IsKeyType(const std::string& strType) DBErrors CWalletDB::LoadWallet(CWallet* pwallet) { - pwallet->vchDefaultKey = CPubKey(); CWalletScanState wss; bool fNoncriticalErrors = false; DBErrors result = DB_LOAD_OK; @@ -565,7 +566,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) { // losing keys is considered a catastrophic error, anything else // we assume the user can live with: - if (IsKeyType(strType)) + if (IsKeyType(strType) || strType == "defaultkey") result = DB_CORRUPT; else { diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index d78f143eb..4e22143c7 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -191,8 +191,6 @@ class CWalletDB bool WriteOrderPosNext(int64_t nOrderPosNext); - bool WriteDefaultKey(const CPubKey& vchPubKey); - bool ReadPool(int64_t nPool, CKeyPool& keypool); bool WritePool(int64_t nPool, const CKeyPool& keypool); bool ErasePool(int64_t nPool); diff --git a/test/functional/keypool-topup.py b/test/functional/keypool-topup.py index 0e0c0ea74..da29f697e 100755 --- a/test/functional/keypool-topup.py +++ b/test/functional/keypool-topup.py @@ -69,7 +69,7 @@ def run_test(self): assert_equal(self.nodes[1].listtransactions()[0]['category'], "receive") # Check that we have marked all keys up to the used keypool key as used - assert_equal(self.nodes[1].validateaddress(self.nodes[1].getnewaddress())['hdkeypath'], "m/0'/0'/111'") + assert_equal(self.nodes[1].validateaddress(self.nodes[1].getnewaddress())['hdkeypath'], "m/0'/0'/110'") if __name__ == '__main__': KeypoolRestoreTest().main() diff --git a/test/functional/wallet-hd.py b/test/functional/wallet-hd.py index 821575ed1..751512301 100755 --- a/test/functional/wallet-hd.py +++ b/test/functional/wallet-hd.py @@ -42,7 +42,7 @@ def run_test (self): non_hd_add = self.nodes[0].getnewaddress() self.nodes[1].importprivkey(self.nodes[0].dumpprivkey(non_hd_add)) - # This should be enough to keep the master key and the non-HD key + # This should be enough to keep the master key and the non-HD key self.nodes[1].backupwallet(tmpdir + "/hd.bak") #self.nodes[1].dumpwallet(tmpdir + "/hd.dump") @@ -54,7 +54,7 @@ def run_test (self): for i in range(num_hd_adds): hd_add = self.nodes[1].getnewaddress() hd_info = self.nodes[1].validateaddress(hd_add) - assert_equal(hd_info["hdkeypath"], "m/0'/0'/"+str(i+1)+"'") + assert_equal(hd_info["hdkeypath"], "m/0'/0'/"+str(i)+"'") assert_equal(hd_info["hdmasterkeyid"], masterkeyid) self.nodes[0].sendtoaddress(hd_add, 1) self.nodes[0].generate(1) @@ -83,7 +83,7 @@ def run_test (self): for _ in range(num_hd_adds): hd_add_2 = self.nodes[1].getnewaddress() hd_info_2 = self.nodes[1].validateaddress(hd_add_2) - assert_equal(hd_info_2["hdkeypath"], "m/0'/0'/"+str(_+1)+"'") + assert_equal(hd_info_2["hdkeypath"], "m/0'/0'/"+str(_)+"'") assert_equal(hd_info_2["hdmasterkeyid"], masterkeyid) assert_equal(hd_add, hd_add_2) connect_nodes_bi(self.nodes, 0, 1) @@ -101,7 +101,7 @@ def run_test (self): for out in outs: if out['value'] != 1: keypath = self.nodes[1].validateaddress(out['scriptPubKey']['addresses'][0])['hdkeypath'] - + assert_equal(keypath[0:7], "m/0'/1'") if __name__ == '__main__': From f42fc1d508f24447519f79cf6304b2e4e2233a51 Mon Sep 17 00:00:00 2001 From: klemens Date: Wed, 16 Aug 2017 00:24:39 +0200 Subject: [PATCH 076/382] doc: spelling fixes --- contrib/debian/examples/bitcoin.conf | 2 +- doc/developer-notes.md | 2 +- src/httpserver.cpp | 2 +- src/qt/networkstyle.cpp | 2 +- src/qt/test/rpcnestedtests.cpp | 2 +- src/qt/test/test_main.cpp | 2 +- src/test/blockencodings_tests.cpp | 2 +- src/test/coins_tests.cpp | 2 +- src/test/data/tx_invalid.json | 2 +- test/functional/bumpfee.py | 2 +- test/functional/example_test.py | 2 +- test/functional/net.py | 2 +- test/functional/pruning.py | 4 ++-- test/functional/test_runner.py | 2 +- 14 files changed, 15 insertions(+), 15 deletions(-) diff --git a/contrib/debian/examples/bitcoin.conf b/contrib/debian/examples/bitcoin.conf index 1029a5107..14a59fdf6 100644 --- a/contrib/debian/examples/bitcoin.conf +++ b/contrib/debian/examples/bitcoin.conf @@ -76,7 +76,7 @@ #rpcuser=Ulysseys #rpcpassword=YourSuperGreatPasswordNumber_DO_NOT_USE_THIS_OR_YOU_WILL_GET_ROBBED_385593 # -# The second method `rpcauth` can be added to server startup argument. It is set at intialization time +# The second method `rpcauth` can be added to server startup argument. It is set at initialization time # using the output from the script in share/rpcuser/rpcuser.py after providing a username: # # ./share/rpcuser/rpcuser.py alice diff --git a/doc/developer-notes.md b/doc/developer-notes.md index 81bdcc9fd..6c948d515 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -4,7 +4,7 @@ Developer Notes Various coding styles have been used during the history of the codebase, and the result is not very consistent. However, we're now trying to converge to a single style, which is specified below. When writing patches, favor the new -style over attempting to mimick the surrounding style, except for move-only +style over attempting to mimic the surrounding style, except for move-only commits. Do not submit patches solely to modify the style of existing code. diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 1c53d8d49..56909d5b4 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -415,7 +415,7 @@ bool InitHTTPServer() LogPrintf("HTTP: creating work queue of depth %d\n", workQueueDepth); workQueue = new WorkQueue(workQueueDepth); - // tranfer ownership to eventBase/HTTP via .release() + // transfer ownership to eventBase/HTTP via .release() eventBase = base_ctr.release(); eventHTTP = http_ctr.release(); return true; diff --git a/src/qt/networkstyle.cpp b/src/qt/networkstyle.cpp index 93092501c..4b81c54d3 100644 --- a/src/qt/networkstyle.cpp +++ b/src/qt/networkstyle.cpp @@ -44,7 +44,7 @@ NetworkStyle::NetworkStyle(const QString &_appName, const int iconColorHueShift, // loop through pixels for(int x=0;x &utxoSet) { // except the emphasis is on testing the functionality of UpdateCoins // random txs are created and UpdateCoins is used to update the cache stack // In particular it is tested that spending a duplicate coinbase tx -// has the expected effect (the other duplicate is overwitten at all cache levels) +// has the expected effect (the other duplicate is overwritten at all cache levels) BOOST_AUTO_TEST_CASE(updatecoins_simulation_test) { bool spent_a_duplicate_coinbase = false; diff --git a/src/test/data/tx_invalid.json b/src/test/data/tx_invalid.json index 2235bd0ae..09442b7f9 100644 --- a/src/test/data/tx_invalid.json +++ b/src/test/data/tx_invalid.json @@ -205,7 +205,7 @@ [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4259839 CHECKSEQUENCEVERIFY 1"]], "020000000100010000000000000000000000000000000000000000000000000000000000000000000000feff40000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], -["By-time locks, with argument just beyond txin.nSequence (but within numerical boundries)"], +["By-time locks, with argument just beyond txin.nSequence (but within numerical boundaries)"], [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194305 CHECKSEQUENCEVERIFY 1"]], "020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4259839 CHECKSEQUENCEVERIFY 1"]], diff --git a/test/functional/bumpfee.py b/test/functional/bumpfee.py index 9237f0924..ddc7c23e8 100755 --- a/test/functional/bumpfee.py +++ b/test/functional/bumpfee.py @@ -90,7 +90,7 @@ def test_simple_bumpfee_succeeds(rbf_node, peer_node, dest_address): bumped_tx = rbf_node.bumpfee(rbfid) assert_equal(bumped_tx["errors"], []) assert bumped_tx["fee"] - abs(rbftx["fee"]) > 0 - # check that bumped_tx propogates, original tx was evicted and has a wallet conflict + # check that bumped_tx propagates, original tx was evicted and has a wallet conflict sync_mempools((rbf_node, peer_node)) assert bumped_tx["txid"] in rbf_node.getrawmempool() assert bumped_tx["txid"] in peer_node.getrawmempool() diff --git a/test/functional/example_test.py b/test/functional/example_test.py index 1ba5f756c..57fd305b7 100755 --- a/test/functional/example_test.py +++ b/test/functional/example_test.py @@ -196,7 +196,7 @@ def run_test(self): node2.add_connection(connections[1]) node2.wait_for_verack() - self.log.info("Wait for node2 reach current tip. Test that it has propogated all the blocks to us") + self.log.info("Wait for node2 reach current tip. Test that it has propagated all the blocks to us") for block in blocks: getdata_request = msg_getdata() diff --git a/test/functional/net.py b/test/functional/net.py index 3ba3764cf..1e63d3803 100755 --- a/test/functional/net.py +++ b/test/functional/net.py @@ -85,7 +85,7 @@ def _test_getaddednodeinfo(self): added_nodes = self.nodes[0].getaddednodeinfo(ip_port) assert_equal(len(added_nodes), 1) assert_equal(added_nodes[0]['addednode'], ip_port) - # check that a non-existant node returns an error + # check that a non-existent node returns an error assert_raises_jsonrpc(-24, "Node has not been added", self.nodes[0].getaddednodeinfo, '1.1.1.1') diff --git a/test/functional/pruning.py b/test/functional/pruning.py index 0af91e065..3e00a34ac 100755 --- a/test/functional/pruning.py +++ b/test/functional/pruning.py @@ -136,7 +136,7 @@ def reorg_test(self): self.log.info("Invalidating block %s at height %d" % (badhash,invalidheight)) self.nodes[1].invalidateblock(badhash) - # We've now switched to our previously mined-24 block fork on node 1, but thats not what we want + # We've now switched to our previously mined-24 block fork on node 1, but that's not what we want # So invalidate that fork as well, until we're on the same chain as node 0/2 (but at an ancestor 288 blocks ago) mainchainhash = self.nodes[0].getblockhash(invalidheight - 1) curhash = self.nodes[1].getblockhash(invalidheight - 1) @@ -199,7 +199,7 @@ def reorg_back(self): goalbesthash = self.mainchainhash2 # As of 0.10 the current block download logic is not able to reorg to the original chain created in - # create_chain_with_stale_blocks because it doesn't know of any peer thats on that chain from which to + # create_chain_with_stale_blocks because it doesn't know of any peer that's on that chain from which to # redownload its missing blocks. # Invalidate the reorg_test chain in node 0 as well, it can successfully switch to the original chain # because it has all the block data. diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 54f625514..80e296e65 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -169,7 +169,7 @@ def main(): Help text and arguments for individual test script:''', formatter_class=argparse.RawTextHelpFormatter) parser.add_argument('--coverage', action='store_true', help='generate a basic coverage report for the RPC interface') - parser.add_argument('--exclude', '-x', help='specify a comma-seperated-list of scripts to exclude.') + parser.add_argument('--exclude', '-x', help='specify a comma-separated-list of scripts to exclude.') parser.add_argument('--extended', action='store_true', help='run the extended test suite in addition to the basic tests') parser.add_argument('--force', '-f', action='store_true', help='run tests even on platforms where they are disabled by default (e.g. windows).') parser.add_argument('--help', '-h', '-?', action='store_true', help='print help text and exit') From 36d326e8b0866df4e70f81c2aa0a2e19d544399c Mon Sep 17 00:00:00 2001 From: practicalswift Date: Wed, 21 Jun 2017 21:10:00 +0200 Subject: [PATCH 077/382] Use nullptr instead of zero (0) as the null pointer constant --- src/bench/lockedpool.cpp | 4 ++-- src/coins.cpp | 2 +- src/httprpc.cpp | 4 ++-- src/httpserver.cpp | 14 +++++++------- src/net.cpp | 10 +++++----- src/netbase.h | 4 ++-- src/support/lockedpool.h | 2 +- src/sync.cpp | 2 +- src/torcontrol.cpp | 8 ++++---- src/validation.h | 2 +- src/wallet/rpcwallet.cpp | 4 ++-- src/wallet/wallet.cpp | 16 ++++++++-------- src/wallet/walletdb.cpp | 2 +- src/zmq/zmqabstractnotifier.h | 2 +- src/zmq/zmqnotificationinterface.cpp | 2 +- src/zmq/zmqpublishnotifier.cpp | 4 ++-- 16 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/bench/lockedpool.cpp b/src/bench/lockedpool.cpp index 43a142279..c6a05567b 100644 --- a/src/bench/lockedpool.cpp +++ b/src/bench/lockedpool.cpp @@ -21,14 +21,14 @@ static void BenchLockedPool(benchmark::State& state) std::vector addr; for (int x=0; x> 16) & (MSIZE-1)); } diff --git a/src/coins.cpp b/src/coins.cpp index e30bda930..b45dfc334 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -14,7 +14,7 @@ bool CCoinsView::GetCoin(const COutPoint &outpoint, Coin &coin) const { return f uint256 CCoinsView::GetBestBlock() const { return uint256(); } std::vector CCoinsView::GetHeadBlocks() const { return std::vector(); } bool CCoinsView::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return false; } -CCoinsViewCursor *CCoinsView::Cursor() const { return 0; } +CCoinsViewCursor *CCoinsView::Cursor() const { return nullptr; } bool CCoinsView::HaveCoin(const COutPoint &outpoint) const { diff --git a/src/httprpc.cpp b/src/httprpc.cpp index dd219c729..8c1a74677 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -62,7 +62,7 @@ class HTTPRPCTimerInterface : public RPCTimerInterface /* Pre-base64-encoded authentication token */ static std::string strRPCUserColonPass; /* Stored RPC timer interface (for unregistration) */ -static HTTPRPCTimerInterface* httpRPCTimerInterface = 0; +static HTTPRPCTimerInterface* httpRPCTimerInterface = nullptr; static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const UniValue& id) { @@ -255,6 +255,6 @@ void StopHTTPRPC() if (httpRPCTimerInterface) { RPCUnsetTimerInterface(httpRPCTimerInterface); delete httpRPCTimerInterface; - httpRPCTimerInterface = 0; + httpRPCTimerInterface = nullptr; } } diff --git a/src/httpserver.cpp b/src/httpserver.cpp index ba3b0d1a6..815d66028 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -164,13 +164,13 @@ struct HTTPPathHandler /** HTTP module state */ //! libevent event loop -static struct event_base* eventBase = 0; +static struct event_base* eventBase = nullptr; //! HTTP server -struct evhttp* eventHTTP = 0; +struct evhttp* eventHTTP = nullptr; //! List of subnets to allow RPC connections from static std::vector rpc_allow_subnets; //! Work queue for handling longer requests off the event loop thread -static WorkQueue* workQueue = 0; +static WorkQueue* workQueue = nullptr; //! Handlers for (sub)paths std::vector pathHandlers; //! Bound listening sockets @@ -495,11 +495,11 @@ void StopHTTPServer() } if (eventHTTP) { evhttp_free(eventHTTP); - eventHTTP = 0; + eventHTTP = nullptr; } if (eventBase) { event_base_free(eventBase); - eventBase = 0; + eventBase = nullptr; } LogPrint(BCLog::HTTP, "Stopped HTTP server\n"); } @@ -601,9 +601,9 @@ void HTTPRequest::WriteReply(int nStatus, const std::string& strReply) evbuffer_add(evb, strReply.data(), strReply.size()); HTTPEvent* ev = new HTTPEvent(eventBase, true, std::bind(evhttp_send_reply, req, nStatus, (const char*)nullptr, (struct evbuffer *)nullptr)); - ev->trigger(0); + ev->trigger(nullptr); replySent = true; - req = 0; // transferred back to main thread + req = nullptr; // transferred back to main thread } CService HTTPRequest::GetPeer() diff --git a/src/net.cpp b/src/net.cpp index 599a6128f..1af317726 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1438,9 +1438,9 @@ void CConnman::WakeMessageHandler() void ThreadMapPort() { std::string port = strprintf("%u", GetListenPort()); - const char * multicastif = 0; - const char * minissdpdpath = 0; - struct UPNPDev * devlist = 0; + const char * multicastif = nullptr; + const char * minissdpdpath = nullptr; + struct UPNPDev * devlist = nullptr; char lanaddr[64]; #ifndef UPNPDISCOVER_SUCCESS @@ -1510,13 +1510,13 @@ void ThreadMapPort() { r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0); LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r); - freeUPNPDevlist(devlist); devlist = 0; + freeUPNPDevlist(devlist); devlist = nullptr; FreeUPNPUrls(&urls); throw; } } else { LogPrintf("No valid UPnP IGDs found\n"); - freeUPNPDevlist(devlist); devlist = 0; + freeUPNPDevlist(devlist); devlist = nullptr; if (r != 0) FreeUPNPUrls(&urls); } diff --git a/src/netbase.h b/src/netbase.h index 941da31f9..f02c5e0b0 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -50,8 +50,8 @@ bool Lookup(const char *pszName, CService& addr, int portDefault, bool fAllowLoo bool Lookup(const char *pszName, std::vector& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions); CService LookupNumeric(const char *pszName, int portDefault = 0); bool LookupSubNet(const char *pszName, CSubNet& subnet); -bool ConnectSocket(const CService &addr, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed = 0); -bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, int portDefault, int nTimeout, bool *outProxyConnectionFailed = 0); +bool ConnectSocket(const CService &addr, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed = nullptr); +bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, int portDefault, int nTimeout, bool *outProxyConnectionFailed = nullptr); /** Return readable error string for a network error code */ std::string NetworkErrorString(int err); /** Close socket and set hSocket to INVALID_SOCKET */ diff --git a/src/support/lockedpool.h b/src/support/lockedpool.h index f5212bc26..7641d8147 100644 --- a/src/support/lockedpool.h +++ b/src/support/lockedpool.h @@ -150,7 +150,7 @@ class LockedPool * If this callback is provided and returns false, the allocation fails (hard fail), if * it returns true the allocation proceeds, but it could warn. */ - LockedPool(std::unique_ptr allocator, LockingFailed_Callback lf_cb_in = 0); + LockedPool(std::unique_ptr allocator, LockingFailed_Callback lf_cb_in = nullptr); ~LockedPool(); /** Allocate size bytes from this arena. diff --git a/src/sync.cpp b/src/sync.cpp index b82f3770e..9c351ea48 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -162,7 +162,7 @@ void DeleteLock(void* cs) return; } boost::unique_lock lock(lockdata.dd_mutex); - std::pair item = std::make_pair(cs, (void*)0); + std::pair item = std::make_pair(cs, nullptr); LockOrders::iterator it = lockdata.lockorders.lower_bound(item); while (it != lockdata.lockorders.end() && it->first.first == cs) { std::pair invitem = std::make_pair(it->first.second, it->first.first); diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index e8bc3e5e7..6b96c24af 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -121,7 +121,7 @@ class TorControlConnection }; TorControlConnection::TorControlConnection(struct event_base *_base): - base(_base), b_conn(0) + base(_base), b_conn(nullptr) { } @@ -227,7 +227,7 @@ bool TorControlConnection::Disconnect() { if (b_conn) bufferevent_free(b_conn); - b_conn = 0; + b_conn = nullptr; return true; } @@ -476,7 +476,7 @@ TorController::~TorController() { if (reconnect_ev) { event_free(reconnect_ev); - reconnect_ev = 0; + reconnect_ev = nullptr; } if (service.IsValid()) { RemoveLocal(service); @@ -770,7 +770,7 @@ void StopTorControl() if (gBase) { torControlThread.join(); event_base_free(gBase); - gBase = 0; + gBase = nullptr; } } diff --git a/src/validation.h b/src/validation.h index edb8eab89..d0f6cdc13 100644 --- a/src/validation.h +++ b/src/validation.h @@ -365,7 +365,7 @@ class CScriptCheck PrecomputedTransactionData *txdata; public: - CScriptCheck(): amount(0), ptxTo(0), nIn(0), nFlags(0), cacheStore(false), error(SCRIPT_ERR_UNKNOWN_ERROR) {} + CScriptCheck(): amount(0), ptxTo(nullptr), nIn(0), nFlags(0), cacheStore(false), error(SCRIPT_ERR_UNKNOWN_ERROR) {} CScriptCheck(const CScript& scriptPubKeyIn, const CAmount amountIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn, PrecomputedTransactionData* txdataIn) : scriptPubKey(scriptPubKeyIn), amount(amountIn), ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), error(SCRIPT_ERR_UNKNOWN_ERROR), txdata(txdataIn) { } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 75f836a29..094357639 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1644,10 +1644,10 @@ UniValue listtransactions(const JSONRPCRequest& request) for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) { CWalletTx *const pwtx = (*it).second.first; - if (pwtx != 0) + if (pwtx != nullptr) ListTransactions(pwallet, *pwtx, strAccount, 0, true, ret, filter); CAccountingEntry *const pacentry = (*it).second.second; - if (pacentry != 0) + if (pacentry != nullptr) AcentryToJSON(*pacentry, strAccount, ret); if ((int)ret.size() >= (nCount+nFrom)) break; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 489952e30..f8c6f9e87 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -739,13 +739,13 @@ DBErrors CWallet::ReorderTransactions() for (std::map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { CWalletTx* wtx = &((*it).second); - txByTime.insert(std::make_pair(wtx->nTimeReceived, TxPair(wtx, (CAccountingEntry*)0))); + txByTime.insert(std::make_pair(wtx->nTimeReceived, TxPair(wtx, nullptr))); } std::list acentries; walletdb.ListAccountCreditDebit("", acentries); for (CAccountingEntry& entry : acentries) { - txByTime.insert(std::make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry))); + txByTime.insert(std::make_pair(entry.nTime, TxPair(nullptr, &entry))); } nOrderPosNext = 0; @@ -754,7 +754,7 @@ DBErrors CWallet::ReorderTransactions() { CWalletTx *const pwtx = (*it).second.first; CAccountingEntry *const pacentry = (*it).second.second; - int64_t& nOrderPos = (pwtx != 0) ? pwtx->nOrderPos : pacentry->nOrderPos; + int64_t& nOrderPos = (pwtx != nullptr) ? pwtx->nOrderPos : pacentry->nOrderPos; if (nOrderPos == -1) { @@ -939,7 +939,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) { wtx.nTimeReceived = GetAdjustedTime(); wtx.nOrderPos = IncOrderPosNext(&walletdb); - wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0))); + wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, nullptr))); wtx.nTimeSmart = ComputeTimeSmart(wtx); AddToSpends(hash); } @@ -1004,7 +1004,7 @@ bool CWallet::LoadToWallet(const CWalletTx& wtxIn) mapWallet[hash] = wtxIn; CWalletTx& wtx = mapWallet[hash]; wtx.BindWallet(this); - wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0))); + wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, nullptr))); AddToSpends(hash); for (const CTxIn& txin : wtx.tx->vin) { if (mapWallet.count(txin.prevout.hash)) { @@ -1794,7 +1794,7 @@ CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const { - if (pwallet == 0) + if (pwallet == nullptr) return 0; // Must wait until coinbase is safely deep enough in the chain before valuing it @@ -1838,7 +1838,7 @@ CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool& fUseCache) const CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool& fUseCache) const { - if (pwallet == 0) + if (pwallet == nullptr) return 0; // Must wait until coinbase is safely deep enough in the chain before valuing it @@ -3026,7 +3026,7 @@ bool CWallet::AddAccountingEntry(const CAccountingEntry& acentry, CWalletDB *pwa laccentries.push_back(acentry); CAccountingEntry & entry = laccentries.back(); - wtxOrdered.insert(std::make_pair(entry.nOrderPos, TxPair((CWalletTx*)0, &entry))); + wtxOrdered.insert(std::make_pair(entry.nOrderPos, TxPair(nullptr, &entry))); return true; } diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 72c22d225..cd26d87fd 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -621,7 +621,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) pwallet->laccentries.clear(); ListAccountCreditDebit("*", pwallet->laccentries); for (CAccountingEntry& entry : pwallet->laccentries) { - pwallet->wtxOrdered.insert(make_pair(entry.nOrderPos, CWallet::TxPair((CWalletTx*)0, &entry))); + pwallet->wtxOrdered.insert(make_pair(entry.nOrderPos, CWallet::TxPair(nullptr, &entry))); } return result; diff --git a/src/zmq/zmqabstractnotifier.h b/src/zmq/zmqabstractnotifier.h index 77cf5141e..782882214 100644 --- a/src/zmq/zmqabstractnotifier.h +++ b/src/zmq/zmqabstractnotifier.h @@ -15,7 +15,7 @@ typedef CZMQAbstractNotifier* (*CZMQNotifierFactory)(); class CZMQAbstractNotifier { public: - CZMQAbstractNotifier() : psocket(0) { } + CZMQAbstractNotifier() : psocket(nullptr) { } virtual ~CZMQAbstractNotifier(); template diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp index c410cc269..9909395d8 100644 --- a/src/zmq/zmqnotificationinterface.cpp +++ b/src/zmq/zmqnotificationinterface.cpp @@ -120,7 +120,7 @@ void CZMQNotificationInterface::Shutdown() } zmq_ctx_destroy(pcontext); - pcontext = 0; + pcontext = nullptr; } } diff --git a/src/zmq/zmqpublishnotifier.cpp b/src/zmq/zmqpublishnotifier.cpp index 700c39f66..ab54e2bb8 100644 --- a/src/zmq/zmqpublishnotifier.cpp +++ b/src/zmq/zmqpublishnotifier.cpp @@ -126,7 +126,7 @@ void CZMQAbstractPublishNotifier::Shutdown() zmq_close(psocket); } - psocket = 0; + psocket = nullptr; } bool CZMQAbstractPublishNotifier::SendMessage(const char *command, const void* data, size_t size) @@ -136,7 +136,7 @@ bool CZMQAbstractPublishNotifier::SendMessage(const char *command, const void* d /* send three parts, command & data & a LE 4byte sequence number */ unsigned char msgseq[sizeof(uint32_t)]; WriteLE32(&msgseq[0], nSequence); - int rc = zmq_send_multipart(psocket, command, strlen(command), data, size, msgseq, (size_t)sizeof(uint32_t), (void*)0); + int rc = zmq_send_multipart(psocket, command, strlen(command), data, size, msgseq, (size_t)sizeof(uint32_t), nullptr); if (rc == -1) return false; From 64fb0ac016c7fd01c60c39af60f6431bac57f9ee Mon Sep 17 00:00:00 2001 From: practicalswift Date: Tue, 1 Aug 2017 12:22:41 +0200 Subject: [PATCH 078/382] Declare single-argument (non-converting) constructors "explicit" In order to avoid unintended implicit conversions. --- src/addrdb.h | 2 +- src/base58.cpp | 2 +- src/bench/checkqueue.cpp | 2 +- src/blockencodings.h | 6 +++--- src/chain.h | 2 +- src/checkqueue.h | 2 +- src/compressor.h | 4 ++-- src/crypto/aes.h | 8 ++++---- src/cuckoocache.h | 2 +- src/dbwrapper.h | 4 ++-- src/hash.h | 2 +- src/httprpc.cpp | 2 +- src/httpserver.cpp | 4 ++-- src/httpserver.h | 2 +- src/init.cpp | 2 +- src/limitedmap.h | 2 +- src/miner.h | 6 +++--- src/net_processing.cpp | 2 +- src/net_processing.h | 2 +- src/netaddress.h | 8 ++++---- src/netbase.h | 2 +- src/netmessagemaker.h | 2 +- src/policy/fees.h | 2 +- src/primitives/block.h | 2 +- src/protocol.h | 2 +- src/pubkey.h | 4 ++-- src/qt/callback.h | 2 +- src/qt/coincontroldialog.h | 6 +++--- src/qt/intro.cpp | 2 +- src/qt/notificator.cpp | 2 +- src/qt/overviewpage.cpp | 2 +- src/qt/paymentrequestplus.cpp | 2 +- src/qt/paymentserver.h | 2 +- src/qt/utilitydialog.h | 2 +- src/rest.cpp | 2 +- src/reverse_iterator.h | 2 +- src/rpc/mining.cpp | 2 +- src/rpc/misc.cpp | 2 +- src/rpc/server.h | 2 +- src/scheduler.cpp | 2 +- src/scheduler.h | 2 +- src/script/interpreter.h | 2 +- src/script/sign.h | 4 ++-- src/script/standard.cpp | 2 +- src/serialize.h | 6 +++--- src/support/lockedpool.h | 4 ++-- src/sync.h | 4 ++-- src/test/base58_tests.cpp | 4 ++-- src/test/bip32_tests.cpp | 2 +- src/test/blockencodings_tests.cpp | 4 ++-- src/test/coins_tests.cpp | 2 +- src/test/dbwrapper_tests.cpp | 2 +- src/test/test_bitcoin.h | 4 ++-- src/tinyformat.h | 8 ++++---- src/torcontrol.cpp | 2 +- src/txdb.cpp | 2 +- src/txdb.h | 4 ++-- src/txmempool.h | 6 +++--- src/uint256.h | 4 ++-- src/undo.h | 4 ++-- src/univalue/lib/univalue_utffilter.h | 2 +- src/validation.cpp | 4 ++-- src/validationinterface.cpp | 2 +- src/versionbits.cpp | 2 +- src/wallet/rpcwallet.cpp | 2 +- src/wallet/test/wallet_test_fixture.h | 2 +- src/wallet/wallet.h | 8 ++++---- src/wallet/walletdb.h | 4 ++-- 68 files changed, 105 insertions(+), 105 deletions(-) diff --git a/src/addrdb.h b/src/addrdb.h index 8105ebc9b..d930de204 100644 --- a/src/addrdb.h +++ b/src/addrdb.h @@ -37,7 +37,7 @@ class CBanEntry SetNull(); } - CBanEntry(int64_t nCreateTimeIn) + explicit CBanEntry(int64_t nCreateTimeIn) { SetNull(); nCreateTime = nCreateTimeIn; diff --git a/src/base58.cpp b/src/base58.cpp index c70e358eb..3802f953f 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -218,7 +218,7 @@ class CBitcoinAddressVisitor : public boost::static_visitor CBitcoinAddress* addr; public: - CBitcoinAddressVisitor(CBitcoinAddress* addrIn) : addr(addrIn) {} + explicit CBitcoinAddressVisitor(CBitcoinAddress* addrIn) : addr(addrIn) {} bool operator()(const CKeyID& id) const { return addr->Set(id); } bool operator()(const CScriptID& id) const { return addr->Set(id); } diff --git a/src/bench/checkqueue.cpp b/src/bench/checkqueue.cpp index 88a2a570f..b7ae5c2d5 100644 --- a/src/bench/checkqueue.cpp +++ b/src/bench/checkqueue.cpp @@ -67,7 +67,7 @@ static void CCheckQueueSpeedPrevectorJob(benchmark::State& state) prevector p; PrevectorJob(){ } - PrevectorJob(FastRandomContext& insecure_rand){ + explicit PrevectorJob(FastRandomContext& insecure_rand){ p.resize(insecure_rand.randrange(PREVECTOR_SIZE*2)); } bool operator()() diff --git a/src/blockencodings.h b/src/blockencodings.h index 5a1d80d42..50478f9f3 100644 --- a/src/blockencodings.h +++ b/src/blockencodings.h @@ -16,7 +16,7 @@ struct TransactionCompressor { private: CTransactionRef& tx; public: - TransactionCompressor(CTransactionRef& txIn) : tx(txIn) {} + explicit TransactionCompressor(CTransactionRef& txIn) : tx(txIn) {} ADD_SERIALIZE_METHODS; @@ -75,7 +75,7 @@ class BlockTransactions { std::vector txn; BlockTransactions() {} - BlockTransactions(const BlockTransactionsRequest& req) : + explicit BlockTransactions(const BlockTransactionsRequest& req) : blockhash(req.blockhash), txn(req.indexes.size()) {} ADD_SERIALIZE_METHODS; @@ -198,7 +198,7 @@ class PartiallyDownloadedBlock { CTxMemPool* pool; public: CBlockHeader header; - PartiallyDownloadedBlock(CTxMemPool* poolIn) : pool(poolIn) {} + explicit PartiallyDownloadedBlock(CTxMemPool* poolIn) : pool(poolIn) {} // extra_txn is a list of extra transactions to look at, in form ReadStatus InitData(const CBlockHeaderAndShortTxIDs& cmpctblock, const std::vector>& extra_txn); diff --git a/src/chain.h b/src/chain.h index 02db9ec77..ef7e6f955 100644 --- a/src/chain.h +++ b/src/chain.h @@ -247,7 +247,7 @@ class CBlockIndex SetNull(); } - CBlockIndex(const CBlockHeader& block) + explicit CBlockIndex(const CBlockHeader& block) { SetNull(); diff --git a/src/checkqueue.h b/src/checkqueue.h index 4bc6be45f..6377fbe94 100644 --- a/src/checkqueue.h +++ b/src/checkqueue.h @@ -131,7 +131,7 @@ class CCheckQueue boost::mutex ControlMutex; //! Create a new check queue - CCheckQueue(unsigned int nBatchSizeIn) : nIdle(0), nTotal(0), fAllOk(true), nTodo(0), fQuit(false), nBatchSize(nBatchSizeIn) {} + explicit CCheckQueue(unsigned int nBatchSizeIn) : nIdle(0), nTotal(0), fAllOk(true), nTodo(0), fQuit(false), nBatchSize(nBatchSizeIn) {} //! Worker thread void Thread() diff --git a/src/compressor.h b/src/compressor.h index 015911484..094c1bcfe 100644 --- a/src/compressor.h +++ b/src/compressor.h @@ -53,7 +53,7 @@ class CScriptCompressor unsigned int GetSpecialSize(unsigned int nSize) const; bool Decompress(unsigned int nSize, const std::vector &out); public: - CScriptCompressor(CScript &scriptIn) : script(scriptIn) { } + explicit CScriptCompressor(CScript &scriptIn) : script(scriptIn) { } template void Serialize(Stream &s) const { @@ -99,7 +99,7 @@ class CTxOutCompressor static uint64_t CompressAmount(uint64_t nAmount); static uint64_t DecompressAmount(uint64_t nAmount); - CTxOutCompressor(CTxOut &txoutIn) : txout(txoutIn) { } + explicit CTxOutCompressor(CTxOut &txoutIn) : txout(txoutIn) { } ADD_SERIALIZE_METHODS; diff --git a/src/crypto/aes.h b/src/crypto/aes.h index e9f1b52e7..a7b63b19d 100644 --- a/src/crypto/aes.h +++ b/src/crypto/aes.h @@ -22,7 +22,7 @@ class AES128Encrypt AES128_ctx ctx; public: - AES128Encrypt(const unsigned char key[16]); + explicit AES128Encrypt(const unsigned char key[16]); ~AES128Encrypt(); void Encrypt(unsigned char ciphertext[16], const unsigned char plaintext[16]) const; }; @@ -34,7 +34,7 @@ class AES128Decrypt AES128_ctx ctx; public: - AES128Decrypt(const unsigned char key[16]); + explicit AES128Decrypt(const unsigned char key[16]); ~AES128Decrypt(); void Decrypt(unsigned char plaintext[16], const unsigned char ciphertext[16]) const; }; @@ -46,7 +46,7 @@ class AES256Encrypt AES256_ctx ctx; public: - AES256Encrypt(const unsigned char key[32]); + explicit AES256Encrypt(const unsigned char key[32]); ~AES256Encrypt(); void Encrypt(unsigned char ciphertext[16], const unsigned char plaintext[16]) const; }; @@ -58,7 +58,7 @@ class AES256Decrypt AES256_ctx ctx; public: - AES256Decrypt(const unsigned char key[32]); + explicit AES256Decrypt(const unsigned char key[32]); ~AES256Decrypt(); void Decrypt(unsigned char plaintext[16], const unsigned char ciphertext[16]) const; }; diff --git a/src/cuckoocache.h b/src/cuckoocache.h index fd24d05ee..9246a3924 100644 --- a/src/cuckoocache.h +++ b/src/cuckoocache.h @@ -58,7 +58,7 @@ class bit_packed_atomic_flags * @post All calls to bit_is_set (without subsequent bit_unset) will return * true. */ - bit_packed_atomic_flags(uint32_t size) + explicit bit_packed_atomic_flags(uint32_t size) { // pad out the size if needed size = (size + 7) / 8; diff --git a/src/dbwrapper.h b/src/dbwrapper.h index 54092f9f5..e19fde51c 100644 --- a/src/dbwrapper.h +++ b/src/dbwrapper.h @@ -22,7 +22,7 @@ static const size_t DBWRAPPER_PREALLOC_VALUE_SIZE = 1024; class dbwrapper_error : public std::runtime_error { public: - dbwrapper_error(const std::string& msg) : std::runtime_error(msg) {} + explicit dbwrapper_error(const std::string& msg) : std::runtime_error(msg) {} }; class CDBWrapper; @@ -61,7 +61,7 @@ class CDBBatch /** * @param[in] _parent CDBWrapper that this batch is to be submitted to */ - CDBBatch(const CDBWrapper &_parent) : parent(_parent), ssKey(SER_DISK, CLIENT_VERSION), ssValue(SER_DISK, CLIENT_VERSION), size_estimate(0) { }; + explicit CDBBatch(const CDBWrapper &_parent) : parent(_parent), ssKey(SER_DISK, CLIENT_VERSION), ssValue(SER_DISK, CLIENT_VERSION), size_estimate(0) { }; void Clear() { diff --git a/src/hash.h b/src/hash.h index b9952d39f..ad59bb181 100644 --- a/src/hash.h +++ b/src/hash.h @@ -168,7 +168,7 @@ class CHashVerifier : public CHashWriter Source* source; public: - CHashVerifier(Source* source_) : CHashWriter(source_->GetType(), source_->GetVersion()), source(source_) {} + explicit CHashVerifier(Source* source_) : CHashWriter(source_->GetType(), source_->GetVersion()), source(source_) {} void read(char* pch, size_t nSize) { diff --git a/src/httprpc.cpp b/src/httprpc.cpp index 8c1a74677..91f96ef20 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -43,7 +43,7 @@ class HTTPRPCTimer : public RPCTimerBase class HTTPRPCTimerInterface : public RPCTimerInterface { public: - HTTPRPCTimerInterface(struct event_base* _base) : base(_base) + explicit HTTPRPCTimerInterface(struct event_base* _base) : base(_base) { } const char* Name() override diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 815d66028..ed1ee5390 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -79,7 +79,7 @@ class WorkQueue { public: WorkQueue &wq; - ThreadCounter(WorkQueue &w): wq(w) + explicit ThreadCounter(WorkQueue &w): wq(w) { std::lock_guard lock(wq.cs); wq.numThreads += 1; @@ -93,7 +93,7 @@ class WorkQueue }; public: - WorkQueue(size_t _maxDepth) : running(true), + explicit WorkQueue(size_t _maxDepth) : running(true), maxDepth(_maxDepth), numThreads(0) { diff --git a/src/httpserver.h b/src/httpserver.h index 3e434bf0a..91ce5b4e0 100644 --- a/src/httpserver.h +++ b/src/httpserver.h @@ -61,7 +61,7 @@ class HTTPRequest bool replySent; public: - HTTPRequest(struct evhttp_request* req); + explicit HTTPRequest(struct evhttp_request* req); ~HTTPRequest(); enum RequestMethod { diff --git a/src/init.cpp b/src/init.cpp index f9a63348f..fbce71862 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -136,7 +136,7 @@ bool ShutdownRequested() class CCoinsViewErrorCatcher : public CCoinsViewBacked { public: - CCoinsViewErrorCatcher(CCoinsView* view) : CCoinsViewBacked(view) {} + explicit CCoinsViewErrorCatcher(CCoinsView* view) : CCoinsViewBacked(view) {} bool GetCoin(const COutPoint &outpoint, Coin &coin) const override { try { return CCoinsViewBacked::GetCoin(outpoint, coin); diff --git a/src/limitedmap.h b/src/limitedmap.h index e9dcb6def..7afc8b458 100644 --- a/src/limitedmap.h +++ b/src/limitedmap.h @@ -27,7 +27,7 @@ class limitedmap size_type nMaxSize; public: - limitedmap(size_type nMaxSizeIn) + explicit limitedmap(size_type nMaxSizeIn) { assert(nMaxSizeIn > 0); nMaxSize = nMaxSizeIn; diff --git a/src/miner.h b/src/miner.h index 685b4e0cc..6e5fe761d 100644 --- a/src/miner.h +++ b/src/miner.h @@ -33,7 +33,7 @@ struct CBlockTemplate // Container for tracking updates to ancestor feerate as we include (parent) // transactions in a block struct CTxMemPoolModifiedEntry { - CTxMemPoolModifiedEntry(CTxMemPool::txiter entry) + explicit CTxMemPoolModifiedEntry(CTxMemPool::txiter entry) { iter = entry; nSizeWithAncestors = entry->GetSizeWithAncestors(); @@ -116,7 +116,7 @@ typedef indexed_modified_transaction_set::index::type::iterator struct update_for_parent_inclusion { - update_for_parent_inclusion(CTxMemPool::txiter it) : iter(it) {} + explicit update_for_parent_inclusion(CTxMemPool::txiter it) : iter(it) {} void operator() (CTxMemPoolModifiedEntry &e) { @@ -164,7 +164,7 @@ class BlockAssembler CFeeRate blockMinFeeRate; }; - BlockAssembler(const CChainParams& params); + explicit BlockAssembler(const CChainParams& params); BlockAssembler(const CChainParams& params, const Options& options); /** Construct a new block template with coinbase to scriptPubKeyIn */ diff --git a/src/net_processing.cpp b/src/net_processing.cpp index e09b56a63..596ae1139 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -2783,7 +2783,7 @@ class CompareInvMempoolOrder { CTxMemPool *mp; public: - CompareInvMempoolOrder(CTxMemPool *_mempool) + explicit CompareInvMempoolOrder(CTxMemPool *_mempool) { mp = _mempool; } diff --git a/src/net_processing.h b/src/net_processing.h index db6d81e6b..f4a43980a 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -32,7 +32,7 @@ class PeerLogicValidation : public CValidationInterface { CConnman* connman; public: - PeerLogicValidation(CConnman* connmanIn); + explicit PeerLogicValidation(CConnman* connmanIn); void BlockConnected(const std::shared_ptr& pblock, const CBlockIndex* pindexConnected, const std::vector& vtxConflicted) override; void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override; diff --git a/src/netaddress.h b/src/netaddress.h index 57ce897db..6ca99b36b 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -36,7 +36,7 @@ class CNetAddr public: CNetAddr(); - CNetAddr(const struct in_addr& ipv4Addr); + explicit CNetAddr(const struct in_addr& ipv4Addr); void Init(); void SetIP(const CNetAddr& ip); @@ -82,7 +82,7 @@ class CNetAddr std::vector GetGroup() const; int GetReachabilityFrom(const CNetAddr *paddrPartner = nullptr) const; - CNetAddr(const struct in6_addr& pipv6Addr, const uint32_t scope = 0); + explicit CNetAddr(const struct in6_addr& pipv6Addr, const uint32_t scope = 0); bool GetIn6Addr(struct in6_addr* pipv6Addr) const; friend bool operator==(const CNetAddr& a, const CNetAddr& b); @@ -146,7 +146,7 @@ class CService : public CNetAddr CService(); CService(const CNetAddr& ip, unsigned short port); CService(const struct in_addr& ipv4Addr, unsigned short port); - CService(const struct sockaddr_in& addr); + explicit CService(const struct sockaddr_in& addr); void Init(); unsigned short GetPort() const; bool GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const; @@ -160,7 +160,7 @@ class CService : public CNetAddr std::string ToStringIPPort() const; CService(const struct in6_addr& ipv6Addr, unsigned short port); - CService(const struct sockaddr_in6& addr); + explicit CService(const struct sockaddr_in6& addr); ADD_SERIALIZE_METHODS; diff --git a/src/netbase.h b/src/netbase.h index f02c5e0b0..6572f0a12 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -29,7 +29,7 @@ class proxyType { public: proxyType(): randomize_credentials(false) {} - proxyType(const CService &_proxy, bool _randomize_credentials=false): proxy(_proxy), randomize_credentials(_randomize_credentials) {} + explicit proxyType(const CService &_proxy, bool _randomize_credentials=false): proxy(_proxy), randomize_credentials(_randomize_credentials) {} bool IsValid() const { return proxy.IsValid(); } diff --git a/src/netmessagemaker.h b/src/netmessagemaker.h index 8e8a6e4a0..79b2501c5 100644 --- a/src/netmessagemaker.h +++ b/src/netmessagemaker.h @@ -12,7 +12,7 @@ class CNetMsgMaker { public: - CNetMsgMaker(int nVersionIn) : nVersion(nVersionIn){} + explicit CNetMsgMaker(int nVersionIn) : nVersion(nVersionIn){} template CSerializedNetMsg Make(int nFlags, std::string sCommand, Args&&... args) const diff --git a/src/policy/fees.h b/src/policy/fees.h index f4ef79364..6edaf2871 100644 --- a/src/policy/fees.h +++ b/src/policy/fees.h @@ -284,7 +284,7 @@ class FeeFilterRounder public: /** Create new FeeFilterRounder */ - FeeFilterRounder(const CFeeRate& minIncrementalFee); + explicit FeeFilterRounder(const CFeeRate& minIncrementalFee); /** Quantize a minimum fee for privacy purpose before broadcast **/ CAmount round(CAmount currentMinFee); diff --git a/src/primitives/block.h b/src/primitives/block.h index c90a1dfa6..292df4089 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -129,7 +129,7 @@ struct CBlockLocator CBlockLocator() {} - CBlockLocator(const std::vector& vHaveIn) : vHave(vHaveIn) {} + explicit CBlockLocator(const std::vector& vHaveIn) : vHave(vHaveIn) {} ADD_SERIALIZE_METHODS; diff --git a/src/protocol.h b/src/protocol.h index 7890bb627..67e01d960 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -39,7 +39,7 @@ class CMessageHeader }; typedef unsigned char MessageStartChars[MESSAGE_START_SIZE]; - CMessageHeader(const MessageStartChars& pchMessageStartIn); + explicit CMessageHeader(const MessageStartChars& pchMessageStartIn); CMessageHeader(const MessageStartChars& pchMessageStartIn, const char* pszCommand, unsigned int nMessageSizeIn); std::string GetCommand() const; diff --git a/src/pubkey.h b/src/pubkey.h index dbf0e23f2..65738d8fe 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -30,7 +30,7 @@ class CKeyID : public uint160 { public: CKeyID() : uint160() {} - CKeyID(const uint160& in) : uint160(in) {} + explicit CKeyID(const uint160& in) : uint160(in) {} }; typedef uint256 ChainCode; @@ -88,7 +88,7 @@ class CPubKey } //! Construct a public key from a byte vector. - CPubKey(const std::vector& _vch) + explicit CPubKey(const std::vector& _vch) { Set(_vch.begin(), _vch.end()); } diff --git a/src/qt/callback.h b/src/qt/callback.h index a8b593a65..da6b0c4c2 100644 --- a/src/qt/callback.h +++ b/src/qt/callback.h @@ -16,7 +16,7 @@ class FunctionCallback : public Callback F f; public: - FunctionCallback(F f_) : f(std::move(f_)) {} + explicit FunctionCallback(F f_) : f(std::move(f_)) {} ~FunctionCallback() override {} void call() override { f(this); } }; diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h index 99a9f893f..4949c9177 100644 --- a/src/qt/coincontroldialog.h +++ b/src/qt/coincontroldialog.h @@ -30,9 +30,9 @@ namespace Ui { class CCoinControlWidgetItem : public QTreeWidgetItem { public: - CCoinControlWidgetItem(QTreeWidget *parent, int type = Type) : QTreeWidgetItem(parent, type) {} - CCoinControlWidgetItem(int type = Type) : QTreeWidgetItem(type) {} - CCoinControlWidgetItem(QTreeWidgetItem *parent, int type = Type) : QTreeWidgetItem(parent, type) {} + explicit CCoinControlWidgetItem(QTreeWidget *parent, int type = Type) : QTreeWidgetItem(parent, type) {} + explicit CCoinControlWidgetItem(int type = Type) : QTreeWidgetItem(type) {} + explicit CCoinControlWidgetItem(QTreeWidgetItem *parent, int type = Type) : QTreeWidgetItem(parent, type) {} bool operator<(const QTreeWidgetItem &other) const; }; diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index 72809942f..0ff95d850 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -43,7 +43,7 @@ class FreespaceChecker : public QObject Q_OBJECT public: - FreespaceChecker(Intro *intro); + explicit FreespaceChecker(Intro *intro); enum Status { ST_OK, diff --git a/src/qt/notificator.cpp b/src/qt/notificator.cpp index 8718929c6..a7a7a4ce1 100644 --- a/src/qt/notificator.cpp +++ b/src/qt/notificator.cpp @@ -93,7 +93,7 @@ class FreedesktopImage { public: FreedesktopImage() {} - FreedesktopImage(const QImage &img); + explicit FreedesktopImage(const QImage &img); static int metaType(); diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index ba344f4db..ba1839e7b 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -25,7 +25,7 @@ class TxViewDelegate : public QAbstractItemDelegate { Q_OBJECT public: - TxViewDelegate(const PlatformStyle *_platformStyle, QObject *parent=nullptr): + explicit TxViewDelegate(const PlatformStyle *_platformStyle, QObject *parent=nullptr): QAbstractItemDelegate(parent), unit(BitcoinUnits::BTC), platformStyle(_platformStyle) { diff --git a/src/qt/paymentrequestplus.cpp b/src/qt/paymentrequestplus.cpp index 0a5cb9866..d3799f59a 100644 --- a/src/qt/paymentrequestplus.cpp +++ b/src/qt/paymentrequestplus.cpp @@ -22,7 +22,7 @@ class SSLVerifyError : public std::runtime_error { public: - SSLVerifyError(std::string err) : std::runtime_error(err) { } + explicit SSLVerifyError(std::string err) : std::runtime_error(err) { } }; bool PaymentRequestPlus::parse(const QByteArray& data) diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h index f845c4c7c..98b2364b9 100644 --- a/src/qt/paymentserver.h +++ b/src/qt/paymentserver.h @@ -72,7 +72,7 @@ class PaymentServer : public QObject static bool ipcSendCommandLine(); // parent should be QApplication object - PaymentServer(QObject* parent, bool startLocalServer = true); + explicit PaymentServer(QObject* parent, bool startLocalServer = true); ~PaymentServer(); // Load root certificate authorities. Pass nullptr (default) diff --git a/src/qt/utilitydialog.h b/src/qt/utilitydialog.h index acaa86414..738eeed13 100644 --- a/src/qt/utilitydialog.h +++ b/src/qt/utilitydialog.h @@ -41,7 +41,7 @@ class ShutdownWindow : public QWidget Q_OBJECT public: - ShutdownWindow(QWidget *parent=0, Qt::WindowFlags f=0); + explicit ShutdownWindow(QWidget *parent=0, Qt::WindowFlags f=0); static QWidget *showShutdownWindow(BitcoinGUI *window); protected: diff --git a/src/rest.cpp b/src/rest.cpp index 6a4b005f9..154ee04ee 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -48,7 +48,7 @@ struct CCoin { ADD_SERIALIZE_METHODS; CCoin() : nHeight(0) {} - CCoin(Coin&& in) : nHeight(in.nHeight), out(std::move(in.out)) {} + explicit CCoin(Coin&& in) : nHeight(in.nHeight), out(std::move(in.out)) {} template inline void SerializationOp(Stream& s, Operation ser_action) diff --git a/src/reverse_iterator.h b/src/reverse_iterator.h index 46789e541..ab467f07c 100644 --- a/src/reverse_iterator.h +++ b/src/reverse_iterator.h @@ -17,7 +17,7 @@ class reverse_range T &m_x; public: - reverse_range(T &x) : m_x(x) {} + explicit reverse_range(T &x) : m_x(x) {} auto begin() const -> decltype(this->m_x.rbegin()) { diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index f498b5c8e..652d886c7 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -686,7 +686,7 @@ class submitblock_StateCatcher : public CValidationInterface bool found; CValidationState state; - submitblock_StateCatcher(const uint256 &hashIn) : hash(hashIn), found(false), state() {} + explicit submitblock_StateCatcher(const uint256 &hashIn) : hash(hashIn), found(false), state() {} protected: void BlockChecked(const CBlock& block, const CValidationState& stateIn) override { diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index efff4a99a..1dd660eb8 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -125,7 +125,7 @@ class DescribeAddressVisitor : public boost::static_visitor public: CWallet * const pwallet; - DescribeAddressVisitor(CWallet *_pwallet) : pwallet(_pwallet) {} + explicit DescribeAddressVisitor(CWallet *_pwallet) : pwallet(_pwallet) {} UniValue operator()(const CNoDestination &dest) const { return UniValue(UniValue::VOBJ); } diff --git a/src/rpc/server.h b/src/rpc/server.h index dd6f76324..89b1d169d 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -31,7 +31,7 @@ namespace RPCServer /** Wrapper for UniValue::VType, which includes typeAny: * Used to denote don't care type. Only used by RPCTypeCheckObj */ struct UniValueType { - UniValueType(UniValue::VType _type) : typeAny(false), type(_type) {} + explicit UniValueType(UniValue::VType _type) : typeAny(false), type(_type) {} UniValueType() : typeAny(true) {} bool typeAny; UniValue::VType type; diff --git a/src/scheduler.cpp b/src/scheduler.cpp index 1d3fb1f6e..4edb2c6d9 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -174,7 +174,7 @@ void SingleThreadedSchedulerClient::ProcessQueue() { // to ensure both happen safely even if callback() throws. struct RAIICallbacksRunning { SingleThreadedSchedulerClient* instance; - RAIICallbacksRunning(SingleThreadedSchedulerClient* _instance) : instance(_instance) {} + explicit RAIICallbacksRunning(SingleThreadedSchedulerClient* _instance) : instance(_instance) {} ~RAIICallbacksRunning() { { LOCK(instance->m_cs_callbacks_pending); diff --git a/src/scheduler.h b/src/scheduler.h index cc2f01f2f..db93bcb21 100644 --- a/src/scheduler.h +++ b/src/scheduler.h @@ -102,7 +102,7 @@ class SingleThreadedSchedulerClient { void ProcessQueue(); public: - SingleThreadedSchedulerClient(CScheduler *pschedulerIn) : m_pscheduler(pschedulerIn) {} + explicit SingleThreadedSchedulerClient(CScheduler *pschedulerIn) : m_pscheduler(pschedulerIn) {} void AddToProcessQueue(std::function func); // Processes all remaining queue members on the calling thread, blocking until queue is empty diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 437826b5d..f845e1943 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -114,7 +114,7 @@ struct PrecomputedTransactionData { uint256 hashPrevouts, hashSequence, hashOutputs; - PrecomputedTransactionData(const CTransaction& tx); + explicit PrecomputedTransactionData(const CTransaction& tx); }; enum SigVersion diff --git a/src/script/sign.h b/src/script/sign.h index bd4586289..a0d8ee4ff 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -21,7 +21,7 @@ class BaseSignatureCreator { const CKeyStore* keystore; public: - BaseSignatureCreator(const CKeyStore* keystoreIn) : keystore(keystoreIn) {} + explicit BaseSignatureCreator(const CKeyStore* keystoreIn) : keystore(keystoreIn) {} const CKeyStore& KeyStore() const { return *keystore; }; virtual ~BaseSignatureCreator() {} virtual const BaseSignatureChecker& Checker() const =0; @@ -54,7 +54,7 @@ class MutableTransactionSignatureCreator : public TransactionSignatureCreator { /** A signature creator that just produces 72-byte empty signatures. */ class DummySignatureCreator : public BaseSignatureCreator { public: - DummySignatureCreator(const CKeyStore* keystoreIn) : BaseSignatureCreator(keystoreIn) {} + explicit DummySignatureCreator(const CKeyStore* keystoreIn) : BaseSignatureCreator(keystoreIn) {} const BaseSignatureChecker& Checker() const override; bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override; }; diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 9570b8c8d..0f720d9e7 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -253,7 +253,7 @@ class CScriptVisitor : public boost::static_visitor private: CScript *script; public: - CScriptVisitor(CScript *scriptin) { script = scriptin; } + explicit CScriptVisitor(CScript *scriptin) { script = scriptin; } bool operator()(const CNoDestination &dest) const { script->clear(); diff --git a/src/serialize.h b/src/serialize.h index 8b86a07a7..eeb05fa76 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -402,7 +402,7 @@ class CVarInt protected: I &n; public: - CVarInt(I& nIn) : n(nIn) { } + explicit CVarInt(I& nIn) : n(nIn) { } template void Serialize(Stream &s) const { @@ -420,7 +420,7 @@ class CCompactSize protected: uint64_t &n; public: - CCompactSize(uint64_t& nIn) : n(nIn) { } + explicit CCompactSize(uint64_t& nIn) : n(nIn) { } template void Serialize(Stream &s) const { @@ -439,7 +439,7 @@ class LimitedString protected: std::string& string; public: - LimitedString(std::string& _string) : string(_string) {} + explicit LimitedString(std::string& _string) : string(_string) {} template void Unserialize(Stream& s) diff --git a/src/support/lockedpool.h b/src/support/lockedpool.h index 7641d8147..cecbdec1a 100644 --- a/src/support/lockedpool.h +++ b/src/support/lockedpool.h @@ -150,7 +150,7 @@ class LockedPool * If this callback is provided and returns false, the allocation fails (hard fail), if * it returns true the allocation proceeds, but it could warn. */ - LockedPool(std::unique_ptr allocator, LockingFailed_Callback lf_cb_in = nullptr); + explicit LockedPool(std::unique_ptr allocator, LockingFailed_Callback lf_cb_in = nullptr); ~LockedPool(); /** Allocate size bytes from this arena. @@ -217,7 +217,7 @@ class LockedPoolManager : public LockedPool } private: - LockedPoolManager(std::unique_ptr allocator); + explicit LockedPoolManager(std::unique_ptr allocator); /** Create a new LockedPoolManager specialized to the OS */ static void CreateInstance(); diff --git a/src/sync.h b/src/sync.h index ddaf62b3b..0871c5fb4 100644 --- a/src/sync.h +++ b/src/sync.h @@ -196,7 +196,7 @@ class CSemaphore int value; public: - CSemaphore(int init) : value(init) {} + explicit CSemaphore(int init) : value(init) {} void wait() { @@ -267,7 +267,7 @@ class CSemaphoreGrant CSemaphoreGrant() : sem(nullptr), fHaveGrant(false) {} - CSemaphoreGrant(CSemaphore& sema, bool fTry = false) : sem(&sema), fHaveGrant(false) + explicit CSemaphoreGrant(CSemaphore& sema, bool fTry = false) : sem(&sema), fHaveGrant(false) { if (fTry) TryAcquire(); diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index b33cdb9fe..ee633249e 100644 --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -78,7 +78,7 @@ class TestAddrTypeVisitor : public boost::static_visitor private: std::string exp_addrType; public: - TestAddrTypeVisitor(const std::string &_exp_addrType) : exp_addrType(_exp_addrType) { } + explicit TestAddrTypeVisitor(const std::string &_exp_addrType) : exp_addrType(_exp_addrType) { } bool operator()(const CKeyID &id) const { return (exp_addrType == "pubkey"); @@ -99,7 +99,7 @@ class TestPayloadVisitor : public boost::static_visitor private: std::vector exp_payload; public: - TestPayloadVisitor(std::vector &_exp_payload) : exp_payload(_exp_payload) { } + explicit TestPayloadVisitor(std::vector &_exp_payload) : exp_payload(_exp_payload) { } bool operator()(const CKeyID &id) const { uint160 exp_key(exp_payload); diff --git a/src/test/bip32_tests.cpp b/src/test/bip32_tests.cpp index 6bcd550d7..c851ab284 100644 --- a/src/test/bip32_tests.cpp +++ b/src/test/bip32_tests.cpp @@ -24,7 +24,7 @@ struct TestVector { std::string strHexMaster; std::vector vDerive; - TestVector(std::string strHexMasterIn) : strHexMaster(strHexMasterIn) {} + explicit TestVector(std::string strHexMasterIn) : strHexMaster(strHexMasterIn) {} TestVector& operator()(std::string pub, std::string prv, unsigned int nChild) { vDerive.push_back(TestDerivation()); diff --git a/src/test/blockencodings_tests.cpp b/src/test/blockencodings_tests.cpp index 812e8534c..f2d5b385d 100644 --- a/src/test/blockencodings_tests.cpp +++ b/src/test/blockencodings_tests.cpp @@ -118,12 +118,12 @@ class TestHeaderAndShortIDs { std::vector shorttxids; std::vector prefilledtxn; - TestHeaderAndShortIDs(const CBlockHeaderAndShortTxIDs& orig) { + explicit TestHeaderAndShortIDs(const CBlockHeaderAndShortTxIDs& orig) { CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); stream << orig; stream >> *this; } - TestHeaderAndShortIDs(const CBlock& block) : + explicit TestHeaderAndShortIDs(const CBlock& block) : TestHeaderAndShortIDs(CBlockHeaderAndShortTxIDs(block, true)) {} uint64_t GetShortID(const uint256& txhash) const { diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index 37ddf83f5..dc358bff9 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -74,7 +74,7 @@ class CCoinsViewTest : public CCoinsView class CCoinsViewCacheTest : public CCoinsViewCache { public: - CCoinsViewCacheTest(CCoinsView* _base) : CCoinsViewCache(_base) {} + explicit CCoinsViewCacheTest(CCoinsView* _base) : CCoinsViewCache(_base) {} void SelfTest() const { diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp index 6ed6e7744..251d5a714 100644 --- a/src/test/dbwrapper_tests.cpp +++ b/src/test/dbwrapper_tests.cpp @@ -231,7 +231,7 @@ struct StringContentsSerializer { // This is a terrible idea std::string str; StringContentsSerializer() {} - StringContentsSerializer(const std::string& inp) : str(inp) {} + explicit StringContentsSerializer(const std::string& inp) : str(inp) {} StringContentsSerializer& operator+=(const std::string& s) { str += s; diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h index dd3b13c8c..2ddac2f07 100644 --- a/src/test/test_bitcoin.h +++ b/src/test/test_bitcoin.h @@ -41,7 +41,7 @@ static inline bool InsecureRandBool() { return insecure_rand_ctx.randbool(); } struct BasicTestingSetup { ECCVerifyHandle globalVerifyHandle; - BasicTestingSetup(const std::string& chainName = CBaseChainParams::MAIN); + explicit BasicTestingSetup(const std::string& chainName = CBaseChainParams::MAIN); ~BasicTestingSetup(); }; @@ -56,7 +56,7 @@ struct TestingSetup: public BasicTestingSetup { CConnman* connman; CScheduler scheduler; - TestingSetup(const std::string& chainName = CBaseChainParams::MAIN); + explicit TestingSetup(const std::string& chainName = CBaseChainParams::MAIN); ~TestingSetup(); }; diff --git a/src/tinyformat.h b/src/tinyformat.h index 5022d4680..2e453e56b 100644 --- a/src/tinyformat.h +++ b/src/tinyformat.h @@ -167,7 +167,7 @@ namespace tinyformat { class format_error: public std::runtime_error { public: - format_error(const std::string &what): std::runtime_error(what) { + explicit format_error(const std::string &what): std::runtime_error(what) { } }; @@ -498,7 +498,7 @@ class FormatArg FormatArg() {} template - FormatArg(const T& value) + explicit FormatArg(const T& value) : m_value(static_cast(&value)), m_formatImpl(&formatImpl), m_toIntImpl(&toIntImpl) @@ -867,7 +867,7 @@ class FormatListN : public FormatList public: #ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES template - FormatListN(const Args&... args) + explicit FormatListN(const Args&... args) : FormatList(&m_formatterStore[0], N), m_formatterStore { FormatArg(args)... } { static_assert(sizeof...(args) == N, "Number of args must be N"); } @@ -876,7 +876,7 @@ class FormatListN : public FormatList # define TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR(n) \ \ template \ - FormatListN(TINYFORMAT_VARARGS(n)) \ + explicit FormatListN(TINYFORMAT_VARARGS(n)) \ : FormatList(&m_formatterStore[0], n) \ { assert(n == N); init(0, TINYFORMAT_PASSARGS(n)); } \ \ diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index 6b96c24af..1cea19766 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -76,7 +76,7 @@ class TorControlConnection /** Create a new TorControlConnection. */ - TorControlConnection(struct event_base *base); + explicit TorControlConnection(struct event_base *base); ~TorControlConnection(); /** diff --git a/src/txdb.cpp b/src/txdb.cpp index 4c1b04cd9..797ae5713 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -35,7 +35,7 @@ namespace { struct CoinEntry { COutPoint* outpoint; char key; - CoinEntry(const COutPoint* ptr) : outpoint(const_cast(ptr)), key(DB_COIN) {} + explicit CoinEntry(const COutPoint* ptr) : outpoint(const_cast(ptr)), key(DB_COIN) {} template void Serialize(Stream &s) const { diff --git a/src/txdb.h b/src/txdb.h index adcbc7338..603eb08a7 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -69,7 +69,7 @@ class CCoinsViewDB : public CCoinsView protected: CDBWrapper db; public: - CCoinsViewDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); + explicit CCoinsViewDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); bool GetCoin(const COutPoint &outpoint, Coin &coin) const override; bool HaveCoin(const COutPoint &outpoint) const override; @@ -109,7 +109,7 @@ class CCoinsViewDBCursor: public CCoinsViewCursor class CBlockTreeDB : public CDBWrapper { public: - CBlockTreeDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); + explicit CBlockTreeDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); private: CBlockTreeDB(const CBlockTreeDB&); void operator=(const CBlockTreeDB&); diff --git a/src/txmempool.h b/src/txmempool.h index 0b183cbd9..5b0db5266 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -167,7 +167,7 @@ struct update_ancestor_state struct update_fee_delta { - update_fee_delta(int64_t _feeDelta) : feeDelta(_feeDelta) { } + explicit update_fee_delta(int64_t _feeDelta) : feeDelta(_feeDelta) { } void operator() (CTxMemPoolEntry &e) { e.UpdateFeeDelta(feeDelta); } @@ -177,7 +177,7 @@ struct update_fee_delta struct update_lock_points { - update_lock_points(const LockPoints& _lp) : lp(_lp) { } + explicit update_lock_points(const LockPoints& _lp) : lp(_lp) { } void operator() (CTxMemPoolEntry &e) { e.UpdateLockPoints(lp); } @@ -501,7 +501,7 @@ class CTxMemPool /** Create a new CTxMemPool. */ - CTxMemPool(CBlockPolicyEstimator* estimator = nullptr); + explicit CTxMemPool(CBlockPolicyEstimator* estimator = nullptr); /** * If sanity-checking is turned on, check makes sure the pool is diff --git a/src/uint256.h b/src/uint256.h index a92ce07f1..3ed694d72 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -111,7 +111,7 @@ class base_blob class uint160 : public base_blob<160> { public: uint160() {} - uint160(const base_blob<160>& b) : base_blob<160>(b) {} + explicit uint160(const base_blob<160>& b) : base_blob<160>(b) {} explicit uint160(const std::vector& vch) : base_blob<160>(vch) {} }; @@ -123,7 +123,7 @@ class uint160 : public base_blob<160> { class uint256 : public base_blob<256> { public: uint256() {} - uint256(const base_blob<256>& b) : base_blob<256>(b) {} + explicit uint256(const base_blob<256>& b) : base_blob<256>(b) {} explicit uint256(const std::vector& vch) : base_blob<256>(vch) {} /** A cheap hash function that just returns 64 bits from the result, it can be diff --git a/src/undo.h b/src/undo.h index 0f9d041bb..a720de4ac 100644 --- a/src/undo.h +++ b/src/undo.h @@ -33,7 +33,7 @@ class TxInUndoSerializer ::Serialize(s, CTxOutCompressor(REF(txout->out))); } - TxInUndoSerializer(const Coin* coin) : txout(coin) {} + explicit TxInUndoSerializer(const Coin* coin) : txout(coin) {} }; class TxInUndoDeserializer @@ -57,7 +57,7 @@ class TxInUndoDeserializer ::Unserialize(s, REF(CTxOutCompressor(REF(txout->out)))); } - TxInUndoDeserializer(Coin* coin) : txout(coin) {} + explicit TxInUndoDeserializer(Coin* coin) : txout(coin) {} }; static const size_t MIN_TRANSACTION_INPUT_WEIGHT = WITNESS_SCALE_FACTOR * ::GetSerializeSize(CTxIn(), SER_NETWORK, PROTOCOL_VERSION); diff --git a/src/univalue/lib/univalue_utffilter.h b/src/univalue/lib/univalue_utffilter.h index 0e330dce9..2fb6a492d 100644 --- a/src/univalue/lib/univalue_utffilter.h +++ b/src/univalue/lib/univalue_utffilter.h @@ -13,7 +13,7 @@ class JSONUTF8StringFilter { public: - JSONUTF8StringFilter(std::string &s): + explicit JSONUTF8StringFilter(std::string &s): str(s), is_valid(true), codepoint(0), state(0), surpair(0) { } diff --git a/src/validation.cpp b/src/validation.cpp index 8bd23a0f1..d1a8b8460 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1563,7 +1563,7 @@ class WarningBitsConditionChecker : public AbstractThresholdConditionChecker int bit; public: - WarningBitsConditionChecker(int bitIn) : bit(bitIn) {} + explicit WarningBitsConditionChecker(int bitIn) : bit(bitIn) {} int64_t BeginTime(const Consensus::Params& params) const override { return 0; } int64_t EndTime(const Consensus::Params& params) const override { return std::numeric_limits::max(); } @@ -2135,7 +2135,7 @@ class ConnectTrace { CTxMemPool &pool; public: - ConnectTrace(CTxMemPool &_pool) : blocksConnected(1), pool(_pool) { + explicit ConnectTrace(CTxMemPool &_pool) : blocksConnected(1), pool(_pool) { pool.NotifyEntryRemoved.connect(boost::bind(&ConnectTrace::NotifyEntryRemoved, this, _1, _2)); } diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index bf20d606f..be5029dec 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -30,7 +30,7 @@ struct MainSignalsInstance { // our own queue here :( SingleThreadedSchedulerClient m_schedulerClient; - MainSignalsInstance(CScheduler *pscheduler) : m_schedulerClient(pscheduler) {} + explicit MainSignalsInstance(CScheduler *pscheduler) : m_schedulerClient(pscheduler) {} }; static CMainSignals g_signals; diff --git a/src/versionbits.cpp b/src/versionbits.cpp index 59588023a..64ae93967 100644 --- a/src/versionbits.cpp +++ b/src/versionbits.cpp @@ -185,7 +185,7 @@ class VersionBitsConditionChecker : public AbstractThresholdConditionChecker { } public: - VersionBitsConditionChecker(Consensus::DeploymentPos id_) : id(id_) {} + explicit VersionBitsConditionChecker(Consensus::DeploymentPos id_) : id(id_) {} uint32_t Mask(const Consensus::Params& params) const { return ((uint32_t)1) << params.vDeployments[id].bit; } }; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 094357639..9a5349183 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1123,7 +1123,7 @@ class Witnessifier : public boost::static_visitor CWallet * const pwallet; CScriptID result; - Witnessifier(CWallet *_pwallet) : pwallet(_pwallet) {} + explicit Witnessifier(CWallet *_pwallet) : pwallet(_pwallet) {} bool operator()(const CNoDestination &dest) const { return false; } diff --git a/src/wallet/test/wallet_test_fixture.h b/src/wallet/test/wallet_test_fixture.h index 97a6d9839..9373b7907 100644 --- a/src/wallet/test/wallet_test_fixture.h +++ b/src/wallet/test/wallet_test_fixture.h @@ -10,7 +10,7 @@ /** Testing setup and teardown for wallet. */ struct WalletTestingSetup: public TestingSetup { - WalletTestingSetup(const std::string& chainName = CBaseChainParams::MAIN); + explicit WalletTestingSetup(const std::string& chainName = CBaseChainParams::MAIN); ~WalletTestingSetup(); }; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index f97a99d82..19e1638d3 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -208,7 +208,7 @@ class CMerkleTx Init(); } - CMerkleTx(CTransactionRef arg) + explicit CMerkleTx(CTransactionRef arg) { SetTx(std::move(arg)); Init(); @@ -548,7 +548,7 @@ class CWalletKey //! todo: add something to note what created it (user, getnewaddress, change) //! maybe should have a map property map - CWalletKey(int64_t nExpires=0); + explicit CWalletKey(int64_t nExpires=0); ADD_SERIALIZE_METHODS; @@ -765,7 +765,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface } // Create wallet with passed-in database handle - CWallet(std::unique_ptr dbw_in) : dbw(std::move(dbw_in)) + explicit CWallet(std::unique_ptr dbw_in) : dbw(std::move(dbw_in)) { SetNull(); } @@ -1145,7 +1145,7 @@ class CReserveKey : public CReserveScript CPubKey vchPubKey; bool fInternal; public: - CReserveKey(CWallet* pwalletIn) + explicit CReserveKey(CWallet* pwalletIn) { nIndex = -1; pwallet = pwalletIn; diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index d78f143eb..e4857f6ca 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -105,7 +105,7 @@ class CKeyMetadata { SetNull(); } - CKeyMetadata(int64_t nCreateTime_) + explicit CKeyMetadata(int64_t nCreateTime_) { SetNull(); nCreateTime = nCreateTime_; @@ -162,7 +162,7 @@ class CWalletDB } public: - CWalletDB(CWalletDBWrapper& dbw, const char* pszMode = "r+", bool _fFlushOnClose = true) : + explicit CWalletDB(CWalletDBWrapper& dbw, const char* pszMode = "r+", bool _fFlushOnClose = true) : batch(dbw, pszMode, _fFlushOnClose), m_dbw(dbw) { From 1221f60c94971c0f66abe5fdf086087a173bb0ac Mon Sep 17 00:00:00 2001 From: John Newbery Date: Mon, 14 Aug 2017 13:36:25 -0400 Subject: [PATCH 079/382] [wallet] Remove keypool_topup_cleanups Unused function. Mostly reverts c25d90f125d69e33688288eff439eb7be75012e9 c25d90f... was merged as part of PR 11022 but is not required. --- src/wallet/wallet.cpp | 5 ----- src/wallet/wallet.h | 2 -- 2 files changed, 7 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 599e74149..aa0713ea0 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3663,11 +3663,6 @@ void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id) } } -bool CWallet::HasUnusedKeys(int min_keys) const -{ - return setExternalKeyPool.size() >= min_keys && (setInternalKeyPool.size() >= min_keys || !CanSupportFeature(FEATURE_HD_SPLIT)); -} - void CWallet::GetScriptForMining(std::shared_ptr &script) { std::shared_ptr rKey = std::make_shared(this); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index f97a99d82..296bb7321 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -984,8 +984,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface */ void MarkReserveKeysAsUsed(int64_t keypool_id); const std::map& GetAllReserveKeys() const { return m_pool_key_to_index; } - /** Does the wallet have at least min_keys in the keypool? */ - bool HasUnusedKeys(int min_keys) const; std::set< std::set > GetAddressGroupings(); std::map GetAddressBalances(); From 67ceff4039ae038ae16f06128f868a2e8395b59a Mon Sep 17 00:00:00 2001 From: John Newbery Date: Mon, 14 Aug 2017 13:38:21 -0400 Subject: [PATCH 080/382] [wallet] Add logging to MarkReserveKeysAsUsed --- src/wallet/wallet.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index aa0713ea0..680c5ed9b 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3659,6 +3659,7 @@ void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id) m_pool_key_to_index.erase(keypool.vchPubKey.GetID()); } walletdb.ErasePool(index); + LogPrintf("keypool index %d removed\n", index); it = setKeyPool->erase(it); } } From d1e6f91f85ba81394297ba72271226ece7047303 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Wed, 16 Aug 2017 21:22:56 +0200 Subject: [PATCH 081/382] Prefer compile-time checking over run-time checking --- doc/developer-notes.md | 1 + src/arith_uint256.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/developer-notes.md b/doc/developer-notes.md index 461d7e1dc..77260bbdf 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -37,6 +37,7 @@ code. - **Miscellaneous** - `++i` is preferred over `i++`. + - `static_assert` is preferred over `assert` where possible. Generally; compile-time checking is preferred over run-time checking. Block style example: ```c++ diff --git a/src/arith_uint256.h b/src/arith_uint256.h index 6223e4afe..5fd4fe96c 100644 --- a/src/arith_uint256.h +++ b/src/arith_uint256.h @@ -250,7 +250,7 @@ class base_uint uint64_t GetLow64() const { - assert(WIDTH >= 2); + static_assert(WIDTH >= 2, "Assertion WIDTH >= 2 failed (WIDTH = BITS / 32). BITS is a template parameter."); return pn[0] | (uint64_t)pn[1] << 32; } }; From bfebc0b8071beb82e20afa2b5d751a5c6e725329 Mon Sep 17 00:00:00 2001 From: Eelis Date: Thu, 17 Aug 2017 14:23:30 +0200 Subject: [PATCH 082/382] Remove dead store in ecdsa_signature_parse_der_lax. This was one of the issues found by Clang's static analyzer (#9573). --- src/pubkey.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pubkey.cpp b/src/pubkey.cpp index 2da7be783..2dd0a87fc 100644 --- a/src/pubkey.cpp +++ b/src/pubkey.cpp @@ -126,7 +126,6 @@ static int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1 return 0; } spos = pos; - pos += slen; /* Ignore leading zeroes in R */ while (rlen > 0 && input[rpos] == 0) { From 08ce33f8e95efa81b37ddc6b3350462c61bbfd51 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Wed, 16 Aug 2017 18:17:34 +0200 Subject: [PATCH 083/382] qa: Move wait_until to util --- test/functional/bip65-cltv-p2p.py | 4 +- test/functional/bipdersig-p2p.py | 4 +- test/functional/disconnect_ban.py | 16 ++++---- test/functional/example_test.py | 4 +- test/functional/mempool_persist.py | 5 +-- test/functional/p2p-compactblocks.py | 47 ++++++++-------------- test/functional/p2p-leaktests.py | 14 +++---- test/functional/sendheaders.py | 6 +-- test/functional/test_framework/comptool.py | 12 +++--- test/functional/test_framework/mininode.py | 33 ++++----------- test/functional/test_framework/util.py | 22 ++++++++++ 11 files changed, 78 insertions(+), 89 deletions(-) diff --git a/test/functional/bip65-cltv-p2p.py b/test/functional/bip65-cltv-p2p.py index 7e5e4cf68..65ae8de55 100755 --- a/test/functional/bip65-cltv-p2p.py +++ b/test/functional/bip65-cltv-p2p.py @@ -109,7 +109,7 @@ def run_test(self): node0.send_and_ping(msg_block(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) - assert wait_until(lambda: "reject" in node0.last_message.keys()) + wait_until(lambda: "reject" in node0.last_message.keys(), lock=mininode_lock) with mininode_lock: assert_equal(node0.last_message["reject"].code, REJECT_OBSOLETE) assert_equal(node0.last_message["reject"].reason, b'bad-version(0x00000003)') @@ -138,7 +138,7 @@ def run_test(self): node0.send_and_ping(msg_block(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) - assert wait_until (lambda: "reject" in node0.last_message.keys()) + wait_until(lambda: "reject" in node0.last_message.keys(), lock=mininode_lock) with mininode_lock: assert node0.last_message["reject"].code in [REJECT_INVALID, REJECT_NONSTANDARD] assert_equal(node0.last_message["reject"].data, block.sha256) diff --git a/test/functional/bipdersig-p2p.py b/test/functional/bipdersig-p2p.py index 38a900954..977597089 100755 --- a/test/functional/bipdersig-p2p.py +++ b/test/functional/bipdersig-p2p.py @@ -98,7 +98,7 @@ def run_test(self): node0.send_and_ping(msg_block(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) - assert wait_until(lambda: "reject" in node0.last_message.keys()) + wait_until(lambda: "reject" in node0.last_message.keys(), lock=mininode_lock) with mininode_lock: assert_equal(node0.last_message["reject"].code, REJECT_OBSOLETE) assert_equal(node0.last_message["reject"].reason, b'bad-version(0x00000002)') @@ -128,7 +128,7 @@ def run_test(self): node0.send_and_ping(msg_block(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) - assert wait_until (lambda: "reject" in node0.last_message.keys()) + wait_until(lambda: "reject" in node0.last_message.keys(), lock=mininode_lock) with mininode_lock: # We can receive different reject messages depending on whether # bitcoind is running with multiple script check threads. If script diff --git a/test/functional/disconnect_ban.py b/test/functional/disconnect_ban.py index 89b68aeb2..19723226d 100755 --- a/test/functional/disconnect_ban.py +++ b/test/functional/disconnect_ban.py @@ -5,11 +5,13 @@ """Test node disconnect and ban behavior""" import time -from test_framework.mininode import wait_until from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import (assert_equal, - assert_raises_jsonrpc, - connect_nodes_bi) +from test_framework.util import ( + assert_equal, + assert_raises_jsonrpc, + connect_nodes_bi, + wait_until, +) class DisconnectBanTest(BitcoinTestFramework): @@ -24,7 +26,7 @@ def run_test(self): self.log.info("setban: successfully ban single IP address") assert_equal(len(self.nodes[1].getpeerinfo()), 2) # node1 should have 2 connections to node0 at this point self.nodes[1].setban("127.0.0.1", "add") - assert wait_until(lambda: len(self.nodes[1].getpeerinfo()) == 0, timeout=10) + wait_until(lambda: len(self.nodes[1].getpeerinfo()) == 0, timeout=10) assert_equal(len(self.nodes[1].getpeerinfo()), 0) # all nodes must be disconnected at this point assert_equal(len(self.nodes[1].listbanned()), 1) @@ -90,7 +92,7 @@ def run_test(self): self.log.info("disconnectnode: successfully disconnect node by address") address1 = self.nodes[0].getpeerinfo()[0]['addr'] self.nodes[0].disconnectnode(address=address1) - assert wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 1, timeout=10) + wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 1, timeout=10) assert not [node for node in self.nodes[0].getpeerinfo() if node['addr'] == address1] self.log.info("disconnectnode: successfully reconnect node") @@ -101,7 +103,7 @@ def run_test(self): self.log.info("disconnectnode: successfully disconnect node by node id") id1 = self.nodes[0].getpeerinfo()[0]['id'] self.nodes[0].disconnectnode(nodeid=id1) - assert wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 1, timeout=10) + wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 1, timeout=10) assert not [node for node in self.nodes[0].getpeerinfo() if node['id'] == id1] if __name__ == '__main__': diff --git a/test/functional/example_test.py b/test/functional/example_test.py index 79940a426..4f9e0a7dd 100755 --- a/test/functional/example_test.py +++ b/test/functional/example_test.py @@ -23,13 +23,13 @@ mininode_lock, msg_block, msg_getdata, - wait_until, ) from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, connect_nodes, p2p_port, + wait_until, ) # NodeConnCB is a class containing callbacks to be executed when a P2P @@ -209,7 +209,7 @@ def run_test(self): # wait_until() will loop until a predicate condition is met. Use it to test properties of the # NodeConnCB objects. - assert wait_until(lambda: sorted(blocks) == sorted(list(node2.block_receive_map.keys())), timeout=5) + wait_until(lambda: sorted(blocks) == sorted(list(node2.block_receive_map.keys())), timeout=5, lock=mininode_lock) self.log.info("Check that each block was received only once") # The network thread uses a global lock on data access to the NodeConn objects when sending and receiving diff --git a/test/functional/mempool_persist.py b/test/functional/mempool_persist.py index e0889fd5e..807edeb7a 100755 --- a/test/functional/mempool_persist.py +++ b/test/functional/mempool_persist.py @@ -32,7 +32,6 @@ """ import time -from test_framework.mininode import wait_until from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * @@ -69,7 +68,7 @@ def run_test(self): self.nodes.append(self.start_node(1, self.options.tmpdir)) # Give bitcoind a second to reload the mempool time.sleep(1) - assert wait_until(lambda: len(self.nodes[0].getrawmempool()) == 5) + wait_until(lambda: len(self.nodes[0].getrawmempool()) == 5) assert_equal(len(self.nodes[1].getrawmempool()), 0) self.log.debug("Stop-start node0 with -persistmempool=0. Verify that it doesn't load its mempool.dat file.") @@ -84,7 +83,7 @@ def run_test(self): self.stop_nodes() self.nodes = [] self.nodes.append(self.start_node(0, self.options.tmpdir)) - assert wait_until(lambda: len(self.nodes[0].getrawmempool()) == 5) + wait_until(lambda: len(self.nodes[0].getrawmempool()) == 5) if __name__ == '__main__': MempoolPersistTest().main() diff --git a/test/functional/p2p-compactblocks.py b/test/functional/p2p-compactblocks.py index ff76e49fb..c5c264765 100755 --- a/test/functional/p2p-compactblocks.py +++ b/test/functional/p2p-compactblocks.py @@ -70,7 +70,7 @@ def send_header_for_blocks(self, new_blocks): def request_headers_and_sync(self, locator, hashstop=0): self.clear_block_announcement() self.get_headers(locator, hashstop) - assert wait_until(self.received_block_announcement, timeout=30) + wait_until(self.received_block_announcement, timeout=30, lock=mininode_lock) self.clear_block_announcement() # Block until a block announcement for a particular block hash is @@ -78,7 +78,7 @@ def request_headers_and_sync(self, locator, hashstop=0): def wait_for_block_announcement(self, block_hash, timeout=30): def received_hash(): return (block_hash in self.announced_blockhashes) - return wait_until(received_hash, timeout=timeout) + wait_until(received_hash, timeout=timeout, lock=mininode_lock) def send_await_disconnect(self, message, timeout=30): """Sends a message to the node and wait for disconnect. @@ -86,11 +86,7 @@ def send_await_disconnect(self, message, timeout=30): This is used when we want to send a message into the node that we expect will get us disconnected, eg an invalid block.""" self.send_message(message) - success = wait_until(lambda: not self.connected, timeout=timeout) - if not success: - logger.error("send_await_disconnect failed!") - raise AssertionError("send_await_disconnect failed!") - return success + wait_until(lambda: not self.connected, timeout=timeout, lock=mininode_lock) class CompactBlocksTest(BitcoinTestFramework): def __init__(self): @@ -150,9 +146,7 @@ def test_sendcmpct(self, node, test_node, preferred_version, old_node=None): # Make sure we get a SENDCMPCT message from our peer def received_sendcmpct(): return (len(test_node.last_sendcmpct) > 0) - got_message = wait_until(received_sendcmpct, timeout=30) - assert(received_sendcmpct()) - assert(got_message) + wait_until(received_sendcmpct, timeout=30, lock=mininode_lock) with mininode_lock: # Check that the first version received is the preferred one assert_equal(test_node.last_sendcmpct[0].version, preferred_version) @@ -167,7 +161,6 @@ def check_announcement_of_new_block(node, peer, predicate): block_hash = int(node.generate(1)[0], 16) peer.wait_for_block_announcement(block_hash, timeout=30) assert(peer.block_announced) - assert(got_message) with mininode_lock: assert predicate(peer), ( @@ -282,7 +275,7 @@ def test_compactblock_construction(self, node, test_node, version, use_witness_a # Wait until we've seen the block announcement for the resulting tip tip = int(node.getbestblockhash(), 16) - assert(test_node.wait_for_block_announcement(tip)) + test_node.wait_for_block_announcement(tip) # Make sure we will receive a fast-announce compact block self.request_cb_announcements(test_node, node, version) @@ -297,8 +290,7 @@ def test_compactblock_construction(self, node, test_node, version, use_witness_a block.rehash() # Wait until the block was announced (via compact blocks) - wait_until(test_node.received_block_announcement, timeout=30) - assert(test_node.received_block_announcement()) + wait_until(test_node.received_block_announcement, timeout=30, lock=mininode_lock) # Now fetch and check the compact block header_and_shortids = None @@ -314,8 +306,7 @@ def test_compactblock_construction(self, node, test_node, version, use_witness_a inv = CInv(4, block_hash) # 4 == "CompactBlock" test_node.send_message(msg_getdata([inv])) - wait_until(test_node.received_block_announcement, timeout=30) - assert(test_node.received_block_announcement()) + wait_until(test_node.received_block_announcement, timeout=30, lock=mininode_lock) # Now fetch and check the compact block header_and_shortids = None @@ -386,13 +377,11 @@ def test_compactblock_requests(self, node, test_node, version, segwit): if announce == "inv": test_node.send_message(msg_inv([CInv(2, block.sha256)])) - success = wait_until(lambda: "getheaders" in test_node.last_message, timeout=30) - assert(success) + wait_until(lambda: "getheaders" in test_node.last_message, timeout=30, lock=mininode_lock) test_node.send_header_for_blocks([block]) else: test_node.send_header_for_blocks([block]) - success = wait_until(lambda: "getdata" in test_node.last_message, timeout=30) - assert(success) + wait_until(lambda: "getdata" in test_node.last_message, timeout=30, lock=mininode_lock) assert_equal(len(test_node.last_message["getdata"].inv), 1) assert_equal(test_node.last_message["getdata"].inv[0].type, 4) assert_equal(test_node.last_message["getdata"].inv[0].hash, block.sha256) @@ -571,8 +560,7 @@ def test_incorrect_blocktxn_response(self, node, test_node, version): assert_equal(int(node.getbestblockhash(), 16), block.hashPrevBlock) # We should receive a getdata request - success = wait_until(lambda: "getdata" in test_node.last_message, timeout=10) - assert(success) + wait_until(lambda: "getdata" in test_node.last_message, timeout=10, lock=mininode_lock) assert_equal(len(test_node.last_message["getdata"].inv), 1) assert(test_node.last_message["getdata"].inv[0].type == 2 or test_node.last_message["getdata"].inv[0].type == 2|MSG_WITNESS_FLAG) assert_equal(test_node.last_message["getdata"].inv[0].hash, block.sha256) @@ -599,8 +587,7 @@ def test_getblocktxn_handler(self, node, test_node, version): num_to_request = random.randint(1, len(block.vtx)) msg.block_txn_request.from_absolute(sorted(random.sample(range(len(block.vtx)), num_to_request))) test_node.send_message(msg) - success = wait_until(lambda: "blocktxn" in test_node.last_message, timeout=10) - assert(success) + wait_until(lambda: "blocktxn" in test_node.last_message, timeout=10, lock=mininode_lock) [tx.calc_sha256() for tx in block.vtx] with mininode_lock: @@ -639,22 +626,20 @@ def test_compactblocks_not_at_tip(self, node, test_node): for i in range(MAX_CMPCTBLOCK_DEPTH + 1): test_node.clear_block_announcement() new_blocks.append(node.generate(1)[0]) - wait_until(test_node.received_block_announcement, timeout=30) + wait_until(test_node.received_block_announcement, timeout=30, lock=mininode_lock) test_node.clear_block_announcement() test_node.send_message(msg_getdata([CInv(4, int(new_blocks[0], 16))])) - success = wait_until(lambda: "cmpctblock" in test_node.last_message, timeout=30) - assert(success) + wait_until(lambda: "cmpctblock" in test_node.last_message, timeout=30, lock=mininode_lock) test_node.clear_block_announcement() node.generate(1) - wait_until(test_node.received_block_announcement, timeout=30) + wait_until(test_node.received_block_announcement, timeout=30, lock=mininode_lock) test_node.clear_block_announcement() with mininode_lock: test_node.last_message.pop("block", None) test_node.send_message(msg_getdata([CInv(4, int(new_blocks[0], 16))])) - success = wait_until(lambda: "block" in test_node.last_message, timeout=30) - assert(success) + wait_until(lambda: "block" in test_node.last_message, timeout=30, lock=mininode_lock) with mininode_lock: test_node.last_message["block"].block.calc_sha256() assert_equal(test_node.last_message["block"].block.sha256, int(new_blocks[0], 16)) @@ -705,7 +690,7 @@ def test_end_to_end_block_relay(self, node, listeners): node.submitblock(ToHex(block)) for l in listeners: - wait_until(lambda: l.received_block_announcement(), timeout=30) + wait_until(lambda: l.received_block_announcement(), timeout=30, lock=mininode_lock) with mininode_lock: for l in listeners: assert "cmpctblock" in l.last_message diff --git a/test/functional/p2p-leaktests.py b/test/functional/p2p-leaktests.py index 5611c876a..f0d4d9a8b 100755 --- a/test/functional/p2p-leaktests.py +++ b/test/functional/p2p-leaktests.py @@ -119,11 +119,11 @@ def run_test(self): NetworkThread().start() # Start up network handling in another thread - assert wait_until(lambda: no_version_bannode.ever_connected, timeout=10) - assert wait_until(lambda: no_version_idlenode.ever_connected, timeout=10) - assert wait_until(lambda: no_verack_idlenode.version_received, timeout=10) - assert wait_until(lambda: unsupported_service_bit5_node.ever_connected, timeout=10) - assert wait_until(lambda: unsupported_service_bit7_node.ever_connected, timeout=10) + wait_until(lambda: no_version_bannode.ever_connected, timeout=10, lock=mininode_lock) + wait_until(lambda: no_version_idlenode.ever_connected, timeout=10, lock=mininode_lock) + wait_until(lambda: no_verack_idlenode.version_received, timeout=10, lock=mininode_lock) + wait_until(lambda: unsupported_service_bit5_node.ever_connected, timeout=10, lock=mininode_lock) + wait_until(lambda: unsupported_service_bit7_node.ever_connected, timeout=10, lock=mininode_lock) # Mine a block and make sure that it's not sent to the connected nodes self.nodes[0].generate(1) @@ -158,8 +158,8 @@ def run_test(self): allowed_service_bit5_node.add_connection(connections[5]) allowed_service_bit7_node.add_connection(connections[6]) - assert wait_until(lambda: allowed_service_bit5_node.message_count["verack"], timeout=10) - assert wait_until(lambda: allowed_service_bit7_node.message_count["verack"], timeout=10) + wait_until(lambda: allowed_service_bit5_node.message_count["verack"], timeout=10, lock=mininode_lock) + wait_until(lambda: allowed_service_bit7_node.message_count["verack"], timeout=10, lock=mininode_lock) if __name__ == '__main__': P2PLeakTest().main() diff --git a/test/functional/sendheaders.py b/test/functional/sendheaders.py index e47e07fb8..6451b097c 100755 --- a/test/functional/sendheaders.py +++ b/test/functional/sendheaders.py @@ -128,7 +128,7 @@ def check_last_announcement(self, headers=None, inv=None): expect_headers = headers if headers != None else [] expect_inv = inv if inv != None else [] test_function = lambda: self.block_announced - assert(wait_until(test_function, timeout=60)) + wait_until(test_function, timeout=60, lock=mininode_lock) with mininode_lock: self.block_announced = False @@ -155,12 +155,12 @@ def wait_for_getdata(self, hash_list, timeout=60): return test_function = lambda: "getdata" in self.last_message and [x.hash for x in self.last_message["getdata"].inv] == hash_list - assert(wait_until(test_function, timeout=timeout)) + wait_until(test_function, timeout=timeout, lock=mininode_lock) return def wait_for_block_announcement(self, block_hash, timeout=60): test_function = lambda: self.last_blockhash_announced == block_hash - assert(wait_until(test_function, timeout=timeout)) + wait_until(test_function, timeout=timeout, lock=mininode_lock) return def send_header_for_blocks(self, new_blocks): diff --git a/test/functional/test_framework/comptool.py b/test/functional/test_framework/comptool.py index 9f062865a..bfbc0c3b0 100755 --- a/test/functional/test_framework/comptool.py +++ b/test/functional/test_framework/comptool.py @@ -19,7 +19,7 @@ from .mininode import * from .blockstore import BlockStore, TxStore -from .util import p2p_port +from .util import p2p_port, wait_until import logging @@ -189,7 +189,7 @@ def clear_all_connections(self): def wait_for_disconnections(self): def disconnected(): return all(node.closed for node in self.test_nodes) - return wait_until(disconnected, timeout=10) + wait_until(disconnected, timeout=10, lock=mininode_lock) def wait_for_verack(self): return all(node.wait_for_verack() for node in self.test_nodes) @@ -197,7 +197,7 @@ def wait_for_verack(self): def wait_for_pings(self, counter): def received_pongs(): return all(node.received_ping_response(counter) for node in self.test_nodes) - return wait_until(received_pongs) + wait_until(received_pongs, lock=mininode_lock) # sync_blocks: Wait for all connections to request the blockhash given # then send get_headers to find out the tip of each node, and synchronize @@ -210,8 +210,7 @@ def blocks_requested(): ) # --> error if not requested - if not wait_until(blocks_requested, attempts=20*num_blocks): - raise AssertionError("Not all nodes requested block") + wait_until(blocks_requested, attempts=20*num_blocks, lock=mininode_lock) # Send getheaders message [ c.cb.send_getheaders() for c in self.connections ] @@ -231,8 +230,7 @@ def transaction_requested(): ) # --> error if not requested - if not wait_until(transaction_requested, attempts=20*num_events): - raise AssertionError("Not all nodes requested transaction") + wait_until(transaction_requested, attempts=20*num_events, lock=mininode_lock) # Get the mempool [ c.cb.send_mempool() for c in self.connections ] diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py index a4d85501e..d0753276d 100755 --- a/test/functional/test_framework/mininode.py +++ b/test/functional/test_framework/mininode.py @@ -35,7 +35,7 @@ from threading import RLock, Thread from test_framework.siphash import siphash256 -from test_framework.util import hex_str_to_bytes, bytes_to_hex_str +from test_framework.util import hex_str_to_bytes, bytes_to_hex_str, wait_until BIP0031_VERSION = 60000 MY_VERSION = 70014 # past bip-31 for ping/pong @@ -1358,23 +1358,6 @@ def __repr__(self): return "msg_reject: %s %d %s [%064x]" \ % (self.message, self.code, self.reason, self.data) -# Helper function -def wait_until(predicate, *, attempts=float('inf'), timeout=float('inf')): - if attempts == float('inf') and timeout == float('inf'): - timeout = 60 - attempt = 0 - elapsed = 0 - - while attempt < attempts and elapsed < timeout: - with mininode_lock: - if predicate(): - return True - attempt += 1 - elapsed += 0.05 - time.sleep(0.05) - - return False - class msg_feefilter(object): command = b"feefilter" @@ -1591,21 +1574,21 @@ def add_connection(self, conn): def wait_for_disconnect(self, timeout=60): test_function = lambda: not self.connected - assert wait_until(test_function, timeout=timeout) + wait_until(test_function, timeout=timeout, lock=mininode_lock) # Message receiving helper methods def wait_for_block(self, blockhash, timeout=60): test_function = lambda: self.last_message.get("block") and self.last_message["block"].block.rehash() == blockhash - assert wait_until(test_function, timeout=timeout) + wait_until(test_function, timeout=timeout, lock=mininode_lock) def wait_for_getdata(self, timeout=60): test_function = lambda: self.last_message.get("getdata") - assert wait_until(test_function, timeout=timeout) + wait_until(test_function, timeout=timeout, lock=mininode_lock) def wait_for_getheaders(self, timeout=60): test_function = lambda: self.last_message.get("getheaders") - assert wait_until(test_function, timeout=timeout) + wait_until(test_function, timeout=timeout, lock=mininode_lock) def wait_for_inv(self, expected_inv, timeout=60): """Waits for an INV message and checks that the first inv object in the message was as expected.""" @@ -1614,11 +1597,11 @@ def wait_for_inv(self, expected_inv, timeout=60): test_function = lambda: self.last_message.get("inv") and \ self.last_message["inv"].inv[0].type == expected_inv[0].type and \ self.last_message["inv"].inv[0].hash == expected_inv[0].hash - assert wait_until(test_function, timeout=timeout) + wait_until(test_function, timeout=timeout, lock=mininode_lock) def wait_for_verack(self, timeout=60): test_function = lambda: self.message_count["verack"] - assert wait_until(test_function, timeout=timeout) + wait_until(test_function, timeout=timeout, lock=mininode_lock) # Message sending helper functions @@ -1636,7 +1619,7 @@ def send_and_ping(self, message): def sync_with_ping(self, timeout=60): self.send_message(msg_ping(nonce=self.ping_counter)) test_function = lambda: self.last_message.get("pong") and self.last_message["pong"].nonce == self.ping_counter - assert wait_until(test_function, timeout=timeout) + wait_until(test_function, timeout=timeout, lock=mininode_lock) self.ping_counter += 1 return True diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index 4098fd861..a14cda07d 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -157,6 +157,28 @@ def str_to_b64str(string): def satoshi_round(amount): return Decimal(amount).quantize(Decimal('0.00000001'), rounding=ROUND_DOWN) +def wait_until(predicate, *, attempts=float('inf'), timeout=float('inf'), lock=None): + if attempts == float('inf') and timeout == float('inf'): + timeout = 60 + attempt = 0 + timeout += time.time() + + while attempt < attempts and time.time() < timeout: + if lock: + with lock: + if predicate(): + return + else: + if predicate(): + return + attempt += 1 + time.sleep(0.05) + + # Print the cause of the timeout + assert_greater_than(attempts, attempt) + assert_greater_than(timeout, time.time()) + raise RuntimeError('Unreachable') + # RPC/P2P connection constants and functions ############################################ From 5be6e9b4f9991a5f8ff74aa8aaa4962cb45728b6 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 17 Aug 2017 19:20:01 +0200 Subject: [PATCH 084/382] doc: Update build-openbsd for 6.1 - Bump "updated for" - Fix link to boost (haenet mirror is broken) - Upgrade boost version to 1.64 --- doc/build-openbsd.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/build-openbsd.md b/doc/build-openbsd.md index f4a9826d8..cd800464b 100644 --- a/doc/build-openbsd.md +++ b/doc/build-openbsd.md @@ -1,6 +1,6 @@ OpenBSD build guide ====================== -(updated for OpenBSD 6.0) +(updated for OpenBSD 6.1) This guide describes how to build bitcoind and command-line utilities on OpenBSD. @@ -48,13 +48,13 @@ BOOST_PREFIX="${BITCOIN_ROOT}/boost" mkdir -p $BOOST_PREFIX # Fetch the source and verify that it is not tampered with -curl -o boost_1_61_0.tar.bz2 http://heanet.dl.sourceforge.net/project/boost/boost/1.61.0/boost_1_61_0.tar.bz2 -echo 'a547bd06c2fd9a71ba1d169d9cf0339da7ebf4753849a8f7d6fdb8feee99b640 boost_1_61_0.tar.bz2' | sha256 -c -# MUST output: (SHA256) boost_1_61_0.tar.bz2: OK -tar -xjf boost_1_61_0.tar.bz2 +curl -o boost_1_64_0.tar.bz2 https://netcologne.dl.sourceforge.net/project/boost/boost/1.64.0/boost_1_64_0.tar.bz2 +echo '7bcc5caace97baa948931d712ea5f37038dbb1c5d89b43ad4def4ed7cb683332 boost_1_64_0.tar.bz2' | sha256 -c +# MUST output: (SHA256) boost_1_64_0.tar.bz2: OK +tar -xjf boost_1_64_0.tar.bz2 -# Boost 1.61 needs one small patch for OpenBSD -cd boost_1_61_0 +# Boost 1.64 needs one small patch for OpenBSD +cd boost_1_64_0 # Also here: https://gist.githubusercontent.com/laanwj/bf359281dc319b8ff2e1/raw/92250de8404b97bb99d72ab898f4a8cb35ae1ea3/patch-boost_test_impl_execution_monitor_ipp.patch patch -p0 < /usr/ports/devel/boost/patches/patch-boost_test_impl_execution_monitor_ipp From 6bbdafcdc4f9d9e3f9de72ed686c060fb4b8b465 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Fri, 11 Aug 2017 12:21:14 -0700 Subject: [PATCH 085/382] Pass serialization flags and whether to include hex to TxToUniv --- src/core_io.h | 2 +- src/core_write.cpp | 6 ++++-- src/rpc/blockchain.cpp | 2 +- src/rpc/rawtransaction.cpp | 4 ++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/core_io.h b/src/core_io.h index 3f25faf0e..ccc72ebb3 100644 --- a/src/core_io.h +++ b/src/core_io.h @@ -31,6 +31,6 @@ UniValue ValueFromAmount(const CAmount& amount); std::string FormatScript(const CScript& script); std::string EncodeHexTx(const CTransaction& tx, const int serializeFlags = 0); void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex); -void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry); +void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex = true, int serialize_flags = 0); #endif // BITCOIN_CORE_IO_H diff --git a/src/core_write.cpp b/src/core_write.cpp index 217b491a0..c32b1ca22 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -153,7 +153,7 @@ void ScriptPubKeyToUniv(const CScript& scriptPubKey, out.pushKV("addresses", a); } -void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry) +void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex, int serialize_flags) { entry.pushKV("txid", tx.GetHash().GetHex()); entry.pushKV("hash", tx.GetWitnessHash().GetHex()); @@ -207,5 +207,7 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry) if (!hashBlock.IsNull()) entry.pushKV("blockhash", hashBlock.GetHex()); - entry.pushKV("hex", EncodeHexTx(tx)); // the hex-encoded transaction. used the name "hex" to be consistent with the verbose output of "getrawtransaction". + if (include_hex) { + entry.pushKV("hex", EncodeHexTx(tx, serialize_flags)); // the hex-encoded transaction. used the name "hex" to be consistent with the verbose output of "getrawtransaction". + } } diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index d65e107e3..f3c65a2e0 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -125,7 +125,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx if(txDetails) { UniValue objTx(UniValue::VOBJ); - TxToUniv(*tx, uint256(), objTx); + TxToUniv(*tx, uint256(), objTx, true, RPCSerializationFlags()); txs.push_back(objTx); } else diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index ad6067db4..0d0c6449b 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -41,7 +41,7 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) // Blockchain contextual information (confirmations and blocktime) is not // available to code in bitcoin-common, so we query them here and push the // data into the returned UniValue. - TxToUniv(tx, uint256(), entry); + TxToUniv(tx, uint256(), entry, true, RPCSerializationFlags()); if (!hashBlock.IsNull()) { entry.push_back(Pair("blockhash", hashBlock.GetHex())); @@ -480,7 +480,7 @@ UniValue decoderawtransaction(const JSONRPCRequest& request) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); UniValue result(UniValue::VOBJ); - TxToUniv(CTransaction(std::move(mtx)), uint256(), result); + TxToUniv(CTransaction(std::move(mtx)), uint256(), result, false); return result; } From 07685d1bc1b0b815c00a68a5b7b335ffa0d4d90d Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Thu, 17 Aug 2017 21:54:23 +0200 Subject: [PATCH 086/382] Add length check for CExtKey deserialization --- src/key.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/key.h b/src/key.h index 2c6f15172..151e63531 100644 --- a/src/key.h +++ b/src/key.h @@ -172,6 +172,8 @@ struct CExtKey { { unsigned int len = ::ReadCompactSize(s); unsigned char code[BIP32_EXTKEY_SIZE]; + if (len != BIP32_EXTKEY_SIZE) + throw std::runtime_error("Invalid extended key size\n"); s.read((char *)&code[0], len); Decode(code); } From f9ca0fe44ec673695e601045630f151a806e000d Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 17 Aug 2017 19:35:30 -0700 Subject: [PATCH 087/382] Fix combinerawtransaction RPC help result section --- src/rpc/rawtransaction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 810051185..0ca1aa66e 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -572,7 +572,7 @@ UniValue combinerawtransaction(const JSONRPCRequest& request) " ]\n" "\nResult:\n" - "\"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n" + "\"hex\" (string) The hex-encoded raw transaction with signature(s)\n" "\nExamples:\n" + HelpExampleCli("combinerawtransaction", "[\"myhex1\", \"myhex2\", \"myhex3\"]") From bea8e9e66ea98a957b8318cd49d91b598f38cbd3 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Wed, 16 Aug 2017 17:26:07 +0200 Subject: [PATCH 088/382] Document the preference of nullptr over NULL or (void*)0 --- doc/developer-notes.md | 3 ++- src/qt/macdockiconhandler.mm | 8 ++++---- src/qt/macnotificationhandler.mm | 2 +- src/random.cpp | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/doc/developer-notes.md b/doc/developer-notes.md index 77260bbdf..9b85600cc 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -37,6 +37,7 @@ code. - **Miscellaneous** - `++i` is preferred over `i++`. + - `nullptr` is preferred over `NULL` or `(void*)0`. - `static_assert` is preferred over `assert` where possible. Generally; compile-time checking is preferred over run-time checking. Block style example: @@ -276,7 +277,7 @@ Wallet - *Rationale*: In RPC code that conditionally uses the wallet (such as `validateaddress`) it is easy to forget that global pointer `pwalletMain` - can be NULL. See `test/functional/disablewallet.py` for functional tests + can be nullptr. See `test/functional/disablewallet.py` for functional tests exercising the API with `-disablewallet` - Include `db_cxx.h` (BerkeleyDB header) only when `ENABLE_WALLET` is set diff --git a/src/qt/macdockiconhandler.mm b/src/qt/macdockiconhandler.mm index a41d39d51..9e7de0f98 100644 --- a/src/qt/macdockiconhandler.mm +++ b/src/qt/macdockiconhandler.mm @@ -18,7 +18,7 @@ extern void qt_mac_set_dock_menu(QMenu *); #endif -static MacDockIconHandler *s_instance = NULL; +static MacDockIconHandler *s_instance = nullptr; bool dockClickHandler(id self,SEL _cmd,...) { Q_UNUSED(self) @@ -34,7 +34,7 @@ void setupDockClickHandler() { Class cls = objc_getClass("NSApplication"); id appInst = objc_msgSend((id)cls, sel_registerName("sharedApplication")); - if (appInst != NULL) { + if (appInst != nullptr) { id delegate = objc_msgSend(appInst, sel_registerName("delegate")); Class delClass = (Class)objc_msgSend(delegate, sel_registerName("class")); SEL shouldHandle = sel_registerName("applicationShouldHandleReopen:hasVisibleWindows:"); @@ -53,7 +53,7 @@ void setupDockClickHandler() { setupDockClickHandler(); this->m_dummyWidget = new QWidget(); this->m_dockMenu = new QMenu(this->m_dummyWidget); - this->setMainWindow(NULL); + this->setMainWindow(nullptr); #if QT_VERSION < 0x050000 qt_mac_set_dock_menu(this->m_dockMenu); #elif QT_VERSION >= 0x050200 @@ -69,7 +69,7 @@ void setupDockClickHandler() { MacDockIconHandler::~MacDockIconHandler() { delete this->m_dummyWidget; - this->setMainWindow(NULL); + this->setMainWindow(nullptr); } QMenu *MacDockIconHandler::dockMenu() diff --git a/src/qt/macnotificationhandler.mm b/src/qt/macnotificationhandler.mm index dd3f62281..4c96d08c8 100644 --- a/src/qt/macnotificationhandler.mm +++ b/src/qt/macnotificationhandler.mm @@ -75,7 +75,7 @@ - (NSString *)__bundleIdentifier MacNotificationHandler *MacNotificationHandler::instance() { - static MacNotificationHandler *s_instance = NULL; + static MacNotificationHandler *s_instance = nullptr; if (!s_instance) { s_instance = new MacNotificationHandler(); diff --git a/src/random.cpp b/src/random.cpp index b004bfa91..4bcb8945f 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -242,7 +242,7 @@ void GetOSRand(unsigned char *ent32) } #elif defined(HAVE_GETENTROPY_RAND) && defined(MAC_OSX) // We need a fallback for OSX < 10.12 - if (&getentropy != NULL) { + if (&getentropy != nullptr) { if (getentropy(ent32, NUM_OS_RANDOM_BYTES) != 0) { RandFailure(); } From 360b464a08ac42a9a249b34c1a780ada2d4f610a Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Tue, 15 Aug 2017 17:55:26 -0700 Subject: [PATCH 089/382] Comments: More comments on functions/globals in standard.h. --- src/script/standard.cpp | 3 -- src/script/standard.h | 62 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 0f720d9e7..2aed39392 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -34,9 +34,6 @@ const char* GetTxnOutputType(txnouttype t) return nullptr; } -/** - * Return public keys or hashes from scriptPubKey, for 'standard' transaction types. - */ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector >& vSolutionsRet) { // Templates diff --git a/src/script/standard.h b/src/script/standard.h index 097e0c374..761919226 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -27,8 +27,19 @@ class CScriptID : public uint160 CScriptID(const uint160& in) : uint160(in) {} }; -static const unsigned int MAX_OP_RETURN_RELAY = 83; //!< bytes (+1 for OP_RETURN, +2 for the pushdata opcodes) +/** + * Default setting for nMaxDatacarrierBytes. 80 bytes of data, +1 for OP_RETURN, + * +2 for the pushdata opcodes. + */ +static const unsigned int MAX_OP_RETURN_RELAY = 83; + +/** + * A data carrying output is an unspendable output containing data. The script + * type is designated as TX_NULL_DATA. + */ extern bool fAcceptDatacarrier; + +/** Maximum size of TX_NULL_DATA scripts that this node considers standard. */ extern unsigned nMaxDatacarrierBytes; /** @@ -36,7 +47,7 @@ extern unsigned nMaxDatacarrierBytes; * them to be valid. (but old blocks may not comply with) Currently just P2SH, * but in the future other flags may be added, such as a soft-fork to enforce * strict DER encoding. - * + * * Failing one of these tests may trigger a DoS ban - see CheckInputs() for * details. */ @@ -50,7 +61,7 @@ enum txnouttype TX_PUBKEYHASH, TX_SCRIPTHASH, TX_MULTISIG, - TX_NULL_DATA, + TX_NULL_DATA, //!< unspendable OP_RETURN script that carries data TX_WITNESS_V0_SCRIPTHASH, TX_WITNESS_V0_KEYHASH, }; @@ -61,7 +72,7 @@ class CNoDestination { friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; } }; -/** +/** * A txout script template with a specific destination. It is either: * * CNoDestination: no destination set * * CKeyID: TX_PUBKEYHASH destination @@ -70,15 +81,58 @@ class CNoDestination { */ typedef boost::variant CTxDestination; +/** Get the name of a txnouttype as a C string, or nullptr if unknown. */ const char* GetTxnOutputType(txnouttype t); +/** + * Parse a scriptPubKey and identify script type for standard scripts. If + * successful, returns script type and parsed pubkeys or hashes, depending on + * the type. For example, for a P2SH script, vSolutionsRet will contain the + * script hash, for P2PKH it will contain the key hash, etc. + * + * @param[in] scriptPubKey Script to parse + * @param[out] typeRet The script type + * @param[out] vSolutionsRet Vector of parsed pubkeys and hashes + * @return True if script matches standard template + */ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector >& vSolutionsRet); + +/** + * Parse a standard scriptPubKey for the destination address. Assigns result to + * the addressRet parameter and returns true if successful. For multisig + * scripts, instead use ExtractDestinations. Currently only works for P2PK, + * P2PKH, and P2SH scripts. + */ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet); + +/** + * Parse a standard scriptPubKey with one or more destination addresses. For + * multisig scripts, this populates the addressRet vector with the pubkey IDs + * and nRequiredRet with the n required to spend. For other destinations, + * addressRet is populated with a single value and nRequiredRet is set to 1. + * Returns true if successful. Currently does not extract address from + * pay-to-witness scripts. + */ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector& addressRet, int& nRequiredRet); +/** + * Generate a Bitcoin scriptPubKey for the given CTxDestination. Returns a P2PKH + * script for a CKeyID destination, a P2SH script for a CScriptID, and an empty + * script for CNoDestination. + */ CScript GetScriptForDestination(const CTxDestination& dest); + +/** Generate a P2PK script for the given pubkey. */ CScript GetScriptForRawPubKey(const CPubKey& pubkey); + +/** Generate a multisig script. */ CScript GetScriptForMultisig(int nRequired, const std::vector& keys); + +/** + * Generate a pay-to-witness script for the given redeem script. If the redeem + * script is P2PK or P2PKH, this returns a P2WPKH script, otherwise it returns a + * P2WSH script. + */ CScript GetScriptForWitness(const CScript& redeemscript); #endif // BITCOIN_SCRIPT_STANDARD_H From 06a3aecf06a52ff44370ee20462cc45b25e88d6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20B=C3=ADlek?= Date: Sat, 19 Aug 2017 11:26:40 +0200 Subject: [PATCH 090/382] Docs: Hash in ZMQ hash is raw bytes, not hex Transaction hash cannot be in hexadecimal, that would be 64 bytes. In reality it's just raw bytes. --- doc/zmq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/zmq.md b/doc/zmq.md index 1019ff665..38c58fb7f 100644 --- a/doc/zmq.md +++ b/doc/zmq.md @@ -72,7 +72,7 @@ For instance: Each PUB notification has a topic and body, where the header corresponds to the notification type. For instance, for the notification `-zmqpubhashtx` the topic is `hashtx` (no null -terminator) and the body is the hexadecimal transaction hash (32 +terminator) and the body is the transaction hash (32 bytes). These options can also be provided in bitcoin.conf. From a65e02803cc76e9250f67a34c610234c263d4ab2 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Sun, 30 Jul 2017 19:56:51 +0200 Subject: [PATCH 091/382] Build with --enable-werror under OS X --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 12f91096c..dadac3165 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,7 +34,7 @@ env: # x86_64 Linux, No wallet - HOST=x86_64-unknown-linux-gnu PACKAGES="python3" DEP_OPTS="NO_WALLET=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports" # Cross-Mac - - HOST=x86_64-apple-darwin11 PACKAGES="cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev" BITCOIN_CONFIG="--enable-gui --enable-reduce-exports" OSX_SDK=10.11 GOAL="deploy" + - HOST=x86_64-apple-darwin11 PACKAGES="cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev" BITCOIN_CONFIG="--enable-gui --enable-reduce-exports --enable-werror" OSX_SDK=10.11 GOAL="deploy" before_install: - export PATH=$(echo $PATH | tr ':' "\n" | sed '/\/opt\/python/d' | tr "\n" ":" | sed "s|::|:|g") From 4452829b104842c547bd100714a241e9f209e1f4 Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Sat, 19 Aug 2017 15:22:06 -0400 Subject: [PATCH 092/382] gitian: quick hack to fix version string in releases Release version strings were broken in Gitian by 7522. This is a minimal fix suitable for 0.15. After this, we should fix up version handling for good so that gitian packages the correct string in the release tarball, so that git is not required to get the tag name. --- contrib/gitian-descriptors/gitian-linux.yml | 9 ++++++++- contrib/gitian-descriptors/gitian-osx.yml | 9 ++++++++- contrib/gitian-descriptors/gitian-win.yml | 9 ++++++++- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml index 0569c6a77..c80e19edb 100644 --- a/contrib/gitian-descriptors/gitian-linux.yml +++ b/contrib/gitian-descriptors/gitian-linux.yml @@ -132,7 +132,6 @@ script: | export PATH=${WRAP_DIR}:${PATH} # Create the release tarball using (arbitrarily) the first host - export GIT_DIR="$PWD/.git" ./autogen.sh CONFIG_SITE=${BASEPREFIX}/`echo "${HOSTS}" | awk '{print $1;}'`/share/config.site ./configure --prefix=/ make dist @@ -145,6 +144,9 @@ script: | find bitcoin-* | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ../$SOURCEDIST popd + # Workaround for tarball not building with the bare tag version (prep) + make -C src obj/build.h + ORIGPATH="$PATH" # Extract the release tarball into a dir for each host and build for i in ${HOSTS}; do @@ -155,6 +157,11 @@ script: | mkdir -p ${INSTALLPATH} tar --strip-components=1 -xf ../$SOURCEDIST + # Workaround for tarball not building with the bare tag version + echo '#!/bin/true' >share/genbuild.sh + mkdir src/obj + cp ../src/obj/build.h src/obj/ + CONFIG_SITE=${BASEPREFIX}/${i}/share/config.site ./configure --prefix=/ --disable-ccache --disable-maintainer-mode --disable-dependency-tracking ${CONFIGFLAGS} CFLAGS="${HOST_CFLAGS}" CXXFLAGS="${HOST_CXXFLAGS}" LDFLAGS="${HOST_LDFLAGS}" make ${MAKEOPTS} make ${MAKEOPTS} -C src check-security diff --git a/contrib/gitian-descriptors/gitian-osx.yml b/contrib/gitian-descriptors/gitian-osx.yml index d31517358..cbf286d2c 100644 --- a/contrib/gitian-descriptors/gitian-osx.yml +++ b/contrib/gitian-descriptors/gitian-osx.yml @@ -101,7 +101,6 @@ script: | export PATH=${WRAP_DIR}:${PATH} # Create the release tarball using (arbitrarily) the first host - export GIT_DIR="$PWD/.git" ./autogen.sh CONFIG_SITE=${BASEPREFIX}/`echo "${HOSTS}" | awk '{print $1;}'`/share/config.site ./configure --prefix=/ make dist @@ -115,6 +114,9 @@ script: | find bitcoin-* | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ../$SOURCEDIST popd + # Workaround for tarball not building with the bare tag version (prep) + make -C src obj/build.h + ORIGPATH="$PATH" # Extract the release tarball into a dir for each host and build for i in ${HOSTS}; do @@ -125,6 +127,11 @@ script: | mkdir -p ${INSTALLPATH} tar --strip-components=1 -xf ../$SOURCEDIST + # Workaround for tarball not building with the bare tag version + echo '#!/bin/true' >share/genbuild.sh + mkdir src/obj + cp ../src/obj/build.h src/obj/ + CONFIG_SITE=${BASEPREFIX}/${i}/share/config.site ./configure --prefix=/ --disable-ccache --disable-maintainer-mode --disable-dependency-tracking ${CONFIGFLAGS} make ${MAKEOPTS} make install-strip DESTDIR=${INSTALLPATH} diff --git a/contrib/gitian-descriptors/gitian-win.yml b/contrib/gitian-descriptors/gitian-win.yml index aba46df26..95ff9759c 100644 --- a/contrib/gitian-descriptors/gitian-win.yml +++ b/contrib/gitian-descriptors/gitian-win.yml @@ -116,7 +116,6 @@ script: | export PATH=${WRAP_DIR}:${PATH} # Create the release tarball using (arbitrarily) the first host - export GIT_DIR="$PWD/.git" ./autogen.sh CONFIG_SITE=${BASEPREFIX}/`echo "${HOSTS}" | awk '{print $1;}'`/share/config.site ./configure --prefix=/ make dist @@ -132,6 +131,9 @@ script: | cp ../$SOURCEDIST $OUTDIR/src popd + # Workaround for tarball not building with the bare tag version (prep) + make -C src obj/build.h + ORIGPATH="$PATH" # Extract the release tarball into a dir for each host and build for i in ${HOSTS}; do @@ -142,6 +144,11 @@ script: | mkdir -p ${INSTALLPATH} tar --strip-components=1 -xf ../$SOURCEDIST + # Workaround for tarball not building with the bare tag version + echo '#!/bin/true' >share/genbuild.sh + mkdir src/obj + cp ../src/obj/build.h src/obj/ + CONFIG_SITE=${BASEPREFIX}/${i}/share/config.site ./configure --prefix=/ --disable-ccache --disable-maintainer-mode --disable-dependency-tracking ${CONFIGFLAGS} CFLAGS="${HOST_CFLAGS}" CXXFLAGS="${HOST_CXXFLAGS}" make ${MAKEOPTS} make ${MAKEOPTS} -C src check-security From c1470a058f21bf98d83b9dc345c61626b87035cc Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 18 Aug 2017 22:09:58 +0200 Subject: [PATCH 093/382] test: Increase initial RPC timeout to 60 seconds When running the tests locally with a parallelism of 4 on an otherwise busy system, RPC can take quite a wait to come up. Change the timeout to 60 seconds just to be safe. --- test/functional/test_framework/test_node.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 66f89d43f..4b5dc9a79 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -65,9 +65,9 @@ def start(self): def wait_for_rpc_connection(self): """Sets up an RPC connection to the bitcoind process. Returns False if unable to connect.""" - - # Wait for up to 10 seconds for the RPC server to respond - for _ in range(40): + timeout_s = 60 # Wait for up to 60 seconds for the RPC server to respond + poll_per_s = 4 # Poll at a rate of four times per second + for _ in range(timeout_s*poll_per_s): assert not self.process.poll(), "bitcoind exited with status %i during initialization" % self.process.returncode try: self.rpc = get_rpc_proxy(rpc_url(self.datadir, self.index, self.rpchost), self.index, coveragedir=self.coverage_dir) @@ -86,7 +86,7 @@ def wait_for_rpc_connection(self): except ValueError as e: # cookie file not found and no rpcuser or rpcassword. bitcoind still starting if "No RPC credentials" not in str(e): raise - time.sleep(0.25) + time.sleep(1.0 / poll_per_s) raise AssertionError("Unable to connect to bitcoind") def get_wallet_rpc(self, wallet_name): From ee4d1493e2a871b26201580c5a990a1df7a5e3f7 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Fri, 7 Jul 2017 17:09:55 -0400 Subject: [PATCH 094/382] Drop upgrade-cancel callback registration for a generic "resumeable" Instead of passing a StartShutdown reference all the way up from txdb, give ShowProgress a "resumeable" boolean, which is used to inform the user if the action will be resumed, but cancel is always allowed by just calling StartShutdown(). --- src/qt/splashscreen.cpp | 32 +++++++++++--------------------- src/qt/splashscreen.h | 4 ---- src/txdb.cpp | 6 +++--- src/ui_interface.h | 10 +++++----- src/validation.cpp | 12 ++++++------ 5 files changed, 25 insertions(+), 39 deletions(-) diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp index 1b7cc6923..a1fbba963 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -142,8 +142,8 @@ SplashScreen::~SplashScreen() bool SplashScreen::eventFilter(QObject * obj, QEvent * ev) { if (ev->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast(ev); - if(keyEvent->text()[0] == 'q' && breakAction != nullptr) { - breakAction(); + if(keyEvent->text()[0] == 'q') { + StartShutdown(); } } return QObject::eventFilter(obj, ev); @@ -170,27 +170,18 @@ static void InitMessage(SplashScreen *splash, const std::string &message) Q_ARG(QColor, QColor(55,55,55))); } -static void ShowProgress(SplashScreen *splash, const std::string &title, int nProgress) +static void ShowProgress(SplashScreen *splash, const std::string &title, int nProgress, bool resume_possible) { - InitMessage(splash, title + strprintf("%d", nProgress) + "%"); -} - -void SplashScreen::setBreakAction(const std::function &action) -{ - breakAction = action; -} - -static void SetProgressBreakAction(SplashScreen *splash, const std::function &action) -{ - QMetaObject::invokeMethod(splash, "setBreakAction", - Qt::QueuedConnection, - Q_ARG(std::function, action)); + InitMessage(splash, title + std::string("\n") + + (resume_possible ? _("(press q to shutdown and continue later)") + : _("press q to shutdown")) + + strprintf("\n%d", nProgress) + "%"); } #ifdef ENABLE_WALLET void SplashScreen::ConnectWallet(CWallet* wallet) { - wallet->ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2)); + wallet->ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2, false)); connectedWallets.push_back(wallet); } #endif @@ -199,8 +190,7 @@ void SplashScreen::subscribeToCoreSignals() { // Connect signals to client uiInterface.InitMessage.connect(boost::bind(InitMessage, this, _1)); - uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2)); - uiInterface.SetProgressBreakAction.connect(boost::bind(SetProgressBreakAction, this, _1)); + uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2, _3)); #ifdef ENABLE_WALLET uiInterface.LoadWallet.connect(boost::bind(&SplashScreen::ConnectWallet, this, _1)); #endif @@ -210,10 +200,10 @@ void SplashScreen::unsubscribeFromCoreSignals() { // Disconnect signals from client uiInterface.InitMessage.disconnect(boost::bind(InitMessage, this, _1)); - uiInterface.ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2)); + uiInterface.ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2, _3)); #ifdef ENABLE_WALLET for (CWallet* const & pwallet : connectedWallets) { - pwallet->ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2)); + pwallet->ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2, false)); } #endif } diff --git a/src/qt/splashscreen.h b/src/qt/splashscreen.h index a88ebb98a..c6cfd503f 100644 --- a/src/qt/splashscreen.h +++ b/src/qt/splashscreen.h @@ -36,8 +36,6 @@ public Q_SLOTS: /** Show message and progress */ void showMessage(const QString &message, int alignment, const QColor &color); - /** Sets the break action */ - void setBreakAction(const std::function &action); protected: bool eventFilter(QObject * obj, QEvent * ev); @@ -55,8 +53,6 @@ public Q_SLOTS: int curAlignment; QList connectedWallets; - - std::function breakAction; }; #endif // BITCOIN_QT_SPLASHSCREEN_H diff --git a/src/txdb.cpp b/src/txdb.cpp index 4c1b04cd9..bbad3899e 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -371,9 +371,9 @@ bool CCoinsViewDB::Upgrade() { int64_t count = 0; LogPrintf("Upgrading utxo-set database...\n"); LogPrintf("[0%%]..."); + uiInterface.ShowProgress(_("Upgrading UTXO database"), 0, true); size_t batch_size = 1 << 24; CDBBatch batch(db); - uiInterface.SetProgressBreakAction(StartShutdown); int reportDone = 0; std::pair key; std::pair prev_key = {DB_COINS, uint256()}; @@ -386,7 +386,7 @@ bool CCoinsViewDB::Upgrade() { if (count++ % 256 == 0) { uint32_t high = 0x100 * *key.second.begin() + *(key.second.begin() + 1); int percentageDone = (int)(high * 100.0 / 65536.0 + 0.5); - uiInterface.ShowProgress(_("Upgrading UTXO database") + "\n"+ _("(press q to shutdown and continue later)") + "\n", percentageDone); + uiInterface.ShowProgress(_("Upgrading UTXO database"), percentageDone, true); if (reportDone < percentageDone/10) { // report max. every 10% step LogPrintf("[%d%%]...", percentageDone); @@ -420,7 +420,7 @@ bool CCoinsViewDB::Upgrade() { } db.WriteBatch(batch); db.CompactRange({DB_COINS, uint256()}, key); - uiInterface.SetProgressBreakAction(std::function()); + uiInterface.ShowProgress("", 100, false); LogPrintf("[%s].\n", ShutdownRequested() ? "CANCELLED" : "DONE"); return !ShutdownRequested(); } diff --git a/src/ui_interface.h b/src/ui_interface.h index 762dd19b1..7f68c578e 100644 --- a/src/ui_interface.h +++ b/src/ui_interface.h @@ -94,11 +94,11 @@ class CClientUIInterface /** A wallet has been loaded. */ boost::signals2::signal LoadWallet; - /** Show progress e.g. for verifychain */ - boost::signals2::signal ShowProgress; - - /** Set progress break action (possible "cancel button" triggers that action) */ - boost::signals2::signal action)> SetProgressBreakAction; + /** + * Show progress e.g. for verifychain. + * resume_possible indicates shutting down now will result in the current progress action resuming upon restart. + */ + boost::signals2::signal ShowProgress; /** New block has been accepted */ boost::signals2::signal NotifyBlockTip; diff --git a/src/validation.cpp b/src/validation.cpp index 8bd23a0f1..e0df39708 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -3563,12 +3563,12 @@ bool LoadChainTip(const CChainParams& chainparams) CVerifyDB::CVerifyDB() { - uiInterface.ShowProgress(_("Verifying blocks..."), 0); + uiInterface.ShowProgress(_("Verifying blocks..."), 0, false); } CVerifyDB::~CVerifyDB() { - uiInterface.ShowProgress("", 100); + uiInterface.ShowProgress("", 100, false); } bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth) @@ -3598,7 +3598,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, LogPrintf("[%d%%]...", percentageDone); reportDone = percentageDone/10; } - uiInterface.ShowProgress(_("Verifying blocks..."), percentageDone); + uiInterface.ShowProgress(_("Verifying blocks..."), percentageDone, false); if (pindex->nHeight < chainActive.Height()-nCheckDepth) break; if (fPruneMode && !(pindex->nStatus & BLOCK_HAVE_DATA)) { @@ -3649,7 +3649,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, CBlockIndex *pindex = pindexState; while (pindex != chainActive.Tip()) { boost::this_thread::interruption_point(); - uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99, 100 - (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * 50)))); + uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99, 100 - (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * 50))), false); pindex = chainActive.Next(pindex); CBlock block; if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus())) @@ -3696,7 +3696,7 @@ bool ReplayBlocks(const CChainParams& params, CCoinsView* view) if (hashHeads.empty()) return true; // We're already in a consistent state. if (hashHeads.size() != 2) return error("ReplayBlocks(): unknown inconsistent state"); - uiInterface.ShowProgress(_("Replaying blocks..."), 0); + uiInterface.ShowProgress(_("Replaying blocks..."), 0, false); LogPrintf("Replaying blocks\n"); const CBlockIndex* pindexOld = nullptr; // Old tip during the interrupted flush. @@ -3747,7 +3747,7 @@ bool ReplayBlocks(const CChainParams& params, CCoinsView* view) cache.SetBestBlock(pindexNew->GetBlockHash()); cache.Flush(); - uiInterface.ShowProgress("", 100); + uiInterface.ShowProgress("", 100, false); return true; } From cd0ea487422028bec1f5df62ab4c57909c2bcc90 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Tue, 22 Aug 2017 13:32:17 -0400 Subject: [PATCH 095/382] Changing -txindex requires -reindex, not -reindex-chainstate --- src/init.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/init.cpp b/src/init.cpp index 55a58cc7b..ae25c341f 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1428,7 +1428,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) // Check for changed -txindex state if (fTxIndex != gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)) { - strLoadError = _("You need to rebuild the database using -reindex-chainstate to change -txindex"); + strLoadError = _("You need to rebuild the database using -reindex to change -txindex"); break; } From f1708ef89a38efe857daeb4252c6dfab5b7c258e Mon Sep 17 00:00:00 2001 From: practicalswift Date: Tue, 22 Aug 2017 22:50:03 +0200 Subject: [PATCH 096/382] Add recommendation: By default, declare single-argument constructors `explicit` --- doc/developer-notes.md | 6 ++++++ src/test/addrman_tests.cpp | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/developer-notes.md b/doc/developer-notes.md index ad15aa662..2f04d6ee6 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -332,6 +332,12 @@ C++ data structures - *Rationale*: Ensure determinism by avoiding accidental use of uninitialized values. Also, static analyzers balk about this. +- By default, declare single-argument constructors `explicit`. + + - *Rationale*: This is a precaution to avoid unintended conversions that might + arise when single-argument constructors are used as implicit conversion + functions. + - Use explicitly signed or unsigned `char`s, or even better `uint8_t` and `int8_t`. Do not use bare `char` unless it is to pass to a third-party API. This type can be signed or unsigned depending on the architecture, which can diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp index 2ad22d34a..7be29c6d6 100644 --- a/src/test/addrman_tests.cpp +++ b/src/test/addrman_tests.cpp @@ -15,7 +15,7 @@ class CAddrManTest : public CAddrMan uint64_t state; public: - CAddrManTest(bool makeDeterministic = true) + explicit CAddrManTest(bool makeDeterministic = true) { state = 1; From c0019924406e1ce8368465c768de11019ad5eeed Mon Sep 17 00:00:00 2001 From: MeshCollider Date: Wed, 23 Aug 2017 19:47:56 +1200 Subject: [PATCH 097/382] Fix potential null dereferences --- src/miner.cpp | 1 + src/net_processing.cpp | 2 ++ src/rpc/blockchain.cpp | 3 +++ src/validation.cpp | 2 ++ 4 files changed, 8 insertions(+) diff --git a/src/miner.cpp b/src/miner.cpp index f1942ec57..249ea172a 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -145,6 +145,7 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc LOCK2(cs_main, mempool.cs); CBlockIndex* pindexPrev = chainActive.Tip(); + assert(pindexPrev != nullptr); nHeight = pindexPrev->nHeight + 1; pblock->nVersion = ComputeBlockVersion(pindexPrev, chainparams.GetConsensus()); diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 596ae1139..9a446a930 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -281,6 +281,7 @@ void FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) { fUpdateConnectionTime = false; LOCK(cs_main); CNodeState *state = State(nodeid); + assert(state != nullptr); if (state->fSyncStarted) nSyncStarted--; @@ -315,6 +316,7 @@ bool MarkBlockAsReceived(const uint256& hash) { std::map::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash); if (itInFlight != mapBlocksInFlight.end()) { CNodeState *state = State(itInFlight->second.first); + assert(state != nullptr); state->nBlocksInFlightValidHeaders -= itInFlight->second.second->fValidatedHeaders; if (state->nBlocksInFlightValidHeaders == 0 && itInFlight->second.second->fValidatedHeaders) { // Last validated block on the queue was received. diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 34f553f3b..bcb179264 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -812,6 +812,7 @@ static void ApplyStats(CCoinsStats &stats, CHashWriter& ss, const uint256& hash, static bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats) { std::unique_ptr pcursor(view->Cursor()); + assert(pcursor); CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); stats.hashBlock = pcursor->GetBestBlock(); @@ -1514,6 +1515,8 @@ UniValue getchaintxstats(const JSONRPCRequest& request) pindex = chainActive.Tip(); } } + + assert(pindex != nullptr); if (blockcount < 1 || blockcount >= pindex->nHeight) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block count: should be between 1 and the block's height"); diff --git a/src/validation.cpp b/src/validation.cpp index d1a8b8460..51dad02e8 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -251,6 +251,8 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool AssertLockHeld(mempool.cs); CBlockIndex* tip = chainActive.Tip(); + assert(tip != nullptr); + CBlockIndex index; index.pprev = tip; // CheckSequenceLocks() uses chainActive.Height()+1 to evaluate From ecb11f561c3bff0aa386d5421356c4e910ff8a91 Mon Sep 17 00:00:00 2001 From: Andreas Schildbach Date: Tue, 27 Jun 2017 08:35:12 +0200 Subject: [PATCH 098/382] Document the non-strict-DER-conformance of one test in tx_valid.json. In a signature, it contains an ASN1 integer which isn't strict-DER conformant due to excessive 0xff padding: 0xffda47bfc776bcd269da4832626ac332adfca6dd835e8ecd83cd1ebe7d709b0e --- src/test/data/tx_valid.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/data/tx_valid.json b/src/test/data/tx_valid.json index e6b382af1..ad74b7cf1 100644 --- a/src/test/data/tx_valid.json +++ b/src/test/data/tx_valid.json @@ -45,7 +45,7 @@ "01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", "P2SH"], ["The following is f7fdd091fa6d8f5e7a8c2458f5c38faffff2d3f1406b6e4fe2c99dcc0d2d1cbb"], -["It caught a bug in the workaround for 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63 in an overly simple implementation"], +["It caught a bug in the workaround for 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63 in an overly simple implementation. In a signature, it contains an ASN1 integer which isn't strict-DER conformant due to being negative, which doesn't make sense in a signature. Before BIP66 activated, it was a valid signature. After it activated, it's not valid any more."], [[["b464e85df2a238416f8bdae11d120add610380ea07f4ef19c5f9dfd472f96c3d", 0, "DUP HASH160 0x14 0xbef80ecf3a44500fda1bc92176e442891662aed2 EQUALVERIFY CHECKSIG"], ["b7978cc96e59a8b13e0865d3f95657561a7f725be952438637475920bac9eb21", 1, "DUP HASH160 0x14 0xbef80ecf3a44500fda1bc92176e442891662aed2 EQUALVERIFY CHECKSIG"]], "01000000023d6cf972d4dff9c519eff407ea800361dd0a121de1da8b6f4138a2f25de864b4000000008a4730440220ffda47bfc776bcd269da4832626ac332adfca6dd835e8ecd83cd1ebe7d709b0e022049cffa1cdc102a0b56e0e04913606c70af702a1149dc3b305ab9439288fee090014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff21ebc9ba20594737864352e95b727f1a565756f9d365083eb1a8596ec98c97b7010000008a4730440220503ff10e9f1e0de731407a4a245531c9ff17676eda461f8ceeb8c06049fa2c810220c008ac34694510298fa60b3f000df01caa244f165b727d4896eb84f81e46bcc4014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff01f0da5200000000001976a914857ccd42dded6df32949d4646dfa10a92458cfaa88ac00000000", "P2SH"], From fa14b6797088065f890b33c1a2c7f6ee4af20c6c Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Wed, 23 Aug 2017 10:10:14 -0400 Subject: [PATCH 099/382] [doc] build-windows: Mention that only trusty works --- doc/build-windows.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/build-windows.md b/doc/build-windows.md index 9549a4b9d..ca1a05dfb 100644 --- a/doc/build-windows.md +++ b/doc/build-windows.md @@ -6,6 +6,9 @@ Below are some notes on how to build Bitcoin Core for Windows. Most developers use cross-compilation from Ubuntu to build executables for Windows. This is also used to build the release binaries. +Currently only building on Ubuntu Trusty 14.04 is supported. +Other versions are unsupported or known to be broken (e.g. Ubuntu Xenial 16.04). + While there are potentially a number of ways to build on Windows (for example using msys / mingw-w64), using the Windows Subsystem For Linux is the most straightforward. If you are building with another method, please contribute the instructions here for others who are running versions From 2b4ea520b717b3ca894adbac17796786667764d3 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Wed, 16 Aug 2017 15:46:48 -0400 Subject: [PATCH 100/382] [tests] fix timeout issues from TestNode Fixes a couple of bugs from the introduction of TestNode: - test scripts were no longer able to specify a custom timeout for starting a node. Therefore tests with nodes that take a long time to start up (eg pruning.py) would fail. - the test for whether a node has failed on start up was broken by changing 'assert x is None' to 'assert not x'. Since subprocess.poll() can return None (indicating the node is still running) or 0 (indicating the node exited with return code 0), this was a regression. --- test/functional/dbcrash.py | 7 ++++--- test/functional/test_framework/test_node.py | 14 +++++++++----- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/test/functional/dbcrash.py b/test/functional/dbcrash.py index 8339305f5..a7fcc411c 100755 --- a/test/functional/dbcrash.py +++ b/test/functional/dbcrash.py @@ -64,7 +64,8 @@ def __init__(self): self.extra_args = [self.node0_args, self.node1_args, self.node2_args, self.node3_args] def setup_network(self): - self.setup_nodes() + # Need a bit of extra time for the nodes to start up for this test + self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args, timewait=90) # Leave them unconnected, we'll use submitblock directly in this test def restart_node(self, node_index, expected_tip): @@ -74,10 +75,10 @@ def restart_node(self, node_index, expected_tip): after 60 seconds. Returns the utxo hash of the given node.""" time_start = time.time() - while time.time() - time_start < 60: + while time.time() - time_start < 120: try: # Any of these RPC calls could throw due to node crash - self.nodes[node_index] = self.start_node(node_index, self.options.tmpdir, self.extra_args[node_index]) + self.nodes[node_index] = self.start_node(node_index, self.options.tmpdir, self.extra_args[node_index], timewait=90) self.nodes[node_index].waitforblock(expected_tip) utxo_hash = self.nodes[node_index].gettxoutsetinfo()['hash_serialized_2'] return utxo_hash diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 4b5dc9a79..a803df5b4 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -34,7 +34,11 @@ def __init__(self, i, dirname, extra_args, rpchost, timewait, binary, stderr, mo self.index = i self.datadir = os.path.join(dirname, "node" + str(i)) self.rpchost = rpchost - self.rpc_timeout = timewait + if timewait: + self.rpc_timeout = timewait + else: + # Wait for up to 60 seconds for the RPC server to respond + self.rpc_timeout = 60 if binary is None: self.binary = os.getenv("BITCOIND", "bitcoind") else: @@ -65,10 +69,10 @@ def start(self): def wait_for_rpc_connection(self): """Sets up an RPC connection to the bitcoind process. Returns False if unable to connect.""" - timeout_s = 60 # Wait for up to 60 seconds for the RPC server to respond - poll_per_s = 4 # Poll at a rate of four times per second - for _ in range(timeout_s*poll_per_s): - assert not self.process.poll(), "bitcoind exited with status %i during initialization" % self.process.returncode + # Poll at a rate of four times per second + poll_per_s = 4 + for _ in range(poll_per_s * self.rpc_timeout): + assert self.process.poll() is None, "bitcoind exited with status %i during initialization" % self.process.returncode try: self.rpc = get_rpc_proxy(rpc_url(self.datadir, self.index, self.rpchost), self.index, coveragedir=self.coverage_dir) self.rpc.getblockcount() From 79191f51b50314920b387f5f7322427d14cbab19 Mon Sep 17 00:00:00 2001 From: Joe Harvell Date: Wed, 23 Aug 2017 12:48:00 -0600 Subject: [PATCH 101/382] Add option -stdinrpcpass to allow RPC password to be read from standard input --- src/bitcoin-cli.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 8bec0289f..fca6083ea 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -45,7 +45,8 @@ std::string HelpMessageCli() strUsage += HelpMessageOpt("-rpcuser=", _("Username for JSON-RPC connections")); strUsage += HelpMessageOpt("-rpcpassword=", _("Password for JSON-RPC connections")); strUsage += HelpMessageOpt("-rpcclienttimeout=", strprintf(_("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)"), DEFAULT_HTTP_CLIENT_TIMEOUT)); - strUsage += HelpMessageOpt("-stdin", _("Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases)")); + strUsage += HelpMessageOpt("-stdinrpcpass", strprintf(_("Read RPC password from standard input as a single line. When combined with -stdin, the first line from standard input is used for the RPC password."))); + strUsage += HelpMessageOpt("-stdin", _("Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases). When combined with -stdinrpcpass, the first line from standard input is used for the RPC password.")); strUsage += HelpMessageOpt("-rpcwallet=", _("Send RPC for non-default wallet on RPC server (argument is wallet filename in bitcoind directory, required if bitcoind/-Qt runs with multiple wallets)")); return strUsage; @@ -190,7 +191,7 @@ static void http_error_cb(enum evhttp_request_error err, void *ctx) } #endif -UniValue CallRPC(const std::string& strMethod, const UniValue& params) +static UniValue CallRPC(const std::string& strMethod, const UniValue& params) { std::string host; // In preference order, we choose the following for the port: @@ -222,7 +223,7 @@ UniValue CallRPC(const std::string& strMethod, const UniValue& params) // Try fall back to cookie-based authentication if no password is provided if (!GetAuthCookie(&strRPCUserColonPass)) { throw std::runtime_error(strprintf( - _("Could not locate RPC credentials. No authentication cookie could be found, and no rpcpassword is set in the configuration file (%s)"), + _("Could not locate RPC credentials. No authentication cookie could be found, and RPC password is not set. See -rpcpassword and -stdinrpcpass. Configuration file: (%s)"), GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)).string().c_str())); } @@ -293,6 +294,12 @@ int CommandLineRPC(int argc, char *argv[]) argc--; argv++; } + std::string rpcPass; + if (gArgs.GetBoolArg("-stdinrpcpass", false)) { + if(!std::getline(std::cin,rpcPass)) + throw std::runtime_error("-stdinrpcpass specified but failed to read from standard input"); + gArgs.ForceSetArg("-rpcpassword", rpcPass); + } std::vector args = std::vector(&argv[1], &argv[argc]); if (gArgs.GetBoolArg("-stdin", false)) { // Read one arg per line from stdin and append From c6a995e7e5e6ae37dc1684f6060038e96864e947 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Thu, 24 Aug 2017 09:19:00 +0200 Subject: [PATCH 102/382] Improve readability of DecodeBase58Check(...) --- src/base58.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/base58.cpp b/src/base58.cpp index 3802f953f..56acf392c 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -136,7 +136,7 @@ bool DecodeBase58Check(const char* psz, std::vector& vchRet) } // re-calculate the checksum, ensure it matches the included 4-byte checksum uint256 hash = Hash(vchRet.begin(), vchRet.end() - 4); - if (memcmp(&hash, &vchRet.end()[-4], 4) != 0) { + if (memcmp(&hash, &vchRet[vchRet.size() - 4], 4) != 0) { vchRet.clear(); return false; } From de9a1db2ed14e0c75ffd82dc031f7ad30c56d195 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Thu, 24 Aug 2017 14:12:21 -0400 Subject: [PATCH 103/382] Acquire cs_main lock before cs_wallet during wallet initialization CWallet::MarkConflicted may acquire the cs_main lock after CWalletDB::LoadWallet acquires the cs_wallet lock during wallet initialization. (CWalletDB::LoadWallet calls ReadKeyValue which calls CWallet::LoadToWallet which calls CWallet::MarkConflicted). This is the opposite order that cs_main and cs_wallet locks are acquired in the rest of the code, and so leads to POTENTIAL DEADLOCK DETECTED errors if bitcoin is built with -DDEBUG_LOCKORDER. This commit changes CWallet::LoadWallet (which calls CWalletDB::LoadWallet) to acquire both locks in the standard order. It also fixes some tests that were acquiring wallet and main locks out of order and failed with the new locking in CWallet::LoadWallet. Error was reported by Luke Dashjr in https://botbot.me/freenode/bitcoin-core-dev/msg/90244330/ --- src/wallet/test/wallet_tests.cpp | 17 ++++++++++------- src/wallet/wallet.cpp | 3 ++- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 4a2cc9a13..5ebacd57d 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -364,6 +364,12 @@ BOOST_AUTO_TEST_CASE(ApproximateBestSubset) empty_wallet(); } +static void AddKey(CWallet& wallet, const CKey& key) +{ + LOCK(wallet.cs_wallet); + wallet.AddKeyPubKey(key, key.GetPubKey()); +} + BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) { LOCK(cs_main); @@ -379,8 +385,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) // and new block files. { CWallet wallet; - LOCK(wallet.cs_wallet); - wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); + AddKey(wallet, coinbaseKey); BOOST_CHECK_EQUAL(nullBlock, wallet.ScanForWalletTransactions(oldTip)); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 100 * COIN); } @@ -393,8 +398,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) // file. { CWallet wallet; - LOCK(wallet.cs_wallet); - wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); + AddKey(wallet, coinbaseKey); BOOST_CHECK_EQUAL(oldTip, wallet.ScanForWalletTransactions(oldTip)); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN); } @@ -599,8 +603,7 @@ class ListCoinsTestingSetup : public TestChain100Setup wallet.reset(new CWallet(std::unique_ptr(new CWalletDBWrapper(&bitdb, "wallet_test.dat")))); bool firstRun; wallet->LoadWallet(firstRun); - LOCK(wallet->cs_wallet); - wallet->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); + AddKey(*wallet, coinbaseKey); wallet->ScanForWalletTransactions(chainActive.Genesis()); } @@ -635,7 +638,7 @@ class ListCoinsTestingSetup : public TestChain100Setup BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup) { std::string coinbaseAddress = coinbaseKey.GetPubKey().GetID().ToString(); - LOCK(wallet->cs_wallet); + LOCK2(cs_main, wallet->cs_wallet); // Confirm ListCoins initially returns 1 coin grouped under coinbaseKey // address. diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 291bcc7a2..ecf473a96 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3107,13 +3107,14 @@ CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, const CCoinControl& coin_c DBErrors CWallet::LoadWallet(bool& fFirstRunRet) { + LOCK2(cs_main, cs_wallet); + fFirstRunRet = false; DBErrors nLoadWalletRet = CWalletDB(*dbw,"cr+").LoadWallet(this); if (nLoadWalletRet == DB_NEED_REWRITE) { if (dbw->Rewrite("\x04pool")) { - LOCK(cs_wallet); setInternalKeyPool.clear(); setExternalKeyPool.clear(); m_pool_key_to_index.clear(); From 0063d2c3dce9d9c1678197d2c65ee878793d1ef9 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Thu, 17 Aug 2017 11:35:45 -0400 Subject: [PATCH 104/382] [tests] Make p2p-leaktests.py more robust --- test/functional/p2p-leaktests.py | 9 +++++++-- test/functional/test_framework/mininode.py | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/test/functional/p2p-leaktests.py b/test/functional/p2p-leaktests.py index f0d4d9a8b..26f6f86af 100755 --- a/test/functional/p2p-leaktests.py +++ b/test/functional/p2p-leaktests.py @@ -140,6 +140,9 @@ def run_test(self): [conn.disconnect_node() for conn in connections] + # Wait until all connections are closed + wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 0) + # Make sure no unexpected messages came in assert(no_version_bannode.unexpected_msg == False) assert(no_version_idlenode.unexpected_msg == False) @@ -158,8 +161,10 @@ def run_test(self): allowed_service_bit5_node.add_connection(connections[5]) allowed_service_bit7_node.add_connection(connections[6]) - wait_until(lambda: allowed_service_bit5_node.message_count["verack"], timeout=10, lock=mininode_lock) - wait_until(lambda: allowed_service_bit7_node.message_count["verack"], timeout=10, lock=mininode_lock) + NetworkThread().start() # Network thread stopped when all previous NodeConnCBs disconnected. Restart it + + wait_until(lambda: allowed_service_bit5_node.message_count["verack"], lock=mininode_lock) + wait_until(lambda: allowed_service_bit7_node.message_count["verack"], lock=mininode_lock) if __name__ == '__main__': P2PLeakTest().main() diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py index d0753276d..592453023 100755 --- a/test/functional/test_framework/mininode.py +++ b/test/functional/test_framework/mininode.py @@ -1837,6 +1837,7 @@ def run(self): disconnected.append(obj) [ obj.handle_close() for obj in disconnected ] asyncore.loop(0.1, use_poll=True, map=mininode_socket_map, count=1) + logger.debug("Network thread closing") # An exception we can raise if we detect a potential disconnect From 33366768afe21ff85259c0712176604aab56d9ae Mon Sep 17 00:00:00 2001 From: Akio Nakamura Date: Thu, 10 Aug 2017 16:53:59 +0900 Subject: [PATCH 105/382] Fix getchaintxstats() 1. Calculate nblocks more adaptive. If not specify nblocks-parameter, illegal parameter error will happen when target block height is below blocks for 1 month. To avoid this error, set default nblocks to min(blocks for 1 month, target block's height - 1) And allowing 0 so that this RPC works good even if target block is genesis block or 1st block. 2. Correct error message. nblocks accepts [0 .. block's height -1] . so fix as following: "Invalid block count: should be between 0 and the block's height - 1" 3. Add check 0-divide. If nTimeDiff = 0 then returns {... "txrate":} and bitcoin-cli cannot handle the response. To avoid this error, do not return "txrate" if nTimeDiff = 0. 4. Add following 3 elements to the return object. 1) 'window_block_count' : Size of the window in number of blocks. 2) 'window_tx_count' : The number of transactions in the window. 3) 'window_interval' : The elapsed time in the window. They clarify how 'txrate' is calculated. 2) and 3) are returned only if 'window_block_count' is a positive value. 5. Improve help text for 'time' as following. 'The timestamp for the final block in the window in UNIX format. --- src/rpc/blockchain.cpp | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 34f553f3b..f7fef5ffe 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1477,9 +1477,12 @@ UniValue getchaintxstats(const JSONRPCRequest& request) "2. \"blockhash\" (string, optional) The hash of the block that ends the window.\n" "\nResult:\n" "{\n" - " \"time\": xxxxx, (numeric) The timestamp for the statistics in UNIX format.\n" - " \"txcount\": xxxxx, (numeric) The total number of transactions in the chain up to that point.\n" - " \"txrate\": x.xx, (numeric) The average rate of transactions per second in the window.\n" + " \"time\": xxxxx, (numeric) The timestamp for the final block in the window in UNIX format.\n" + " \"txcount\": xxxxx, (numeric) The total number of transactions in the chain up to that point.\n" + " \"window_block_count\": xxxxx, (numeric) Size of the window in number of blocks.\n" + " \"window_tx_count\": xxxxx, (numeric) The number of transactions in the window. Only returned if \"window_block_count\" is > 0.\n" + " \"window_interval\": xxxxx, (numeric) The elapsed time in the window in seconds. Only returned if \"window_block_count\" is > 0.\n" + " \"txrate\": x.xx, (numeric) The average rate of transactions per second in the window. Only returned if \"window_interval\" is > 0.\n" "}\n" "\nExamples:\n" + HelpExampleCli("getchaintxstats", "") @@ -1489,10 +1492,6 @@ UniValue getchaintxstats(const JSONRPCRequest& request) const CBlockIndex* pindex; int blockcount = 30 * 24 * 60 * 60 / Params().GetConsensus().nPowTargetSpacing; // By default: 1 month - if (!request.params[0].isNull()) { - blockcount = request.params[0].get_int(); - } - bool havehash = !request.params[1].isNull(); uint256 hash; if (havehash) { @@ -1515,8 +1514,14 @@ UniValue getchaintxstats(const JSONRPCRequest& request) } } - if (blockcount < 1 || blockcount >= pindex->nHeight) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block count: should be between 1 and the block's height"); + if (request.params[0].isNull()) { + blockcount = std::max(0, std::min(blockcount, pindex->nHeight - 1)); + } else { + blockcount = request.params[0].get_int(); + + if (blockcount < 0 || (blockcount > 0 && blockcount >= pindex->nHeight)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block count: should be between 0 and the block's height - 1"); + } } const CBlockIndex* pindexPast = pindex->GetAncestor(pindex->nHeight - blockcount); @@ -1526,7 +1531,14 @@ UniValue getchaintxstats(const JSONRPCRequest& request) UniValue ret(UniValue::VOBJ); ret.push_back(Pair("time", (int64_t)pindex->nTime)); ret.push_back(Pair("txcount", (int64_t)pindex->nChainTx)); - ret.push_back(Pair("txrate", ((double)nTxDiff) / nTimeDiff)); + ret.push_back(Pair("window_block_count", blockcount)); + if (blockcount > 0) { + ret.push_back(Pair("window_tx_count", nTxDiff)); + ret.push_back(Pair("window_interval", nTimeDiff)); + if (nTimeDiff > 0) { + ret.push_back(Pair("txrate", ((double)nTxDiff) / nTimeDiff)); + } + } return ret; } From 07704c1b3768d6c290046c783063644fc7b7d1da Mon Sep 17 00:00:00 2001 From: Akio Nakamura Date: Mon, 14 Aug 2017 16:29:00 +0900 Subject: [PATCH 106/382] Add some tests for getchaintxstats 1. Add a test for no parameters. 2. Add a test for the block's height = 1. 3. Add a test for nblocks is out of range. --- test/functional/blockchain.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/functional/blockchain.py b/test/functional/blockchain.py index 0812e1b0d..99594d999 100755 --- a/test/functional/blockchain.py +++ b/test/functional/blockchain.py @@ -56,6 +56,28 @@ def _test_getchaintxstats(self): # we have to round because of binary math assert_equal(round(chaintxstats['txrate'] * 600, 10), Decimal(1)) + b1 = self.nodes[0].getblock(self.nodes[0].getblockhash(1)) + b200 = self.nodes[0].getblock(self.nodes[0].getblockhash(200)) + time_diff = b200['mediantime'] - b1['mediantime'] + + chaintxstats = self.nodes[0].getchaintxstats() + assert_equal(chaintxstats['time'], b200['time']) + assert_equal(chaintxstats['txcount'], 201) + assert_equal(chaintxstats['window_block_count'], 199) + assert_equal(chaintxstats['window_tx_count'], 199) + assert_equal(chaintxstats['window_interval'], time_diff) + assert_equal(round(chaintxstats['txrate'] * time_diff, 10), Decimal(199)) + + chaintxstats = self.nodes[0].getchaintxstats(blockhash=b1['hash']) + assert_equal(chaintxstats['time'], b1['time']) + assert_equal(chaintxstats['txcount'], 2) + assert_equal(chaintxstats['window_block_count'], 0) + assert('window_tx_count' not in chaintxstats) + assert('window_interval' not in chaintxstats) + assert('txrate' not in chaintxstats) + + assert_raises_jsonrpc(-8, "Invalid block count: should be between 0 and the block's height - 1", self.nodes[0].getchaintxstats, 201) + def _test_gettxoutsetinfo(self): node = self.nodes[0] res = node.gettxoutsetinfo() From 82dd7195e1fb943f9cd45a48188f9678219c0206 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 25 Aug 2017 12:39:30 +0200 Subject: [PATCH 107/382] rpc: Write authcookie atomically Use POSIX rename atomicity at the `bitcoind` side to create a working cookie atomically: - Write `.cookie.tmp`, close file - Rename `.cookie.tmp` to `.cookie` This avoids clients reading invalid/partial cookies as in #11129. --- src/rpc/protocol.cpp | 21 ++++++++++++++++----- src/rpc/protocol.h | 2 -- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp index db0626b5e..dc6bcec38 100644 --- a/src/rpc/protocol.cpp +++ b/src/rpc/protocol.cpp @@ -66,9 +66,14 @@ static const std::string COOKIEAUTH_USER = "__cookie__"; /** Default name for auth cookie file */ static const std::string COOKIEAUTH_FILE = ".cookie"; -fs::path GetAuthCookieFile() +/** Get name of RPC authentication cookie file */ +static fs::path GetAuthCookieFile(bool temp=false) { - fs::path path(gArgs.GetArg("-rpccookiefile", COOKIEAUTH_FILE)); + std::string arg = gArgs.GetArg("-rpccookiefile", COOKIEAUTH_FILE); + if (temp) { + arg += ".tmp"; + } + fs::path path(arg); if (!path.is_complete()) path = GetDataDir() / path; return path; } @@ -84,14 +89,20 @@ bool GenerateAuthCookie(std::string *cookie_out) * these are set to 077 in init.cpp unless overridden with -sysperms. */ std::ofstream file; - fs::path filepath = GetAuthCookieFile(); - file.open(filepath.string().c_str()); + fs::path filepath_tmp = GetAuthCookieFile(true); + file.open(filepath_tmp.string().c_str()); if (!file.is_open()) { - LogPrintf("Unable to open cookie authentication file %s for writing\n", filepath.string()); + LogPrintf("Unable to open cookie authentication file %s for writing\n", filepath_tmp.string()); return false; } file << cookie; file.close(); + + fs::path filepath = GetAuthCookieFile(false); + if (!RenameOver(filepath_tmp, filepath)) { + LogPrintf("Unable to rename cookie authentication file %s to %s\n", filepath_tmp.string(), filepath.string()); + return false; + } LogPrintf("Generated RPC authentication cookie %s\n", filepath.string()); if (cookie_out) diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h index 4bd4702d6..5c9c64f67 100644 --- a/src/rpc/protocol.h +++ b/src/rpc/protocol.h @@ -91,8 +91,6 @@ UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const Un std::string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id); UniValue JSONRPCError(int code, const std::string& message); -/** Get name of RPC authentication cookie file */ -fs::path GetAuthCookieFile(); /** Generate a new RPC authentication cookie and write it to disk */ bool GenerateAuthCookie(std::string *cookie_out); /** Read the RPC authentication cookie from disk */ From 62ecce75e4c40bb9bf51a1ee5f183e28cb76c1fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= Date: Fri, 25 Aug 2017 15:52:35 +0100 Subject: [PATCH 108/382] [doc] Add RPC response notes --- doc/developer-notes.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/developer-notes.md b/doc/developer-notes.md index 2f04d6ee6..4694175a9 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -619,3 +619,8 @@ A few guidelines for introducing and reviewing new RPC interfaces: - *Rationale*: as well as complicating the implementation and interfering with the introduction of multi-wallet, wallet and non-wallet code should be separated to avoid introducing circular dependencies between code units. + +- Try to make the RPC response a JSON object. + + - *Rationale*: If a RPC response is not a JSON object then it is harder to avoid API breakage if + new data in the response is needed. From e40fa987e40c78a10263ee928b0c278778dbc01c Mon Sep 17 00:00:00 2001 From: danra Date: Fri, 25 Aug 2017 19:23:02 +0300 Subject: [PATCH 109/382] Simplify bswap_16 implementation Simplify bswap_16 implementation on platforms which don't already have it defined. This has no effect on the generated assembly; it just simplifies the source code. --- src/compat/byteswap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compat/byteswap.h b/src/compat/byteswap.h index 3c5a5c083..d93ff7413 100644 --- a/src/compat/byteswap.h +++ b/src/compat/byteswap.h @@ -35,7 +35,7 @@ #if HAVE_DECL_BSWAP_16 == 0 inline uint16_t bswap_16(uint16_t x) { - return (x >> 8) | ((x & 0x00ff) << 8); + return (x >> 8) | (x << 8); } #endif // HAVE_DECL_BSWAP16 From eefc2f36f306bf522776c83e797fd235747b85b4 Mon Sep 17 00:00:00 2001 From: danra Date: Fri, 25 Aug 2017 22:13:07 +0300 Subject: [PATCH 110/382] Move local include to before system includes Prevents accidental missing includes and hidden dependencies in the local file. --- src/compat/endian.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compat/endian.h b/src/compat/endian.h index 79d6b2fdb..dbf178f53 100644 --- a/src/compat/endian.h +++ b/src/compat/endian.h @@ -9,10 +9,10 @@ #include "config/bitcoin-config.h" #endif -#include - #include "compat/byteswap.h" +#include + #if defined(HAVE_ENDIAN_H) #include #elif defined(HAVE_SYS_ENDIAN_H) From a54c7b94f8825e9b52fec9066fe7c1d5b6f53482 Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Fri, 25 Aug 2017 14:46:07 -0500 Subject: [PATCH 111/382] Fix rounding errors in calculation of minimum change size --- src/wallet/wallet.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 291bcc7a2..503354b66 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2861,13 +2861,15 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, CWalletT // new inputs. We now know we only need the smaller fee // (because of reduced tx size) and so we should add a // change output. Only try this once. - CAmount fee_needed_for_change = GetMinimumFee(change_prototype_size, coin_control, ::mempool, ::feeEstimator, nullptr); - CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, discard_rate); - CAmount max_excess_fee = fee_needed_for_change + minimum_value_for_change; - if (nFeeRet > nFeeNeeded + max_excess_fee && nChangePosInOut == -1 && nSubtractFeeFromAmount == 0 && pick_new_inputs) { - pick_new_inputs = false; - nFeeRet = nFeeNeeded + fee_needed_for_change; - continue; + if (nChangePosInOut == -1 && nSubtractFeeFromAmount == 0 && pick_new_inputs) { + unsigned int tx_size_with_change = nBytes + change_prototype_size + 2; // Add 2 as a buffer in case increasing # of outputs changes compact size + CAmount fee_needed_with_change = GetMinimumFee(tx_size_with_change, coin_control, ::mempool, ::feeEstimator, nullptr); + CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, discard_rate); + if (nFeeRet >= fee_needed_with_change + minimum_value_for_change) { + pick_new_inputs = false; + nFeeRet = fee_needed_with_change; + continue; + } } // If we have change output already, just increase it From 6af49dddeaeec7f134e86d6f8cf839c55870b7ab Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Fri, 25 Aug 2017 14:49:44 -0500 Subject: [PATCH 112/382] Output a bit more information for fee calculation report. --- src/wallet/wallet.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 503354b66..8c0daf6cb 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2679,6 +2679,7 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, CWalletT assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); assert(txNew.nLockTime < LOCKTIME_THRESHOLD); FeeCalculation feeCalc; + CAmount nFeeNeeded; unsigned int nBytes; { std::set setCoins; @@ -2840,7 +2841,7 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, CWalletT vin.scriptWitness.SetNull(); } - CAmount nFeeNeeded = GetMinimumFee(nBytes, coin_control, ::mempool, ::feeEstimator, &feeCalc); + nFeeNeeded = GetMinimumFee(nBytes, coin_control, ::mempool, ::feeEstimator, &feeCalc); // If we made it here and we aren't even able to meet the relay fee on the next pass, give up // because we must be at the maximum allowed fee. @@ -2964,8 +2965,8 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, CWalletT } } - LogPrintf("Fee Calculation: Fee:%d Bytes:%u Tgt:%d (requested %d) Reason:\"%s\" Decay %.5f: Estimation: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n", - nFeeRet, nBytes, feeCalc.returnedTarget, feeCalc.desiredTarget, StringForFeeReason(feeCalc.reason), feeCalc.est.decay, + LogPrintf("Fee Calculation: Fee:%d Bytes:%u Needed:%d Tgt:%d (requested %d) Reason:\"%s\" Decay %.5f: Estimation: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n", + nFeeRet, nBytes, nFeeNeeded, feeCalc.returnedTarget, feeCalc.desiredTarget, StringForFeeReason(feeCalc.reason), feeCalc.est.decay, feeCalc.est.pass.start, feeCalc.est.pass.end, 100 * feeCalc.est.pass.withinTarget / (feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool), feeCalc.est.pass.withinTarget, feeCalc.est.pass.totalConfirmed, feeCalc.est.pass.inMempool, feeCalc.est.pass.leftMempool, From bc70ab5dffce5cc9894929cac7c107dcb9c25053 Mon Sep 17 00:00:00 2001 From: Dan Raviv Date: Sat, 26 Aug 2017 02:56:53 +0300 Subject: [PATCH 113/382] Fix header guards using reserved identifiers Identifiers beginning with an underscore followed immediately by an uppercase letter are reserved. --- src/cuckoocache.h | 6 +++--- src/prevector.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cuckoocache.h b/src/cuckoocache.h index 9246a3924..947e1a718 100644 --- a/src/cuckoocache.h +++ b/src/cuckoocache.h @@ -2,8 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef _BITCOIN_CUCKOOCACHE_H_ -#define _BITCOIN_CUCKOOCACHE_H_ +#ifndef BITCOIN_CUCKOOCACHE_H +#define BITCOIN_CUCKOOCACHE_H #include #include @@ -478,4 +478,4 @@ class cache }; } // namespace CuckooCache -#endif +#endif // BITCOIN_CUCKOOCACHE_H diff --git a/src/prevector.h b/src/prevector.h index f7bde8911..eb29b3ae7 100644 --- a/src/prevector.h +++ b/src/prevector.h @@ -2,8 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef _BITCOIN_PREVECTOR_H_ -#define _BITCOIN_PREVECTOR_H_ +#ifndef BITCOIN_PREVECTOR_H +#define BITCOIN_PREVECTOR_H #include #include @@ -514,4 +514,4 @@ class prevector { }; #pragma pack(pop) -#endif +#endif // BITCOIN_PREVECTOR_H From 8897b1b1b11a1e1e5d67ca9c50f8efc17fb54afe Mon Sep 17 00:00:00 2001 From: MeshCollider Date: Sat, 26 Aug 2017 18:23:55 +1200 Subject: [PATCH 114/382] Add meshcollider gitian key --- contrib/gitian-keys/meshcollider-key.pgp | 51 ++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 contrib/gitian-keys/meshcollider-key.pgp diff --git a/contrib/gitian-keys/meshcollider-key.pgp b/contrib/gitian-keys/meshcollider-key.pgp new file mode 100644 index 000000000..20963e7e2 --- /dev/null +++ b/contrib/gitian-keys/meshcollider-key.pgp @@ -0,0 +1,51 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +gb52/WPhR6CJQ2/7y3Uj7u8sPNpb7BGI +=as33 +-----END PGP PUBLIC KEY BLOCK----- From 37c4362f22dc7a605a35194576e19fe5a23de0b0 Mon Sep 17 00:00:00 2001 From: danra Date: Sat, 26 Aug 2017 13:20:36 +0300 Subject: [PATCH 115/382] Trivial: Documentation fixes for CVectorWriter ctors --- src/streams.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/streams.h b/src/streams.h index a3fc91971..159847279 100644 --- a/src/streams.h +++ b/src/streams.h @@ -82,7 +82,7 @@ class CVectorWriter * @param[in] nVersionIn Serialization Version (including any flags) * @param[in] vchDataIn Referenced byte vector to overwrite/append * @param[in] nPosIn Starting position. Vector index where writes should start. The vector will initially - * grow as necessary to max(index, vec.size()). So to append, use vec.size(). + * grow as necessary to max(nPosIn, vec.size()). So to append, use vec.size(). */ CVectorWriter(int nTypeIn, int nVersionIn, std::vector& vchDataIn, size_t nPosIn) : nType(nTypeIn), nVersion(nVersionIn), vchData(vchDataIn), nPos(nPosIn) { @@ -91,7 +91,7 @@ class CVectorWriter } /* * (other params same as above) - * @param[in] args A list of items to serialize starting at nPos. + * @param[in] args A list of items to serialize starting at nPosIn. */ template CVectorWriter(int nTypeIn, int nVersionIn, std::vector& vchDataIn, size_t nPosIn, Args&&... args) : CVectorWriter(nTypeIn, nVersionIn, vchDataIn, nPosIn) From 9b348ff9eb75e04249dfa873a34e0d1a31993b9c Mon Sep 17 00:00:00 2001 From: Dan Raviv Date: Sat, 26 Aug 2017 13:23:19 +0300 Subject: [PATCH 116/382] Fix memory leaks in qt/guiutil.cpp on macOS: listSnapshot was leaking in findStartupItemInList() bitcoinAppUrl was leaking in [Get|Set]StartOnSystemStartup() --- src/qt/guiutil.cpp | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index f3c5daebe..2557344c1 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -780,47 +780,64 @@ bool SetStartOnSystemStartup(bool fAutoStart) LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef findUrl); LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef findUrl) { - // loop through the list of startup items and try to find the bitcoin app CFArrayRef listSnapshot = LSSharedFileListCopySnapshot(list, nullptr); + if (listSnapshot == nullptr) { + return nullptr; + } + + // loop through the list of startup items and try to find the bitcoin app for(int i = 0; i < CFArrayGetCount(listSnapshot); i++) { LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(listSnapshot, i); UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes; CFURLRef currentItemURL = nullptr; #if defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED >= 10100 - if(&LSSharedFileListItemCopyResolvedURL) - currentItemURL = LSSharedFileListItemCopyResolvedURL(item, resolutionFlags, nullptr); + if(&LSSharedFileListItemCopyResolvedURL) + currentItemURL = LSSharedFileListItemCopyResolvedURL(item, resolutionFlags, nullptr); #if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && MAC_OS_X_VERSION_MIN_REQUIRED < 10100 - else - LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, nullptr); + else + LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, nullptr); #endif #else - LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, nullptr); + LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, nullptr); #endif - if(currentItemURL && CFEqual(currentItemURL, findUrl)) { - // found - CFRelease(currentItemURL); - return item; - } if(currentItemURL) { + if (CFEqual(currentItemURL, findUrl)) { + // found + CFRelease(listSnapshot); + CFRelease(currentItemURL); + return item; + } CFRelease(currentItemURL); } } + + CFRelease(listSnapshot); return nullptr; } bool GetStartOnSystemStartup() { CFURLRef bitcoinAppUrl = CFBundleCopyBundleURL(CFBundleGetMainBundle()); + if (bitcoinAppUrl == nullptr) { + return false; + } + LSSharedFileListRef loginItems = LSSharedFileListCreate(nullptr, kLSSharedFileListSessionLoginItems, nullptr); LSSharedFileListItemRef foundItem = findStartupItemInList(loginItems, bitcoinAppUrl); + + CFRelease(bitcoinAppUrl); return !!foundItem; // return boolified object } bool SetStartOnSystemStartup(bool fAutoStart) { CFURLRef bitcoinAppUrl = CFBundleCopyBundleURL(CFBundleGetMainBundle()); + if (bitcoinAppUrl == nullptr) { + return false; + } + LSSharedFileListRef loginItems = LSSharedFileListCreate(nullptr, kLSSharedFileListSessionLoginItems, nullptr); LSSharedFileListItemRef foundItem = findStartupItemInList(loginItems, bitcoinAppUrl); @@ -832,6 +849,8 @@ bool SetStartOnSystemStartup(bool fAutoStart) // remove item LSSharedFileListItemRemove(loginItems, foundItem); } + + CFRelease(bitcoinAppUrl); return true; } #pragma GCC diagnostic pop From 5ac072caa242d7ecf724e0c3a23f4a7c477a3a1e Mon Sep 17 00:00:00 2001 From: Dan Raviv Date: Sat, 26 Aug 2017 21:09:00 +0300 Subject: [PATCH 117/382] Fix boost headers included as user instead of system headers In most of the project, boost headers are included as system headers. Fix the few inconsistent places where they aren't. --- configure.ac | 6 +++--- src/miner.h | 4 ++-- src/txmempool.h | 7 +++---- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/configure.ac b/configure.ac index e5ed93894..4513099d5 100644 --- a/configure.ac +++ b/configure.ac @@ -827,14 +827,14 @@ TEMP_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" AC_MSG_CHECKING([for mismatched boost c++11 scoped enums]) AC_LINK_IFELSE([AC_LANG_PROGRAM([[ - #include "boost/config.hpp" - #include "boost/version.hpp" + #include + #include #if !defined(BOOST_NO_SCOPED_ENUMS) && !defined(BOOST_NO_CXX11_SCOPED_ENUMS) && BOOST_VERSION < 105700 #define BOOST_NO_SCOPED_ENUMS #define BOOST_NO_CXX11_SCOPED_ENUMS #define CHECK #endif - #include "boost/filesystem.hpp" + #include ]],[[ #if defined(CHECK) boost::filesystem::copy_file("foo", "bar"); diff --git a/src/miner.h b/src/miner.h index 6e5fe761d..abd2ff619 100644 --- a/src/miner.h +++ b/src/miner.h @@ -11,8 +11,8 @@ #include #include -#include "boost/multi_index_container.hpp" -#include "boost/multi_index/ordered_index.hpp" +#include +#include class CBlockIndex; class CChainParams; diff --git a/src/txmempool.h b/src/txmempool.h index 5b0db5266..1ff812092 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -21,11 +21,10 @@ #include "sync.h" #include "random.h" -#include "boost/multi_index_container.hpp" -#include "boost/multi_index/ordered_index.hpp" -#include "boost/multi_index/hashed_index.hpp" +#include +#include +#include #include - #include class CBlockIndex; From e2548302f40ca73f990583848292965f42ecc576 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Sun, 27 Aug 2017 01:08:19 -0400 Subject: [PATCH 118/382] Make tabs toolbar no longer have a context menu Adds a contextMenuPolicy of Qt::PreventContextMenu to prevent the tabs toolbar from showing a context menu that allows it to be hidden. --- src/qt/bitcoingui.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index e3970298e..be2d21dae 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -454,6 +454,7 @@ void BitcoinGUI::createToolBars() if(walletFrame) { QToolBar *toolbar = addToolBar(tr("Tabs toolbar")); + toolbar->setContextMenuPolicy(Qt::PreventContextMenu); toolbar->setMovable(false); toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); toolbar->addAction(overviewAction); From 946638d0a0884b0edcf866ea8aa2f7ffd15062b0 Mon Sep 17 00:00:00 2001 From: danra Date: Sat, 26 Aug 2017 15:12:00 +0300 Subject: [PATCH 119/382] Improve versionbits_computeblockversion test code consistency In this test, `nTime` is used for all the calls to `Mine()`, each time being set to the correct time beforehand, except for in the last few calls to `Mine()` where `nStartTime` is used directly, even though `nTime` is still set to `nStartTime` beforehand. `nTime` just remains unused for these last few calls to `Mine()`. Changed the last few calls to `Mine()` to use `nTime` instead, improving consistency. This also fixes an unused value static analyzer warning about `nTime` being set to a value which is never used. --- src/test/versionbits_tests.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp index f433aad88..882afb2e2 100644 --- a/src/test/versionbits_tests.cpp +++ b/src/test/versionbits_tests.cpp @@ -314,20 +314,20 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion) // Mine one period worth of blocks, and check that the bit will be on for the // next period. - lastBlock = secondChain.Mine(2016, nStartTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip(); + lastBlock = secondChain.Mine(2016, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip(); BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1< Date: Sat, 26 Aug 2017 16:36:46 +0300 Subject: [PATCH 120/382] Remove redundant explicitly defined copy ctors CFeeRate and CTxMemPoolEntry have explicitly defined copy ctors which has the same functionality as the implicit default copy ctors which would have been generated otherwise. Besides being redundant, it violates the rule of three (see https://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming) ). (Of course, the rule of three doesn't -really- cause a resource management issue here, but the reason for that is exactly that there is no need for an explicit copy ctor in the first place since no resources are being managed). CFeeRate has an explicitly defined copy ctor which has the same functionality as the implicit default copy ctor which would h ave been generated otherwise. --- src/policy/feerate.h | 1 - src/txmempool.cpp | 5 ----- src/txmempool.h | 2 -- 3 files changed, 8 deletions(-) diff --git a/src/policy/feerate.h b/src/policy/feerate.h index 565da6c15..7e519e3ef 100644 --- a/src/policy/feerate.h +++ b/src/policy/feerate.h @@ -26,7 +26,6 @@ class CFeeRate explicit CFeeRate(const CAmount& _nSatoshisPerK): nSatoshisPerK(_nSatoshisPerK) { } /** Constructor for a fee rate in satoshis per kB. The size in bytes must not exceed (2^63 - 1)*/ CFeeRate(const CAmount& nFeePaid, size_t nBytes); - CFeeRate(const CFeeRate& other) { nSatoshisPerK = other.nSatoshisPerK; } /** * Return the fee in satoshis for the given size in bytes. */ diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 4a8105523..f68d67764 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -39,11 +39,6 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFe nSigOpCostWithAncestors = sigOpCost; } -CTxMemPoolEntry::CTxMemPoolEntry(const CTxMemPoolEntry& other) -{ - *this = other; -} - void CTxMemPoolEntry::UpdateFeeDelta(int64_t newFeeDelta) { nModFeesWithDescendants += newFeeDelta - feeDelta; diff --git a/src/txmempool.h b/src/txmempool.h index 5b0db5266..65586a6e6 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -95,8 +95,6 @@ class CTxMemPoolEntry bool spendsCoinbase, int64_t nSigOpsCost, LockPoints lp); - CTxMemPoolEntry(const CTxMemPoolEntry& other); - const CTransaction& GetTx() const { return *this->tx; } CTransactionRef GetSharedTx() const { return this->tx; } const CAmount& GetFee() const { return nFee; } From 55509f1a11003837714071d05ea878b340757a76 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Mon, 28 Aug 2017 09:20:50 +0200 Subject: [PATCH 121/382] Document assumptions that are being made to avoid division by zero --- src/policy/fees.cpp | 1 + src/qt/coincontroldialog.cpp | 1 + src/wallet/wallet.cpp | 1 + 3 files changed, 3 insertions(+) diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp index b9476407c..5c92a0eb4 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -503,6 +503,7 @@ void TxConfirmStats::removeTx(unsigned int entryHeight, unsigned int nBestSeenHe } } if (!inBlock && (unsigned int)blocksAgo >= scale) { // Only counts as a failure if not confirmed for entire period + assert(scale != 0); unsigned int periodsAgo = blocksAgo / scale; for (size_t i = 0; i < periodsAgo && i < failAvg.size(); i++) { failAvg[i][bucketindex]++; diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index f3ee0fbe3..0225dd3cd 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -581,6 +581,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) QString toolTipDust = tr("This label turns red if any recipient receives an amount smaller than the current dust threshold."); // how many satoshis the estimated fee can vary per byte we guess wrong + assert(nBytes != 0); double dFeeVary = (double)nPayFee / nBytes; QString toolTip4 = tr("Can vary +/- %1 satoshi(s) per input.").arg(dFeeVary); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 291bcc7a2..6d94154da 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2741,6 +2741,7 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, CWalletT if (recipient.fSubtractFeeFromAmount) { + assert(nSubtractFeeFromAmount != 0); txout.nValue -= nFeeRet / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient if (fFirst) // first receiver pays the remainder not divisible by output count From ce5381e7fe6702a950a9a81693ca678d87912125 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 28 Aug 2017 11:01:28 +0200 Subject: [PATCH 122/382] build: Rename --enable-experimental-asm to --enable-asm and enable by default Now that 0.15 is branched off, enable assembler SHA256 optimizations by default. --- configure.ac | 18 +++++++++--------- src/Makefile.am | 2 +- src/crypto/sha256.cpp | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/configure.ac b/configure.ac index e5ed93894..714ff5de7 100644 --- a/configure.ac +++ b/configure.ac @@ -177,14 +177,14 @@ AC_ARG_ENABLE([glibc-back-compat], [use_glibc_compat=$enableval], [use_glibc_compat=no]) -AC_ARG_ENABLE([experimental-asm], - [AS_HELP_STRING([--enable-experimental-asm], - [Enable experimental assembly routines (default is no)])], - [experimental_asm=$enableval], - [experimental_asm=no]) - -if test "x$experimental_asm" = xyes; then - AC_DEFINE(EXPERIMENTAL_ASM, 1, [Define this symbol to build in experimental assembly routines]) +AC_ARG_ENABLE([asm], + [AS_HELP_STRING([--enable-asm], + [Enable assembly routines (default is yes)])], + [use_asm=$enableval], + [use_asm=yes]) + +if test "x$use_asm" = xyes; then + AC_DEFINE(USE_ASM, 1, [Define this symbol to build in assembly routines]) fi AC_ARG_WITH([system-univalue], @@ -1179,7 +1179,7 @@ AM_CONDITIONAL([USE_LCOV],[test x$use_lcov = xyes]) AM_CONDITIONAL([GLIBC_BACK_COMPAT],[test x$use_glibc_compat = xyes]) AM_CONDITIONAL([HARDEN],[test x$use_hardening = xyes]) AM_CONDITIONAL([ENABLE_HWCRC32],[test x$enable_hwcrc32 = xyes]) -AM_CONDITIONAL([EXPERIMENTAL_ASM],[test x$experimental_asm = xyes]) +AM_CONDITIONAL([USE_ASM],[test x$use_asm = xyes]) AC_DEFINE(CLIENT_VERSION_MAJOR, _CLIENT_VERSION_MAJOR, [Major version]) AC_DEFINE(CLIENT_VERSION_MINOR, _CLIENT_VERSION_MINOR, [Minor version]) diff --git a/src/Makefile.am b/src/Makefile.am index dea656869..fa8e1c1b1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -271,7 +271,7 @@ crypto_libbitcoin_crypto_a_SOURCES = \ crypto/sha512.cpp \ crypto/sha512.h -if EXPERIMENTAL_ASM +if USE_ASM crypto_libbitcoin_crypto_a_SOURCES += crypto/sha256_sse4.cpp endif diff --git a/src/crypto/sha256.cpp b/src/crypto/sha256.cpp index 15d6db90c..29afe86ec 100644 --- a/src/crypto/sha256.cpp +++ b/src/crypto/sha256.cpp @@ -10,7 +10,7 @@ #include #if defined(__x86_64__) || defined(__amd64__) -#if defined(EXPERIMENTAL_ASM) +#if defined(USE_ASM) #include namespace sha256_sse4 { @@ -178,7 +178,7 @@ TransformType Transform = sha256::Transform; std::string SHA256AutoDetect() { -#if defined(EXPERIMENTAL_ASM) && (defined(__x86_64__) || defined(__amd64__)) +#if defined(USE_ASM) && (defined(__x86_64__) || defined(__amd64__)) uint32_t eax, ebx, ecx, edx; if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) && (ecx >> 19) & 1) { Transform = sha256_sse4::Transform; From 538cc0ca8b1cea0b8e62ced3c6e276b5e67c812d Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 28 Aug 2017 11:19:56 +0200 Subject: [PATCH 123/382] build: Mention use of asm in summary --- configure.ac | 1 + 1 file changed, 1 insertion(+) diff --git a/configure.ac b/configure.ac index 714ff5de7..fb6d7d484 100644 --- a/configure.ac +++ b/configure.ac @@ -1297,6 +1297,7 @@ echo " with zmq = $use_zmq" echo " with test = $use_tests" echo " with bench = $use_bench" echo " with upnp = $use_upnp" +echo " use asm = $use_asm" echo " debug enabled = $enable_debug" echo " werror = $enable_werror" echo From 25cd520fc429c464846b0f986104db45b3bfaebb Mon Sep 17 00:00:00 2001 From: practicalswift Date: Sun, 9 Jul 2017 22:57:02 +0200 Subject: [PATCH 124/382] Use sys.exit(...) instead of exit(...): exit(...) should not be used in programs --- contrib/devtools/check-doc.py | 3 +- contrib/devtools/github-merge.py | 37 +++++++++++++------------ contrib/devtools/security-check.py | 2 +- contrib/devtools/symbol-check.py | 2 +- contrib/devtools/update-translations.py | 4 +-- contrib/linearize/linearize-hashes.py | 2 +- contrib/seeds/generate-seeds.py | 2 +- contrib/zmq/zmq_sub.py | 2 +- contrib/zmq/zmq_sub3.4.py | 2 +- share/qt/extract_strings_qt.py | 2 +- 10 files changed, 30 insertions(+), 28 deletions(-) diff --git a/contrib/devtools/check-doc.py b/contrib/devtools/check-doc.py index 3b7a8f9a6..3279dc361 100755 --- a/contrib/devtools/check-doc.py +++ b/contrib/devtools/check-doc.py @@ -12,6 +12,7 @@ from subprocess import check_output import re +import sys FOLDER_GREP = 'src' FOLDER_TEST = 'src/test/' @@ -39,7 +40,7 @@ def main(): print "Args unknown : %s" % len(args_unknown) print args_unknown - exit(len(args_need_doc)) + sys.exit(len(args_need_doc)) if __name__ == "__main__": main() diff --git a/contrib/devtools/github-merge.py b/contrib/devtools/github-merge.py index c664cf81f..7545c4340 100755 --- a/contrib/devtools/github-merge.py +++ b/contrib/devtools/github-merge.py @@ -20,6 +20,7 @@ import argparse import hashlib import subprocess +import sys import json,codecs try: from urllib.request import Request,urlopen @@ -158,11 +159,11 @@ def main(): if repo is None: print("ERROR: No repository configured. Use this command to set:", file=stderr) print("git config githubmerge.repository /", file=stderr) - exit(1) + sys.exit(1) if signingkey is None: print("ERROR: No GPG signing key set. Set one using:",file=stderr) print("git config --global user.signingkey ",file=stderr) - exit(1) + sys.exit(1) host_repo = host+":"+repo # shortcut for push/pull target @@ -173,7 +174,7 @@ def main(): # Receive pull information from github info = retrieve_pr_info(repo,pull) if info is None: - exit(1) + sys.exit(1) title = info['title'].strip() body = info['body'].strip() # precedence order for destination branch argument: @@ -194,27 +195,27 @@ def main(): subprocess.check_call([GIT,'checkout','-q',branch]) except subprocess.CalledProcessError as e: print("ERROR: Cannot check out branch %s." % (branch), file=stderr) - exit(3) + sys.exit(3) try: subprocess.check_call([GIT,'fetch','-q',host_repo,'+refs/pull/'+pull+'/*:refs/heads/pull/'+pull+'/*']) except subprocess.CalledProcessError as e: print("ERROR: Cannot find pull request #%s on %s." % (pull,host_repo), file=stderr) - exit(3) + sys.exit(3) try: subprocess.check_call([GIT,'log','-q','-1','refs/heads/'+head_branch], stdout=devnull, stderr=stdout) except subprocess.CalledProcessError as e: print("ERROR: Cannot find head of pull request #%s on %s." % (pull,host_repo), file=stderr) - exit(3) + sys.exit(3) try: subprocess.check_call([GIT,'log','-q','-1','refs/heads/'+merge_branch], stdout=devnull, stderr=stdout) except subprocess.CalledProcessError as e: print("ERROR: Cannot find merge of pull request #%s on %s." % (pull,host_repo), file=stderr) - exit(3) + sys.exit(3) try: subprocess.check_call([GIT,'fetch','-q',host_repo,'+refs/heads/'+branch+':refs/heads/'+base_branch]) except subprocess.CalledProcessError as e: print("ERROR: Cannot find branch %s on %s." % (branch,host_repo), file=stderr) - exit(3) + sys.exit(3) subprocess.check_call([GIT,'checkout','-q',base_branch]) subprocess.call([GIT,'branch','-q','-D',local_merge_branch], stderr=devnull) subprocess.check_call([GIT,'checkout','-q','-b',local_merge_branch]) @@ -236,17 +237,17 @@ def main(): except subprocess.CalledProcessError as e: print("ERROR: Cannot be merged cleanly.",file=stderr) subprocess.check_call([GIT,'merge','--abort']) - exit(4) + sys.exit(4) logmsg = subprocess.check_output([GIT,'log','--pretty=format:%s','-n','1']).decode('utf-8') if logmsg.rstrip() != firstline.rstrip(): print("ERROR: Creating merge failed (already merged?).",file=stderr) - exit(4) + sys.exit(4) symlink_files = get_symlink_files() for f in symlink_files: print("ERROR: File %s was a symlink" % f) if len(symlink_files) > 0: - exit(4) + sys.exit(4) # Put tree SHA512 into the message try: @@ -254,12 +255,12 @@ def main(): message += '\n\nTree-SHA512: ' + first_sha512 except subprocess.CalledProcessError as e: printf("ERROR: Unable to compute tree hash") - exit(4) + sys.exit(4) try: subprocess.check_call([GIT,'commit','--amend','-m',message.encode('utf-8')]) except subprocess.CalledProcessError as e: printf("ERROR: Cannot update message.",file=stderr) - exit(4) + sys.exit(4) print_merge_details(pull, title, branch, base_branch, head_branch) print() @@ -268,7 +269,7 @@ def main(): if testcmd: if subprocess.call(testcmd,shell=True): print("ERROR: Running %s failed." % testcmd,file=stderr) - exit(5) + sys.exit(5) # Show the created merge. diff = subprocess.check_output([GIT,'diff',merge_branch+'..'+local_merge_branch]) @@ -279,7 +280,7 @@ def main(): if reply.lower() == 'ignore': print("Difference with github ignored.",file=stderr) else: - exit(6) + sys.exit(6) else: # Verify the result manually. print("Dropping you on a shell so you can try building/testing the merged source.",file=stderr) @@ -292,7 +293,7 @@ def main(): second_sha512 = tree_sha512sum() if first_sha512 != second_sha512: print("ERROR: Tree hash changed unexpectedly",file=stderr) - exit(8) + sys.exit(8) # Sign the merge commit. print_merge_details(pull, title, branch, base_branch, head_branch) @@ -306,7 +307,7 @@ def main(): print("Error while signing, asking again.",file=stderr) elif reply == 'x': print("Not signing off on merge, exiting.",file=stderr) - exit(1) + sys.exit(1) # Put the result in branch. subprocess.check_call([GIT,'checkout','-q',branch]) @@ -326,7 +327,7 @@ def main(): subprocess.check_call([GIT,'push',host_repo,'refs/heads/'+branch]) break elif reply == 'x': - exit(1) + sys.exit(1) if __name__ == '__main__': main() diff --git a/contrib/devtools/security-check.py b/contrib/devtools/security-check.py index c90541e27..6eb566745 100755 --- a/contrib/devtools/security-check.py +++ b/contrib/devtools/security-check.py @@ -212,5 +212,5 @@ def identify_executable(executable): except IOError: print('%s: cannot open' % filename) retval = 1 - exit(retval) + sys.exit(retval) diff --git a/contrib/devtools/symbol-check.py b/contrib/devtools/symbol-check.py index 8f8685006..98daa1bd7 100755 --- a/contrib/devtools/symbol-check.py +++ b/contrib/devtools/symbol-check.py @@ -159,6 +159,6 @@ def read_libraries(filename): print('%s: NEEDED library %s is not allowed' % (filename, library_name.decode('utf-8'))) retval = 1 - exit(retval) + sys.exit(retval) diff --git a/contrib/devtools/update-translations.py b/contrib/devtools/update-translations.py index 201184100..e1924749d 100755 --- a/contrib/devtools/update-translations.py +++ b/contrib/devtools/update-translations.py @@ -36,12 +36,12 @@ def check_at_repository_root(): if not os.path.exists('.git'): print('No .git directory found') print('Execute this script at the root of the repository', file=sys.stderr) - exit(1) + sys.exit(1) def fetch_all_translations(): if subprocess.call([TX, 'pull', '-f', '-a']): print('Error while fetching translations', file=sys.stderr) - exit(1) + sys.exit(1) def find_format_specifiers(s): '''Find all format specifiers in a string.''' diff --git a/contrib/linearize/linearize-hashes.py b/contrib/linearize/linearize-hashes.py index db8eb7021..7719f4ccd 100755 --- a/contrib/linearize/linearize-hashes.py +++ b/contrib/linearize/linearize-hashes.py @@ -87,7 +87,7 @@ def get_block_hashes(settings, max_blocks_per_call=10000): for x,resp_obj in enumerate(reply): if rpc.response_is_error(resp_obj): print('JSON-RPC: error at height', height+x, ': ', resp_obj['error'], file=sys.stderr) - exit(1) + sys.exit(1) assert(resp_obj['id'] == x) # assume replies are in-sequence if settings['rev_hash_bytes'] == 'true': resp_obj['result'] = hex_switchEndian(resp_obj['result']) diff --git a/contrib/seeds/generate-seeds.py b/contrib/seeds/generate-seeds.py index b0ac92ae0..28068a752 100755 --- a/contrib/seeds/generate-seeds.py +++ b/contrib/seeds/generate-seeds.py @@ -114,7 +114,7 @@ def process_nodes(g, f, structname, defaultport): def main(): if len(sys.argv)<2: print(('Usage: %s ' % sys.argv[0]), file=sys.stderr) - exit(1) + sys.exit(1) g = sys.stdout indir = sys.argv[1] g.write('#ifndef BITCOIN_CHAINPARAMSSEEDS_H\n') diff --git a/contrib/zmq/zmq_sub.py b/contrib/zmq/zmq_sub.py index ea398a27e..5cc19761d 100755 --- a/contrib/zmq/zmq_sub.py +++ b/contrib/zmq/zmq_sub.py @@ -32,7 +32,7 @@ if not (sys.version_info.major >= 3 and sys.version_info.minor >= 5): print("This example only works with Python 3.5 and greater") - exit(1) + sys.exit(1) port = 28332 diff --git a/contrib/zmq/zmq_sub3.4.py b/contrib/zmq/zmq_sub3.4.py index 1cb7eec0c..bfb7ea9ea 100755 --- a/contrib/zmq/zmq_sub3.4.py +++ b/contrib/zmq/zmq_sub3.4.py @@ -36,7 +36,7 @@ if not (sys.version_info.major >= 3 and sys.version_info.minor >= 4): print("This example only works with Python 3.4 and greater") - exit(1) + sys.exit(1) port = 28332 diff --git a/share/qt/extract_strings_qt.py b/share/qt/extract_strings_qt.py index 5492fdb8c..e3be18205 100755 --- a/share/qt/extract_strings_qt.py +++ b/share/qt/extract_strings_qt.py @@ -58,7 +58,7 @@ def parse_po(text): if not XGETTEXT: print('Cannot extract strings: xgettext utility is not installed or not configured.',file=sys.stderr) print('Please install package "gettext" and re-run \'./configure\'.',file=sys.stderr) - exit(1) + sys.exit(1) child = Popen([XGETTEXT,'--output=-','-n','--keyword=_'] + files, stdout=PIPE) (out, err) = child.communicate() From 51cb6b8221560e5e62894b566c9580dbbd1c65a4 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Sun, 9 Jul 2017 23:34:10 +0200 Subject: [PATCH 125/382] Use print(...) instead of undefined printf(...) --- contrib/devtools/github-merge.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/devtools/github-merge.py b/contrib/devtools/github-merge.py index 7545c4340..4b1bae610 100755 --- a/contrib/devtools/github-merge.py +++ b/contrib/devtools/github-merge.py @@ -254,12 +254,12 @@ def main(): first_sha512 = tree_sha512sum() message += '\n\nTree-SHA512: ' + first_sha512 except subprocess.CalledProcessError as e: - printf("ERROR: Unable to compute tree hash") + print("ERROR: Unable to compute tree hash") sys.exit(4) try: subprocess.check_call([GIT,'commit','--amend','-m',message.encode('utf-8')]) except subprocess.CalledProcessError as e: - printf("ERROR: Cannot update message.",file=stderr) + print("ERROR: Cannot update message.", file=stderr) sys.exit(4) print_merge_details(pull, title, branch, base_branch, head_branch) From 9b94054b7c60183cc38f1b9f3681c1770c043d77 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Sun, 9 Jul 2017 22:53:45 +0200 Subject: [PATCH 126/382] Avoid reference to undefined name: stderr does not exist, sys.stderr does --- contrib/linearize/linearize-hashes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/linearize/linearize-hashes.py b/contrib/linearize/linearize-hashes.py index 7719f4ccd..58fec6ddd 100755 --- a/contrib/linearize/linearize-hashes.py +++ b/contrib/linearize/linearize-hashes.py @@ -140,7 +140,7 @@ def get_rpc_cookie(): if 'datadir' in settings and not use_userpass: use_datadir = True if not use_userpass and not use_datadir: - print("Missing datadir or username and/or password in cfg file", file=stderr) + print("Missing datadir or username and/or password in cfg file", file=sys.stderr) sys.exit(1) settings['port'] = int(settings['port']) From 2e6080bbf31d5cc2e38e8a7b436e8ce1857e589b Mon Sep 17 00:00:00 2001 From: practicalswift Date: Thu, 20 Jul 2017 23:10:37 +0200 Subject: [PATCH 127/382] Remove unused variables and/or function calls --- test/functional/bip68-sequence.py | 2 +- test/functional/bumpfee.py | 2 +- test/functional/fundrawtransaction.py | 1 - test/functional/import-rescan.py | 1 - test/functional/importmulti.py | 13 - test/functional/importprunedfunds.py | 5 +- test/functional/p2p-fullblocktest.py | 18 +- test/functional/p2p-segwit.py | 1 - test/functional/rawtransactions.py | 2 - test/functional/replace-by-fee.py | 6 +- test/functional/segwit.py | 1 - test/functional/test_framework/address.py | 10 - test/functional/test_framework/bignum.py | 39 --- test/functional/test_framework/blockstore.py | 10 - test/functional/test_framework/mininode.py | 11 +- test/functional/test_framework/netutil.py | 20 +- test/functional/test_framework/script.py | 245 ------------------- test/functional/wallet.py | 2 +- 18 files changed, 29 insertions(+), 360 deletions(-) diff --git a/test/functional/bip68-sequence.py b/test/functional/bip68-sequence.py index 87a50692f..6968b46f7 100755 --- a/test/functional/bip68-sequence.py +++ b/test/functional/bip68-sequence.py @@ -387,7 +387,7 @@ def test_version2_relay(self): tx = FromHex(CTransaction(), rawtxfund) tx.nVersion = 2 tx_signed = self.nodes[1].signrawtransaction(ToHex(tx))["hex"] - tx_id = self.nodes[1].sendrawtransaction(tx_signed) + self.nodes[1].sendrawtransaction(tx_signed) if __name__ == '__main__': BIP68Test().main() diff --git a/test/functional/bumpfee.py b/test/functional/bumpfee.py index 9633ffdeb..553ef4cd0 100755 --- a/test/functional/bumpfee.py +++ b/test/functional/bumpfee.py @@ -167,7 +167,7 @@ def test_bumpfee_with_descendant_fails(rbf_node, rbf_node_address, dest_address) parent_id = spend_one_input(rbf_node, rbf_node_address) tx = rbf_node.createrawtransaction([{"txid": parent_id, "vout": 0}], {dest_address: 0.00020000}) tx = rbf_node.signrawtransaction(tx) - txid = rbf_node.sendrawtransaction(tx["hex"]) + rbf_node.sendrawtransaction(tx["hex"]) assert_raises_jsonrpc(-8, "Transaction has descendants in the wallet", rbf_node.bumpfee, parent_id) diff --git a/test/functional/fundrawtransaction.py b/test/functional/fundrawtransaction.py index f2f4efcf2..9074223cb 100755 --- a/test/functional/fundrawtransaction.py +++ b/test/functional/fundrawtransaction.py @@ -312,7 +312,6 @@ def run_test(self): ############################################## # test a fundrawtransaction with invalid vin # ############################################## - listunspent = self.nodes[2].listunspent() inputs = [ {'txid' : "1c7f966dab21119bac53213a2bc7532bff1fa844c124fd750a7d0b1332440bd1", 'vout' : 0} ] #invalid vin! outputs = { self.nodes[0].getnewaddress() : 1.0} rawtx = self.nodes[2].createrawtransaction(inputs, outputs) diff --git a/test/functional/import-rescan.py b/test/functional/import-rescan.py index 4fc507821..13e1bdecd 100755 --- a/test/functional/import-rescan.py +++ b/test/functional/import-rescan.py @@ -161,7 +161,6 @@ def run_test(self): variant.check() # Create new transactions sending to each address. - fee = self.nodes[0].getnetworkinfo()["relayfee"] for i, variant in enumerate(IMPORT_VARIANTS): variant.sent_amount = 10 - (2 * i + 1) / 8.0 variant.sent_txid = self.nodes[0].sendtoaddress(variant.address["address"], variant.sent_amount) diff --git a/test/functional/importmulti.py b/test/functional/importmulti.py index e83e85de1..18a3df028 100755 --- a/test/functional/importmulti.py +++ b/test/functional/importmulti.py @@ -21,16 +21,7 @@ def run_test (self): self.nodes[1].generate(1) timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime'] - # keyword definition - PRIV_KEY = 'privkey' - PUB_KEY = 'pubkey' - ADDRESS_KEY = 'address' - SCRIPT_KEY = 'script' - - node0_address1 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) - node0_address2 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) - node0_address3 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) #Check only one address assert_equal(node0_address1['ismine'], True) @@ -230,7 +221,6 @@ def run_test (self): transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00) self.nodes[1].generate(1) timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime'] - transaction = self.nodes[1].gettransaction(transactionid) self.log.info("Should import a p2sh") result = self.nodes[1].importmulti([{ @@ -258,7 +248,6 @@ def run_test (self): transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00) self.nodes[1].generate(1) timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime'] - transaction = self.nodes[1].gettransaction(transactionid) self.log.info("Should import a p2sh with respective redeem script") result = self.nodes[1].importmulti([{ @@ -286,7 +275,6 @@ def run_test (self): transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00) self.nodes[1].generate(1) timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime'] - transaction = self.nodes[1].gettransaction(transactionid) self.log.info("Should import a p2sh with respective redeem script and private keys") result = self.nodes[1].importmulti([{ @@ -314,7 +302,6 @@ def run_test (self): transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00) self.nodes[1].generate(1) timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime'] - transaction = self.nodes[1].gettransaction(transactionid) self.log.info("Should import a p2sh with respective redeem script and private keys") result = self.nodes[1].importmulti([{ diff --git a/test/functional/importprunedfunds.py b/test/functional/importprunedfunds.py index 94753fe43..4e88fc592 100755 --- a/test/functional/importprunedfunds.py +++ b/test/functional/importprunedfunds.py @@ -24,7 +24,6 @@ def run_test(self): address1 = self.nodes[0].getnewaddress() # pubkey address2 = self.nodes[0].getnewaddress() - address2_pubkey = self.nodes[0].validateaddress(address2)['pubkey'] # Using pubkey # privkey address3 = self.nodes[0].getnewaddress() address3_privkey = self.nodes[0].dumpprivkey(address3) # Using privkey @@ -77,13 +76,13 @@ def run_test(self): #Import with affiliated address with no rescan self.nodes[1].importaddress(address2, "add2", False) - result2 = self.nodes[1].importprunedfunds(rawtxn2, proof2) + self.nodes[1].importprunedfunds(rawtxn2, proof2) balance2 = self.nodes[1].getbalance("add2", 0, True) assert_equal(balance2, Decimal('0.05')) #Import with private key with no rescan self.nodes[1].importprivkey(address3_privkey, "add3", False) - result3 = self.nodes[1].importprunedfunds(rawtxn3, proof3) + self.nodes[1].importprunedfunds(rawtxn3, proof3) balance3 = self.nodes[1].getbalance("add3", 0, False) assert_equal(balance3, Decimal('0.025')) balance3 = self.nodes[1].getbalance("*", 0, True) diff --git a/test/functional/p2p-fullblocktest.py b/test/functional/p2p-fullblocktest.py index e7fe7372c..92af0fefd 100755 --- a/test/functional/p2p-fullblocktest.py +++ b/test/functional/p2p-fullblocktest.py @@ -397,7 +397,7 @@ def update_block(block_number, new_transactions): yield rejected(RejectResult(16, b'bad-cb-length')) # Extend the b26 chain to make sure bitcoind isn't accepting b26 - b27 = block(27, spend=out[7]) + block(27, spend=out[7]) yield rejected(False) # Now try a too-large-coinbase script @@ -409,7 +409,7 @@ def update_block(block_number, new_transactions): yield rejected(RejectResult(16, b'bad-cb-length')) # Extend the b28 chain to make sure bitcoind isn't accepting b28 - b29 = block(29, spend=out[7]) + block(29, spend=out[7]) yield rejected(False) # b30 has a max-sized coinbase scriptSig. @@ -581,7 +581,7 @@ def update_block(block_number, new_transactions): # same as b40, but one less sigop tip(39) - b41 = block(41, spend=None) + block(41, spend=None) update_block(41, b40.vtx[1:-1]) b41_sigops_to_fill = b40_sigops_to_fill - 1 tx = CTransaction() @@ -927,7 +927,7 @@ def update_block(block_number, new_transactions): # -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) # tip(64) - b65 = block(65) + block(65) tx1 = create_and_sign_tx(out[19].tx, out[19].n, out[19].tx.vout[0].nValue) tx2 = create_and_sign_tx(tx1, 0, 0) update_block(65, [tx1, tx2]) @@ -939,7 +939,7 @@ def update_block(block_number, new_transactions): # -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) # \-> b66 (20) tip(65) - b66 = block(66) + block(66) tx1 = create_and_sign_tx(out[20].tx, out[20].n, out[20].tx.vout[0].nValue) tx2 = create_and_sign_tx(tx1, 0, 1) update_block(66, [tx2, tx1]) @@ -952,7 +952,7 @@ def update_block(block_number, new_transactions): # # tip(65) - b67 = block(67) + block(67) tx1 = create_and_sign_tx(out[20].tx, out[20].n, out[20].tx.vout[0].nValue) tx2 = create_and_sign_tx(tx1, 0, 1) tx3 = create_and_sign_tx(tx1, 0, 2) @@ -972,7 +972,7 @@ def update_block(block_number, new_transactions): # this succeeds # tip(65) - b68 = block(68, additional_coinbase_value=10) + block(68, additional_coinbase_value=10) tx = create_and_sign_tx(out[20].tx, out[20].n, out[20].tx.vout[0].nValue-9) update_block(68, [tx]) yield rejected(RejectResult(16, b'bad-cb-amount')) @@ -1175,7 +1175,7 @@ def update_block(block_number, new_transactions): # # -> b81 (26) -> b82 (27) -> b83 (28) # - b83 = block(83) + block(83) op_codes = [OP_IF, OP_INVALIDOPCODE, OP_ELSE, OP_TRUE, OP_ENDIF] script = CScript(op_codes) tx1 = create_and_sign_tx(out[28].tx, out[28].n, out[28].tx.vout[0].nValue, script) @@ -1195,7 +1195,7 @@ def update_block(block_number, new_transactions): # \-> b85 (29) -> b86 (30) \-> b89a (32) # # - b84 = block(84) + block(84) tx1 = create_tx(out[29].tx, out[29].n, 0, CScript([OP_RETURN])) tx1.vout.append(CTxOut(0, CScript([OP_TRUE]))) tx1.vout.append(CTxOut(0, CScript([OP_TRUE]))) diff --git a/test/functional/p2p-segwit.py b/test/functional/p2p-segwit.py index 63dfbb8ae..de69cba39 100755 --- a/test/functional/p2p-segwit.py +++ b/test/functional/p2p-segwit.py @@ -17,7 +17,6 @@ # The versionbit bit used to signal activation of SegWit VB_WITNESS_BIT = 1 VB_PERIOD = 144 -VB_ACTIVATION_THRESHOLD = 108 VB_TOP_BITS = 0x20000000 MAX_SIGOP_COST = 80000 diff --git a/test/functional/rawtransactions.py b/test/functional/rawtransactions.py index b6b90d678..847553097 100755 --- a/test/functional/rawtransactions.py +++ b/test/functional/rawtransactions.py @@ -63,7 +63,6 @@ def run_test(self): addr2Obj = self.nodes[2].validateaddress(addr2) mSigObj = self.nodes[2].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey']]) - mSigObjValid = self.nodes[2].validateaddress(mSigObj) #use balance deltas instead of absolute values bal = self.nodes[2].getbalance() @@ -87,7 +86,6 @@ def run_test(self): addr3Obj = self.nodes[2].validateaddress(addr3) mSigObj = self.nodes[2].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey'], addr3Obj['pubkey']]) - mSigObjValid = self.nodes[2].validateaddress(mSigObj) txId = self.nodes[0].sendtoaddress(mSigObj, 2.2) decTx = self.nodes[0].gettransaction(txId) diff --git a/test/functional/replace-by-fee.py b/test/functional/replace-by-fee.py index bc6765498..4f33bb58b 100755 --- a/test/functional/replace-by-fee.py +++ b/test/functional/replace-by-fee.py @@ -270,7 +270,7 @@ def test_replacement_feeperkb(self): tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)] tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))] tx1a_hex = txToHex(tx1a) - tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) + self.nodes[0].sendrawtransaction(tx1a_hex, True) # Higher fee, but the fee per KB is much lower, so the replacement is # rejected. @@ -331,7 +331,7 @@ def test_new_unconfirmed_inputs(self): tx1.vin = [CTxIn(confirmed_utxo)] tx1.vout = [CTxOut(1*COIN, CScript([b'a']))] tx1_hex = txToHex(tx1) - tx1_txid = self.nodes[0].sendrawtransaction(tx1_hex, True) + self.nodes[0].sendrawtransaction(tx1_hex, True) tx2 = CTransaction() tx2.vin = [CTxIn(confirmed_utxo), CTxIn(unconfirmed_utxo)] @@ -499,7 +499,7 @@ def test_prioritised_transactions(self): tx2a.vin = [CTxIn(tx1_outpoint, nSequence=0)] tx2a.vout = [CTxOut(1*COIN, CScript([b'a']))] tx2a_hex = txToHex(tx2a) - tx2a_txid = self.nodes[0].sendrawtransaction(tx2a_hex, True) + self.nodes[0].sendrawtransaction(tx2a_hex, True) # Lower fee, but we'll prioritise it tx2b = CTransaction() diff --git a/test/functional/segwit.py b/test/functional/segwit.py index 51eaa34a5..7bcb39301 100755 --- a/test/functional/segwit.py +++ b/test/functional/segwit.py @@ -12,7 +12,6 @@ from io import BytesIO NODE_0 = 0 -NODE_1 = 1 NODE_2 = 2 WIT_V0 = 0 WIT_V1 = 1 diff --git a/test/functional/test_framework/address.py b/test/functional/test_framework/address.py index 96bebe1ea..180dac197 100644 --- a/test/functional/test_framework/address.py +++ b/test/functional/test_framework/address.py @@ -44,16 +44,6 @@ def script_to_p2sh(script, main = False): script = check_script(script) return scripthash_to_p2sh(hash160(script), main) -def key_to_p2sh_p2wpkh(key, main = False): - key = check_key(key) - p2shscript = CScript([OP_0, hash160(key)]) - return script_to_p2sh(p2shscript, main) - -def script_to_p2sh_p2wsh(script, main = False): - script = check_script(script) - p2shscript = CScript([OP_0, sha256(script)]) - return script_to_p2sh(p2shscript, main) - def check_key(key): if (type(key) is str): key = hex_str_to_bytes(key) # Assuming this is hex string diff --git a/test/functional/test_framework/bignum.py b/test/functional/test_framework/bignum.py index 024611da6..db5ccd62c 100644 --- a/test/functional/test_framework/bignum.py +++ b/test/functional/test_framework/bignum.py @@ -26,12 +26,6 @@ def bn2bin(v): i -= 1 return s -def bin2bn(s): - l = 0 - for ch in s: - l = (l << 8) | ch - return l - def bn2mpi(v): have_ext = False if v.bit_length() > 0: @@ -54,30 +48,6 @@ def bn2mpi(v): v_bin[0] |= 0x80 return s + ext + v_bin -def mpi2bn(s): - if len(s) < 4: - return None - s_size = bytes(s[:4]) - v_len = struct.unpack(b">I", s_size)[0] - if len(s) != (v_len + 4): - return None - if v_len == 0: - return 0 - - v_str = bytearray(s[4:]) - neg = False - i = v_str[0] - if i & 0x80: - neg = True - i &= ~0x80 - v_str[0] = i - - v = bin2bn(v_str) - - if neg: - return -v - return v - # bitcoin-specific little endian format, with implicit size def mpi2vch(s): r = s[4:] # strip size @@ -86,12 +56,3 @@ def mpi2vch(s): def bn2vch(v): return bytes(mpi2vch(bn2mpi(v))) - -def vch2mpi(s): - r = struct.pack(b">I", len(s)) # size - r += s[::-1] # reverse string, converting LE->BE - return r - -def vch2bn(s): - return mpi2bn(vch2mpi(s)) - diff --git a/test/functional/test_framework/blockstore.py b/test/functional/test_framework/blockstore.py index 4cfd682bb..4b2170a03 100644 --- a/test/functional/test_framework/blockstore.py +++ b/test/functional/test_framework/blockstore.py @@ -143,16 +143,6 @@ def get(self, txhash): return None return value - def get_transaction(self, txhash): - ret = None - serialized_tx = self.get(txhash) - if serialized_tx is not None: - f = BytesIO(serialized_tx) - ret = CTransaction() - ret.deserialize(f) - ret.calc_sha256() - return ret - def add_transaction(self, tx): tx.calc_sha256() try: diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py index d0753276d..2607b9b07 100755 --- a/test/functional/test_framework/mininode.py +++ b/test/functional/test_framework/mininode.py @@ -48,8 +48,8 @@ COIN = 100000000 # 1 btc in satoshis NODE_NETWORK = (1 << 0) -NODE_GETUTXO = (1 << 1) -NODE_BLOOM = (1 << 2) +# NODE_GETUTXO = (1 << 1) +# NODE_BLOOM = (1 << 2) NODE_WITNESS = (1 << 3) NODE_UNSUPPORTED_SERVICE_BIT_5 = (1 << 5) NODE_UNSUPPORTED_SERVICE_BIT_7 = (1 << 7) @@ -1479,9 +1479,6 @@ def __init__(self): # before acquiring the global lock and delivering the next message. self.deliver_sleep_time = None - # Remember the services our peer has advertised - self.peer_services = None - # Message receiving methods def deliver(self, conn, message): @@ -1506,10 +1503,6 @@ def deliver(self, conn, message): print("ERROR delivering %s (%s)" % (repr(message), sys.exc_info()[0])) - def set_deliver_sleep_time(self, value): - with mininode_lock: - self.deliver_sleep_time = value - def get_deliver_sleep_time(self): with mininode_lock: return self.deliver_sleep_time diff --git a/test/functional/test_framework/netutil.py b/test/functional/test_framework/netutil.py index 45d8e22d2..e5d415788 100644 --- a/test/functional/test_framework/netutil.py +++ b/test/functional/test_framework/netutil.py @@ -15,17 +15,17 @@ import os from binascii import unhexlify, hexlify -STATE_ESTABLISHED = '01' -STATE_SYN_SENT = '02' -STATE_SYN_RECV = '03' -STATE_FIN_WAIT1 = '04' -STATE_FIN_WAIT2 = '05' -STATE_TIME_WAIT = '06' -STATE_CLOSE = '07' -STATE_CLOSE_WAIT = '08' -STATE_LAST_ACK = '09' +# STATE_ESTABLISHED = '01' +# STATE_SYN_SENT = '02' +# STATE_SYN_RECV = '03' +# STATE_FIN_WAIT1 = '04' +# STATE_FIN_WAIT2 = '05' +# STATE_TIME_WAIT = '06' +# STATE_CLOSE = '07' +# STATE_CLOSE_WAIT = '08' +# STATE_LAST_ACK = '09' STATE_LISTEN = '0A' -STATE_CLOSING = '0B' +# STATE_CLOSING = '0B' def get_socket_inodes(pid): ''' diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py index 3d9572788..8f5339a02 100644 --- a/test/functional/test_framework/script.py +++ b/test/functional/test_framework/script.py @@ -23,9 +23,7 @@ from .bignum import bn2vch -MAX_SCRIPT_SIZE = 10000 MAX_SCRIPT_ELEMENT_SIZE = 520 -MAX_SCRIPT_OPCODES = 201 OPCODE_NAMES = {} @@ -242,131 +240,6 @@ def __new__(cls, n): OP_INVALIDOPCODE = CScriptOp(0xff) -VALID_OPCODES = { - OP_1NEGATE, - OP_RESERVED, - OP_1, - OP_2, - OP_3, - OP_4, - OP_5, - OP_6, - OP_7, - OP_8, - OP_9, - OP_10, - OP_11, - OP_12, - OP_13, - OP_14, - OP_15, - OP_16, - - OP_NOP, - OP_VER, - OP_IF, - OP_NOTIF, - OP_VERIF, - OP_VERNOTIF, - OP_ELSE, - OP_ENDIF, - OP_VERIFY, - OP_RETURN, - - OP_TOALTSTACK, - OP_FROMALTSTACK, - OP_2DROP, - OP_2DUP, - OP_3DUP, - OP_2OVER, - OP_2ROT, - OP_2SWAP, - OP_IFDUP, - OP_DEPTH, - OP_DROP, - OP_DUP, - OP_NIP, - OP_OVER, - OP_PICK, - OP_ROLL, - OP_ROT, - OP_SWAP, - OP_TUCK, - - OP_CAT, - OP_SUBSTR, - OP_LEFT, - OP_RIGHT, - OP_SIZE, - - OP_INVERT, - OP_AND, - OP_OR, - OP_XOR, - OP_EQUAL, - OP_EQUALVERIFY, - OP_RESERVED1, - OP_RESERVED2, - - OP_1ADD, - OP_1SUB, - OP_2MUL, - OP_2DIV, - OP_NEGATE, - OP_ABS, - OP_NOT, - OP_0NOTEQUAL, - - OP_ADD, - OP_SUB, - OP_MUL, - OP_DIV, - OP_MOD, - OP_LSHIFT, - OP_RSHIFT, - - OP_BOOLAND, - OP_BOOLOR, - OP_NUMEQUAL, - OP_NUMEQUALVERIFY, - OP_NUMNOTEQUAL, - OP_LESSTHAN, - OP_GREATERTHAN, - OP_LESSTHANOREQUAL, - OP_GREATERTHANOREQUAL, - OP_MIN, - OP_MAX, - - OP_WITHIN, - - OP_RIPEMD160, - OP_SHA1, - OP_SHA256, - OP_HASH160, - OP_HASH256, - OP_CODESEPARATOR, - OP_CHECKSIG, - OP_CHECKSIGVERIFY, - OP_CHECKMULTISIG, - OP_CHECKMULTISIGVERIFY, - - OP_NOP1, - OP_CHECKLOCKTIMEVERIFY, - OP_CHECKSEQUENCEVERIFY, - OP_NOP4, - OP_NOP5, - OP_NOP6, - OP_NOP7, - OP_NOP8, - OP_NOP9, - OP_NOP10, - - OP_SMALLINTEGER, - OP_PUBKEYS, - OP_PUBKEYHASH, - OP_PUBKEY, -} - OPCODE_NAMES.update({ OP_0 : 'OP_0', OP_PUSHDATA1 : 'OP_PUSHDATA1', @@ -486,124 +359,6 @@ def __new__(cls, n): OP_INVALIDOPCODE : 'OP_INVALIDOPCODE', }) -OPCODES_BY_NAME = { - 'OP_0' : OP_0, - 'OP_PUSHDATA1' : OP_PUSHDATA1, - 'OP_PUSHDATA2' : OP_PUSHDATA2, - 'OP_PUSHDATA4' : OP_PUSHDATA4, - 'OP_1NEGATE' : OP_1NEGATE, - 'OP_RESERVED' : OP_RESERVED, - 'OP_1' : OP_1, - 'OP_2' : OP_2, - 'OP_3' : OP_3, - 'OP_4' : OP_4, - 'OP_5' : OP_5, - 'OP_6' : OP_6, - 'OP_7' : OP_7, - 'OP_8' : OP_8, - 'OP_9' : OP_9, - 'OP_10' : OP_10, - 'OP_11' : OP_11, - 'OP_12' : OP_12, - 'OP_13' : OP_13, - 'OP_14' : OP_14, - 'OP_15' : OP_15, - 'OP_16' : OP_16, - 'OP_NOP' : OP_NOP, - 'OP_VER' : OP_VER, - 'OP_IF' : OP_IF, - 'OP_NOTIF' : OP_NOTIF, - 'OP_VERIF' : OP_VERIF, - 'OP_VERNOTIF' : OP_VERNOTIF, - 'OP_ELSE' : OP_ELSE, - 'OP_ENDIF' : OP_ENDIF, - 'OP_VERIFY' : OP_VERIFY, - 'OP_RETURN' : OP_RETURN, - 'OP_TOALTSTACK' : OP_TOALTSTACK, - 'OP_FROMALTSTACK' : OP_FROMALTSTACK, - 'OP_2DROP' : OP_2DROP, - 'OP_2DUP' : OP_2DUP, - 'OP_3DUP' : OP_3DUP, - 'OP_2OVER' : OP_2OVER, - 'OP_2ROT' : OP_2ROT, - 'OP_2SWAP' : OP_2SWAP, - 'OP_IFDUP' : OP_IFDUP, - 'OP_DEPTH' : OP_DEPTH, - 'OP_DROP' : OP_DROP, - 'OP_DUP' : OP_DUP, - 'OP_NIP' : OP_NIP, - 'OP_OVER' : OP_OVER, - 'OP_PICK' : OP_PICK, - 'OP_ROLL' : OP_ROLL, - 'OP_ROT' : OP_ROT, - 'OP_SWAP' : OP_SWAP, - 'OP_TUCK' : OP_TUCK, - 'OP_CAT' : OP_CAT, - 'OP_SUBSTR' : OP_SUBSTR, - 'OP_LEFT' : OP_LEFT, - 'OP_RIGHT' : OP_RIGHT, - 'OP_SIZE' : OP_SIZE, - 'OP_INVERT' : OP_INVERT, - 'OP_AND' : OP_AND, - 'OP_OR' : OP_OR, - 'OP_XOR' : OP_XOR, - 'OP_EQUAL' : OP_EQUAL, - 'OP_EQUALVERIFY' : OP_EQUALVERIFY, - 'OP_RESERVED1' : OP_RESERVED1, - 'OP_RESERVED2' : OP_RESERVED2, - 'OP_1ADD' : OP_1ADD, - 'OP_1SUB' : OP_1SUB, - 'OP_2MUL' : OP_2MUL, - 'OP_2DIV' : OP_2DIV, - 'OP_NEGATE' : OP_NEGATE, - 'OP_ABS' : OP_ABS, - 'OP_NOT' : OP_NOT, - 'OP_0NOTEQUAL' : OP_0NOTEQUAL, - 'OP_ADD' : OP_ADD, - 'OP_SUB' : OP_SUB, - 'OP_MUL' : OP_MUL, - 'OP_DIV' : OP_DIV, - 'OP_MOD' : OP_MOD, - 'OP_LSHIFT' : OP_LSHIFT, - 'OP_RSHIFT' : OP_RSHIFT, - 'OP_BOOLAND' : OP_BOOLAND, - 'OP_BOOLOR' : OP_BOOLOR, - 'OP_NUMEQUAL' : OP_NUMEQUAL, - 'OP_NUMEQUALVERIFY' : OP_NUMEQUALVERIFY, - 'OP_NUMNOTEQUAL' : OP_NUMNOTEQUAL, - 'OP_LESSTHAN' : OP_LESSTHAN, - 'OP_GREATERTHAN' : OP_GREATERTHAN, - 'OP_LESSTHANOREQUAL' : OP_LESSTHANOREQUAL, - 'OP_GREATERTHANOREQUAL' : OP_GREATERTHANOREQUAL, - 'OP_MIN' : OP_MIN, - 'OP_MAX' : OP_MAX, - 'OP_WITHIN' : OP_WITHIN, - 'OP_RIPEMD160' : OP_RIPEMD160, - 'OP_SHA1' : OP_SHA1, - 'OP_SHA256' : OP_SHA256, - 'OP_HASH160' : OP_HASH160, - 'OP_HASH256' : OP_HASH256, - 'OP_CODESEPARATOR' : OP_CODESEPARATOR, - 'OP_CHECKSIG' : OP_CHECKSIG, - 'OP_CHECKSIGVERIFY' : OP_CHECKSIGVERIFY, - 'OP_CHECKMULTISIG' : OP_CHECKMULTISIG, - 'OP_CHECKMULTISIGVERIFY' : OP_CHECKMULTISIGVERIFY, - 'OP_NOP1' : OP_NOP1, - 'OP_CHECKLOCKTIMEVERIFY' : OP_CHECKLOCKTIMEVERIFY, - 'OP_CHECKSEQUENCEVERIFY' : OP_CHECKSEQUENCEVERIFY, - 'OP_NOP4' : OP_NOP4, - 'OP_NOP5' : OP_NOP5, - 'OP_NOP6' : OP_NOP6, - 'OP_NOP7' : OP_NOP7, - 'OP_NOP8' : OP_NOP8, - 'OP_NOP9' : OP_NOP9, - 'OP_NOP10' : OP_NOP10, - 'OP_SMALLINTEGER' : OP_SMALLINTEGER, - 'OP_PUBKEYS' : OP_PUBKEYS, - 'OP_PUBKEYHASH' : OP_PUBKEYHASH, - 'OP_PUBKEY' : OP_PUBKEY, -} - class CScriptInvalidError(Exception): """Base class for CScript exceptions""" pass diff --git a/test/functional/wallet.py b/test/functional/wallet.py index 3e3e8fcdd..40225ad55 100755 --- a/test/functional/wallet.py +++ b/test/functional/wallet.py @@ -206,7 +206,7 @@ def run_test(self): signedRawTx = self.nodes[1].signrawtransaction(rawTx) decRawTx = self.nodes[1].decoderawtransaction(signedRawTx['hex']) zeroValueTxid= decRawTx['txid'] - sendResp = self.nodes[1].sendrawtransaction(signedRawTx['hex']) + self.nodes[1].sendrawtransaction(signedRawTx['hex']) self.sync_all() self.nodes[1].generate(1) #mine a block From 8239794360587d46895150b90172c36fec16d13f Mon Sep 17 00:00:00 2001 From: practicalswift Date: Thu, 20 Jul 2017 23:21:41 +0200 Subject: [PATCH 128/382] Use the variable name _ for unused return values --- test/functional/mempool_packages.py | 2 +- test/functional/preciousblock.py | 6 +++--- test/functional/test_framework/socks5.py | 2 +- test/functional/wallet-dump.py | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py index e22549381..423d03eac 100755 --- a/test/functional/mempool_packages.py +++ b/test/functional/mempool_packages.py @@ -213,7 +213,7 @@ def run_test(self): value = send_value # Create tx1 - (tx1_id, tx1_value) = self.chain_transaction(self.nodes[0], tx0_id, 0, value, fee, 1) + tx1_id, _ = self.chain_transaction(self.nodes[0], tx0_id, 0, value, fee, 1) # Create tx2-7 vout = 1 diff --git a/test/functional/preciousblock.py b/test/functional/preciousblock.py index 04b41e76b..a69b8fb20 100755 --- a/test/functional/preciousblock.py +++ b/test/functional/preciousblock.py @@ -47,16 +47,16 @@ def run_test(self): self.log.info("Ensure submitblock can in principle reorg to a competing chain") self.nodes[0].generate(1) assert_equal(self.nodes[0].getblockcount(), 1) - (hashY, hashZ) = self.nodes[1].generate(2) + hashZ = self.nodes[1].generate(2)[-1] assert_equal(self.nodes[1].getblockcount(), 2) node_sync_via_rpc(self.nodes[0:3]) assert_equal(self.nodes[0].getbestblockhash(), hashZ) self.log.info("Mine blocks A-B-C on Node 0") - (hashA, hashB, hashC) = self.nodes[0].generate(3) + hashC = self.nodes[0].generate(3)[-1] assert_equal(self.nodes[0].getblockcount(), 5) self.log.info("Mine competing blocks E-F-G on Node 1") - (hashE, hashF, hashG) = self.nodes[1].generate(3) + hashG = self.nodes[1].generate(3)[-1] assert_equal(self.nodes[1].getblockcount(), 5) assert(hashC != hashG) self.log.info("Connect nodes and check no reorg occurs") diff --git a/test/functional/test_framework/socks5.py b/test/functional/test_framework/socks5.py index a08b03ed2..007084416 100644 --- a/test/functional/test_framework/socks5.py +++ b/test/functional/test_framework/socks5.py @@ -91,7 +91,7 @@ def handle(self): self.conn.sendall(bytearray([0x01, 0x00])) # Read connect request - (ver,cmd,rsv,atyp) = recvall(self.conn, 4) + ver, cmd, _, atyp = recvall(self.conn, 4) if ver != 0x05: raise IOError('Invalid socks version %i in connect request' % ver) if cmd != Command.CONNECT: diff --git a/test/functional/wallet-dump.py b/test/functional/wallet-dump.py index 61ad00330..b573a95ab 100755 --- a/test/functional/wallet-dump.py +++ b/test/functional/wallet-dump.py @@ -101,7 +101,7 @@ def run_test (self): self.nodes[0].keypoolrefill() self.nodes[0].dumpwallet(tmpdir + "/node0/wallet.encrypted.dump") - found_addr, found_addr_chg, found_addr_rsv, hd_master_addr_enc = \ + found_addr, found_addr_chg, found_addr_rsv, _ = \ read_dump(tmpdir + "/node0/wallet.encrypted.dump", addrs, hd_master_addr_unenc) assert_equal(found_addr, test_addr_count) assert_equal(found_addr_chg, 90*2 + 50) # old reserve keys are marked as change now From 78214588d6b8b7199015113c9d0d8250d6150014 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Sun, 9 Jul 2017 22:51:47 +0200 Subject: [PATCH 129/382] Use for-loop instead of list comprehension To make it clear that we are intentionally using it for its side-effects. --- test/functional/p2p-compactblocks.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/functional/p2p-compactblocks.py b/test/functional/p2p-compactblocks.py index c5c264765..91c0c406f 100755 --- a/test/functional/p2p-compactblocks.py +++ b/test/functional/p2p-compactblocks.py @@ -286,7 +286,8 @@ def test_compactblock_construction(self, node, test_node, version, use_witness_a # Store the raw block in our internal format. block = FromHex(CBlock(), node.getblock("%02x" % block_hash, False)) - [tx.calc_sha256() for tx in block.vtx] + for tx in block.vtx: + tx.calc_sha256() block.rehash() # Wait until the block was announced (via compact blocks) From 4f2905b76b502a9de235dbe739e5fe504ef5bc22 Mon Sep 17 00:00:00 2001 From: Cristian Mircea Messel Date: Sat, 26 Aug 2017 01:20:33 +0300 Subject: [PATCH 130/382] Add getmininginfo functional test --- test/functional/mining.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/test/functional/mining.py b/test/functional/mining.py index dbd4e29ec..f3d1f3e90 100755 --- a/test/functional/mining.py +++ b/test/functional/mining.py @@ -4,16 +4,18 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test mining RPCs +- getmininginfo - getblocktemplate proposal mode - submitblock""" -from binascii import b2a_hex import copy +from binascii import b2a_hex +from decimal import Decimal from test_framework.blocktools import create_coinbase -from test_framework.test_framework import BitcoinTestFramework from test_framework.mininode import CBlock -from test_framework.util import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal, assert_raises_jsonrpc def b2x(b): return b2a_hex(b).decode('ascii') @@ -33,6 +35,18 @@ def __init__(self): def run_test(self): node = self.nodes[0] + + self.log.info('getmininginfo') + mining_info = node.getmininginfo() + assert_equal(mining_info['blocks'], 200) + assert_equal(mining_info['chain'], 'regtest') + assert_equal(mining_info['currentblocksize'], 0) + assert_equal(mining_info['currentblocktx'], 0) + assert_equal(mining_info['currentblockweight'], 0) + assert_equal(mining_info['difficulty'], Decimal('4.656542373906925E-10')) + assert_equal(mining_info['networkhashps'], Decimal('0.003333333333333334')) + assert_equal(mining_info['pooledtx'], 0) + # Mine a block to leave initial block download node.generate(1) tmpl = node.getblocktemplate() From 6d2d2eb49389d70a5db327e133c9c90748f82d5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Tue, 18 Jul 2017 02:19:21 +0200 Subject: [PATCH 131/382] RPC: gettxout: Slightly improve doc and tests --- src/rpc/blockchain.cpp | 7 ++++--- test/functional/wallet.py | 12 ++++++++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 6178a1c7a..10eb7f13a 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -944,9 +944,10 @@ UniValue gettxout(const JSONRPCRequest& request) "gettxout \"txid\" n ( include_mempool )\n" "\nReturns details about an unspent transaction output.\n" "\nArguments:\n" - "1. \"txid\" (string, required) The transaction id\n" - "2. n (numeric, required) vout number\n" - "3. include_mempool (boolean, optional) Whether to include the mempool\n" + "1. \"txid\" (string, required) The transaction id\n" + "2. \"n\" (numeric, required) vout number\n" + "3. \"include_mempool\" (boolean, optional) Whether to include the mempool. Default: true." + " Note that an unspent output that is spent in the mempool won't appear.\n" "\nResult:\n" "{\n" " \"bestblock\" : \"hash\", (string) the block hash\n" diff --git a/test/functional/wallet.py b/test/functional/wallet.py index 3e3e8fcdd..a37c457bc 100755 --- a/test/functional/wallet.py +++ b/test/functional/wallet.py @@ -56,6 +56,15 @@ def run_test(self): assert_equal(len(self.nodes[1].listunspent()), 1) assert_equal(len(self.nodes[2].listunspent()), 0) + self.log.info("test gettxout") + confirmed_txid, confirmed_index = utxos[0]["txid"], utxos[0]["vout"] + # First, outputs that are unspent both in the chain and in the + # mempool should appear with or without include_mempool + txout = self.nodes[0].gettxout(txid=confirmed_txid, n=confirmed_index, include_mempool=False) + assert_equal(txout['value'], 50) + txout = self.nodes[0].gettxout(txid=confirmed_txid, n=confirmed_index, include_mempool=True) + assert_equal(txout['value'], 50) + # Send 21 BTC from 0 to 2 using sendtoaddress call. # Locked memory should use at least 32 bytes to sign each transaction self.log.info("test getmemoryinfo") @@ -65,10 +74,9 @@ def run_test(self): memory_after = self.nodes[0].getmemoryinfo() assert(memory_before['locked']['used'] + 64 <= memory_after['locked']['used']) - self.log.info("test gettxout") + self.log.info("test gettxout (second part)") # utxo spent in mempool should be visible if you exclude mempool # but invisible if you include mempool - confirmed_txid, confirmed_index = utxos[0]["txid"], utxos[0]["vout"] txout = self.nodes[0].gettxout(confirmed_txid, confirmed_index, False) assert_equal(txout['value'], 50) txout = self.nodes[0].gettxout(confirmed_txid, confirmed_index, True) From ec6902d0ea2bbe75179684fc71849d5e34647a14 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Thu, 8 Jun 2017 17:38:23 -0700 Subject: [PATCH 132/382] rpc: Push down safe mode checks This contains most of the changes of 10563 "remove safe mode", but doesn't remove the safe mode yet, but put an `ObserveSafeMode()` check in individual calls with okSafeMode=false. This cleans up the ugly "okSafeMode" flag from the dispatch tables, which is not a concern for the RPC server. Extra-author: Wladimir J. van der Laan --- src/Makefile.am | 2 + src/init.cpp | 12 +-- src/qt/test/rpcnestedtests.cpp | 2 +- src/rpc/blockchain.cpp | 54 +++++++------- src/rpc/mining.cpp | 21 +++--- src/rpc/misc.cpp | 22 +++--- src/rpc/net.cpp | 26 +++---- src/rpc/rawtransaction.cpp | 25 ++++--- src/rpc/safemode.cpp | 14 ++++ src/rpc/safemode.h | 12 +++ src/rpc/server.cpp | 15 ++-- src/rpc/server.h | 2 - src/wallet/rpcdump.cpp | 2 + src/wallet/rpcwallet.cpp | 130 +++++++++++++++++++-------------- 14 files changed, 189 insertions(+), 150 deletions(-) create mode 100644 src/rpc/safemode.cpp create mode 100644 src/rpc/safemode.h diff --git a/src/Makefile.am b/src/Makefile.am index dea656869..a1cbe75dd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -130,6 +130,7 @@ BITCOIN_CORE_H = \ rpc/client.h \ rpc/mining.h \ rpc/protocol.h \ + rpc/safemode.h \ rpc/server.h \ rpc/register.h \ scheduler.h \ @@ -210,6 +211,7 @@ libbitcoin_server_a_SOURCES = \ rpc/misc.cpp \ rpc/net.cpp \ rpc/rawtransaction.cpp \ + rpc/safemode.cpp \ rpc/server.cpp \ script/sigcache.cpp \ script/ismine.cpp \ diff --git a/src/init.cpp b/src/init.cpp index 7c99dc74e..6114bbaa2 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -30,6 +30,7 @@ #include "policy/policy.h" #include "rpc/server.h" #include "rpc/register.h" +#include "rpc/safemode.h" #include "rpc/blockchain.h" #include "script/standard.h" #include "script/sigcache.h" @@ -70,7 +71,6 @@ bool fFeeEstimatesInitialized = false; static const bool DEFAULT_PROXYRANDOMIZE = true; static const bool DEFAULT_REST_ENABLE = false; -static const bool DEFAULT_DISABLE_SAFEMODE = true; static const bool DEFAULT_STOPAFTERBLOCKIMPORT = false; std::unique_ptr g_connman; @@ -318,15 +318,6 @@ void OnRPCStopped() LogPrint(BCLog::RPC, "RPC stopped.\n"); } -void OnRPCPreCommand(const CRPCCommand& cmd) -{ - // Observe safe mode - std::string strWarning = GetWarnings("rpc"); - if (strWarning != "" && !gArgs.GetBoolArg("-disablesafemode", DEFAULT_DISABLE_SAFEMODE) && - !cmd.okSafeMode) - throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, std::string("Safe mode: ") + strWarning); -} - std::string HelpMessage(HelpMessageMode mode) { const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN); @@ -721,7 +712,6 @@ bool AppInitServers(boost::thread_group& threadGroup) { RPCServer::OnStarted(&OnRPCStarted); RPCServer::OnStopped(&OnRPCStopped); - RPCServer::OnPreCommand(&OnRPCPreCommand); if (!InitHTTPServer()) return false; if (!StartRPC()) diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp index cd9ab2345..cb781c832 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, {} }, }; void RPCNestedTests::rpcNestedTests() diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 34f553f3b..6e1af263d 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1532,35 +1532,35 @@ UniValue getchaintxstats(const JSONRPCRequest& request) } static const CRPCCommand commands[] = -{ // category name actor (function) okSafe argNames - // --------------------- ------------------------ ----------------------- ------ ---------- - { "blockchain", "getblockchaininfo", &getblockchaininfo, true, {} }, - { "blockchain", "getchaintxstats", &getchaintxstats, true, {"nblocks", "blockhash"} }, - { "blockchain", "getbestblockhash", &getbestblockhash, true, {} }, - { "blockchain", "getblockcount", &getblockcount, true, {} }, - { "blockchain", "getblock", &getblock, true, {"blockhash","verbosity|verbose"} }, - { "blockchain", "getblockhash", &getblockhash, true, {"height"} }, - { "blockchain", "getblockheader", &getblockheader, true, {"blockhash","verbose"} }, - { "blockchain", "getchaintips", &getchaintips, true, {} }, - { "blockchain", "getdifficulty", &getdifficulty, true, {} }, - { "blockchain", "getmempoolancestors", &getmempoolancestors, true, {"txid","verbose"} }, - { "blockchain", "getmempooldescendants", &getmempooldescendants, true, {"txid","verbose"} }, - { "blockchain", "getmempoolentry", &getmempoolentry, true, {"txid"} }, - { "blockchain", "getmempoolinfo", &getmempoolinfo, true, {} }, - { "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"} }, +{ // category name actor (function) argNames + // --------------------- ------------------------ ----------------------- ---------- + { "blockchain", "getblockchaininfo", &getblockchaininfo, {} }, + { "blockchain", "getchaintxstats", &getchaintxstats, {"nblocks", "blockhash"} }, + { "blockchain", "getbestblockhash", &getbestblockhash, {} }, + { "blockchain", "getblockcount", &getblockcount, {} }, + { "blockchain", "getblock", &getblock, {"blockhash","verbosity|verbose"} }, + { "blockchain", "getblockhash", &getblockhash, {"height"} }, + { "blockchain", "getblockheader", &getblockheader, {"blockhash","verbose"} }, + { "blockchain", "getchaintips", &getchaintips, {} }, + { "blockchain", "getdifficulty", &getdifficulty, {} }, + { "blockchain", "getmempoolancestors", &getmempoolancestors, {"txid","verbose"} }, + { "blockchain", "getmempooldescendants", &getmempooldescendants, {"txid","verbose"} }, + { "blockchain", "getmempoolentry", &getmempoolentry, {"txid"} }, + { "blockchain", "getmempoolinfo", &getmempoolinfo, {} }, + { "blockchain", "getrawmempool", &getrawmempool, {"verbose"} }, + { "blockchain", "gettxout", &gettxout, {"txid","n","include_mempool"} }, + { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, {} }, + { "blockchain", "pruneblockchain", &pruneblockchain, {"height"} }, + { "blockchain", "verifychain", &verifychain, {"checklevel","nblocks"} }, + + { "blockchain", "preciousblock", &preciousblock, {"blockhash"} }, /* Not shown in help */ - { "hidden", "invalidateblock", &invalidateblock, true, {"blockhash"} }, - { "hidden", "reconsiderblock", &reconsiderblock, true, {"blockhash"} }, - { "hidden", "waitfornewblock", &waitfornewblock, true, {"timeout"} }, - { "hidden", "waitforblock", &waitforblock, true, {"blockhash","timeout"} }, - { "hidden", "waitforblockheight", &waitforblockheight, true, {"height","timeout"} }, + { "hidden", "invalidateblock", &invalidateblock, {"blockhash"} }, + { "hidden", "reconsiderblock", &reconsiderblock, {"blockhash"} }, + { "hidden", "waitfornewblock", &waitfornewblock, {"timeout"} }, + { "hidden", "waitforblock", &waitforblock, {"blockhash","timeout"} }, + { "hidden", "waitforblockheight", &waitforblockheight, {"height","timeout"} }, }; void RegisterBlockchainRPCCommands(CRPCTable &t) diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 2692e5915..ca406ce44 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -967,20 +967,21 @@ UniValue estimaterawfee(const JSONRPCRequest& request) } static const CRPCCommand commands[] = -{ // category name actor (function) okSafeMode +{ // category name actor (function) argNames // --------------------- ------------------------ ----------------------- ---------- - { "mining", "getnetworkhashps", &getnetworkhashps, true, {"nblocks","height"} }, - { "mining", "getmininginfo", &getmininginfo, true, {} }, - { "mining", "prioritisetransaction", &prioritisetransaction, true, {"txid","dummy","fee_delta"} }, - { "mining", "getblocktemplate", &getblocktemplate, true, {"template_request"} }, - { "mining", "submitblock", &submitblock, true, {"hexdata","dummy"} }, + { "mining", "getnetworkhashps", &getnetworkhashps, {"nblocks","height"} }, + { "mining", "getmininginfo", &getmininginfo, {} }, + { "mining", "prioritisetransaction", &prioritisetransaction, {"txid","dummy","fee_delta"} }, + { "mining", "getblocktemplate", &getblocktemplate, {"template_request"} }, + { "mining", "submitblock", &submitblock, {"hexdata","dummy"} }, - { "generating", "generatetoaddress", &generatetoaddress, true, {"nblocks","address","maxtries"} }, - { "util", "estimatefee", &estimatefee, true, {"nblocks"} }, - { "util", "estimatesmartfee", &estimatesmartfee, true, {"conf_target", "estimate_mode"} }, + { "generating", "generatetoaddress", &generatetoaddress, {"nblocks","address","maxtries"} }, - { "hidden", "estimaterawfee", &estimaterawfee, true, {"conf_target", "threshold"} }, + { "util", "estimatefee", &estimatefee, {"nblocks"} }, + { "util", "estimatesmartfee", &estimatesmartfee, {"nblocks", "estimate_mode"} }, + + { "hidden", "estimaterawfee", &estimaterawfee, {"conf_target", "threshold"} }, }; void RegisterMiningRPCCommands(CRPCTable &t) diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index a6af24f7e..76eefabd2 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -649,20 +649,20 @@ UniValue echo(const JSONRPCRequest& request) } static const CRPCCommand commands[] = -{ // category name actor (function) okSafeMode +{ // category name actor (function) argNames // --------------------- ------------------------ ----------------------- ---------- - { "control", "getinfo", &getinfo, true, {} }, /* uses wallet if enabled */ - { "control", "getmemoryinfo", &getmemoryinfo, true, {"mode"} }, - { "util", "validateaddress", &validateaddress, true, {"address"} }, /* uses wallet if enabled */ - { "util", "createmultisig", &createmultisig, true, {"nrequired","keys"} }, - { "util", "verifymessage", &verifymessage, true, {"address","signature","message"} }, - { "util", "signmessagewithprivkey", &signmessagewithprivkey, true, {"privkey","message"} }, + { "control", "getinfo", &getinfo, {} }, /* uses wallet if enabled */ + { "control", "getmemoryinfo", &getmemoryinfo, {"mode"} }, + { "util", "validateaddress", &validateaddress, {"address"} }, /* uses wallet if enabled */ + { "util", "createmultisig", &createmultisig, {"nrequired","keys"} }, + { "util", "verifymessage", &verifymessage, {"address","signature","message"} }, + { "util", "signmessagewithprivkey", &signmessagewithprivkey, {"privkey","message"} }, /* Not shown in help */ - { "hidden", "setmocktime", &setmocktime, true, {"timestamp"}}, - { "hidden", "echo", &echo, true, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}}, - { "hidden", "echojson", &echo, true, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}}, - { "hidden", "logging", &logging, true, {"include", "exclude"}}, + { "hidden", "setmocktime", &setmocktime, {"timestamp"}}, + { "hidden", "echo", &echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}}, + { "hidden", "echojson", &echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}}, + { "hidden", "logging", &logging, {"include", "exclude"}}, }; void RegisterMiscRPCCommands(CRPCTable &t) diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index f19b96824..7faf21604 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -623,20 +623,20 @@ UniValue setnetworkactive(const JSONRPCRequest& request) } static const CRPCCommand commands[] = -{ // category name actor (function) okSafeMode +{ // category name actor (function) argNames // --------------------- ------------------------ ----------------------- ---------- - { "network", "getconnectioncount", &getconnectioncount, true, {} }, - { "network", "ping", &ping, true, {} }, - { "network", "getpeerinfo", &getpeerinfo, true, {} }, - { "network", "addnode", &addnode, true, {"node","command"} }, - { "network", "disconnectnode", &disconnectnode, true, {"address", "nodeid"} }, - { "network", "getaddednodeinfo", &getaddednodeinfo, true, {"node"} }, - { "network", "getnettotals", &getnettotals, true, {} }, - { "network", "getnetworkinfo", &getnetworkinfo, true, {} }, - { "network", "setban", &setban, true, {"subnet", "command", "bantime", "absolute"} }, - { "network", "listbanned", &listbanned, true, {} }, - { "network", "clearbanned", &clearbanned, true, {} }, - { "network", "setnetworkactive", &setnetworkactive, true, {"state"} }, + { "network", "getconnectioncount", &getconnectioncount, {} }, + { "network", "ping", &ping, {} }, + { "network", "getpeerinfo", &getpeerinfo, {} }, + { "network", "addnode", &addnode, {"node","command"} }, + { "network", "disconnectnode", &disconnectnode, {"address", "nodeid"} }, + { "network", "getaddednodeinfo", &getaddednodeinfo, {"node"} }, + { "network", "getnettotals", &getnettotals, {} }, + { "network", "getnetworkinfo", &getnetworkinfo, {} }, + { "network", "setban", &setban, {"subnet", "command", "bantime", "absolute"} }, + { "network", "listbanned", &listbanned, {} }, + { "network", "clearbanned", &clearbanned, {} }, + { "network", "setnetworkactive", &setnetworkactive, {"state"} }, }; void RegisterNetRPCCommands(CRPCTable &t) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 934576a39..27daefaf5 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -16,6 +16,7 @@ #include "policy/policy.h" #include "policy/rbf.h" #include "primitives/transaction.h" +#include "rpc/safemode.h" #include "rpc/server.h" #include "script/script.h" #include "script/script_error.h" @@ -703,6 +704,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request) + HelpExampleRpc("signrawtransaction", "\"myhex\"") ); + ObserveSafeMode(); #ifdef ENABLE_WALLET LOCK2(cs_main, pwallet ? &pwallet->cs_wallet : nullptr); #else @@ -908,6 +910,7 @@ UniValue sendrawtransaction(const JSONRPCRequest& request) + HelpExampleRpc("sendrawtransaction", "\"signedhex\"") ); + ObserveSafeMode(); LOCK(cs_main); RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL}); @@ -959,18 +962,18 @@ UniValue sendrawtransaction(const JSONRPCRequest& request) } static const CRPCCommand commands[] = -{ // category name actor (function) okSafeMode +{ // category name actor (function) argNames // --------------------- ------------------------ ----------------------- ---------- - { "rawtransactions", "getrawtransaction", &getrawtransaction, true, {"txid","verbose"} }, - { "rawtransactions", "createrawtransaction", &createrawtransaction, true, {"inputs","outputs","locktime","replaceable"} }, - { "rawtransactions", "decoderawtransaction", &decoderawtransaction, true, {"hexstring"} }, - { "rawtransactions", "decodescript", &decodescript, true, {"hexstring"} }, - { "rawtransactions", "sendrawtransaction", &sendrawtransaction, false, {"hexstring","allowhighfees"} }, - { "rawtransactions", "combinerawtransaction", &combinerawtransaction, true, {"txs"} }, - { "rawtransactions", "signrawtransaction", &signrawtransaction, false, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */ - - { "blockchain", "gettxoutproof", &gettxoutproof, true, {"txids", "blockhash"} }, - { "blockchain", "verifytxoutproof", &verifytxoutproof, true, {"proof"} }, + { "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose"} }, + { "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime","replaceable"} }, + { "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring"} }, + { "rawtransactions", "decodescript", &decodescript, {"hexstring"} }, + { "rawtransactions", "sendrawtransaction", &sendrawtransaction, {"hexstring","allowhighfees"} }, + { "rawtransactions", "combinerawtransaction", &combinerawtransaction, {"txs"} }, + { "rawtransactions", "signrawtransaction", &signrawtransaction, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */ + + { "blockchain", "gettxoutproof", &gettxoutproof, {"txids", "blockhash"} }, + { "blockchain", "verifytxoutproof", &verifytxoutproof, {"proof"} }, }; void RegisterRawTransactionRPCCommands(CRPCTable &t) diff --git a/src/rpc/safemode.cpp b/src/rpc/safemode.cpp new file mode 100644 index 000000000..24770ad47 --- /dev/null +++ b/src/rpc/safemode.cpp @@ -0,0 +1,14 @@ +#include "safemode.h" + +#include "rpc/protocol.h" +#include "util.h" +#include "warnings.h" + +void ObserveSafeMode() +{ + std::string warning = GetWarnings("rpc"); + if (warning != "" && !gArgs.GetBoolArg("-disablesafemode", DEFAULT_DISABLE_SAFEMODE)) { + throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, std::string("Safe mode: ") + warning); + } +} + diff --git a/src/rpc/safemode.h b/src/rpc/safemode.h new file mode 100644 index 000000000..8466d6b2f --- /dev/null +++ b/src/rpc/safemode.h @@ -0,0 +1,12 @@ +// Copyright (c) 2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_RPC_SAFEMODE_H +#define BITCOIN_RPC_SAFEMODE_H + +static const bool DEFAULT_DISABLE_SAFEMODE = true; + +void ObserveSafeMode(); + +#endif // BITCOIN_RPC_SAFEMODE_H diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 9ad8d228f..428ab3b9b 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -51,11 +51,6 @@ void RPCServer::OnStopped(std::function slot) g_rpcSignals.Stopped.connect(slot); } -void RPCServer::OnPreCommand(std::function slot) -{ - g_rpcSignals.PreCommand.connect(boost::bind(slot, _1)); -} - void RPCTypeCheck(const UniValue& params, const std::list& typesExpected, bool fAllowNull) @@ -267,12 +262,12 @@ UniValue uptime(const JSONRPCRequest& jsonRequest) * Call Table */ static const CRPCCommand vRPCCommands[] = -{ // category name actor (function) okSafe argNames - // --------------------- ------------------------ ----------------------- ------ ---------- +{ // category name actor (function) argNames + // --------------------- ------------------------ ----------------------- ---------- /* Overall control/query calls */ - { "control", "help", &help, true, {"command"} }, - { "control", "stop", &stop, true, {} }, - { "control", "uptime", &uptime, true, {} }, + { "control", "help", &help, {"command"} }, + { "control", "stop", &stop, {} }, + { "control", "uptime", &uptime, {} }, }; CRPCTable::CRPCTable() diff --git a/src/rpc/server.h b/src/rpc/server.h index 89b1d169d..777acbcb9 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -25,7 +25,6 @@ namespace RPCServer { void OnStarted(std::function slot); void OnStopped(std::function slot); - void OnPreCommand(std::function slot); } /** Wrapper for UniValue::VType, which includes typeAny: @@ -134,7 +133,6 @@ class CRPCCommand std::string category; std::string name; rpcfn_type actor; - bool okSafeMode; std::vector argNames; }; diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 67c6d9ec6..db6f6b48a 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -4,6 +4,7 @@ #include "base58.h" #include "chain.h" +#include "rpc/safemode.h" #include "rpc/server.h" #include "init.h" #include "validation.h" @@ -174,6 +175,7 @@ UniValue abortrescan(const JSONRPCRequest& request) + HelpExampleRpc("abortrescan", "") ); + ObserveSafeMode(); if (!pwallet->IsScanning() || pwallet->IsAbortingRescan()) return false; pwallet->AbortRescan(); return true; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 4ea53c413..0829c9ec4 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -16,6 +16,7 @@ #include "policy/policy.h" #include "policy/rbf.h" #include "rpc/mining.h" +#include "rpc/safemode.h" #include "rpc/server.h" #include "script/sign.h" #include "timedata.h" @@ -450,6 +451,7 @@ UniValue sendtoaddress(const JSONRPCRequest& request) + HelpExampleRpc("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.1, \"donation\", \"seans outpost\"") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); CBitcoinAddress address(request.params[0].get_str()); @@ -526,6 +528,7 @@ UniValue listaddressgroupings(const JSONRPCRequest& request) + HelpExampleRpc("listaddressgroupings", "") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); UniValue jsonGroupings(UniValue::VARR); @@ -635,6 +638,7 @@ UniValue getreceivedbyaddress(const JSONRPCRequest& request) + HelpExampleRpc("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", 6") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); // Bitcoin address @@ -695,6 +699,7 @@ UniValue getreceivedbyaccount(const JSONRPCRequest& request) + HelpExampleRpc("getreceivedbyaccount", "\"tabby\", 6") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); // Minimum confirmations @@ -767,6 +772,7 @@ UniValue getbalance(const JSONRPCRequest& request) + HelpExampleRpc("getbalance", "\"*\", 6") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); const UniValue& account_value = request.params[0]; @@ -811,6 +817,7 @@ UniValue getunconfirmedbalance(const JSONRPCRequest &request) "getunconfirmedbalance\n" "Returns the server's total unconfirmed balance\n"); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); return ValueFromAmount(pwallet->GetUnconfirmedBalance()); @@ -845,6 +852,7 @@ UniValue movecmd(const JSONRPCRequest& request) + HelpExampleRpc("move", "\"timotei\", \"akiko\", 0.01, 6, \"happy birthday!\"") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); std::string strFrom = AccountFromValue(request.params[0]); @@ -903,6 +911,7 @@ UniValue sendfrom(const JSONRPCRequest& request) + HelpExampleRpc("sendfrom", "\"tabby\", \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.01, 6, \"donation\", \"seans outpost\"") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); std::string strAccount = AccountFromValue(request.params[0]); @@ -986,6 +995,7 @@ UniValue sendmany(const JSONRPCRequest& request) + HelpExampleRpc("sendmany", "\"\", \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\", 6, \"testing\"") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); if (pwallet->GetBroadcastTransactions() && !g_connman) { @@ -1403,6 +1413,7 @@ UniValue listreceivedbyaddress(const JSONRPCRequest& request) + HelpExampleRpc("listreceivedbyaddress", "6, true, true") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); return ListReceived(pwallet, request.params, false); @@ -1442,6 +1453,7 @@ UniValue listreceivedbyaccount(const JSONRPCRequest& request) + HelpExampleRpc("listreceivedbyaccount", "6, true, true") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); return ListReceived(pwallet, request.params, true); @@ -1629,6 +1641,7 @@ UniValue listtransactions(const JSONRPCRequest& request) + HelpExampleRpc("listtransactions", "\"*\", 20, 100") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); std::string strAccount = "*"; @@ -1722,6 +1735,7 @@ UniValue listaccounts(const JSONRPCRequest& request) + HelpExampleRpc("listaccounts", "6") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); int nMinDepth = 1; @@ -1830,6 +1844,7 @@ UniValue listsinceblock(const JSONRPCRequest& request) + HelpExampleRpc("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\", 6") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); const CBlockIndex* pindex = nullptr; // Block index of the specified block or the common ancestor, if the block provided was in a deactivated chain. @@ -1961,6 +1976,7 @@ UniValue gettransaction(const JSONRPCRequest& request) + HelpExampleRpc("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); uint256 hash; @@ -2022,6 +2038,7 @@ UniValue abandontransaction(const JSONRPCRequest& request) + HelpExampleRpc("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); uint256 hash; @@ -2454,6 +2471,7 @@ UniValue listlockunspent(const JSONRPCRequest& request) + HelpExampleRpc("listlockunspent", "") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); std::vector vOutpts; @@ -2532,6 +2550,7 @@ UniValue getwalletinfo(const JSONRPCRequest& request) + HelpExampleRpc("getwalletinfo", "") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); UniValue obj(UniValue::VOBJ); @@ -2684,6 +2703,8 @@ UniValue listunspent(const JSONRPCRequest& request) + HelpExampleRpc("listunspent", "6, 9999999, [] , true, { \"minimumAmount\": 0.005 } ") ); + ObserveSafeMode(); + int nMinDepth = 1; if (!request.params[0].isNull()) { RPCTypeCheckArgument(request.params[0], UniValue::VNUM); @@ -2845,6 +2866,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) + HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"") ); + ObserveSafeMode(); RPCTypeCheck(request.params, {UniValue::VSTR}); CCoinControl coinControl; @@ -3159,60 +3181,60 @@ extern UniValue removeprunedfunds(const JSONRPCRequest& request); extern UniValue importmulti(const JSONRPCRequest& request); static const CRPCCommand commands[] = -{ // category name actor (function) okSafeMode - // --------------------- ------------------------ ----------------------- ---------- - { "rawtransactions", "fundrawtransaction", &fundrawtransaction, false, {"hexstring","options"} }, - { "hidden", "resendwallettransactions", &resendwallettransactions, true, {} }, - { "wallet", "abandontransaction", &abandontransaction, false, {"txid"} }, - { "wallet", "abortrescan", &abortrescan, false, {} }, - { "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"} }, - { "wallet", "getaccountaddress", &getaccountaddress, true, {"account"} }, - { "wallet", "getaccount", &getaccount, true, {"address"} }, - { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, true, {"account"} }, - { "wallet", "getbalance", &getbalance, false, {"account","minconf","include_watchonly"} }, - { "wallet", "getnewaddress", &getnewaddress, true, {"account"} }, - { "wallet", "getrawchangeaddress", &getrawchangeaddress, true, {} }, - { "wallet", "getreceivedbyaccount", &getreceivedbyaccount, false, {"account","minconf"} }, - { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, false, {"address","minconf"} }, - { "wallet", "gettransaction", &gettransaction, false, {"txid","include_watchonly"} }, - { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, false, {} }, - { "wallet", "getwalletinfo", &getwalletinfo, false, {} }, - { "wallet", "importmulti", &importmulti, true, {"requests","options"} }, - { "wallet", "importprivkey", &importprivkey, true, {"privkey","label","rescan"} }, - { "wallet", "importwallet", &importwallet, true, {"filename"} }, - { "wallet", "importaddress", &importaddress, true, {"address","label","rescan","p2sh"} }, - { "wallet", "importprunedfunds", &importprunedfunds, true, {"rawtransaction","txoutproof"} }, - { "wallet", "importpubkey", &importpubkey, true, {"pubkey","label","rescan"} }, - { "wallet", "keypoolrefill", &keypoolrefill, true, {"newsize"} }, - { "wallet", "listaccounts", &listaccounts, false, {"minconf","include_watchonly"} }, - { "wallet", "listaddressgroupings", &listaddressgroupings, false, {} }, - { "wallet", "listlockunspent", &listlockunspent, false, {} }, - { "wallet", "listreceivedbyaccount", &listreceivedbyaccount, false, {"minconf","include_empty","include_watchonly"} }, - { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false, {"minconf","include_empty","include_watchonly"} }, - { "wallet", "listsinceblock", &listsinceblock, false, {"blockhash","target_confirmations","include_watchonly","include_removed"} }, - { "wallet", "listtransactions", &listtransactions, false, {"account","count","skip","include_watchonly"} }, - { "wallet", "listunspent", &listunspent, false, {"minconf","maxconf","addresses","include_unsafe","query_options"} }, - { "wallet", "listwallets", &listwallets, true, {} }, - { "wallet", "lockunspent", &lockunspent, true, {"unlock","transactions"} }, - { "wallet", "move", &movecmd, false, {"fromaccount","toaccount","amount","minconf","comment"} }, - { "wallet", "sendfrom", &sendfrom, false, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} }, - { "wallet", "sendmany", &sendmany, false, {"fromaccount","amounts","minconf","comment","subtractfeefrom","replaceable","conf_target","estimate_mode"} }, - { "wallet", "sendtoaddress", &sendtoaddress, false, {"address","amount","comment","comment_to","subtractfeefromamount","replaceable","conf_target","estimate_mode"} }, - { "wallet", "setaccount", &setaccount, true, {"address","account"} }, - { "wallet", "settxfee", &settxfee, true, {"amount"} }, - { "wallet", "signmessage", &signmessage, true, {"address","message"} }, - { "wallet", "walletlock", &walletlock, true, {} }, - { "wallet", "walletpassphrasechange", &walletpassphrasechange, true, {"oldpassphrase","newpassphrase"} }, - { "wallet", "walletpassphrase", &walletpassphrase, true, {"passphrase","timeout"} }, - { "wallet", "removeprunedfunds", &removeprunedfunds, true, {"txid"} }, - - { "generating", "generate", &generate, true, {"nblocks","maxtries"} }, +{ // category name actor (function) argNames + // --------------------- ------------------------ ----------------------- ---------- + { "rawtransactions", "fundrawtransaction", &fundrawtransaction, {"hexstring","options"} }, + { "hidden", "resendwallettransactions", &resendwallettransactions, {} }, + { "wallet", "abandontransaction", &abandontransaction, {"txid"} }, + { "wallet", "abortrescan", &abortrescan, {} }, + { "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","account"} }, + { "wallet", "addwitnessaddress", &addwitnessaddress, {"address"} }, + { "wallet", "backupwallet", &backupwallet, {"destination"} }, + { "wallet", "bumpfee", &bumpfee, {"txid", "options"} }, + { "wallet", "dumpprivkey", &dumpprivkey, {"address"} }, + { "wallet", "dumpwallet", &dumpwallet, {"filename"} }, + { "wallet", "encryptwallet", &encryptwallet, {"passphrase"} }, + { "wallet", "getaccountaddress", &getaccountaddress, {"account"} }, + { "wallet", "getaccount", &getaccount, {"address"} }, + { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, {"account"} }, + { "wallet", "getbalance", &getbalance, {"account","minconf","include_watchonly"} }, + { "wallet", "getnewaddress", &getnewaddress, {"account"} }, + { "wallet", "getrawchangeaddress", &getrawchangeaddress, {} }, + { "wallet", "getreceivedbyaccount", &getreceivedbyaccount, {"account","minconf"} }, + { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, {"address","minconf"} }, + { "wallet", "gettransaction", &gettransaction, {"txid","include_watchonly"} }, + { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, {} }, + { "wallet", "getwalletinfo", &getwalletinfo, {} }, + { "wallet", "importmulti", &importmulti, {"requests","options"} }, + { "wallet", "importprivkey", &importprivkey, {"privkey","label","rescan"} }, + { "wallet", "importwallet", &importwallet, {"filename"} }, + { "wallet", "importaddress", &importaddress, {"address","label","rescan","p2sh"} }, + { "wallet", "importprunedfunds", &importprunedfunds, {"rawtransaction","txoutproof"} }, + { "wallet", "importpubkey", &importpubkey, {"pubkey","label","rescan"} }, + { "wallet", "keypoolrefill", &keypoolrefill, {"newsize"} }, + { "wallet", "listaccounts", &listaccounts, {"minconf","include_watchonly"} }, + { "wallet", "listaddressgroupings", &listaddressgroupings, {} }, + { "wallet", "listlockunspent", &listlockunspent, {} }, + { "wallet", "listreceivedbyaccount", &listreceivedbyaccount, {"minconf","include_empty","include_watchonly"} }, + { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, {"minconf","include_empty","include_watchonly"} }, + { "wallet", "listsinceblock", &listsinceblock, {"blockhash","target_confirmations","include_watchonly","include_removed"} }, + { "wallet", "listtransactions", &listtransactions, {"account","count","skip","include_watchonly"} }, + { "wallet", "listunspent", &listunspent, {"minconf","maxconf","addresses","include_unsafe","query_options"} }, + { "wallet", "listwallets", &listwallets, {} }, + { "wallet", "lockunspent", &lockunspent, {"unlock","transactions"} }, + { "wallet", "move", &movecmd, {"fromaccount","toaccount","amount","minconf","comment"} }, + { "wallet", "sendfrom", &sendfrom, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} }, + { "wallet", "sendmany", &sendmany, {"fromaccount","amounts","minconf","comment","subtractfeefrom","replaceable","conf_target","estimate_mode"} }, + { "wallet", "sendtoaddress", &sendtoaddress, {"address","amount","comment","comment_to","subtractfeefromamount","replaceable","conf_target","estimate_mode"} }, + { "wallet", "setaccount", &setaccount, {"address","account"} }, + { "wallet", "settxfee", &settxfee, {"amount"} }, + { "wallet", "signmessage", &signmessage, {"address","message"} }, + { "wallet", "walletlock", &walletlock, {} }, + { "wallet", "walletpassphrasechange", &walletpassphrasechange, {"oldpassphrase","newpassphrase"} }, + { "wallet", "walletpassphrase", &walletpassphrase, {"passphrase","timeout"} }, + { "wallet", "removeprunedfunds", &removeprunedfunds, {"txid"} }, + + { "generating", "generate", &generate, {"nblocks","maxtries"} }, }; void RegisterWalletRPCCommands(CRPCTable &t) From e912118786f867d6821e2c1a2e4e1d4937fefd85 Mon Sep 17 00:00:00 2001 From: Johnson Lau Date: Sun, 30 Jul 2017 17:32:39 +0800 Subject: [PATCH 133/382] [Refactor] Combine scriptPubKey and amount as CTxOut in CScriptCheck --- src/test/script_P2SH_tests.cpp | 3 +-- src/test/transaction_tests.cpp | 3 +-- src/validation.cpp | 8 +++----- src/validation.h | 13 +++++-------- 4 files changed, 10 insertions(+), 17 deletions(-) diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index efd0f77d9..58aa32c96 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -112,8 +112,7 @@ BOOST_AUTO_TEST_CASE(sign) { CScript sigSave = txTo[i].vin[0].scriptSig; txTo[i].vin[0].scriptSig = txTo[j].vin[0].scriptSig; - const CTxOut& output = txFrom.vout[txTo[i].vin[0].prevout.n]; - bool sigOK = CScriptCheck(output.scriptPubKey, output.nValue, txTo[i], 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false, &txdata)(); + bool sigOK = CScriptCheck(txFrom.vout[txTo[i].vin[0].prevout.n], txTo[i], 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false, &txdata)(); if (i == j) BOOST_CHECK_MESSAGE(sigOK, strprintf("VerifySignature %d %d", i, j)); else diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 6654634bf..cb6ab7cdb 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -480,8 +480,7 @@ BOOST_AUTO_TEST_CASE(test_big_witness_transaction) { for(uint32_t i = 0; i < mtx.vin.size(); i++) { std::vector vChecks; - const CTxOut& output = coins[tx.vin[i].prevout.n].out; - CScriptCheck check(output.scriptPubKey, output.nValue, tx, i, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false, &txdata); + CScriptCheck check(coins[tx.vin[i].prevout.n].out, tx, i, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false, &txdata); vChecks.push_back(CScriptCheck()); check.swap(vChecks.back()); control.Add(vChecks); diff --git a/src/validation.cpp b/src/validation.cpp index 3b9636839..d1b080cc9 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1202,7 +1202,7 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight) bool CScriptCheck::operator()() { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; const CScriptWitness *witness = &ptxTo->vin[nIn].scriptWitness; - return VerifyScript(scriptSig, scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore, *txdata), &error); + return VerifyScript(scriptSig, out.scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, out.nValue, cacheStore, *txdata), &error); } int GetSpendHeight(const CCoinsViewCache& inputs) @@ -1284,11 +1284,9 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi // a sanity check that our caching is not introducing consensus // failures through additional data in, eg, the coins being // spent being checked as a part of CScriptCheck. - const CScript& scriptPubKey = coin.out.scriptPubKey; - const CAmount amount = coin.out.nValue; // Verify signature - CScriptCheck check(scriptPubKey, amount, tx, i, flags, cacheSigStore, &txdata); + CScriptCheck check(coin.out, tx, i, flags, cacheSigStore, &txdata); if (pvChecks) { pvChecks->push_back(CScriptCheck()); check.swap(pvChecks->back()); @@ -1300,7 +1298,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi // arguments; if so, don't trigger DoS protection to // avoid splitting the network between upgraded and // non-upgraded nodes. - CScriptCheck check2(scriptPubKey, amount, tx, i, + CScriptCheck check2(coin.out, tx, i, flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheSigStore, &txdata); if (check2()) return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError()))); diff --git a/src/validation.h b/src/validation.h index d0f6cdc13..69751b8c6 100644 --- a/src/validation.h +++ b/src/validation.h @@ -355,8 +355,7 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp = null class CScriptCheck { private: - CScript scriptPubKey; - CAmount amount; + CTxOut out; const CTransaction *ptxTo; unsigned int nIn; unsigned int nFlags; @@ -365,17 +364,15 @@ class CScriptCheck PrecomputedTransactionData *txdata; public: - CScriptCheck(): amount(0), ptxTo(nullptr), nIn(0), nFlags(0), cacheStore(false), error(SCRIPT_ERR_UNKNOWN_ERROR) {} - CScriptCheck(const CScript& scriptPubKeyIn, const CAmount amountIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn, PrecomputedTransactionData* txdataIn) : - scriptPubKey(scriptPubKeyIn), amount(amountIn), - ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), error(SCRIPT_ERR_UNKNOWN_ERROR), txdata(txdataIn) { } + CScriptCheck(): ptxTo(nullptr), nIn(0), nFlags(0), cacheStore(false), error(SCRIPT_ERR_UNKNOWN_ERROR) {} + CScriptCheck(const CTxOut& outIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn, PrecomputedTransactionData* txdataIn) : + out(outIn), ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), error(SCRIPT_ERR_UNKNOWN_ERROR), txdata(txdataIn) { } bool operator()(); void swap(CScriptCheck &check) { - scriptPubKey.swap(check.scriptPubKey); std::swap(ptxTo, check.ptxTo); - std::swap(amount, check.amount); + std::swap(out, check.out); std::swap(nIn, check.nIn); std::swap(nFlags, check.nFlags); std::swap(cacheStore, check.cacheStore); From 14ccd4d8d1847d64903224562ad3c7eb795b3781 Mon Sep 17 00:00:00 2001 From: Daniel Edgecumbe Date: Wed, 30 Aug 2017 19:14:07 +0100 Subject: [PATCH 134/382] [Qt] Fix display of package name on 'open config file' tooltip --- src/qt/optionsdialog.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index b80b6541d..550884ed8 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -80,6 +80,8 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) : ui->bitcoinAtStartup->setToolTip(ui->bitcoinAtStartup->toolTip().arg(tr(PACKAGE_NAME))); ui->bitcoinAtStartup->setText(ui->bitcoinAtStartup->text().arg(tr(PACKAGE_NAME))); + ui->openBitcoinConfButton->setToolTip(ui->openBitcoinConfButton->toolTip().arg(tr(PACKAGE_NAME))); + ui->lang->setToolTip(ui->lang->toolTip().arg(tr(PACKAGE_NAME))); ui->lang->addItem(QString("(") + tr("default") + QString(")"), QVariant("")); for (const QString &langStr : translations.entryList()) From 7bb5d3000ca4b74c3701cf49eaf63e5a37dfe7ac Mon Sep 17 00:00:00 2001 From: danra Date: Fri, 25 Aug 2017 19:04:18 +0300 Subject: [PATCH 135/382] Add python3 to list of dependencies on some platforms python3 is required for running the unit tests on macOS, Ubuntu and Fedora. Without python3 installed, 'make check' fails because /test/util/bitcoin-util-test.py fails to find python3. --- doc/build-osx.md | 2 +- doc/build-unix.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/build-osx.md b/doc/build-osx.md index 32d7dbd69..1320d309c 100644 --- a/doc/build-osx.md +++ b/doc/build-osx.md @@ -16,7 +16,7 @@ Then install [Homebrew](https://brew.sh). Dependencies ---------------------- - brew install automake berkeley-db4 libtool boost --c++11 miniupnpc openssl pkg-config protobuf qt libevent + brew install automake berkeley-db4 libtool boost --c++11 miniupnpc openssl pkg-config protobuf python3 qt libevent If you want to build the disk image with `make deploy` (.dmg / optional), you need RSVG diff --git a/doc/build-unix.md b/doc/build-unix.md index b7eae2a63..2e82934cd 100644 --- a/doc/build-unix.md +++ b/doc/build-unix.md @@ -65,7 +65,7 @@ Dependency Build Instructions: Ubuntu & Debian ---------------------------------------------- Build requirements: - sudo apt-get install build-essential libtool autotools-dev automake pkg-config libssl-dev libevent-dev bsdmainutils + sudo apt-get install build-essential libtool autotools-dev automake pkg-config libssl-dev libevent-dev bsdmainutils python3 Options when installing required Boost library files: @@ -131,7 +131,7 @@ Dependency Build Instructions: Fedora ------------------------------------- Build requirements: - sudo dnf install gcc-c++ libtool make autoconf automake openssl-devel libevent-devel boost-devel libdb4-devel libdb4-cxx-devel + sudo dnf install gcc-c++ libtool make autoconf automake openssl-devel libevent-devel boost-devel libdb4-devel libdb4-cxx-devel python3 Optional: From 7e5d5965d12f4e110c026124ed326b03291fe469 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Thu, 31 Aug 2017 09:04:07 -0400 Subject: [PATCH 136/382] RPC: add wtxid to mempool entry output --- src/rpc/blockchain.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index e6b78516e..9fb9a5cf3 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -343,6 +343,7 @@ std::string EntryDescriptionString() " \"ancestorcount\" : n, (numeric) number of in-mempool ancestor transactions (including this one)\n" " \"ancestorsize\" : n, (numeric) virtual transaction size of in-mempool ancestors (including this one)\n" " \"ancestorfees\" : n, (numeric) modified fees (see above) of in-mempool ancestors (including this one)\n" + " \"wtxid\" : hash, (string) hash of serialized transaction, including witness data\n" " \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n" " \"transactionid\", (string) parent transaction id\n" " ... ]\n"; @@ -363,6 +364,7 @@ void entryToJSON(UniValue &info, const CTxMemPoolEntry &e) info.push_back(Pair("ancestorcount", e.GetCountWithAncestors())); info.push_back(Pair("ancestorsize", e.GetSizeWithAncestors())); info.push_back(Pair("ancestorfees", e.GetModFeesWithAncestors())); + info.push_back(Pair("wtxid", mempool.vTxHashes[e.vTxHashesIdx].first.ToString())); const CTransaction& tx = e.GetTx(); std::set setDepends; for (const CTxIn& txin : tx.vin) From 617c459c6c56911e70855dbe139ab095f435e73e Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Thu, 31 Aug 2017 13:18:35 -0400 Subject: [PATCH 137/382] qa: rpc test for wtxid in mempool entry --- test/functional/segwit.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/functional/segwit.py b/test/functional/segwit.py index 7bcb39301..1736cd10b 100755 --- a/test/functional/segwit.py +++ b/test/functional/segwit.py @@ -281,6 +281,9 @@ def run_test(self): assert(txid2 in template_txids) assert(txid3 in template_txids) + # Check that wtxid is properly reported in mempool entry + assert_equal(int(self.nodes[0].getmempoolentry(txid3)["wtxid"], 16), tx.calc_sha256(True)) + # Mine a block to clear the gbt cache again. self.nodes[0].generate(1) From 5abb93f0eefffb5bb31e75a7dd258534cff4b0a0 Mon Sep 17 00:00:00 2001 From: danra Date: Fri, 25 Aug 2017 20:27:38 +0300 Subject: [PATCH 138/382] Fix include path for bitcoin-config.h in crypto/common.h All the other files in the repo which include bitcoin-config.h do so with the appropriate subfolder prefixed: config/bitcoin-config.h The header should be included with the appropriate subfolder here as well. This canonicalization also allows getting rid of a bit of extra configuration in Makefile.am. --- src/Makefile.am | 3 +-- src/crypto/common.h | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index dea656869..5df2791ab 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,7 +18,6 @@ else LIBUNIVALUE = $(UNIVALUE_LIBS) endif -BITCOIN_CONFIG_INCLUDES=-I$(builddir)/config BITCOIN_INCLUDES=-I$(builddir) -I$(builddir)/obj $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) $(CRYPTO_CFLAGS) $(SSL_CFLAGS) BITCOIN_INCLUDES += -I$(srcdir)/secp256k1/include @@ -250,7 +249,7 @@ libbitcoin_wallet_a_SOURCES = \ $(BITCOIN_CORE_H) # crypto primitives library -crypto_libbitcoin_crypto_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_CONFIG_INCLUDES) +crypto_libbitcoin_crypto_a_CPPFLAGS = $(AM_CPPFLAGS) crypto_libbitcoin_crypto_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) crypto_libbitcoin_crypto_a_SOURCES = \ crypto/aes.cpp \ diff --git a/src/crypto/common.h b/src/crypto/common.h index bcca3d30e..bd9bc9420 100644 --- a/src/crypto/common.h +++ b/src/crypto/common.h @@ -6,7 +6,7 @@ #define BITCOIN_CRYPTO_COMMON_H #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" +#include "config/bitcoin-config.h" #endif #include From dea086f498097d19a2c9acbfc753c9c2d68dbb03 Mon Sep 17 00:00:00 2001 From: MeshCollider Date: Thu, 31 Aug 2017 11:15:48 +1200 Subject: [PATCH 139/382] Stop test_bitcoin-qt touching ~/.bitcoin --- src/qt/test/rpcnestedtests.cpp | 7 ------- src/qt/test/test_main.cpp | 6 ++++++ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp index cd9ab2345..d850e28b1 100644 --- a/src/qt/test/rpcnestedtests.cpp +++ b/src/qt/test/rpcnestedtests.cpp @@ -37,11 +37,6 @@ void RPCNestedTests::rpcNestedTests() // do some test setup // could be moved to a more generic place when we add more tests on QT level tableRPC.appendCommand("rpcNestedTest", &vRPCCommands[0]); - ClearDatadirCache(); - std::string path = QDir::tempPath().toStdString() + "/" + strprintf("test_bitcoin_qt_%lu_%i", (unsigned long)GetTime(), (int)(GetRand(100000))); - QDir dir(QString::fromStdString(path)); - dir.mkpath("."); - gArgs.ForceSetArg("-datadir", path); //mempool.setSanityCheck(1.0); TestingSetup test; @@ -136,6 +131,4 @@ void RPCNestedTests::rpcNestedTests() QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest(abc,,abc)"), std::runtime_error); //don't tollerate empty arguments when using , QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest(abc,,)"), std::runtime_error); //don't tollerate empty arguments when using , #endif - - fs::remove_all(fs::path(path)); } diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp index 80a00a634..4c04e67cc 100644 --- a/src/qt/test/test_main.cpp +++ b/src/qt/test/test_main.cpp @@ -53,6 +53,10 @@ int main(int argc, char *argv[]) SetupNetworking(); SelectParams(CBaseChainParams::MAIN); noui_connect(); + ClearDatadirCache(); + fs::path pathTemp = fs::temp_directory_path() / strprintf("test_bitcoin-qt_%lu_%i", (unsigned long)GetTime(), (int)GetRand(100000)); + fs::create_directories(pathTemp); + gArgs.ForceSetArg("-datadir", pathTemp.string()); bool fInvalid = false; @@ -97,5 +101,7 @@ int main(int argc, char *argv[]) } #endif + fs::remove_all(pathTemp); + return fInvalid; } From be2a2ab6a67beef97e3c3cf42bd5eeea6c4e55cf Mon Sep 17 00:00:00 2001 From: John Newbery Date: Wed, 23 Aug 2017 15:49:01 -0400 Subject: [PATCH 140/382] [tests] fix - use rpc_timeout as rpc timeout --- test/functional/test_framework/test_node.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index a803df5b4..4063f988e 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -74,7 +74,7 @@ def wait_for_rpc_connection(self): for _ in range(poll_per_s * self.rpc_timeout): assert self.process.poll() is None, "bitcoind exited with status %i during initialization" % self.process.returncode try: - self.rpc = get_rpc_proxy(rpc_url(self.datadir, self.index, self.rpchost), self.index, coveragedir=self.coverage_dir) + self.rpc = get_rpc_proxy(rpc_url(self.datadir, self.index, self.rpchost), self.index, timeout=self.rpc_timeout, coveragedir=self.coverage_dir) self.rpc.getblockcount() # If the call to getblockcount() succeeds then the RPC connection is up self.rpc_connected = True From 36b626867087e9fae6d85f926248997ebff327b7 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Fri, 9 Jun 2017 16:35:17 -0400 Subject: [PATCH 141/382] [tests] TestNode: separate add_node from start_node Separates the act of creating a TestNode object from starting the node. The test_framework now keeps track of its list of TestNodes, and test writers can call start_node() and stop_node() without having to update the self.nodes list. --- test/functional/abandonconflict.py | 6 +- test/functional/assumevalid.py | 9 +- test/functional/bip9-softforks.py | 1 + test/functional/blockchain.py | 2 +- test/functional/bumpfee.py | 11 +-- test/functional/create_cache.py | 1 - test/functional/dbcrash.py | 5 +- test/functional/disconnect_ban.py | 2 +- test/functional/forknotify.py | 13 +-- test/functional/fundrawtransaction.py | 4 +- test/functional/import-rescan.py | 3 +- test/functional/importmulti.py | 2 +- test/functional/keypool-topup.py | 4 +- test/functional/keypool.py | 2 +- test/functional/listtransactions.py | 4 - test/functional/maxuploadtarget.py | 2 +- test/functional/mempool_persist.py | 11 +-- test/functional/multiwallet.py | 8 +- test/functional/p2p-segwit.py | 2 +- test/functional/p2p-versionbits-warning.py | 4 +- test/functional/proxy_test.py | 3 +- test/functional/pruning.py | 24 +++-- test/functional/receivedby.py | 4 - test/functional/reindex.py | 2 +- test/functional/resendwallettransactions.py | 2 +- test/functional/rpcbind_test.py | 13 +-- test/functional/smartfees.py | 89 ++++++++++--------- .../test_framework/test_framework.py | 65 +++++++------- test/functional/test_framework/test_node.py | 8 +- test/functional/wallet-dump.py | 5 +- test/functional/wallet-encryption.py | 2 +- test/functional/wallet-hd.py | 8 +- test/functional/wallet.py | 61 +++++++------ test/functional/walletbackup.py | 6 +- test/functional/zapwallettxes.py | 6 +- test/functional/zmq_test.py | 5 +- 36 files changed, 202 insertions(+), 197 deletions(-) diff --git a/test/functional/abandonconflict.py b/test/functional/abandonconflict.py index c87c02492..1db04337a 100755 --- a/test/functional/abandonconflict.py +++ b/test/functional/abandonconflict.py @@ -74,7 +74,7 @@ def run_test(self): # Restart the node with a higher min relay fee so the parent tx is no longer in mempool # TODO: redo with eviction self.stop_node(0) - self.nodes[0] = self.start_node(0, self.options.tmpdir, ["-minrelaytxfee=0.0001"]) + self.start_node(0, extra_args=["-minrelaytxfee=0.0001"]) # Verify txs no longer in either node's mempool assert_equal(len(self.nodes[0].getrawmempool()), 0) @@ -101,7 +101,7 @@ def run_test(self): # Verify that even with a low min relay fee, the tx is not reaccepted from wallet on startup once abandoned self.stop_node(0) - self.nodes[0] = self.start_node(0, self.options.tmpdir, ["-minrelaytxfee=0.00001"]) + self.start_node(0, extra_args=["-minrelaytxfee=0.00001"]) assert_equal(len(self.nodes[0].getrawmempool()), 0) assert_equal(self.nodes[0].getbalance(), balance) @@ -121,7 +121,7 @@ def run_test(self): # Remove using high relay fee again self.stop_node(0) - self.nodes[0] = self.start_node(0, self.options.tmpdir, ["-minrelaytxfee=0.0001"]) + self.start_node(0, extra_args=["-minrelaytxfee=0.0001"]) assert_equal(len(self.nodes[0].getrawmempool()), 0) newbalance = self.nodes[0].getbalance() assert_equal(newbalance, balance - Decimal("24.9996")) diff --git a/test/functional/assumevalid.py b/test/functional/assumevalid.py index 9d17faac5..63e6a384a 100755 --- a/test/functional/assumevalid.py +++ b/test/functional/assumevalid.py @@ -60,10 +60,11 @@ def __init__(self): self.num_nodes = 3 def setup_network(self): + self.add_nodes(3, self.options.tmpdir) # Start node0. We don't start the other nodes yet since # we need to pre-mine a block with an invalid transaction # signature so we can pass in the block hash as assumevalid. - self.nodes = [self.start_node(0, self.options.tmpdir)] + self.start_node(0) def send_blocks_until_disconnected(self, node): """Keep sending blocks to the node until we're disconnected.""" @@ -162,15 +163,13 @@ def run_test(self): height += 1 # Start node1 and node2 with assumevalid so they accept a block with a bad signature. - self.nodes.append(self.start_node(1, self.options.tmpdir, - ["-assumevalid=" + hex(block102.sha256)])) + self.start_node(1, extra_args=["-assumevalid=" + hex(block102.sha256)]) node1 = BaseNode() # connects to node1 connections.append(NodeConn('127.0.0.1', p2p_port(1), self.nodes[1], node1)) node1.add_connection(connections[1]) node1.wait_for_verack() - self.nodes.append(self.start_node(2, self.options.tmpdir, - ["-assumevalid=" + hex(block102.sha256)])) + self.start_node(2, extra_args=["-assumevalid=" + hex(block102.sha256)]) node2 = BaseNode() # connects to node2 connections.append(NodeConn('127.0.0.1', p2p_port(2), self.nodes[2], node2)) node2.add_connection(connections[2]) diff --git a/test/functional/bip9-softforks.py b/test/functional/bip9-softforks.py index f00232c9f..ad1648255 100755 --- a/test/functional/bip9-softforks.py +++ b/test/functional/bip9-softforks.py @@ -241,6 +241,7 @@ def test_BIP(self, bipName, activated_version, invalidate, invalidatePostSignatu # Restart all self.test.clear_all_connections() self.stop_nodes() + self.nodes = [] shutil.rmtree(self.options.tmpdir + "/node0") self.setup_chain() self.setup_network() diff --git a/test/functional/blockchain.py b/test/functional/blockchain.py index 0812e1b0d..a3a6cf2fb 100755 --- a/test/functional/blockchain.py +++ b/test/functional/blockchain.py @@ -146,7 +146,7 @@ def _test_stopatheight(self): pass # The node already shut down before response self.log.debug('Node should stop at this height...') self.nodes[0].process.wait(timeout=BITCOIND_PROC_WAIT_TIMEOUT) - self.nodes[0] = self.start_node(0, self.options.tmpdir) + self.start_node(0) assert_equal(self.nodes[0].getblockcount(), 207) diff --git a/test/functional/bumpfee.py b/test/functional/bumpfee.py index 9633ffdeb..00c085af5 100755 --- a/test/functional/bumpfee.py +++ b/test/functional/bumpfee.py @@ -34,21 +34,18 @@ def __init__(self): super().__init__() self.num_nodes = 2 self.setup_clean_chain = True + self.extra_args = [["-prematurewitness", "-walletprematurewitness", "-walletrbf={}".format(i)] + for i in range(self.num_nodes)] - def setup_network(self, split=False): - extra_args = [["-prematurewitness", "-walletprematurewitness", "-walletrbf={}".format(i)] - for i in range(self.num_nodes)] - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, extra_args) - + def run_test(self): # Encrypt wallet for test_locked_wallet_fails test self.nodes[1].node_encrypt_wallet(WALLET_PASSPHRASE) - self.nodes[1] = self.start_node(1, self.options.tmpdir, extra_args[1]) + self.start_node(1) self.nodes[1].walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT) connect_nodes_bi(self.nodes, 0, 1) self.sync_all() - def run_test(self): peer_node, rbf_node = self.nodes rbf_node_address = rbf_node.getnewaddress() diff --git a/test/functional/create_cache.py b/test/functional/create_cache.py index 39c4c0f47..1a5754b3f 100755 --- a/test/functional/create_cache.py +++ b/test/functional/create_cache.py @@ -18,7 +18,6 @@ def __init__(self): # Test network and test nodes are not required: self.num_nodes = 0 - self.nodes = [] def setup_network(self): pass diff --git a/test/functional/dbcrash.py b/test/functional/dbcrash.py index a7fcc411c..d39369105 100755 --- a/test/functional/dbcrash.py +++ b/test/functional/dbcrash.py @@ -65,7 +65,8 @@ def __init__(self): def setup_network(self): # Need a bit of extra time for the nodes to start up for this test - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args, timewait=90) + self.add_nodes(self.num_nodes, self.options.tmpdir, self.extra_args, timewait=90) + self.start_nodes() # Leave them unconnected, we'll use submitblock directly in this test def restart_node(self, node_index, expected_tip): @@ -78,7 +79,7 @@ def restart_node(self, node_index, expected_tip): while time.time() - time_start < 120: try: # Any of these RPC calls could throw due to node crash - self.nodes[node_index] = self.start_node(node_index, self.options.tmpdir, self.extra_args[node_index], timewait=90) + self.start_node(node_index) self.nodes[node_index].waitforblock(expected_tip) utxo_hash = self.nodes[node_index].gettxoutsetinfo()['hash_serialized_2'] return utxo_hash diff --git a/test/functional/disconnect_ban.py b/test/functional/disconnect_ban.py index 19723226d..cc8901c40 100755 --- a/test/functional/disconnect_ban.py +++ b/test/functional/disconnect_ban.py @@ -68,8 +68,8 @@ def run_test(self): assert_equal(len(self.nodes[1].listbanned()), 3) self.stop_node(1) + self.start_node(1) - self.nodes[1] = self.start_node(1, self.options.tmpdir) listAfterShutdown = self.nodes[1].listbanned() assert_equal("127.0.0.0/24", listAfterShutdown[0]['address']) assert_equal("127.0.0.0/32", listAfterShutdown[1]['address']) diff --git a/test/functional/forknotify.py b/test/functional/forknotify.py index 3bcf0a679..3a2a92709 100755 --- a/test/functional/forknotify.py +++ b/test/functional/forknotify.py @@ -7,7 +7,6 @@ import time from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * class ForkNotifyTest(BitcoinTestFramework): @@ -17,18 +16,12 @@ def __init__(self): self.setup_clean_chain = False def setup_network(self): - self.nodes = [] self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt") with open(self.alert_filename, 'w', encoding='utf8'): pass # Just open then close to create zero-length file - self.nodes.append(self.start_node(0, self.options.tmpdir, - ["-blockversion=2", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""])) - # Node1 mines block.version=211 blocks - self.nodes.append(self.start_node(1, self.options.tmpdir, - ["-blockversion=211"])) - connect_nodes(self.nodes[1], 0) - - self.sync_all() + self.extra_args = [["-blockversion=2", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""], + ["-blockversion=211"]] + super().setup_network() def run_test(self): # Mine 51 up-version blocks diff --git a/test/functional/fundrawtransaction.py b/test/functional/fundrawtransaction.py index f2f4efcf2..242b6eed1 100755 --- a/test/functional/fundrawtransaction.py +++ b/test/functional/fundrawtransaction.py @@ -449,11 +449,11 @@ def run_test(self): ############################################################ # locked wallet test self.stop_node(0) + self.nodes[1].node_encrypt_wallet("test") self.stop_node(2) self.stop_node(3) - self.nodes[1].node_encrypt_wallet("test") - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir) + self.start_nodes() # This test is not meant to test fee estimation and we'd like # to be sure all txs are sent at a consistent desired feerate for node in self.nodes: diff --git a/test/functional/import-rescan.py b/test/functional/import-rescan.py index 4fc507821..be52d5c84 100755 --- a/test/functional/import-rescan.py +++ b/test/functional/import-rescan.py @@ -121,7 +121,8 @@ def setup_network(self): if import_node.prune: extra_args[i] += ["-prune=1"] - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, extra_args) + self.add_nodes(self.num_nodes, self.options.tmpdir, extra_args) + self.start_nodes() for i in range(1, self.num_nodes): connect_nodes(self.nodes[i], 0) diff --git a/test/functional/importmulti.py b/test/functional/importmulti.py index e83e85de1..6aae95549 100755 --- a/test/functional/importmulti.py +++ b/test/functional/importmulti.py @@ -429,7 +429,7 @@ def run_test (self): # restart nodes to check for proper serialization/deserialization of watch only address self.stop_nodes() - self.nodes = self.start_nodes(2, self.options.tmpdir) + self.start_nodes() address_assert = self.nodes[1].validateaddress(watchonly_address) assert_equal(address_assert['iswatchonly'], True) assert_equal(address_assert['ismine'], False) diff --git a/test/functional/keypool-topup.py b/test/functional/keypool-topup.py index da29f697e..5425afc9b 100755 --- a/test/functional/keypool-topup.py +++ b/test/functional/keypool-topup.py @@ -35,7 +35,7 @@ def run_test(self): self.stop_node(1) shutil.copyfile(self.tmpdir + "/node1/regtest/wallet.dat", self.tmpdir + "/wallet.bak") - self.nodes[1] = self.start_node(1, self.tmpdir, self.extra_args[1]) + self.start_node(1, self.extra_args[1]) connect_nodes_bi(self.nodes, 0, 1) self.log.info("Generate keys for wallet") @@ -61,7 +61,7 @@ def run_test(self): self.log.info("Verify keypool is restored and balance is correct") - self.nodes[1] = self.start_node(1, self.tmpdir, self.extra_args[1]) + self.start_node(1, self.extra_args[1]) connect_nodes_bi(self.nodes, 0, 1) self.sync_all() diff --git a/test/functional/keypool.py b/test/functional/keypool.py index 3e7bb0ee0..0f84b5c05 100755 --- a/test/functional/keypool.py +++ b/test/functional/keypool.py @@ -19,7 +19,7 @@ def run_test(self): # Encrypt wallet and wait to terminate nodes[0].node_encrypt_wallet('test') # Restart node 0 - nodes[0] = self.start_node(0, self.options.tmpdir) + self.start_node(0) # Keep creating keys addr = nodes[0].getnewaddress() addr_data = nodes[0].validateaddress(addr) diff --git a/test/functional/listtransactions.py b/test/functional/listtransactions.py index f75a8e29c..9b9702841 100755 --- a/test/functional/listtransactions.py +++ b/test/functional/listtransactions.py @@ -20,11 +20,7 @@ def __init__(self): super().__init__() self.num_nodes = 4 self.setup_clean_chain = False - - def setup_nodes(self): - #This test requires mocktime self.enable_mocktime() - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir) def run_test(self): # Simple send, 0 to 1: diff --git a/test/functional/maxuploadtarget.py b/test/functional/maxuploadtarget.py index 66e5bd29e..6bd6a9e34 100755 --- a/test/functional/maxuploadtarget.py +++ b/test/functional/maxuploadtarget.py @@ -147,7 +147,7 @@ def run_test(self): #stop and start node 0 with 1MB maxuploadtarget, whitelist 127.0.0.1 self.log.info("Restarting nodes with -whitelist=127.0.0.1") self.stop_node(0) - self.nodes[0] = self.start_node(0, self.options.tmpdir, ["-whitelist=127.0.0.1", "-maxuploadtarget=1", "-blockmaxsize=999000"]) + self.start_node(0, ["-whitelist=127.0.0.1", "-maxuploadtarget=1", "-blockmaxsize=999000"]) #recreate/reconnect a test node test_nodes = [TestNode()] diff --git a/test/functional/mempool_persist.py b/test/functional/mempool_persist.py index 807edeb7a..dc5a3263f 100755 --- a/test/functional/mempool_persist.py +++ b/test/functional/mempool_persist.py @@ -63,9 +63,8 @@ def run_test(self): self.log.debug("Stop-start node0 and node1. Verify that node0 has the transactions in its mempool and node1 does not.") self.stop_nodes() - self.nodes = [] - self.nodes.append(self.start_node(0, self.options.tmpdir)) - self.nodes.append(self.start_node(1, self.options.tmpdir)) + self.start_node(0) + self.start_node(1) # Give bitcoind a second to reload the mempool time.sleep(1) wait_until(lambda: len(self.nodes[0].getrawmempool()) == 5) @@ -73,16 +72,14 @@ def run_test(self): self.log.debug("Stop-start node0 with -persistmempool=0. Verify that it doesn't load its mempool.dat file.") self.stop_nodes() - self.nodes = [] - self.nodes.append(self.start_node(0, self.options.tmpdir, ["-persistmempool=0"])) + self.start_node(0, extra_args=["-persistmempool=0"]) # Give bitcoind a second to reload the mempool time.sleep(1) assert_equal(len(self.nodes[0].getrawmempool()), 0) self.log.debug("Stop-start node0. Verify that it has the transactions in its mempool.") self.stop_nodes() - self.nodes = [] - self.nodes.append(self.start_node(0, self.options.tmpdir)) + self.start_node(0) wait_until(lambda: len(self.nodes[0].getrawmempool()) == 5) if __name__ == '__main__': diff --git a/test/functional/multiwallet.py b/test/functional/multiwallet.py index fc6e8e325..7381a9fd5 100755 --- a/test/functional/multiwallet.py +++ b/test/functional/multiwallet.py @@ -23,17 +23,17 @@ def run_test(self): self.stop_node(0) # should not initialize if there are duplicate wallets - self.assert_start_raises_init_error(0, self.options.tmpdir, ['-wallet=w1', '-wallet=w1'], 'Error loading wallet w1. Duplicate -wallet filename specified.') + self.assert_start_raises_init_error(0, ['-wallet=w1', '-wallet=w1'], 'Error loading wallet w1. Duplicate -wallet filename specified.') # should not initialize if wallet file is a directory os.mkdir(os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w11')) - self.assert_start_raises_init_error(0, self.options.tmpdir, ['-wallet=w11'], 'Error loading wallet w11. -wallet filename must be a regular file.') + self.assert_start_raises_init_error(0, ['-wallet=w11'], 'Error loading wallet w11. -wallet filename must be a regular file.') # should not initialize if wallet file is a symlink os.symlink(os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w1'), os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w12')) - self.assert_start_raises_init_error(0, self.options.tmpdir, ['-wallet=w12'], 'Error loading wallet w12. -wallet filename must be a regular file.') + self.assert_start_raises_init_error(0, ['-wallet=w12'], 'Error loading wallet w12. -wallet filename must be a regular file.') - self.nodes[0] = self.start_node(0, self.options.tmpdir, self.extra_args[0]) + self.start_node(0, self.extra_args[0]) w1 = self.nodes[0].get_wallet_rpc("w1") w2 = self.nodes[0].get_wallet_rpc("w2") diff --git a/test/functional/p2p-segwit.py b/test/functional/p2p-segwit.py index 63dfbb8ae..9189f5ef2 100755 --- a/test/functional/p2p-segwit.py +++ b/test/functional/p2p-segwit.py @@ -1496,7 +1496,7 @@ def test_upgrade_after_activation(self, node_id): # Restart with the new binary self.stop_node(node_id) - self.nodes[node_id] = self.start_node(node_id, self.options.tmpdir) + self.start_node(node_id, extra_args=[]) connect_nodes(self.nodes[0], node_id) sync_blocks(self.nodes) diff --git a/test/functional/p2p-versionbits-warning.py b/test/functional/p2p-versionbits-warning.py index df7e8ce5c..a7aacf908 100755 --- a/test/functional/p2p-versionbits-warning.py +++ b/test/functional/p2p-versionbits-warning.py @@ -112,7 +112,7 @@ def run_test(self): # Empty out the alert file with open(self.alert_filename, 'w', encoding='utf8') as _: pass - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args) + self.start_nodes() # Connecting one block should be enough to generate an error. self.nodes[0].generate(1) @@ -123,7 +123,7 @@ def run_test(self): self.test_versionbits_in_alert_file() # Test framework expects the node to still be running... - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args) + self.start_nodes() if __name__ == '__main__': VersionBitsWarningTest().main() diff --git a/test/functional/proxy_test.py b/test/functional/proxy_test.py index ae6f843dd..7f5a3810f 100755 --- a/test/functional/proxy_test.py +++ b/test/functional/proxy_test.py @@ -89,7 +89,8 @@ def setup_nodes(self): ] if self.have_ipv6: args[3] = ['-listen', '-proxy=[%s]:%i' % (self.conf3.addr),'-proxyrandomize=0', '-noonion'] - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, extra_args=args) + self.add_nodes(self.num_nodes, self.options.tmpdir, extra_args=args) + self.start_nodes() def node_test(self, node, proxies, auth, test_onion=True): rv = [] diff --git a/test/functional/pruning.py b/test/functional/pruning.py index 3e00a34ac..5c0e21f32 100755 --- a/test/functional/pruning.py +++ b/test/functional/pruning.py @@ -56,6 +56,10 @@ def setup_network(self): connect_nodes(self.nodes[0], 4) sync_blocks(self.nodes[0:5]) + def setup_nodes(self): + self.add_nodes(self.num_nodes, self.options.tmpdir, self.extra_args, timewait=900) + self.start_nodes() + def create_big_chain(self): # Start by creating some coinbases we can spend later self.nodes[1].generate(200) @@ -98,7 +102,7 @@ def create_chain_with_staleblocks(self): # Node 2 stays connected, so it hears about the stale blocks and then reorg's when node0 reconnects # Stopping node 0 also clears its mempool, so it doesn't have node1's transactions to accidentally mine self.stop_node(0) - self.nodes[0]=self.start_node(0, self.options.tmpdir, self.full_node_default_args, timewait=900) + self.start_node(0, extra_args=self.full_node_default_args) # Mine 24 blocks in node 1 for i in range(24): if j == 0: @@ -126,7 +130,7 @@ def reorg_test(self): # Reboot node 1 to clear its mempool (hopefully make the invalidate faster) # Lower the block max size so we don't keep mining all our big mempool transactions (from disconnected blocks) self.stop_node(1) - self.nodes[1] = self.start_node(1, self.options.tmpdir, ["-maxreceivebuffer=20000","-blockmaxsize=5000", "-checkblocks=5", "-disablesafemode"], timewait=900) + self.start_node(1, extra_args=["-maxreceivebuffer=20000","-blockmaxsize=5000", "-checkblocks=5", "-disablesafemode"]) height = self.nodes[1].getblockcount() self.log.info("Current block height: %d" % height) @@ -149,7 +153,7 @@ def reorg_test(self): # Reboot node1 to clear those giant tx's from mempool self.stop_node(1) - self.nodes[1] = self.start_node(1, self.options.tmpdir, ["-maxreceivebuffer=20000","-blockmaxsize=5000", "-checkblocks=5", "-disablesafemode"], timewait=900) + self.start_node(1, extra_args=["-maxreceivebuffer=20000","-blockmaxsize=5000", "-checkblocks=5", "-disablesafemode"]) self.log.info("Generating new longer chain of 300 more blocks") self.nodes[1].generate(300) @@ -227,13 +231,15 @@ def reorg_back(self): def manual_test(self, node_number, use_timestamp): # at this point, node has 995 blocks and has not yet run in prune mode - node = self.nodes[node_number] = self.start_node(node_number, self.options.tmpdir, timewait=900) + self.start_node(node_number) + node = self.nodes[node_number] assert_equal(node.getblockcount(), 995) assert_raises_jsonrpc(-1, "not in prune mode", node.pruneblockchain, 500) - self.stop_node(node_number) # now re-start in manual pruning mode - node = self.nodes[node_number] = self.start_node(node_number, self.options.tmpdir, ["-prune=1"], timewait=900) + self.stop_node(node_number) + self.start_node(node_number, extra_args=["-prune=1"]) + node = self.nodes[node_number] assert_equal(node.getblockcount(), 995) def height(index): @@ -307,7 +313,7 @@ def has_block(index): # stop node, start back up with auto-prune at 550MB, make sure still runs self.stop_node(node_number) - self.nodes[node_number] = self.start_node(node_number, self.options.tmpdir, ["-prune=550"], timewait=900) + self.start_node(node_number, extra_args=["-prune=550"]) self.log.info("Success") @@ -315,7 +321,7 @@ def wallet_test(self): # check that the pruning node's wallet is still in good shape self.log.info("Stop and start pruning node to trigger wallet rescan") self.stop_node(2) - self.nodes[2] = self.start_node(2, self.options.tmpdir, ["-prune=550"]) + self.start_node(2, extra_args=["-prune=550"]) self.log.info("Success") # check that wallet loads successfully when restarting a pruned node after IBD. @@ -325,7 +331,7 @@ def wallet_test(self): nds = [self.nodes[0], self.nodes[5]] sync_blocks(nds, wait=5, timeout=300) self.stop_node(5) #stop and start to trigger rescan - self.nodes[5] = self.start_node(5, self.options.tmpdir, ["-prune=550"]) + self.start_node(5, extra_args=["-prune=550"]) self.log.info("Success") def run_test(self): diff --git a/test/functional/receivedby.py b/test/functional/receivedby.py index 19d99c9c9..b16583416 100755 --- a/test/functional/receivedby.py +++ b/test/functional/receivedby.py @@ -28,11 +28,7 @@ def __init__(self): super().__init__() self.num_nodes = 4 self.setup_clean_chain = False - - def setup_nodes(self): - #This test requires mocktime self.enable_mocktime() - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir) def run_test(self): ''' diff --git a/test/functional/reindex.py b/test/functional/reindex.py index b446baa04..0d2a10659 100755 --- a/test/functional/reindex.py +++ b/test/functional/reindex.py @@ -25,7 +25,7 @@ def reindex(self, justchainstate=False): blockcount = self.nodes[0].getblockcount() self.stop_nodes() extra_args = [["-reindex-chainstate" if justchainstate else "-reindex", "-checkblockindex=1"]] - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, extra_args) + self.start_nodes(extra_args) while self.nodes[0].getblockcount() < blockcount: time.sleep(0.1) assert_equal(self.nodes[0].getblockcount(), blockcount) diff --git a/test/functional/resendwallettransactions.py b/test/functional/resendwallettransactions.py index 5059aa106..db6f6eec4 100755 --- a/test/functional/resendwallettransactions.py +++ b/test/functional/resendwallettransactions.py @@ -20,7 +20,7 @@ def run_test(self): # Should return an empty array if there aren't unconfirmed wallet transactions. self.stop_node(0) - self.nodes[0] = self.start_node(0, self.options.tmpdir) + self.start_node(0, extra_args=[]) assert_equal(self.nodes[0].resendwallettransactions(), []) # Should return an array with the unconfirmed wallet transaction. diff --git a/test/functional/rpcbind_test.py b/test/functional/rpcbind_test.py index 20808207b..e9d64af20 100755 --- a/test/functional/rpcbind_test.py +++ b/test/functional/rpcbind_test.py @@ -20,10 +20,7 @@ def __init__(self): self.num_nodes = 1 def setup_network(self): - pass - - def setup_nodes(self): - pass + self.add_nodes(self.num_nodes, self.options.tmpdir, None) def run_bind_test(self, allow_ips, connect_to, addresses, expected): ''' @@ -31,12 +28,14 @@ def run_bind_test(self, allow_ips, connect_to, addresses, expected): then try to connect, and check if the set of bound addresses matches the expected set. ''' + self.log.info("Bind test for %s" % str(addresses)) expected = [(addr_to_hex(addr), port) for (addr, port) in expected] base_args = ['-disablewallet', '-nolisten'] if allow_ips: base_args += ['-rpcallowip=' + x for x in allow_ips] binds = ['-rpcbind='+addr for addr in addresses] - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, [base_args + binds], connect_to) + self.nodes[0].rpchost = connect_to + self.start_node(0, base_args + binds) pid = self.nodes[0].process.pid assert_equal(set(get_bind_addrs(pid)), set(expected)) self.stop_nodes() @@ -46,8 +45,10 @@ def run_allowip_test(self, allow_ips, rpchost, rpcport): Start a node with rpcallow IP, and request getnetworkinfo at a non-localhost IP. ''' + self.log.info("Allow IP test for %s:%d" % (rpchost, rpcport)) base_args = ['-disablewallet', '-nolisten'] + ['-rpcallowip='+x for x in allow_ips] - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, [base_args]) + self.nodes[0].rpchost = None + self.start_nodes([base_args]) # connect to node through non-loopback interface node = get_rpc_proxy(rpc_url(get_datadir_path(self.options.tmpdir, 0), 0, "%s:%d" % (rpchost, rpcport)), 0, coveragedir=self.options.coveragedir) node.getnetworkinfo() diff --git a/test/functional/smartfees.py b/test/functional/smartfees.py index bc42a319d..6eb665846 100755 --- a/test/functional/smartfees.py +++ b/test/functional/smartfees.py @@ -153,57 +153,16 @@ def setup_network(self): But first we need to use one node to create a lot of outputs which we will use to generate our transactions. """ - self.nodes = [] + self.add_nodes(3, self.options.tmpdir, extra_args=[["-maxorphantx=1000", "-whitelist=127.0.0.1"], + ["-blockmaxsize=17000", "-maxorphantx=1000"], + ["-blockmaxsize=8000", "-maxorphantx=1000"]]) # Use node0 to mine blocks for input splitting - self.nodes.append(self.start_node(0, self.options.tmpdir, ["-maxorphantx=1000", - "-whitelist=127.0.0.1"])) - - self.log.info("This test is time consuming, please be patient") - self.log.info("Splitting inputs so we can generate tx's") - self.txouts = [] - self.txouts2 = [] - # Split a coinbase into two transaction puzzle outputs - split_inputs(self.nodes[0], self.nodes[0].listunspent(0), self.txouts, True) - - # Mine - while (len(self.nodes[0].getrawmempool()) > 0): - self.nodes[0].generate(1) - - # Repeatedly split those 2 outputs, doubling twice for each rep - # Use txouts to monitor the available utxo, since these won't be tracked in wallet - reps = 0 - while (reps < 5): - #Double txouts to txouts2 - while (len(self.txouts)>0): - split_inputs(self.nodes[0], self.txouts, self.txouts2) - while (len(self.nodes[0].getrawmempool()) > 0): - self.nodes[0].generate(1) - #Double txouts2 to txouts - while (len(self.txouts2)>0): - split_inputs(self.nodes[0], self.txouts2, self.txouts) - while (len(self.nodes[0].getrawmempool()) > 0): - self.nodes[0].generate(1) - reps += 1 - self.log.info("Finished splitting") - - # Now we can connect the other nodes, didn't want to connect them earlier - # so the estimates would not be affected by the splitting transactions # Node1 mines small blocks but that are bigger than the expected transaction rate. # NOTE: the CreateNewBlock code starts counting block size at 1,000 bytes, # (17k is room enough for 110 or so transactions) - self.nodes.append(self.start_node(1, self.options.tmpdir, - ["-blockmaxsize=17000", "-maxorphantx=1000"])) - connect_nodes(self.nodes[1], 0) - # Node2 is a stingy miner, that # produces too small blocks (room for only 55 or so transactions) - node2args = ["-blockmaxsize=8000", "-maxorphantx=1000"] - self.nodes.append(self.start_node(2, self.options.tmpdir, node2args)) - connect_nodes(self.nodes[0], 2) - connect_nodes(self.nodes[2], 1) - - self.sync_all() def transact_and_mine(self, numblocks, mining_node): min_fee = Decimal("0.00001") @@ -232,9 +191,51 @@ def transact_and_mine(self, numblocks, mining_node): self.memutxo = newmem def run_test(self): + self.log.info("This test is time consuming, please be patient") + self.log.info("Splitting inputs so we can generate tx's") + # Make log handler available to helper functions global log log = self.log + + # Start node0 + self.start_node(0) + self.txouts = [] + self.txouts2 = [] + # Split a coinbase into two transaction puzzle outputs + split_inputs(self.nodes[0], self.nodes[0].listunspent(0), self.txouts, True) + + # Mine + while (len(self.nodes[0].getrawmempool()) > 0): + self.nodes[0].generate(1) + + # Repeatedly split those 2 outputs, doubling twice for each rep + # Use txouts to monitor the available utxo, since these won't be tracked in wallet + reps = 0 + while (reps < 5): + #Double txouts to txouts2 + while (len(self.txouts)>0): + split_inputs(self.nodes[0], self.txouts, self.txouts2) + while (len(self.nodes[0].getrawmempool()) > 0): + self.nodes[0].generate(1) + #Double txouts2 to txouts + while (len(self.txouts2)>0): + split_inputs(self.nodes[0], self.txouts2, self.txouts) + while (len(self.nodes[0].getrawmempool()) > 0): + self.nodes[0].generate(1) + reps += 1 + self.log.info("Finished splitting") + + # Now we can connect the other nodes, didn't want to connect them earlier + # so the estimates would not be affected by the splitting transactions + self.start_node(1) + self.start_node(2) + connect_nodes(self.nodes[1], 0) + connect_nodes(self.nodes[0], 2) + connect_nodes(self.nodes[2], 1) + + self.sync_all() + self.fees_per_kb = [] self.memutxo = [] self.confutxo = self.txouts # Start with the set of confirmed txouts after splitting diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 7903bb004..a14ff9c29 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -91,7 +91,8 @@ def setup_nodes(self): extra_args = None if hasattr(self, "extra_args"): extra_args = self.extra_args - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, extra_args) + self.add_nodes(self.num_nodes, self.options.tmpdir, extra_args) + self.start_nodes() def run_test(self): raise NotImplementedError @@ -204,37 +205,39 @@ def main(self): # Public helper methods. These can be accessed by the subclass test scripts. - def start_node(self, i, dirname, extra_args=None, rpchost=None, timewait=None, binary=None, stderr=None): - """Start a bitcoind and return RPC connection to it""" + def add_nodes(self, num_nodes, dirname, extra_args=None, rpchost=None, timewait=None, binary=None): + """Instantiate TestNode objects""" if extra_args is None: - extra_args = [] + extra_args = [[]] * num_nodes if binary is None: - binary = os.getenv("BITCOIND", "bitcoind") - node = TestNode(i, dirname, extra_args, rpchost, timewait, binary, stderr, self.mocktime, coverage_dir=self.options.coveragedir) - node.start() + binary = [None] * num_nodes + assert_equal(len(extra_args), num_nodes) + assert_equal(len(binary), num_nodes) + for i in range(num_nodes): + self.nodes.append(TestNode(i, dirname, extra_args[i], rpchost, timewait=timewait, binary=binary[i], stderr=None, mocktime=self.mocktime, coverage_dir=self.options.coveragedir)) + + def start_node(self, i, extra_args=None, stderr=None): + """Start a bitcoind""" + + node = self.nodes[i] + + node.start(extra_args, stderr) node.wait_for_rpc_connection() if self.options.coveragedir is not None: coverage.write_all_rpc_commands(self.options.coveragedir, node.rpc) - return node - - def start_nodes(self, num_nodes, dirname, extra_args=None, rpchost=None, timewait=None, binary=None): - """Start multiple bitcoinds, return RPC connections to them""" + def start_nodes(self, extra_args=None): + """Start multiple bitcoinds""" if extra_args is None: - extra_args = [[]] * num_nodes - if binary is None: - binary = [None] * num_nodes - assert_equal(len(extra_args), num_nodes) - assert_equal(len(binary), num_nodes) - nodes = [] + extra_args = [None] * self.num_nodes + assert_equal(len(extra_args), self.num_nodes) try: - for i in range(num_nodes): - nodes.append(TestNode(i, dirname, extra_args[i], rpchost, timewait=timewait, binary=binary[i], stderr=None, mocktime=self.mocktime, coverage_dir=self.options.coveragedir)) - nodes[i].start() - for node in nodes: + for i, node in enumerate(self.nodes): + node.start(extra_args[i]) + for node in self.nodes: node.wait_for_rpc_connection() except: # If one node failed to start, stop the others @@ -242,11 +245,9 @@ def start_nodes(self, num_nodes, dirname, extra_args=None, rpchost=None, timewai raise if self.options.coveragedir is not None: - for node in nodes: + for node in self.nodes: coverage.write_all_rpc_commands(self.options.coveragedir, node.rpc) - return nodes - def stop_node(self, i): """Stop a bitcoind test node""" self.nodes[i].stop_node() @@ -264,10 +265,10 @@ def stop_nodes(self): while not node.is_node_stopped(): time.sleep(0.1) - def assert_start_raises_init_error(self, i, dirname, extra_args=None, expected_msg=None): + def assert_start_raises_init_error(self, i, extra_args=None, expected_msg=None): with tempfile.SpooledTemporaryFile(max_size=2**16) as log_stderr: try: - self.start_node(i, dirname, extra_args, stderr=log_stderr) + self.start_node(i, extra_args, stderr=log_stderr) self.stop_node(i) except Exception as e: assert 'bitcoind exited' in str(e) # node must have shutdown @@ -385,7 +386,7 @@ def _initialize_chain(self, test_dir, num_nodes, cachedir): args.append("-connect=127.0.0.1:" + str(p2p_port(0))) self.nodes.append(TestNode(i, cachedir, extra_args=[], rpchost=None, timewait=None, binary=None, stderr=None, mocktime=self.mocktime, coverage_dir=None)) self.nodes[i].args = args - self.nodes[i].start() + self.start_node(i) # Wait for RPC connections to be ready for node in self.nodes: @@ -455,13 +456,13 @@ def add_options(self, parser): help="bitcoind binary to use for reference nodes (if any)") def setup_network(self): - extra_args = [['-whitelist=127.0.0.1']]*self.num_nodes + extra_args = [['-whitelist=127.0.0.1']] * self.num_nodes if hasattr(self, "extra_args"): extra_args = self.extra_args - self.nodes = self.start_nodes( - self.num_nodes, self.options.tmpdir, extra_args, - binary=[self.options.testbinary] + - [self.options.refbinary] * (self.num_nodes - 1)) + self.add_nodes(self.num_nodes, self.options.tmpdir, extra_args, + binary=[self.options.testbinary] + + [self.options.refbinary] * (self.num_nodes - 1)) + self.start_nodes() class SkipTest(Exception): """This exception is raised to skip a test""" diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 4063f988e..d9ee40529 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -61,9 +61,13 @@ def __getattr__(self, *args, **kwargs): assert self.rpc_connected and self.rpc is not None, "Error: no RPC connection" return self.rpc.__getattr__(*args, **kwargs) - def start(self): + def start(self, extra_args=None, stderr=None): """Start the node.""" - self.process = subprocess.Popen(self.args + self.extra_args, stderr=self.stderr) + if extra_args is None: + extra_args = self.extra_args + if stderr is None: + stderr = self.stderr + self.process = subprocess.Popen(self.args + extra_args, stderr=stderr) self.running = True self.log.debug("bitcoind started, waiting for RPC to come up") diff --git a/test/functional/wallet-dump.py b/test/functional/wallet-dump.py index 61ad00330..a588e2386 100755 --- a/test/functional/wallet-dump.py +++ b/test/functional/wallet-dump.py @@ -68,7 +68,8 @@ def setup_network(self, split=False): # longer than the default 30 seconds due to an expensive # CWallet::TopUpKeyPool call, and the encryptwallet RPC made later in # the test often takes even longer. - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args, timewait=60) + self.add_nodes(self.num_nodes, self.options.tmpdir, self.extra_args, timewait=60) + self.start_nodes() def run_test (self): tmpdir = self.options.tmpdir @@ -95,7 +96,7 @@ def run_test (self): #encrypt wallet, restart, unlock and dump self.nodes[0].node_encrypt_wallet('test') - self.nodes[0] = self.start_node(0, self.options.tmpdir, self.extra_args[0]) + self.start_node(0) self.nodes[0].walletpassphrase('test', 10) # Should be a no-op: self.nodes[0].keypoolrefill() diff --git a/test/functional/wallet-encryption.py b/test/functional/wallet-encryption.py index 8fea4140d..2f38e6fee 100755 --- a/test/functional/wallet-encryption.py +++ b/test/functional/wallet-encryption.py @@ -31,7 +31,7 @@ def run_test(self): # Encrypt the wallet self.nodes[0].node_encrypt_wallet(passphrase) - self.nodes[0] = self.start_node(0, self.options.tmpdir) + self.start_node(0) # Test that the wallet is encrypted assert_raises_jsonrpc(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].dumpprivkey, address) diff --git a/test/functional/wallet-hd.py b/test/functional/wallet-hd.py index 751512301..edc74089b 100755 --- a/test/functional/wallet-hd.py +++ b/test/functional/wallet-hd.py @@ -25,8 +25,8 @@ def run_test (self): # Make sure can't switch off usehd after wallet creation self.stop_node(1) - self.assert_start_raises_init_error(1, self.options.tmpdir, ['-usehd=0'], 'already existing HD wallet') - self.nodes[1] = self.start_node(1, self.options.tmpdir, self.extra_args[1]) + self.assert_start_raises_init_error(1, ['-usehd=0'], 'already existing HD wallet') + self.start_node(1) connect_nodes_bi(self.nodes, 0, 1) # Make sure we use hd, keep masterkeyid @@ -76,7 +76,7 @@ def run_test (self): shutil.rmtree(tmpdir + "/node1/regtest/blocks") shutil.rmtree(tmpdir + "/node1/regtest/chainstate") shutil.copyfile(tmpdir + "/hd.bak", tmpdir + "/node1/regtest/wallet.dat") - self.nodes[1] = self.start_node(1, self.options.tmpdir, self.extra_args[1]) + self.start_node(1) # Assert that derivation is deterministic hd_add_2 = None @@ -91,7 +91,7 @@ def run_test (self): # Needs rescan self.stop_node(1) - self.nodes[1] = self.start_node(1, self.options.tmpdir, self.extra_args[1] + ['-rescan']) + self.start_node(1, extra_args=self.extra_args[1] + ['-rescan']) assert_equal(self.nodes[1].getbalance(), num_hd_adds + 1) # send a tx and make sure its using the internal chain for the changeoutput diff --git a/test/functional/wallet.py b/test/functional/wallet.py index 3e3e8fcdd..7694b122c 100755 --- a/test/functional/wallet.py +++ b/test/functional/wallet.py @@ -21,11 +21,14 @@ def __init__(self): self.extra_args = [['-usehd={:d}'.format(i%2==0)] for i in range(4)] def setup_network(self): - self.nodes = self.start_nodes(3, self.options.tmpdir, self.extra_args[:3]) + self.add_nodes(4, self.options.tmpdir, self.extra_args) + self.start_node(0) + self.start_node(1) + self.start_node(2) connect_nodes_bi(self.nodes,0,1) connect_nodes_bi(self.nodes,1,2) connect_nodes_bi(self.nodes,0,2) - self.sync_all() + self.sync_all([self.nodes[0:3]]) def run_test(self): @@ -42,9 +45,9 @@ def run_test(self): assert_equal(walletinfo['immature_balance'], 50) assert_equal(walletinfo['balance'], 0) - self.sync_all() + self.sync_all([self.nodes[0:3]]) self.nodes[1].generate(101) - self.sync_all() + self.sync_all([self.nodes[0:3]]) assert_equal(self.nodes[0].getbalance(), 50) assert_equal(self.nodes[1].getbalance(), 50) @@ -88,7 +91,7 @@ def run_test(self): # Have node0 mine a block, thus it will collect its own fee. self.nodes[0].generate(1) - self.sync_all() + self.sync_all([self.nodes[0:3]]) # Exercise locking of unspent outputs unspent_0 = self.nodes[2].listunspent()[0] @@ -101,7 +104,7 @@ def run_test(self): # Have node1 generate 100 blocks (so node0 can recover the fee) self.nodes[1].generate(100) - self.sync_all() + self.sync_all([self.nodes[0:3]]) # node0 should end up with 100 btc in block rewards plus fees, but # minus the 21 plus fees sent to node2 @@ -130,7 +133,7 @@ def run_test(self): # Have node1 mine a block to confirm transactions: self.nodes[1].generate(1) - self.sync_all() + self.sync_all([self.nodes[0:3]]) assert_equal(self.nodes[0].getbalance(), 0) assert_equal(self.nodes[2].getbalance(), 94) @@ -142,14 +145,14 @@ def run_test(self): self.nodes[2].settxfee(fee_per_byte * 1000) txid = self.nodes[2].sendtoaddress(address, 10, "", "", False) self.nodes[2].generate(1) - self.sync_all() + self.sync_all([self.nodes[0:3]]) node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), Decimal('84'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid))) assert_equal(self.nodes[0].getbalance(), Decimal('10')) # Send 10 BTC with subtract fee from amount txid = self.nodes[2].sendtoaddress(address, 10, "", "", True) self.nodes[2].generate(1) - self.sync_all() + self.sync_all([self.nodes[0:3]]) node_2_bal -= Decimal('10') assert_equal(self.nodes[2].getbalance(), node_2_bal) node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), Decimal('20'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid))) @@ -157,7 +160,7 @@ def run_test(self): # Sendmany 10 BTC txid = self.nodes[2].sendmany('from1', {address: 10}, 0, "", []) self.nodes[2].generate(1) - self.sync_all() + self.sync_all([self.nodes[0:3]]) node_0_bal += Decimal('10') node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), node_2_bal - Decimal('10'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid))) assert_equal(self.nodes[0].getbalance(), node_0_bal) @@ -165,7 +168,7 @@ def run_test(self): # Sendmany 10 BTC with subtract fee from amount txid = self.nodes[2].sendmany('from1', {address: 10}, 0, "", [address]) self.nodes[2].generate(1) - self.sync_all() + self.sync_all([self.nodes[0:3]]) node_2_bal -= Decimal('10') assert_equal(self.nodes[2].getbalance(), node_2_bal) node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), node_0_bal + Decimal('10'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid))) @@ -176,9 +179,9 @@ def run_test(self): # EXPECT: nodes[3] should have those transactions in its mempool. txid1 = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1) txid2 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1) - sync_mempools(self.nodes) + sync_mempools(self.nodes[0:2]) - self.nodes.append(self.start_node(3, self.options.tmpdir, self.extra_args[3])) + self.start_node(3) connect_nodes_bi(self.nodes, 0, 3) sync_blocks(self.nodes) @@ -222,22 +225,24 @@ def run_test(self): #do some -walletbroadcast tests self.stop_nodes() - self.nodes = self.start_nodes(3, self.options.tmpdir, [["-walletbroadcast=0"],["-walletbroadcast=0"],["-walletbroadcast=0"]]) + self.start_node(0, ["-walletbroadcast=0"]) + self.start_node(1, ["-walletbroadcast=0"]) + self.start_node(2, ["-walletbroadcast=0"]) connect_nodes_bi(self.nodes,0,1) connect_nodes_bi(self.nodes,1,2) connect_nodes_bi(self.nodes,0,2) - self.sync_all() + self.sync_all([self.nodes[0:3]]) txIdNotBroadcasted = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2) txObjNotBroadcasted = self.nodes[0].gettransaction(txIdNotBroadcasted) self.nodes[1].generate(1) #mine a block, tx should not be in there - self.sync_all() + self.sync_all([self.nodes[0:3]]) assert_equal(self.nodes[2].getbalance(), node_2_bal) #should not be changed because tx was not broadcasted #now broadcast from another node, mine a block, sync, and check the balance self.nodes[1].sendrawtransaction(txObjNotBroadcasted['hex']) self.nodes[1].generate(1) - self.sync_all() + self.sync_all([self.nodes[0:3]]) node_2_bal += 2 txObjNotBroadcasted = self.nodes[0].gettransaction(txIdNotBroadcasted) assert_equal(self.nodes[2].getbalance(), node_2_bal) @@ -247,14 +252,16 @@ def run_test(self): #restart the nodes with -walletbroadcast=1 self.stop_nodes() - self.nodes = self.start_nodes(3, self.options.tmpdir) + self.start_node(0) + self.start_node(1) + self.start_node(2) connect_nodes_bi(self.nodes,0,1) connect_nodes_bi(self.nodes,1,2) connect_nodes_bi(self.nodes,0,2) - sync_blocks(self.nodes) + sync_blocks(self.nodes[0:3]) self.nodes[0].generate(1) - sync_blocks(self.nodes) + sync_blocks(self.nodes[0:3]) node_2_bal += 2 #tx should be added to balance because after restarting the nodes tx should be broadcastet @@ -285,7 +292,7 @@ def run_test(self): address_to_import = self.nodes[2].getnewaddress() txid = self.nodes[0].sendtoaddress(address_to_import, 1) self.nodes[0].generate(1) - self.sync_all() + self.sync_all([self.nodes[0:3]]) # 2. Import address from node2 to node1 self.nodes[1].importaddress(address_to_import) @@ -311,15 +318,15 @@ def run_test(self): cbAddr = self.nodes[1].getnewaddress() blkHash = self.nodes[0].generatetoaddress(1, cbAddr)[0] cbTxId = self.nodes[0].getblock(blkHash)['tx'][0] - self.sync_all() + self.sync_all([self.nodes[0:3]]) # Check that the txid and balance is found by node1 self.nodes[1].gettransaction(cbTxId) # check if wallet or blockchain maintenance changes the balance - self.sync_all() + self.sync_all([self.nodes[0:3]]) blocks = self.nodes[0].generate(2) - self.sync_all() + self.sync_all([self.nodes[0:3]]) balance_nodes = [self.nodes[i].getbalance() for i in range(3)] block_count = self.nodes[0].getblockcount() @@ -350,7 +357,9 @@ def run_test(self): self.log.info("check " + m) self.stop_nodes() # set lower ancestor limit for later - self.nodes = self.start_nodes(3, self.options.tmpdir, [[m, "-limitancestorcount="+str(chainlimit)]] * 3) + self.start_node(0, [m, "-limitancestorcount="+str(chainlimit)]) + self.start_node(1, [m, "-limitancestorcount="+str(chainlimit)]) + self.start_node(2, [m, "-limitancestorcount="+str(chainlimit)]) while m == '-reindex' and [block_count] * 3 != [self.nodes[i].getblockcount() for i in range(3)]: # reindex will leave rpc warm up "early"; Wait for it to finish time.sleep(0.1) @@ -398,7 +407,7 @@ def run_test(self): # Try with walletrejectlongchains # Double chain limit but require combining inputs, so we pass SelectCoinsMinConf self.stop_node(0) - self.nodes[0] = self.start_node(0, self.options.tmpdir, ["-walletrejectlongchains", "-limitancestorcount="+str(2*chainlimit)]) + self.start_node(0, extra_args=["-walletrejectlongchains", "-limitancestorcount="+str(2*chainlimit)]) # wait for loadmempool timeout = 10 diff --git a/test/functional/walletbackup.py b/test/functional/walletbackup.py index ff51cba4b..688cd6d0c 100755 --- a/test/functional/walletbackup.py +++ b/test/functional/walletbackup.py @@ -78,9 +78,9 @@ def do_one_round(self): # As above, this mirrors the original bash test. def start_three(self): - self.nodes[0] = self.start_node(0, self.options.tmpdir) - self.nodes[1] = self.start_node(1, self.options.tmpdir) - self.nodes[2] = self.start_node(2, self.options.tmpdir) + self.start_node(0) + self.start_node(1) + self.start_node(2) connect_nodes(self.nodes[0], 3) connect_nodes(self.nodes[1], 3) connect_nodes(self.nodes[2], 3) diff --git a/test/functional/zapwallettxes.py b/test/functional/zapwallettxes.py index af867d7a5..1005a4b95 100755 --- a/test/functional/zapwallettxes.py +++ b/test/functional/zapwallettxes.py @@ -48,7 +48,7 @@ def run_test(self): # Stop-start node0. Both confirmed and unconfirmed transactions remain in the wallet. self.stop_node(0) - self.nodes[0] = self.start_node(0, self.options.tmpdir) + self.start_node(0) assert_equal(self.nodes[0].gettransaction(txid1)['txid'], txid1) assert_equal(self.nodes[0].gettransaction(txid2)['txid'], txid2) @@ -56,7 +56,7 @@ def run_test(self): # Stop node0 and restart with zapwallettxes and persistmempool. The unconfirmed # transaction is zapped from the wallet, but is re-added when the mempool is reloaded. self.stop_node(0) - self.nodes[0] = self.start_node(0, self.options.tmpdir, ["-persistmempool=1", "-zapwallettxes=2"]) + self.start_node(0, ["-persistmempool=1", "-zapwallettxes=2"]) assert_equal(self.nodes[0].gettransaction(txid1)['txid'], txid1) assert_equal(self.nodes[0].gettransaction(txid2)['txid'], txid2) @@ -64,7 +64,7 @@ def run_test(self): # Stop node0 and restart with zapwallettxes, but not persistmempool. # The unconfirmed transaction is zapped and is no longer in the wallet. self.stop_node(0) - self.nodes[0] = self.start_node(0, self.options.tmpdir, ["-zapwallettxes=2"]) + self.start_node(0, ["-zapwallettxes=2"]) # tx1 is still be available because it was confirmed assert_equal(self.nodes[0].gettransaction(txid1)['txid'], txid1) diff --git a/test/functional/zmq_test.py b/test/functional/zmq_test.py index 26c946d21..603b7a1b9 100755 --- a/test/functional/zmq_test.py +++ b/test/functional/zmq_test.py @@ -41,8 +41,9 @@ def setup_nodes(self): self.zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashtx") ip_address = "tcp://127.0.0.1:28332" self.zmqSubSocket.connect(ip_address) - extra_args = [['-zmqpubhashtx=%s' % ip_address, '-zmqpubhashblock=%s' % ip_address], []] - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, extra_args) + self.extra_args = [['-zmqpubhashtx=%s' % ip_address, '-zmqpubhashblock=%s' % ip_address], []] + self.add_nodes(self.num_nodes, self.options.tmpdir, self.extra_args) + self.start_nodes() def run_test(self): try: From 6cf094a0229d051ab8a15189c8a0bc6011919e72 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Thu, 24 Aug 2017 11:37:25 -0400 Subject: [PATCH 142/382] [tests] Avoid passing around member variables in test_framework --- test/functional/assumevalid.py | 2 +- test/functional/dbcrash.py | 2 +- test/functional/import-rescan.py | 2 +- test/functional/proxy_test.py | 2 +- test/functional/pruning.py | 2 +- test/functional/rpcbind_test.py | 2 +- test/functional/smartfees.py | 6 +-- .../test_framework/test_framework.py | 50 +++++++++---------- test/functional/wallet-dump.py | 2 +- test/functional/wallet.py | 2 +- test/functional/zmq_test.py | 2 +- 11 files changed, 37 insertions(+), 37 deletions(-) diff --git a/test/functional/assumevalid.py b/test/functional/assumevalid.py index 63e6a384a..ec485281d 100755 --- a/test/functional/assumevalid.py +++ b/test/functional/assumevalid.py @@ -60,7 +60,7 @@ def __init__(self): self.num_nodes = 3 def setup_network(self): - self.add_nodes(3, self.options.tmpdir) + self.add_nodes(3) # Start node0. We don't start the other nodes yet since # we need to pre-mine a block with an invalid transaction # signature so we can pass in the block hash as assumevalid. diff --git a/test/functional/dbcrash.py b/test/functional/dbcrash.py index d39369105..b0c6d39c4 100755 --- a/test/functional/dbcrash.py +++ b/test/functional/dbcrash.py @@ -65,7 +65,7 @@ def __init__(self): def setup_network(self): # Need a bit of extra time for the nodes to start up for this test - self.add_nodes(self.num_nodes, self.options.tmpdir, self.extra_args, timewait=90) + self.add_nodes(self.num_nodes, timewait=90) self.start_nodes() # Leave them unconnected, we'll use submitblock directly in this test diff --git a/test/functional/import-rescan.py b/test/functional/import-rescan.py index be52d5c84..e85086fbc 100755 --- a/test/functional/import-rescan.py +++ b/test/functional/import-rescan.py @@ -121,7 +121,7 @@ def setup_network(self): if import_node.prune: extra_args[i] += ["-prune=1"] - self.add_nodes(self.num_nodes, self.options.tmpdir, extra_args) + self.add_nodes(self.num_nodes, extra_args) self.start_nodes() for i in range(1, self.num_nodes): connect_nodes(self.nodes[i], 0) diff --git a/test/functional/proxy_test.py b/test/functional/proxy_test.py index 7f5a3810f..adbbda278 100755 --- a/test/functional/proxy_test.py +++ b/test/functional/proxy_test.py @@ -89,7 +89,7 @@ def setup_nodes(self): ] if self.have_ipv6: args[3] = ['-listen', '-proxy=[%s]:%i' % (self.conf3.addr),'-proxyrandomize=0', '-noonion'] - self.add_nodes(self.num_nodes, self.options.tmpdir, extra_args=args) + self.add_nodes(self.num_nodes, extra_args=args) self.start_nodes() def node_test(self, node, proxies, auth, test_onion=True): diff --git a/test/functional/pruning.py b/test/functional/pruning.py index 5c0e21f32..1791cd97f 100755 --- a/test/functional/pruning.py +++ b/test/functional/pruning.py @@ -57,7 +57,7 @@ def setup_network(self): sync_blocks(self.nodes[0:5]) def setup_nodes(self): - self.add_nodes(self.num_nodes, self.options.tmpdir, self.extra_args, timewait=900) + self.add_nodes(self.num_nodes, self.extra_args, timewait=900) self.start_nodes() def create_big_chain(self): diff --git a/test/functional/rpcbind_test.py b/test/functional/rpcbind_test.py index e9d64af20..a7661421f 100755 --- a/test/functional/rpcbind_test.py +++ b/test/functional/rpcbind_test.py @@ -20,7 +20,7 @@ def __init__(self): self.num_nodes = 1 def setup_network(self): - self.add_nodes(self.num_nodes, self.options.tmpdir, None) + self.add_nodes(self.num_nodes, None) def run_bind_test(self, allow_ips, connect_to, addresses, expected): ''' diff --git a/test/functional/smartfees.py b/test/functional/smartfees.py index 6eb665846..caf359265 100755 --- a/test/functional/smartfees.py +++ b/test/functional/smartfees.py @@ -153,9 +153,9 @@ def setup_network(self): But first we need to use one node to create a lot of outputs which we will use to generate our transactions. """ - self.add_nodes(3, self.options.tmpdir, extra_args=[["-maxorphantx=1000", "-whitelist=127.0.0.1"], - ["-blockmaxsize=17000", "-maxorphantx=1000"], - ["-blockmaxsize=8000", "-maxorphantx=1000"]]) + self.add_nodes(3, extra_args=[["-maxorphantx=1000", "-whitelist=127.0.0.1"], + ["-blockmaxsize=17000", "-maxorphantx=1000"], + ["-blockmaxsize=8000", "-maxorphantx=1000"]]) # Use node0 to mine blocks for input splitting # Node1 mines small blocks but that are bigger than the expected transaction rate. # NOTE: the CreateNewBlock code starts counting block size at 1,000 bytes, diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index a14ff9c29..5e3644a91 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -73,9 +73,9 @@ def add_options(self, parser): def setup_chain(self): self.log.info("Initializing test directory " + self.options.tmpdir) if self.setup_clean_chain: - self._initialize_chain_clean(self.options.tmpdir, self.num_nodes) + self._initialize_chain_clean() else: - self._initialize_chain(self.options.tmpdir, self.num_nodes, self.options.cachedir) + self._initialize_chain() def setup_network(self): self.setup_nodes() @@ -91,7 +91,7 @@ def setup_nodes(self): extra_args = None if hasattr(self, "extra_args"): extra_args = self.extra_args - self.add_nodes(self.num_nodes, self.options.tmpdir, extra_args) + self.add_nodes(self.num_nodes, extra_args) self.start_nodes() def run_test(self): @@ -205,7 +205,7 @@ def main(self): # Public helper methods. These can be accessed by the subclass test scripts. - def add_nodes(self, num_nodes, dirname, extra_args=None, rpchost=None, timewait=None, binary=None): + def add_nodes(self, num_nodes, extra_args=None, rpchost=None, timewait=None, binary=None): """Instantiate TestNode objects""" if extra_args is None: @@ -215,7 +215,7 @@ def add_nodes(self, num_nodes, dirname, extra_args=None, rpchost=None, timewait= assert_equal(len(extra_args), num_nodes) assert_equal(len(binary), num_nodes) for i in range(num_nodes): - self.nodes.append(TestNode(i, dirname, extra_args[i], rpchost, timewait=timewait, binary=binary[i], stderr=None, mocktime=self.mocktime, coverage_dir=self.options.coveragedir)) + self.nodes.append(TestNode(i, self.options.tmpdir, extra_args[i], rpchost, timewait=timewait, binary=binary[i], stderr=None, mocktime=self.mocktime, coverage_dir=self.options.coveragedir)) def start_node(self, i, extra_args=None, stderr=None): """Start a bitcoind""" @@ -357,16 +357,16 @@ def _start_logging(self): rpc_handler.setLevel(logging.DEBUG) rpc_logger.addHandler(rpc_handler) - def _initialize_chain(self, test_dir, num_nodes, cachedir): + def _initialize_chain(self): """Initialize a pre-mined blockchain for use by the test. Create a cache of a 200-block-long chain (with wallet) for MAX_NODES Afterward, create num_nodes copies from the cache.""" - assert num_nodes <= MAX_NODES + assert self.num_nodes <= MAX_NODES create_cache = False for i in range(MAX_NODES): - if not os.path.isdir(os.path.join(cachedir, 'node' + str(i))): + if not os.path.isdir(os.path.join(self.options.cachedir, 'node' + str(i))): create_cache = True break @@ -375,16 +375,16 @@ def _initialize_chain(self, test_dir, num_nodes, cachedir): # find and delete old cache directories if any exist for i in range(MAX_NODES): - if os.path.isdir(os.path.join(cachedir, "node" + str(i))): - shutil.rmtree(os.path.join(cachedir, "node" + str(i))) + if os.path.isdir(os.path.join(self.options.cachedir, "node" + str(i))): + shutil.rmtree(os.path.join(self.options.cachedir, "node" + str(i))) # Create cache directories, run bitcoinds: for i in range(MAX_NODES): - datadir = initialize_datadir(cachedir, i) + datadir = initialize_datadir(self.options.cachedir, i) args = [os.getenv("BITCOIND", "bitcoind"), "-server", "-keypool=1", "-datadir=" + datadir, "-discover=0"] if i > 0: args.append("-connect=127.0.0.1:" + str(p2p_port(0))) - self.nodes.append(TestNode(i, cachedir, extra_args=[], rpchost=None, timewait=None, binary=None, stderr=None, mocktime=self.mocktime, coverage_dir=None)) + self.nodes.append(TestNode(i, self.options.cachedir, extra_args=[], rpchost=None, timewait=None, binary=None, stderr=None, mocktime=self.mocktime, coverage_dir=None)) self.nodes[i].args = args self.start_node(i) @@ -415,24 +415,24 @@ def _initialize_chain(self, test_dir, num_nodes, cachedir): self.nodes = [] self.disable_mocktime() for i in range(MAX_NODES): - os.remove(log_filename(cachedir, i, "debug.log")) - os.remove(log_filename(cachedir, i, "db.log")) - os.remove(log_filename(cachedir, i, "peers.dat")) - os.remove(log_filename(cachedir, i, "fee_estimates.dat")) - - for i in range(num_nodes): - from_dir = os.path.join(cachedir, "node" + str(i)) - to_dir = os.path.join(test_dir, "node" + str(i)) + os.remove(log_filename(self.options.cachedir, i, "debug.log")) + os.remove(log_filename(self.options.cachedir, i, "db.log")) + os.remove(log_filename(self.options.cachedir, i, "peers.dat")) + os.remove(log_filename(self.options.cachedir, i, "fee_estimates.dat")) + + for i in range(self.num_nodes): + from_dir = os.path.join(self.options.cachedir, "node" + str(i)) + to_dir = os.path.join(self.options.tmpdir, "node" + str(i)) shutil.copytree(from_dir, to_dir) - initialize_datadir(test_dir, i) # Overwrite port/rpcport in bitcoin.conf + initialize_datadir(self.options.tmpdir, i) # Overwrite port/rpcport in bitcoin.conf - def _initialize_chain_clean(self, test_dir, num_nodes): + def _initialize_chain_clean(self): """Initialize empty blockchain for use by the test. Create an empty blockchain and num_nodes wallets. Useful if a test case wants complete control over initialization.""" - for i in range(num_nodes): - initialize_datadir(test_dir, i) + for i in range(self.num_nodes): + initialize_datadir(self.options.tmpdir, i) class ComparisonTestFramework(BitcoinTestFramework): """Test framework for doing p2p comparison testing @@ -459,7 +459,7 @@ def setup_network(self): extra_args = [['-whitelist=127.0.0.1']] * self.num_nodes if hasattr(self, "extra_args"): extra_args = self.extra_args - self.add_nodes(self.num_nodes, self.options.tmpdir, extra_args, + self.add_nodes(self.num_nodes, extra_args, binary=[self.options.testbinary] + [self.options.refbinary] * (self.num_nodes - 1)) self.start_nodes() diff --git a/test/functional/wallet-dump.py b/test/functional/wallet-dump.py index a588e2386..e0381b980 100755 --- a/test/functional/wallet-dump.py +++ b/test/functional/wallet-dump.py @@ -68,7 +68,7 @@ def setup_network(self, split=False): # longer than the default 30 seconds due to an expensive # CWallet::TopUpKeyPool call, and the encryptwallet RPC made later in # the test often takes even longer. - self.add_nodes(self.num_nodes, self.options.tmpdir, self.extra_args, timewait=60) + self.add_nodes(self.num_nodes, self.extra_args, timewait=60) self.start_nodes() def run_test (self): diff --git a/test/functional/wallet.py b/test/functional/wallet.py index 7694b122c..12026e84a 100755 --- a/test/functional/wallet.py +++ b/test/functional/wallet.py @@ -21,7 +21,7 @@ def __init__(self): self.extra_args = [['-usehd={:d}'.format(i%2==0)] for i in range(4)] def setup_network(self): - self.add_nodes(4, self.options.tmpdir, self.extra_args) + self.add_nodes(4, self.extra_args) self.start_node(0) self.start_node(1) self.start_node(2) diff --git a/test/functional/zmq_test.py b/test/functional/zmq_test.py index 603b7a1b9..b5a22ea07 100755 --- a/test/functional/zmq_test.py +++ b/test/functional/zmq_test.py @@ -42,7 +42,7 @@ def setup_nodes(self): ip_address = "tcp://127.0.0.1:28332" self.zmqSubSocket.connect(ip_address) self.extra_args = [['-zmqpubhashtx=%s' % ip_address, '-zmqpubhashblock=%s' % ip_address], []] - self.add_nodes(self.num_nodes, self.options.tmpdir, self.extra_args) + self.add_nodes(self.num_nodes, self.extra_args) self.start_nodes() def run_test(self): From 5448a1471d6fc638a2220ea5a2f3782172efe14c Mon Sep 17 00:00:00 2001 From: John Newbery Date: Fri, 9 Jun 2017 18:21:21 -0400 Subject: [PATCH 143/382] [tests] don't override __init__() in individual tests Almost all test scripts currently need to override the __init__() method. When they do that they need to call into super().__init__() as the base class does some generic initialization. This commit makes the base class __init__() call into set_test_params() method. Individual test cases can override set_test_params() to setup their test parameters. --- test/functional/README.md | 6 +- test/functional/abandonconflict.py | 4 +- test/functional/assumevalid.py | 3 +- test/functional/bip65-cltv-p2p.py | 4 +- test/functional/bip68-112-113-p2p.py | 4 +- test/functional/bip68-sequence.py | 4 +- test/functional/bip9-softforks.py | 5 +- test/functional/bipdersig-p2p.py | 4 +- test/functional/blockchain.py | 6 +- test/functional/bumpfee.py | 3 +- test/functional/create_cache.py | 6 +- test/functional/dbcrash.py | 3 +- test/functional/decodescript.py | 4 +- test/functional/disablewallet.py | 5 +- test/functional/disconnect_ban.py | 5 +- test/functional/example_test.py | 10 +-- test/functional/forknotify.py | 5 +- test/functional/fundrawtransaction.py | 6 +- test/functional/getblocktemplate_longpoll.py | 5 -- test/functional/getchaintips.py | 6 -- test/functional/httpbasics.py | 4 +- test/functional/import-rescan.py | 3 +- test/functional/importmulti.py | 3 +- test/functional/importprunedfunds.py | 5 +- test/functional/invalidateblock.py | 4 +- test/functional/invalidblockrequest.py | 4 +- test/functional/invalidtxrequest.py | 4 +- test/functional/keypool-topup.py | 3 +- test/functional/keypool.py | 7 +- test/functional/listsinceblock.py | 5 +- test/functional/listtransactions.py | 5 +- test/functional/maxuploadtarget.py | 3 +- test/functional/mempool_limit.py | 4 +- test/functional/mempool_packages.py | 4 +- test/functional/mempool_persist.py | 6 +- test/functional/mempool_reorg.py | 4 +- test/functional/mempool_resurrect_test.py | 6 +- test/functional/mempool_spendcoinbase.py | 5 +- test/functional/merkle_blocks.py | 5 +- test/functional/mining.py | 4 +- test/functional/multi_rpc.py | 5 +- test/functional/multiwallet.py | 4 +- test/functional/net.py | 4 +- test/functional/nulldummy.py | 3 +- test/functional/p2p-acceptblock.py | 3 +- test/functional/p2p-compactblocks.py | 3 +- test/functional/p2p-feefilter.py | 5 +- test/functional/p2p-fullblocktest.py | 5 +- test/functional/p2p-leaktests.py | 3 +- test/functional/p2p-mempool.py | 4 +- test/functional/p2p-segwit.py | 7 +- test/functional/p2p-timeouts.py | 3 +- test/functional/p2p-versionbits-warning.py | 3 +- test/functional/preciousblock.py | 3 +- test/functional/prioritise_transaction.py | 4 +- test/functional/proxy_test.py | 6 -- test/functional/pruning.py | 4 +- test/functional/rawtransactions.py | 4 +- test/functional/receivedby.py | 6 +- test/functional/reindex.py | 3 +- test/functional/replace-by-fee.py | 4 +- test/functional/resendwallettransactions.py | 6 +- test/functional/rest.py | 3 +- test/functional/rpcbind_test.py | 5 +- test/functional/rpcnamedargs.py | 9 +- test/functional/segwit.py | 4 +- test/functional/sendheaders.py | 3 +- test/functional/signmessages.py | 4 +- test/functional/signrawtransactions.py | 3 +- test/functional/smartfees.py | 5 +- .../test_framework/test_framework.py | 89 +++++++++++-------- test/functional/txn_clone.py | 6 -- test/functional/txn_doublespend.py | 5 -- test/functional/uptime.py | 4 +- test/functional/wallet-accounts.py | 4 +- test/functional/wallet-dump.py | 5 +- test/functional/wallet-encryption.py | 4 +- test/functional/wallet-hd.py | 5 +- test/functional/wallet.py | 18 ++-- test/functional/walletbackup.py | 5 +- test/functional/zapwallettxes.py | 4 +- test/functional/zmq_test.py | 4 +- 82 files changed, 145 insertions(+), 322 deletions(-) diff --git a/test/functional/README.md b/test/functional/README.md index 44efda33d..2558bd017 100644 --- a/test/functional/README.md +++ b/test/functional/README.md @@ -24,8 +24,8 @@ don't have test cases for. - Use a module-level docstring to describe what the test is testing, and how it is testing it. - When subclassing the BitcoinTestFramwork, place overrides for the - `__init__()`, and `setup_xxxx()` methods at the top of the subclass, then - locally-defined helper methods, then the `run_test()` method. + `set_test_params()`, `add_options()` and `setup_xxxx()` methods at the top of + the subclass, then locally-defined helper methods, then the `run_test()` method. #### General test-writing advice @@ -36,7 +36,7 @@ don't have test cases for. - Avoid stop-starting the nodes multiple times during the test if possible. A stop-start takes several seconds, so doing it several times blows up the runtime of the test. -- Set the `self.setup_clean_chain` variable in `__init__()` to control whether +- Set the `self.setup_clean_chain` variable in `set_test_params()` to control whether or not to use the cached data directories. The cached data directories contain a 200-block pre-mined blockchain and wallets for four nodes. Each node has 25 mature blocks (25x50=1250 BTC) in its wallet. diff --git a/test/functional/abandonconflict.py b/test/functional/abandonconflict.py index 1db04337a..e8dbc8646 100755 --- a/test/functional/abandonconflict.py +++ b/test/functional/abandonconflict.py @@ -14,10 +14,8 @@ from test_framework.util import * class AbandonConflictTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 - self.setup_clean_chain = False self.extra_args = [["-minrelaytxfee=0.00001"], []] def run_test(self): diff --git a/test/functional/assumevalid.py b/test/functional/assumevalid.py index ec485281d..beaf8c705 100755 --- a/test/functional/assumevalid.py +++ b/test/functional/assumevalid.py @@ -54,8 +54,7 @@ def send_header_for_blocks(self, new_blocks): self.send_message(headers_message) class AssumeValidTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 diff --git a/test/functional/bip65-cltv-p2p.py b/test/functional/bip65-cltv-p2p.py index 65ae8de55..2cd6df6e3 100755 --- a/test/functional/bip65-cltv-p2p.py +++ b/test/functional/bip65-cltv-p2p.py @@ -60,9 +60,7 @@ def create_transaction(node, coinbase, to_address, amount): return tx class BIP65Test(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 self.extra_args = [['-promiscuousmempoolflags=1', '-whitelist=127.0.0.1']] self.setup_clean_chain = True diff --git a/test/functional/bip68-112-113-p2p.py b/test/functional/bip68-112-113-p2p.py index 5a322e8c0..7e6a4f440 100755 --- a/test/functional/bip68-112-113-p2p.py +++ b/test/functional/bip68-112-113-p2p.py @@ -92,9 +92,9 @@ def all_rlt_txs(txarray): return txs class BIP68_112_113Test(ComparisonTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 + self.setup_clean_chain = True self.extra_args = [['-whitelist=127.0.0.1', '-blockversion=4']] def run_test(self): diff --git a/test/functional/bip68-sequence.py b/test/functional/bip68-sequence.py index 87a50692f..39012adf9 100755 --- a/test/functional/bip68-sequence.py +++ b/test/functional/bip68-sequence.py @@ -17,10 +17,8 @@ NOT_FINAL_ERROR = "64: non-BIP68-final" class BIP68Test(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 - self.setup_clean_chain = False self.extra_args = [[], ["-acceptnonstdtxn=0"]] def run_test(self): diff --git a/test/functional/bip9-softforks.py b/test/functional/bip9-softforks.py index ad1648255..904789301 100755 --- a/test/functional/bip9-softforks.py +++ b/test/functional/bip9-softforks.py @@ -28,11 +28,10 @@ from test_framework.script import CScript, OP_1NEGATE, OP_CHECKSEQUENCEVERIFY, OP_DROP class BIP9SoftForksTest(ComparisonTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 self.extra_args = [['-whitelist=127.0.0.1']] + self.setup_clean_chain = True def run_test(self): self.test = TestManager(self, self.options.tmpdir) diff --git a/test/functional/bipdersig-p2p.py b/test/functional/bipdersig-p2p.py index 977597089..c620d3e15 100755 --- a/test/functional/bipdersig-p2p.py +++ b/test/functional/bipdersig-p2p.py @@ -48,9 +48,7 @@ def create_transaction(node, coinbase, to_address, amount): return tx class BIP66Test(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 self.extra_args = [['-promiscuousmempoolflags=1', '-whitelist=127.0.0.1']] self.setup_clean_chain = True diff --git a/test/functional/blockchain.py b/test/functional/blockchain.py index a3a6cf2fb..5d04de994 100755 --- a/test/functional/blockchain.py +++ b/test/functional/blockchain.py @@ -30,12 +30,8 @@ assert_is_hash_string, ) - class BlockchainTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() - self.setup_clean_chain = False + def set_test_params(self): self.num_nodes = 1 self.extra_args = [['-stopatheight=207']] diff --git a/test/functional/bumpfee.py b/test/functional/bumpfee.py index 00c085af5..3a17fab9e 100755 --- a/test/functional/bumpfee.py +++ b/test/functional/bumpfee.py @@ -30,8 +30,7 @@ class BumpFeeTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 self.setup_clean_chain = True self.extra_args = [["-prematurewitness", "-walletprematurewitness", "-walletrbf={}".format(i)] diff --git a/test/functional/create_cache.py b/test/functional/create_cache.py index 1a5754b3f..7d4d1a529 100755 --- a/test/functional/create_cache.py +++ b/test/functional/create_cache.py @@ -12,11 +12,9 @@ from test_framework.test_framework import BitcoinTestFramework class CreateCache(BitcoinTestFramework): + # Test network and test nodes are not required: - def __init__(self): - super().__init__() - - # Test network and test nodes are not required: + def set_test_params(self): self.num_nodes = 0 def setup_network(self): diff --git a/test/functional/dbcrash.py b/test/functional/dbcrash.py index b0c6d39c4..71424f641 100755 --- a/test/functional/dbcrash.py +++ b/test/functional/dbcrash.py @@ -43,8 +43,7 @@ pass class ChainstateWriteCrashTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 4 self.setup_clean_chain = False diff --git a/test/functional/decodescript.py b/test/functional/decodescript.py index 21a9f1223..6611da883 100755 --- a/test/functional/decodescript.py +++ b/test/functional/decodescript.py @@ -10,9 +10,7 @@ from io import BytesIO class DecodeScriptTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 diff --git a/test/functional/disablewallet.py b/test/functional/disablewallet.py index d34451341..c1d37963b 100755 --- a/test/functional/disablewallet.py +++ b/test/functional/disablewallet.py @@ -11,11 +11,8 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * - class DisableWalletTest (BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 self.extra_args = [["-disablewallet"]] diff --git a/test/functional/disconnect_ban.py b/test/functional/disconnect_ban.py index cc8901c40..a6445b9b3 100755 --- a/test/functional/disconnect_ban.py +++ b/test/functional/disconnect_ban.py @@ -14,11 +14,8 @@ ) class DisconnectBanTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 - self.setup_clean_chain = False def run_test(self): self.log.info("Test setban and listbanned RPCs") diff --git a/test/functional/example_test.py b/test/functional/example_test.py index 4f9e0a7dd..044ef36cf 100755 --- a/test/functional/example_test.py +++ b/test/functional/example_test.py @@ -73,15 +73,11 @@ def custom_function(): class ExampleTest(BitcoinTestFramework): # Each functional test is a subclass of the BitcoinTestFramework class. - # Override the __init__(), add_options(), setup_chain(), setup_network() + # Override the set_test_params(), add_options(), setup_chain(), setup_network() # and setup_nodes() methods to customize the test setup as required. - def __init__(self): - """Initialize the test - - Call super().__init__() first, and then override any test parameters - for your individual test.""" - super().__init__() + def set_test_params(self): + """Override any test parameters for your individual test.""" self.setup_clean_chain = True self.num_nodes = 3 # Use self.extra_args to change command-line arguments for the nodes diff --git a/test/functional/forknotify.py b/test/functional/forknotify.py index 3a2a92709..afcad1f9c 100755 --- a/test/functional/forknotify.py +++ b/test/functional/forknotify.py @@ -9,11 +9,8 @@ from test_framework.test_framework import BitcoinTestFramework class ForkNotifyTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 - self.setup_clean_chain = False def setup_network(self): self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt") diff --git a/test/functional/fundrawtransaction.py b/test/functional/fundrawtransaction.py index 242b6eed1..dec3d41bf 100755 --- a/test/functional/fundrawtransaction.py +++ b/test/functional/fundrawtransaction.py @@ -14,13 +14,9 @@ def get_unspent(listunspent, amount): return utx raise AssertionError('Could not find unspent with amount={}'.format(amount)) - class RawTransactionsTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True - self.num_nodes = 4 def setup_network(self, split=False): self.setup_nodes() diff --git a/test/functional/getblocktemplate_longpoll.py b/test/functional/getblocktemplate_longpoll.py index cca30e268..85d256024 100755 --- a/test/functional/getblocktemplate_longpoll.py +++ b/test/functional/getblocktemplate_longpoll.py @@ -23,11 +23,6 @@ def run(self): self.node.getblocktemplate({'longpollid':self.longpollid}) class GetBlockTemplateLPTest(BitcoinTestFramework): - def __init__(self): - super().__init__() - self.num_nodes = 4 - self.setup_clean_chain = False - def run_test(self): self.log.info("Warning: this test will take about 70 seconds in the best case. Be patient.") self.nodes[0].generate(10) diff --git a/test/functional/getchaintips.py b/test/functional/getchaintips.py index 15f96c565..00fc23c6b 100755 --- a/test/functional/getchaintips.py +++ b/test/functional/getchaintips.py @@ -14,13 +14,7 @@ from test_framework.util import assert_equal class GetChainTipsTest (BitcoinTestFramework): - def __init__(self): - super().__init__() - self.num_nodes = 4 - self.setup_clean_chain = False - def run_test (self): - tips = self.nodes[0].getchaintips () assert_equal (len (tips), 1) assert_equal (tips[0]['branchlen'], 0) diff --git a/test/functional/httpbasics.py b/test/functional/httpbasics.py index 4b32e8d9c..c7682cb49 100755 --- a/test/functional/httpbasics.py +++ b/test/functional/httpbasics.py @@ -11,10 +11,8 @@ import urllib.parse class HTTPBasicsTest (BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 3 - self.setup_clean_chain = False def setup_network(self): self.setup_nodes() diff --git a/test/functional/import-rescan.py b/test/functional/import-rescan.py index e85086fbc..02d742812 100755 --- a/test/functional/import-rescan.py +++ b/test/functional/import-rescan.py @@ -111,8 +111,7 @@ def check(self, txid=None, amount=None, confirmations=None): class ImportRescanTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 + len(IMPORT_NODES) def setup_network(self): diff --git a/test/functional/importmulti.py b/test/functional/importmulti.py index 6aae95549..4c519f40d 100755 --- a/test/functional/importmulti.py +++ b/test/functional/importmulti.py @@ -7,8 +7,7 @@ from test_framework.util import * class ImportMultiTest (BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 self.setup_clean_chain = True diff --git a/test/functional/importprunedfunds.py b/test/functional/importprunedfunds.py index 94753fe43..df90e9ec1 100755 --- a/test/functional/importprunedfunds.py +++ b/test/functional/importprunedfunds.py @@ -6,11 +6,8 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * - class ImportPrunedFundsTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 diff --git a/test/functional/invalidateblock.py b/test/functional/invalidateblock.py index c499d57b9..dd3daf1e0 100755 --- a/test/functional/invalidateblock.py +++ b/test/functional/invalidateblock.py @@ -8,9 +8,7 @@ from test_framework.util import * class InvalidateTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 diff --git a/test/functional/invalidblockrequest.py b/test/functional/invalidblockrequest.py index eabc0db8d..9f44b4492 100755 --- a/test/functional/invalidblockrequest.py +++ b/test/functional/invalidblockrequest.py @@ -23,9 +23,9 @@ class InvalidBlockRequestTest(ComparisonTestFramework): ''' Can either run this test as 1 node with expected answers, or two and compare them. Change the "outcome" variable from each TestInstance object to only do the comparison. ''' - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 + self.setup_clean_chain = True def run_test(self): test = TestManager(self, self.options.tmpdir) diff --git a/test/functional/invalidtxrequest.py b/test/functional/invalidtxrequest.py index a9ac231f0..a22bd8f8c 100755 --- a/test/functional/invalidtxrequest.py +++ b/test/functional/invalidtxrequest.py @@ -19,9 +19,9 @@ class InvalidTxRequestTest(ComparisonTestFramework): ''' Can either run this test as 1 node with expected answers, or two and compare them. Change the "outcome" variable from each TestInstance object to only do the comparison. ''' - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 + self.setup_clean_chain = True def run_test(self): test = TestManager(self, self.options.tmpdir) diff --git a/test/functional/keypool-topup.py b/test/functional/keypool-topup.py index 5425afc9b..b87433a9c 100755 --- a/test/functional/keypool-topup.py +++ b/test/functional/keypool-topup.py @@ -20,8 +20,7 @@ ) class KeypoolRestoreTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 self.extra_args = [['-usehd=0'], ['-usehd=1', '-keypool=100', '-keypoolmin=20']] diff --git a/test/functional/keypool.py b/test/functional/keypool.py index 0f84b5c05..b823ca63b 100755 --- a/test/functional/keypool.py +++ b/test/functional/keypool.py @@ -8,6 +8,8 @@ from test_framework.util import * class KeyPoolTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 1 def run_test(self): nodes = self.nodes @@ -78,10 +80,5 @@ def run_test(self): assert_equal(wi['keypoolsize_hd_internal'], 100) assert_equal(wi['keypoolsize'], 100) - def __init__(self): - super().__init__() - self.setup_clean_chain = False - self.num_nodes = 1 - if __name__ == '__main__': KeyPoolTest().main() diff --git a/test/functional/listsinceblock.py b/test/functional/listsinceblock.py index ce2d556ef..ed1315e80 100755 --- a/test/functional/listsinceblock.py +++ b/test/functional/listsinceblock.py @@ -8,11 +8,8 @@ from test_framework.util import assert_equal class ListSinceBlockTest (BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True - self.num_nodes = 4 def run_test(self): self.nodes[2].generate(101) diff --git a/test/functional/listtransactions.py b/test/functional/listtransactions.py index 9b9702841..5ee85a0da 100755 --- a/test/functional/listtransactions.py +++ b/test/functional/listtransactions.py @@ -16,10 +16,7 @@ def txFromHex(hexstring): return tx class ListTransactionsTest(BitcoinTestFramework): - def __init__(self): - super().__init__() - self.num_nodes = 4 - self.setup_clean_chain = False + def set_test_params(self): self.enable_mocktime() def run_test(self): diff --git a/test/functional/maxuploadtarget.py b/test/functional/maxuploadtarget.py index 6bd6a9e34..1f402798e 100755 --- a/test/functional/maxuploadtarget.py +++ b/test/functional/maxuploadtarget.py @@ -31,8 +31,7 @@ def on_block(self, conn, message): class MaxUploadTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 self.extra_args = [["-maxuploadtarget=800", "-blockmaxsize=999000"]] diff --git a/test/functional/mempool_limit.py b/test/functional/mempool_limit.py index 2777291dd..e24dc5a46 100755 --- a/test/functional/mempool_limit.py +++ b/test/functional/mempool_limit.py @@ -8,9 +8,7 @@ from test_framework.util import * class MempoolLimitTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 self.extra_args = [["-maxmempool=5", "-spendzeroconfchange=0"]] diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py index e22549381..2dedadf8c 100755 --- a/test/functional/mempool_packages.py +++ b/test/functional/mempool_packages.py @@ -12,10 +12,8 @@ MAX_DESCENDANTS = 25 class MempoolPackagesTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 - self.setup_clean_chain = False self.extra_args = [["-maxorphantx=1000"], ["-maxorphantx=1000", "-limitancestorcount=5"]] # Build a transaction that spends parent_txid:vout diff --git a/test/functional/mempool_persist.py b/test/functional/mempool_persist.py index dc5a3263f..01f65b137 100755 --- a/test/functional/mempool_persist.py +++ b/test/functional/mempool_persist.py @@ -36,12 +36,8 @@ from test_framework.util import * class MempoolPersistTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() - # We need 3 nodes for this test. Node1 does not have a persistent mempool. + def set_test_params(self): self.num_nodes = 3 - self.setup_clean_chain = False self.extra_args = [[], ["-persistmempool=0"], []] def run_test(self): diff --git a/test/functional/mempool_reorg.py b/test/functional/mempool_reorg.py index 937bf4bab..7dfddd323 100755 --- a/test/functional/mempool_reorg.py +++ b/test/functional/mempool_reorg.py @@ -13,10 +13,8 @@ # Create one-input, one-output, no-fee transaction: class MempoolCoinbaseTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 - self.setup_clean_chain = False self.extra_args = [["-checkmempool"]] * 2 alert_filename = None # Set by setup_network diff --git a/test/functional/mempool_resurrect_test.py b/test/functional/mempool_resurrect_test.py index a2f6228df..1263c9306 100755 --- a/test/functional/mempool_resurrect_test.py +++ b/test/functional/mempool_resurrect_test.py @@ -9,12 +9,8 @@ # Create one-input, one-output, no-fee transaction: class MempoolCoinbaseTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 - self.setup_clean_chain = False - # Just need one node for this test self.extra_args = [["-checkmempool"]] def run_test(self): diff --git a/test/functional/mempool_spendcoinbase.py b/test/functional/mempool_spendcoinbase.py index 277ea45ad..58ccd3e37 100755 --- a/test/functional/mempool_spendcoinbase.py +++ b/test/functional/mempool_spendcoinbase.py @@ -17,11 +17,8 @@ # Create one-input, one-output, no-fee transaction: class MempoolSpendCoinbaseTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 - self.setup_clean_chain = False self.extra_args = [["-checkmempool"]] def run_test(self): diff --git a/test/functional/merkle_blocks.py b/test/functional/merkle_blocks.py index bcc65c840..2125c6e17 100755 --- a/test/functional/merkle_blocks.py +++ b/test/functional/merkle_blocks.py @@ -8,11 +8,8 @@ from test_framework.util import * class MerkleBlockTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True - self.num_nodes = 4 # Nodes 0/1 are "wallet" nodes, Nodes 2/3 are used for testing self.extra_args = [[], [], [], ["-txindex"]] diff --git a/test/functional/mining.py b/test/functional/mining.py index dbd4e29ec..8edc70467 100755 --- a/test/functional/mining.py +++ b/test/functional/mining.py @@ -25,9 +25,7 @@ def assert_template(node, block, expect, rehash=True): assert_equal(rsp, expect) class MiningTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 self.setup_clean_chain = False diff --git a/test/functional/multi_rpc.py b/test/functional/multi_rpc.py index a30e15ace..a2b346f27 100755 --- a/test/functional/multi_rpc.py +++ b/test/functional/multi_rpc.py @@ -12,10 +12,7 @@ import urllib.parse class HTTPBasicsTest (BitcoinTestFramework): - - def __init__(self): - super().__init__() - self.setup_clean_chain = False + def set_test_params(self): self.num_nodes = 2 def setup_chain(self): diff --git a/test/functional/multiwallet.py b/test/functional/multiwallet.py index 7381a9fd5..e5453e9aa 100755 --- a/test/functional/multiwallet.py +++ b/test/functional/multiwallet.py @@ -12,9 +12,7 @@ from test_framework.util import assert_equal, assert_raises_jsonrpc class MultiWalletTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 self.extra_args = [['-wallet=w1', '-wallet=w2', '-wallet=w3']] diff --git a/test/functional/net.py b/test/functional/net.py index 1e63d3803..830aeb45b 100755 --- a/test/functional/net.py +++ b/test/functional/net.py @@ -17,10 +17,8 @@ p2p_port, ) - class NetTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 diff --git a/test/functional/nulldummy.py b/test/functional/nulldummy.py index 9717add27..60d0d876d 100755 --- a/test/functional/nulldummy.py +++ b/test/functional/nulldummy.py @@ -37,8 +37,7 @@ def trueDummy(tx): class NULLDUMMYTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 self.setup_clean_chain = True self.extra_args = [['-whitelist=127.0.0.1', '-walletprematurewitness']] diff --git a/test/functional/p2p-acceptblock.py b/test/functional/p2p-acceptblock.py index 322cb767d..293bc0553 100755 --- a/test/functional/p2p-acceptblock.py +++ b/test/functional/p2p-acceptblock.py @@ -60,8 +60,7 @@ def add_options(self, parser): default=os.getenv("BITCOIND", "bitcoind"), help="bitcoind binary to test") - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 self.extra_args = [[], ["-whitelist=127.0.0.1"]] diff --git a/test/functional/p2p-compactblocks.py b/test/functional/p2p-compactblocks.py index c5c264765..9c91c0045 100755 --- a/test/functional/p2p-compactblocks.py +++ b/test/functional/p2p-compactblocks.py @@ -89,8 +89,7 @@ def send_await_disconnect(self, message, timeout=30): wait_until(lambda: not self.connected, timeout=timeout, lock=mininode_lock) class CompactBlocksTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True # Node0 = pre-segwit, node1 = segwit-aware self.num_nodes = 2 diff --git a/test/functional/p2p-feefilter.py b/test/functional/p2p-feefilter.py index dbccb633a..8c92365ce 100755 --- a/test/functional/p2p-feefilter.py +++ b/test/functional/p2p-feefilter.py @@ -37,11 +37,8 @@ def clear_invs(self): self.txinvs = [] class FeeFilterTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 - self.setup_clean_chain = False def run_test(self): node1 = self.nodes[1] diff --git a/test/functional/p2p-fullblocktest.py b/test/functional/p2p-fullblocktest.py index e7fe7372c..cb7bbaf7e 100755 --- a/test/functional/p2p-fullblocktest.py +++ b/test/functional/p2p-fullblocktest.py @@ -49,12 +49,11 @@ def normal_serialize(self): return r class FullBlockTest(ComparisonTestFramework): - # Can either run this test as 1 node with expected answers, or two and compare them. # Change the "outcome" variable from each TestInstance object to only do the comparison. - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 + self.setup_clean_chain = True self.block_heights = {} self.coinbase_key = CECKey() self.coinbase_key.set_secretbytes(b"horsebattery") diff --git a/test/functional/p2p-leaktests.py b/test/functional/p2p-leaktests.py index f0d4d9a8b..f27086c97 100755 --- a/test/functional/p2p-leaktests.py +++ b/test/functional/p2p-leaktests.py @@ -92,8 +92,7 @@ def on_version(self, conn, message): conn.send_message(msg_getaddr()) class P2PLeakTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 self.extra_args = [['-banscore='+str(banscore)]] diff --git a/test/functional/p2p-mempool.py b/test/functional/p2p-mempool.py index 34ef249ee..40fcde260 100755 --- a/test/functional/p2p-mempool.py +++ b/test/functional/p2p-mempool.py @@ -13,9 +13,7 @@ from test_framework.util import * class P2PMempoolTests(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 self.extra_args = [["-peerbloomfilters=0"]] diff --git a/test/functional/p2p-segwit.py b/test/functional/p2p-segwit.py index 9189f5ef2..8c2d2f027 100755 --- a/test/functional/p2p-segwit.py +++ b/test/functional/p2p-segwit.py @@ -33,8 +33,7 @@ def get_virtual_size(witness_block): return vsize class TestNode(NodeConnCB): - def __init__(self): - super().__init__() + def set_test_params(self): self.getdataset = set() def on_getdata(self, conn, message): @@ -109,9 +108,7 @@ def sign_P2PK_witness_input(script, txTo, inIdx, hashtype, value, key): class SegWitTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 self.extra_args = [["-whitelist=127.0.0.1"], ["-whitelist=127.0.0.1", "-acceptnonstdtxn=0"], ["-whitelist=127.0.0.1", "-vbparams=segwit:0:0"]] diff --git a/test/functional/p2p-timeouts.py b/test/functional/p2p-timeouts.py index c3b29c215..51d4769ef 100755 --- a/test/functional/p2p-timeouts.py +++ b/test/functional/p2p-timeouts.py @@ -33,8 +33,7 @@ def on_version(self, conn, message): pass class TimeoutsTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 diff --git a/test/functional/p2p-versionbits-warning.py b/test/functional/p2p-versionbits-warning.py index a7aacf908..5dfac6dd1 100755 --- a/test/functional/p2p-versionbits-warning.py +++ b/test/functional/p2p-versionbits-warning.py @@ -28,8 +28,7 @@ def on_inv(self, conn, message): pass class VersionBitsWarningTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 diff --git a/test/functional/preciousblock.py b/test/functional/preciousblock.py index 04b41e76b..40d7bb14e 100755 --- a/test/functional/preciousblock.py +++ b/test/functional/preciousblock.py @@ -35,8 +35,7 @@ def node_sync_via_rpc(nodes): unidirectional_node_sync_via_rpc(node_src, node_dest) class PreciousTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 diff --git a/test/functional/prioritise_transaction.py b/test/functional/prioritise_transaction.py index 4fc03d254..7ad368acd 100755 --- a/test/functional/prioritise_transaction.py +++ b/test/functional/prioritise_transaction.py @@ -9,9 +9,7 @@ from test_framework.mininode import COIN, MAX_BLOCK_BASE_SIZE class PrioritiseTransactionTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 self.extra_args = [["-printpriority=1"], ["-printpriority=1"]] diff --git a/test/functional/proxy_test.py b/test/functional/proxy_test.py index adbbda278..26cb645da 100755 --- a/test/functional/proxy_test.py +++ b/test/functional/proxy_test.py @@ -41,13 +41,7 @@ RANGE_BEGIN = PORT_MIN + 2 * PORT_RANGE # Start after p2p and rpc ports - class ProxyTest(BitcoinTestFramework): - def __init__(self): - super().__init__() - self.num_nodes = 4 - self.setup_clean_chain = False - def setup_nodes(self): self.have_ipv6 = test_ipv6_local() # Create two proxies on different ports diff --git a/test/functional/pruning.py b/test/functional/pruning.py index 1791cd97f..f53fe8288 100755 --- a/test/functional/pruning.py +++ b/test/functional/pruning.py @@ -26,9 +26,7 @@ def calc_usage(blockdir): return sum(os.path.getsize(blockdir+f) for f in os.listdir(blockdir) if os.path.isfile(blockdir+f)) / (1024. * 1024.) class PruneTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 6 diff --git a/test/functional/rawtransactions.py b/test/functional/rawtransactions.py index b6b90d678..c53dc4628 100755 --- a/test/functional/rawtransactions.py +++ b/test/functional/rawtransactions.py @@ -17,9 +17,7 @@ # Create one-input, one-output, no-fee transaction: class RawTransactionsTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 diff --git a/test/functional/receivedby.py b/test/functional/receivedby.py index b16583416..48eb1c51b 100755 --- a/test/functional/receivedby.py +++ b/test/functional/receivedby.py @@ -23,11 +23,7 @@ def get_sub_array_from_array(object_array, to_match): return [] class ReceivedByTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() - self.num_nodes = 4 - self.setup_clean_chain = False + def set_test_params(self): self.enable_mocktime() def run_test(self): diff --git a/test/functional/reindex.py b/test/functional/reindex.py index 0d2a10659..1f684a1af 100755 --- a/test/functional/reindex.py +++ b/test/functional/reindex.py @@ -15,8 +15,7 @@ class ReindexTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 diff --git a/test/functional/replace-by-fee.py b/test/functional/replace-by-fee.py index bc6765498..220e88968 100755 --- a/test/functional/replace-by-fee.py +++ b/test/functional/replace-by-fee.py @@ -61,10 +61,8 @@ def make_utxo(node, amount, confirmed=True, scriptPubKey=CScript([1])): class ReplaceByFeeTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 - self.setup_clean_chain = False self.extra_args= [["-maxorphantx=1000", "-whitelist=127.0.0.1", "-limitancestorcount=50", diff --git a/test/functional/resendwallettransactions.py b/test/functional/resendwallettransactions.py index db6f6eec4..d6ba59139 100755 --- a/test/functional/resendwallettransactions.py +++ b/test/functional/resendwallettransactions.py @@ -8,11 +8,9 @@ from test_framework.util import assert_equal, assert_raises_jsonrpc class ResendWalletTransactionsTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() - self.extra_args = [['--walletbroadcast=false']] + def set_test_params(self): self.num_nodes = 1 + self.extra_args = [['--walletbroadcast=false']] def run_test(self): # Should raise RPC_WALLET_ERROR (-4) if walletbroadcast is disabled. diff --git a/test/functional/rest.py b/test/functional/rest.py index a69dbb501..437111a4d 100755 --- a/test/functional/rest.py +++ b/test/functional/rest.py @@ -43,8 +43,7 @@ def http_post_call(host, port, path, requestdata = '', response_object = 0): class RESTTest (BitcoinTestFramework): FORMAT_SEPARATOR = "." - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 diff --git a/test/functional/rpcbind_test.py b/test/functional/rpcbind_test.py index a7661421f..0cf64beeb 100755 --- a/test/functional/rpcbind_test.py +++ b/test/functional/rpcbind_test.py @@ -11,11 +11,8 @@ from test_framework.util import * from test_framework.netutil import * - class RPCBindTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 diff --git a/test/functional/rpcnamedargs.py b/test/functional/rpcnamedargs.py index 3b286000a..da61cc66e 100755 --- a/test/functional/rpcnamedargs.py +++ b/test/functional/rpcnamedargs.py @@ -10,15 +10,8 @@ assert_raises_jsonrpc, ) - class NamedArgumentTest(BitcoinTestFramework): - """ - Test named arguments on RPC calls. - """ - - def __init__(self): - super().__init__() - self.setup_clean_chain = False + def set_test_params(self): self.num_nodes = 1 def run_test(self): diff --git a/test/functional/segwit.py b/test/functional/segwit.py index 51eaa34a5..c08fbd3e7 100755 --- a/test/functional/segwit.py +++ b/test/functional/segwit.py @@ -75,9 +75,7 @@ def find_unspent(node, min_value): return utxo class SegWitTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 self.extra_args = [["-walletprematurewitness", "-rpcserialversion=0"], diff --git a/test/functional/sendheaders.py b/test/functional/sendheaders.py index 6451b097c..fe577dc20 100755 --- a/test/functional/sendheaders.py +++ b/test/functional/sendheaders.py @@ -174,8 +174,7 @@ def send_getblocks(self, locator): self.send_message(getblocks_message) class SendHeadersTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 diff --git a/test/functional/signmessages.py b/test/functional/signmessages.py index 42f6a9daa..5fdfeceb7 100755 --- a/test/functional/signmessages.py +++ b/test/functional/signmessages.py @@ -7,9 +7,7 @@ from test_framework.test_framework import BitcoinTestFramework class SignMessagesTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 diff --git a/test/functional/signrawtransactions.py b/test/functional/signrawtransactions.py index 415727268..b47ef9395 100755 --- a/test/functional/signrawtransactions.py +++ b/test/functional/signrawtransactions.py @@ -9,8 +9,7 @@ class SignRawTransactionsTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 diff --git a/test/functional/smartfees.py b/test/functional/smartfees.py index caf359265..76632fc57 100755 --- a/test/functional/smartfees.py +++ b/test/functional/smartfees.py @@ -141,11 +141,8 @@ def check_estimates(node, fees_seen, max_invalid, print_estimates = True): class EstimateFeeTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 3 - self.setup_clean_chain = False def setup_network(self): """ diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 5e3644a91..d860bc559 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -48,58 +48,30 @@ class TestStatus(Enum): class BitcoinTestFramework(object): """Base class for a bitcoin test script. - Individual bitcoin test scripts should subclass this class and override the following methods: + Individual bitcoin test scripts should subclass this class and override the run_test() method. - - __init__() + Individual tests can also override the following methods to customize the test setup: + + - set_test_params() - add_options() - setup_chain() - setup_network() - - run_test() + - setup_nodes() - The main() method should not be overridden. + The __init__() and main() methods should not be overridden. This class also contains various public and private helper methods.""" - # Methods to override in subclass test scripts. def __init__(self): + """Sets test framework defaults. Do not override this method. Instead, override the set_test_params() method""" self.num_nodes = 4 self.setup_clean_chain = False self.nodes = [] self.mocktime = 0 - - def add_options(self, parser): - pass - - def setup_chain(self): - self.log.info("Initializing test directory " + self.options.tmpdir) - if self.setup_clean_chain: - self._initialize_chain_clean() - else: - self._initialize_chain() - - def setup_network(self): - self.setup_nodes() - - # Connect the nodes as a "chain". This allows us - # to split the network between nodes 1 and 2 to get - # two halves that can work on competing chains. - for i in range(self.num_nodes - 1): - connect_nodes_bi(self.nodes, i, i + 1) - self.sync_all() - - def setup_nodes(self): - extra_args = None - if hasattr(self, "extra_args"): - extra_args = self.extra_args - self.add_nodes(self.num_nodes, extra_args) - self.start_nodes() - - def run_test(self): - raise NotImplementedError - - # Main function. This should not be overridden by the subclass test scripts. + self.set_test_params() def main(self): + """Main function. This should not be overridden by the subclass test scripts.""" parser = optparse.OptionParser(usage="%prog [options]") parser.add_option("--nocleanup", dest="nocleanup", default=False, action="store_true", @@ -203,6 +175,46 @@ def main(self): logging.shutdown() sys.exit(TEST_EXIT_FAILED) + # Methods to override in subclass test scripts. + def set_test_params(self): + """Override this method to change default values for number of nodes, topology, etc""" + pass + + def add_options(self, parser): + """Override this method to add command-line options to the test""" + pass + + def setup_chain(self): + """Override this method to customize blockchain setup""" + self.log.info("Initializing test directory " + self.options.tmpdir) + if self.setup_clean_chain: + self._initialize_chain_clean() + else: + self._initialize_chain() + + def setup_network(self): + """Override this method to customize test network topology""" + self.setup_nodes() + + # Connect the nodes as a "chain". This allows us + # to split the network between nodes 1 and 2 to get + # two halves that can work on competing chains. + for i in range(self.num_nodes - 1): + connect_nodes_bi(self.nodes, i, i + 1) + self.sync_all() + + def setup_nodes(self): + """Override this method to customize test node setup""" + extra_args = None + if hasattr(self, "extra_args"): + extra_args = self.extra_args + self.add_nodes(self.num_nodes, extra_args) + self.start_nodes() + + def run_test(self): + """Override this method to define test logic""" + raise NotImplementedError + # Public helper methods. These can be accessed by the subclass test scripts. def add_nodes(self, num_nodes, extra_args=None, rpchost=None, timewait=None, binary=None): @@ -442,8 +454,7 @@ class ComparisonTestFramework(BitcoinTestFramework): - 2 binaries: 1 test binary, 1 ref binary - n>2 binaries: 1 test binary, n-1 ref binaries""" - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 self.setup_clean_chain = True diff --git a/test/functional/txn_clone.py b/test/functional/txn_clone.py index 9b81af96c..fc133050b 100755 --- a/test/functional/txn_clone.py +++ b/test/functional/txn_clone.py @@ -8,12 +8,6 @@ from test_framework.util import * class TxnMallTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() - self.num_nodes = 4 - self.setup_clean_chain = False - def add_options(self, parser): parser.add_option("--mineblock", dest="mine_block", default=False, action="store_true", help="Test double-spend of 1-confirmed transaction") diff --git a/test/functional/txn_doublespend.py b/test/functional/txn_doublespend.py index 1bd3b3271..ff24fac25 100755 --- a/test/functional/txn_doublespend.py +++ b/test/functional/txn_doublespend.py @@ -9,11 +9,6 @@ class TxnMallTest(BitcoinTestFramework): - def __init__(self): - super().__init__() - self.num_nodes = 4 - self.setup_clean_chain = False - def add_options(self, parser): parser.add_option("--mineblock", dest="mine_block", default=False, action="store_true", help="Test double-spend of 1-confirmed transaction") diff --git a/test/functional/uptime.py b/test/functional/uptime.py index b20d6f5cb..78236b239 100755 --- a/test/functional/uptime.py +++ b/test/functional/uptime.py @@ -13,9 +13,7 @@ class UptimeTest(BitcoinTestFramework): - def __init__(self): - super().__init__() - + def set_test_params(self): self.num_nodes = 1 self.setup_clean_chain = True diff --git a/test/functional/wallet-accounts.py b/test/functional/wallet-accounts.py index 158aa9ae8..40726d2a7 100755 --- a/test/functional/wallet-accounts.py +++ b/test/functional/wallet-accounts.py @@ -17,9 +17,7 @@ from test_framework.util import assert_equal class WalletAccountsTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 self.extra_args = [[]] diff --git a/test/functional/wallet-dump.py b/test/functional/wallet-dump.py index e0381b980..39b7164d6 100755 --- a/test/functional/wallet-dump.py +++ b/test/functional/wallet-dump.py @@ -56,10 +56,7 @@ def read_dump(file_name, addrs, hd_master_addr_old): class WalletDumpTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() - self.setup_clean_chain = False + def set_test_params(self): self.num_nodes = 1 self.extra_args = [["-keypool=90"]] diff --git a/test/functional/wallet-encryption.py b/test/functional/wallet-encryption.py index 2f38e6fee..f63bb2ea5 100755 --- a/test/functional/wallet-encryption.py +++ b/test/functional/wallet-encryption.py @@ -13,9 +13,7 @@ ) class WalletEncryptionTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 diff --git a/test/functional/wallet-hd.py b/test/functional/wallet-hd.py index edc74089b..a6b96b745 100755 --- a/test/functional/wallet-hd.py +++ b/test/functional/wallet-hd.py @@ -11,11 +11,8 @@ ) import shutil - class WalletHDTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 self.extra_args = [['-usehd=0'], ['-usehd=1', '-keypool=0']] diff --git a/test/functional/wallet.py b/test/functional/wallet.py index 12026e84a..0e5d641fe 100755 --- a/test/functional/wallet.py +++ b/test/functional/wallet.py @@ -7,17 +7,8 @@ from test_framework.util import * class WalletTest(BitcoinTestFramework): - - def check_fee_amount(self, curr_balance, balance_with_fee, fee_per_byte, tx_size): - """Return curr_balance after asserting the fee was in range""" - fee = balance_with_fee - curr_balance - assert_fee_amount(fee, tx_size, fee_per_byte * 1000) - return curr_balance - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True - self.num_nodes = 4 self.extra_args = [['-usehd={:d}'.format(i%2==0)] for i in range(4)] def setup_network(self): @@ -30,8 +21,13 @@ def setup_network(self): connect_nodes_bi(self.nodes,0,2) self.sync_all([self.nodes[0:3]]) - def run_test(self): + def check_fee_amount(self, curr_balance, balance_with_fee, fee_per_byte, tx_size): + """Return curr_balance after asserting the fee was in range""" + fee = balance_with_fee - curr_balance + assert_fee_amount(fee, tx_size, fee_per_byte * 1000) + return curr_balance + def run_test(self): # Check that there's no UTXO on none of the nodes assert_equal(len(self.nodes[0].listunspent()), 0) assert_equal(len(self.nodes[1].listunspent()), 0) diff --git a/test/functional/walletbackup.py b/test/functional/walletbackup.py index 688cd6d0c..36714164a 100755 --- a/test/functional/walletbackup.py +++ b/test/functional/walletbackup.py @@ -37,11 +37,8 @@ from test_framework.util import * class WalletBackupTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True - self.num_nodes = 4 # nodes 1, 2,3 are spenders, let's give them a keypool=100 self.extra_args = [["-keypool=100"], ["-keypool=100"], ["-keypool=100"], []] diff --git a/test/functional/zapwallettxes.py b/test/functional/zapwallettxes.py index 1005a4b95..c001517a6 100755 --- a/test/functional/zapwallettxes.py +++ b/test/functional/zapwallettxes.py @@ -20,9 +20,7 @@ ) class ZapWalletTXesTest (BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 diff --git a/test/functional/zmq_test.py b/test/functional/zmq_test.py index b5a22ea07..3f2668ee8 100755 --- a/test/functional/zmq_test.py +++ b/test/functional/zmq_test.py @@ -13,9 +13,7 @@ ) class ZMQTest (BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 def setup_nodes(self): From 7148b74dc39110f53c665b94fa9d994c6ad6dc1c Mon Sep 17 00:00:00 2001 From: John Newbery Date: Thu, 24 Aug 2017 11:11:56 -0400 Subject: [PATCH 144/382] [tests] Functional tests must explicitly set num_nodes --- test/functional/example_test.py | 4 +++- test/functional/fundrawtransaction.py | 1 + test/functional/getblocktemplate_longpoll.py | 3 +++ test/functional/getchaintips.py | 3 +++ test/functional/listsinceblock.py | 1 + test/functional/listtransactions.py | 1 + test/functional/merkle_blocks.py | 1 + test/functional/p2p-segwit.py | 1 + test/functional/proxy_test.py | 3 +++ test/functional/receivedby.py | 1 + test/functional/test_framework/test_framework.py | 12 ++++++------ test/functional/txn_clone.py | 3 +++ test/functional/txn_doublespend.py | 2 ++ test/functional/wallet.py | 1 + test/functional/walletbackup.py | 1 + 15 files changed, 31 insertions(+), 7 deletions(-) diff --git a/test/functional/example_test.py b/test/functional/example_test.py index 044ef36cf..43f3415c8 100755 --- a/test/functional/example_test.py +++ b/test/functional/example_test.py @@ -77,7 +77,9 @@ class ExampleTest(BitcoinTestFramework): # and setup_nodes() methods to customize the test setup as required. def set_test_params(self): - """Override any test parameters for your individual test.""" + """Override test parameters for your individual test. + + This method must be overridden and num_nodes must be exlicitly set.""" self.setup_clean_chain = True self.num_nodes = 3 # Use self.extra_args to change command-line arguments for the nodes diff --git a/test/functional/fundrawtransaction.py b/test/functional/fundrawtransaction.py index dec3d41bf..3326b58c4 100755 --- a/test/functional/fundrawtransaction.py +++ b/test/functional/fundrawtransaction.py @@ -16,6 +16,7 @@ def get_unspent(listunspent, amount): class RawTransactionsTest(BitcoinTestFramework): def set_test_params(self): + self.num_nodes = 4 self.setup_clean_chain = True def setup_network(self, split=False): diff --git a/test/functional/getblocktemplate_longpoll.py b/test/functional/getblocktemplate_longpoll.py index 85d256024..89768bd2f 100755 --- a/test/functional/getblocktemplate_longpoll.py +++ b/test/functional/getblocktemplate_longpoll.py @@ -23,6 +23,9 @@ def run(self): self.node.getblocktemplate({'longpollid':self.longpollid}) class GetBlockTemplateLPTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 2 + def run_test(self): self.log.info("Warning: this test will take about 70 seconds in the best case. Be patient.") self.nodes[0].generate(10) diff --git a/test/functional/getchaintips.py b/test/functional/getchaintips.py index 00fc23c6b..21b67bfc6 100755 --- a/test/functional/getchaintips.py +++ b/test/functional/getchaintips.py @@ -14,6 +14,9 @@ from test_framework.util import assert_equal class GetChainTipsTest (BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 4 + def run_test (self): tips = self.nodes[0].getchaintips () assert_equal (len (tips), 1) diff --git a/test/functional/listsinceblock.py b/test/functional/listsinceblock.py index ed1315e80..6f428388e 100755 --- a/test/functional/listsinceblock.py +++ b/test/functional/listsinceblock.py @@ -9,6 +9,7 @@ class ListSinceBlockTest (BitcoinTestFramework): def set_test_params(self): + self.num_nodes = 4 self.setup_clean_chain = True def run_test(self): diff --git a/test/functional/listtransactions.py b/test/functional/listtransactions.py index 5ee85a0da..e4522cc3b 100755 --- a/test/functional/listtransactions.py +++ b/test/functional/listtransactions.py @@ -17,6 +17,7 @@ def txFromHex(hexstring): class ListTransactionsTest(BitcoinTestFramework): def set_test_params(self): + self.num_nodes = 2 self.enable_mocktime() def run_test(self): diff --git a/test/functional/merkle_blocks.py b/test/functional/merkle_blocks.py index 2125c6e17..a58334b2a 100755 --- a/test/functional/merkle_blocks.py +++ b/test/functional/merkle_blocks.py @@ -9,6 +9,7 @@ class MerkleBlockTest(BitcoinTestFramework): def set_test_params(self): + self.num_nodes = 4 self.setup_clean_chain = True # Nodes 0/1 are "wallet" nodes, Nodes 2/3 are used for testing self.extra_args = [[], [], [], ["-txindex"]] diff --git a/test/functional/p2p-segwit.py b/test/functional/p2p-segwit.py index 8c2d2f027..9bb72c819 100755 --- a/test/functional/p2p-segwit.py +++ b/test/functional/p2p-segwit.py @@ -34,6 +34,7 @@ def get_virtual_size(witness_block): class TestNode(NodeConnCB): def set_test_params(self): + self.num_nodes = 3 self.getdataset = set() def on_getdata(self, conn, message): diff --git a/test/functional/proxy_test.py b/test/functional/proxy_test.py index 26cb645da..81b99d1bf 100755 --- a/test/functional/proxy_test.py +++ b/test/functional/proxy_test.py @@ -42,6 +42,9 @@ RANGE_BEGIN = PORT_MIN + 2 * PORT_RANGE # Start after p2p and rpc ports class ProxyTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 4 + def setup_nodes(self): self.have_ipv6 = test_ipv6_local() # Create two proxies on different ports diff --git a/test/functional/receivedby.py b/test/functional/receivedby.py index 48eb1c51b..db6fc86b8 100755 --- a/test/functional/receivedby.py +++ b/test/functional/receivedby.py @@ -24,6 +24,7 @@ def get_sub_array_from_array(object_array, to_match): class ReceivedByTest(BitcoinTestFramework): def set_test_params(self): + self.num_nodes = 2 self.enable_mocktime() def run_test(self): diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index d860bc559..103651f17 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -48,11 +48,10 @@ class TestStatus(Enum): class BitcoinTestFramework(object): """Base class for a bitcoin test script. - Individual bitcoin test scripts should subclass this class and override the run_test() method. + Individual bitcoin test scripts should subclass this class and override the set_test_params() and run_test() methods. Individual tests can also override the following methods to customize the test setup: - - set_test_params() - add_options() - setup_chain() - setup_network() @@ -64,12 +63,13 @@ class BitcoinTestFramework(object): def __init__(self): """Sets test framework defaults. Do not override this method. Instead, override the set_test_params() method""" - self.num_nodes = 4 self.setup_clean_chain = False self.nodes = [] self.mocktime = 0 self.set_test_params() + assert hasattr(self, "num_nodes"), "Test must set self.num_nodes in set_test_params()" + def main(self): """Main function. This should not be overridden by the subclass test scripts.""" @@ -177,8 +177,8 @@ def main(self): # Methods to override in subclass test scripts. def set_test_params(self): - """Override this method to change default values for number of nodes, topology, etc""" - pass + """Tests must this method to change default values for number of nodes, topology, etc""" + raise NotImplementedError def add_options(self, parser): """Override this method to add command-line options to the test""" @@ -212,7 +212,7 @@ def setup_nodes(self): self.start_nodes() def run_test(self): - """Override this method to define test logic""" + """Tests must override this method to define test logic""" raise NotImplementedError # Public helper methods. These can be accessed by the subclass test scripts. diff --git a/test/functional/txn_clone.py b/test/functional/txn_clone.py index fc133050b..740bb2d4c 100755 --- a/test/functional/txn_clone.py +++ b/test/functional/txn_clone.py @@ -8,6 +8,9 @@ from test_framework.util import * class TxnMallTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 4 + def add_options(self, parser): parser.add_option("--mineblock", dest="mine_block", default=False, action="store_true", help="Test double-spend of 1-confirmed transaction") diff --git a/test/functional/txn_doublespend.py b/test/functional/txn_doublespend.py index ff24fac25..69629ef95 100755 --- a/test/functional/txn_doublespend.py +++ b/test/functional/txn_doublespend.py @@ -8,6 +8,8 @@ from test_framework.util import * class TxnMallTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 4 def add_options(self, parser): parser.add_option("--mineblock", dest="mine_block", default=False, action="store_true", diff --git a/test/functional/wallet.py b/test/functional/wallet.py index 0e5d641fe..0b3c267a2 100755 --- a/test/functional/wallet.py +++ b/test/functional/wallet.py @@ -8,6 +8,7 @@ class WalletTest(BitcoinTestFramework): def set_test_params(self): + self.num_nodes = 4 self.setup_clean_chain = True self.extra_args = [['-usehd={:d}'.format(i%2==0)] for i in range(4)] diff --git a/test/functional/walletbackup.py b/test/functional/walletbackup.py index 36714164a..15ea26afa 100755 --- a/test/functional/walletbackup.py +++ b/test/functional/walletbackup.py @@ -38,6 +38,7 @@ class WalletBackupTest(BitcoinTestFramework): def set_test_params(self): + self.num_nodes = 4 self.setup_clean_chain = True # nodes 1, 2,3 are spenders, let's give them a keypool=100 self.extra_args = [["-keypool=100"], ["-keypool=100"], ["-keypool=100"], []] From 3918d93f3cd2305dc12cb964aebba0fc1f8720b4 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Fri, 1 Sep 2017 14:24:30 -0400 Subject: [PATCH 145/382] [tests] fixups from set_test_params() --- test/functional/bitcoin_cli.py | 3 +-- test/functional/example_test.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/test/functional/bitcoin_cli.py b/test/functional/bitcoin_cli.py index 103320209..7acfede3f 100755 --- a/test/functional/bitcoin_cli.py +++ b/test/functional/bitcoin_cli.py @@ -8,8 +8,7 @@ class TestBitcoinCli(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 diff --git a/test/functional/example_test.py b/test/functional/example_test.py index 43f3415c8..87d73ad14 100755 --- a/test/functional/example_test.py +++ b/test/functional/example_test.py @@ -85,7 +85,7 @@ def set_test_params(self): # Use self.extra_args to change command-line arguments for the nodes self.extra_args = [[], ["-logips"], []] - # self.log.info("I've finished __init__") # Oops! Can't run self.log before run_test() + # self.log.info("I've finished set_test_params") # Oops! Can't run self.log before run_test() # Use add_options() to add specific command-line options for your test. # In practice this is not used very much, since the tests are mostly written From dc334fe7495f882aa245abbfbd8a82f0df4d020a Mon Sep 17 00:00:00 2001 From: Utsav Gupta Date: Sat, 2 Sep 2017 09:59:48 +0530 Subject: [PATCH 146/382] Update hmac_sha256.h Fixed a typo --- src/crypto/hmac_sha256.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crypto/hmac_sha256.h b/src/crypto/hmac_sha256.h index 1519c1457..8c42fcfe1 100644 --- a/src/crypto/hmac_sha256.h +++ b/src/crypto/hmac_sha256.h @@ -10,7 +10,7 @@ #include #include -/** A hasher class for HMAC-SHA-512. */ +/** A hasher class for HMAC-SHA-256. */ class CHMAC_SHA256 { private: From d01a9682b126a5f83c7311e652e6e62f2c2e1d20 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Sun, 3 Sep 2017 08:43:35 -0400 Subject: [PATCH 147/382] wallet: update stored witness in AddToWallet Replace witness-stripped wallet transactions with full transactions; this can happen when upgrading from a pre-segwit wallet to a segwit- aware wallet. --- src/wallet/wallet.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index d1d2060b0..4b48735e1 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -914,6 +914,15 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) wtx.fFromMe = wtxIn.fFromMe; fUpdated = true; } + // If we have a witness-stripped version of this transaction, and we + // see a new version with a witness, then we must be upgrading a pre-segwit + // wallet. Store the new version of the transaction with the witness, + // as the stripped-version must be invalid. + // TODO: Store all versions of the transaction, instead of just one. + if (wtxIn.tx->HasWitness() && !wtx.tx->HasWitness()) { + wtx.SetTx(wtxIn.tx); + fUpdated = true; + } } //// debug print From 258d33b41a27917f59e3aee856d032a23cbb5b05 Mon Sep 17 00:00:00 2001 From: Karl-Johan Alm Date: Wed, 16 Aug 2017 17:19:02 +0900 Subject: [PATCH 148/382] [mempool] Mark unaccepted txs present in mempool as 'already there'. On startup, the wallets will start pumping wallet transactions into the mempool in a different thread while LoadMempool() is running. This will sometimes result in transactions "failing" to be accepted into mempool, but only for the reason that they were already put there by a wallet. The log message for mempool load would note this as a 'failure' to import, which was misleading; it should instead mark it as the transaction already being in the mempool. --- src/validation.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/validation.cpp b/src/validation.cpp index 3b9636839..dac979309 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -4278,8 +4278,9 @@ bool LoadMempool(void) } int64_t count = 0; - int64_t skipped = 0; + int64_t expired = 0; int64_t failed = 0; + int64_t already_there = 0; int64_t nNow = GetTime(); try { @@ -4309,10 +4310,18 @@ bool LoadMempool(void) if (state.IsValid()) { ++count; } else { - ++failed; + // mempool may contain the transaction already, e.g. from + // wallet(s) having loaded it while we were processing + // mempool transactions; consider these as valid, instead of + // failed, but mark them as 'already there' + if (mempool.exists(tx->GetHash())) { + ++already_there; + } else { + ++failed; + } } } else { - ++skipped; + ++expired; } if (ShutdownRequested()) return false; @@ -4328,7 +4337,7 @@ bool LoadMempool(void) return false; } - LogPrintf("Imported mempool transactions from disk: %i successes, %i failed, %i expired\n", count, failed, skipped); + LogPrintf("Imported mempool transactions from disk: %i succeeded, %i failed, %i expired, %i already there\n", count, failed, expired, already_there); return true; } From 8fdb6f9126fe1e13b60de11fd34c204247679ef4 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Mon, 4 Sep 2017 11:45:01 -0400 Subject: [PATCH 149/382] [tests] fixup dbcrash interaction with add_nodes() --- test/functional/dbcrash.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/dbcrash.py b/test/functional/dbcrash.py index 71424f641..24b9765b4 100755 --- a/test/functional/dbcrash.py +++ b/test/functional/dbcrash.py @@ -64,7 +64,7 @@ def set_test_params(self): def setup_network(self): # Need a bit of extra time for the nodes to start up for this test - self.add_nodes(self.num_nodes, timewait=90) + self.add_nodes(self.num_nodes, extra_args=self.extra_args, timewait=90) self.start_nodes() # Leave them unconnected, we'll use submitblock directly in this test From 47ba2c312a440baff5ebc911e3bb014643f4e9c3 Mon Sep 17 00:00:00 2001 From: Akio Nakamura Date: Mon, 28 Aug 2017 12:44:57 +0900 Subject: [PATCH 150/382] Fix currency/fee-rate unit string in the help text 1. The RPC help text should use the constant CURRENCY_UNIT defined in policy/feerate.cpp instead of the literal 'BTC'. In the following 2 RPC commands, 'BTC' is written directly in the help text. 1) estimatesmartfee 2) estimaterawfee And also, for these help strings, the notation 'fee-per-kilobyte (in BTC)' is somewhat ambiguous. To write more precisely, this commit changes to 'fee rate in BTC/kB' with using the constant CURRENCY_UNIT. 2. Some RPC command use 'satoshis' as the unit. It should be written as 'satoshis' instead of 'Satoshis' in the RPC help text. So, this commit fixes this typo in getblocktemplate. 3. The phrase that '... feerate (BTC per KB) ...' is used to explain the fee rate in the help text of following 2 RPC commands. 1) getmempoolinfo 2) fundrawtransaction But they are different from other similar help text of the RPCs. And also, 'KB' implies Kibibyte (2^10 byte). To unify and to clarify, this commit changes these phrase to '... fee rate in BTC/kB ...'. (BTC references the constant 'CURRENCY_UNIT') --- src/rpc/blockchain.cpp | 2 +- src/rpc/mining.cpp | 8 ++++---- src/wallet/rpcwallet.cpp | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index e6b78516e..ed079489c 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1344,7 +1344,7 @@ UniValue getmempoolinfo(const JSONRPCRequest& request) " \"bytes\": xxxxx, (numeric) Sum of all virtual transaction sizes as defined in BIP 141. Differs from actual serialized size because witness data is discounted\n" " \"usage\": xxxxx, (numeric) Total memory usage for the mempool\n" " \"maxmempool\": xxxxx, (numeric) Maximum memory usage for the mempool\n" - " \"mempoolminfee\": xxxxx (numeric) Minimum feerate (" + CURRENCY_UNIT + " per KB) for tx to be accepted\n" + " \"mempoolminfee\": xxxxx (numeric) Minimum fee rate in " + CURRENCY_UNIT + "/kB for tx to be accepted\n" "}\n" "\nExamples:\n" + HelpExampleCli("getmempoolinfo", "") diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 2692e5915..0a754553c 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -336,7 +336,7 @@ UniValue getblocktemplate(const JSONRPCRequest& request) " n (numeric) transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is\n" " ,...\n" " ],\n" - " \"fee\": n, (numeric) difference in value between transaction inputs and outputs (in Satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one\n" + " \"fee\": n, (numeric) difference in value between transaction inputs and outputs (in satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one\n" " \"sigops\" : n, (numeric) total SigOps cost, as counted for purposes of block limits; if key is not present, sigop cost is unknown and clients MUST NOT assume it is zero\n" " \"weight\" : n, (numeric) total transaction weight, as counted for purposes of block limits\n" " \"required\" : true|false (boolean) if provided and true, this transaction must be in the final block\n" @@ -346,7 +346,7 @@ UniValue getblocktemplate(const JSONRPCRequest& request) " \"coinbaseaux\" : { (json object) data that should be included in the coinbase's scriptSig content\n" " \"flags\" : \"xx\" (string) key name is to be ignored, and value included in scriptSig\n" " },\n" - " \"coinbasevalue\" : n, (numeric) maximum allowable input to coinbase transaction, including the generation award and transaction fees (in Satoshis)\n" + " \"coinbasevalue\" : n, (numeric) maximum allowable input to coinbase transaction, including the generation award and transaction fees (in satoshis)\n" " \"coinbasetxn\" : { ... }, (json object) information for coinbase transaction\n" " \"target\" : \"xxxx\", (string) The hash target\n" " \"mintime\" : xxx, (numeric) The minimum timestamp appropriate for next block time in seconds since epoch (Jan 1 1970 GMT)\n" @@ -825,7 +825,7 @@ UniValue estimatesmartfee(const JSONRPCRequest& request) " \"CONSERVATIVE\"\n" "\nResult:\n" "{\n" - " \"feerate\" : x.x, (numeric, optional) estimate fee-per-kilobyte (in BTC)\n" + " \"feerate\" : x.x, (numeric, optional) estimate fee rate in " + CURRENCY_UNIT + "/kB\n" " \"errors\": [ str... ] (json array of strings, optional) Errors encountered during processing\n" " \"blocks\" : n (numeric) block number where estimate was found\n" "}\n" @@ -884,7 +884,7 @@ UniValue estimaterawfee(const JSONRPCRequest& request) "\nResult:\n" "{\n" " \"short\" : { (json object, optional) estimate for short time horizon\n" - " \"feerate\" : x.x, (numeric, optional) estimate fee-per-kilobyte (in BTC)\n" + " \"feerate\" : x.x, (numeric, optional) estimate fee rate in " + CURRENCY_UNIT + "/kB\n" " \"decay\" : x.x, (numeric) exponential decay (per block) for historical moving average of confirmation data\n" " \"scale\" : x, (numeric) The resolution of confirmation targets at this time horizon\n" " \"pass\" : { (json object, optional) information about the lowest range of feerates to succeed in meeting the threshold\n" diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 4ea53c413..c4cc02f00 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2812,7 +2812,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) " \"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" + " \"feeRate\" (numeric, optional, default not set: makes wallet determine the fee) Set a specific fee rate in " + CURRENCY_UNIT + "/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" From d1138e36208dac87a27f415f39c70377636e93f1 Mon Sep 17 00:00:00 2001 From: MeshCollider Date: Tue, 5 Sep 2017 15:02:17 +1200 Subject: [PATCH 151/382] Remove redundant testutil files --- src/Makefile.qttest.include | 6 ++---- src/Makefile.test.include | 2 -- src/qt/test/rpcnestedtests.cpp | 1 - src/test/test_bitcoin.cpp | 4 +--- src/test/testutil.cpp | 15 --------------- src/test/testutil.h | 15 --------------- 6 files changed, 3 insertions(+), 40 deletions(-) delete mode 100644 src/test/testutil.cpp delete mode 100644 src/test/testutil.h diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include index 02f30bc95..ea2ed1747 100644 --- a/src/Makefile.qttest.include +++ b/src/Makefile.qttest.include @@ -25,12 +25,10 @@ TEST_QT_H = \ qt/test/wallettests.h TEST_BITCOIN_CPP = \ - test/test_bitcoin.cpp \ - test/testutil.cpp + test/test_bitcoin.cpp TEST_BITCOIN_H = \ - test/test_bitcoin.h \ - test/testutil.h + test/test_bitcoin.h qt_test_test_bitcoin_qt_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_QT_INCLUDES) \ $(QT_INCLUDES) $(QT_TEST_INCLUDES) $(PROTOBUF_CFLAGS) diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 6415b3d2e..01ab0134f 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -74,8 +74,6 @@ BITCOIN_TESTS =\ test/test_bitcoin.cpp \ test/test_bitcoin.h \ test/test_bitcoin_main.cpp \ - test/testutil.cpp \ - test/testutil.h \ test/timedata_tests.cpp \ test/torcontrol_tests.cpp \ test/transaction_tests.cpp \ diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp index cd9ab2345..cd48f9feb 100644 --- a/src/qt/test/rpcnestedtests.cpp +++ b/src/qt/test/rpcnestedtests.cpp @@ -11,7 +11,6 @@ #include "rpc/register.h" #include "rpc/server.h" #include "rpcconsole.h" -#include "test/testutil.h" #include "test/test_bitcoin.h" #include "univalue.h" #include "util.h" diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 94ec7c03f..194f62ca1 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -22,8 +22,6 @@ #include "rpc/register.h" #include "script/sigcache.h" -#include "test/testutil.h" - #include uint256 insecure_rand_seed = GetRandHash(); @@ -61,7 +59,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha RegisterAllCoreRPCCommands(tableRPC); ClearDatadirCache(); - pathTemp = GetTempPath() / strprintf("test_bitcoin_%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(100000))); + pathTemp = fs::temp_directory_path() / strprintf("test_bitcoin_%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(100000))); fs::create_directories(pathTemp); gArgs.ForceSetArg("-datadir", pathTemp.string()); diff --git a/src/test/testutil.cpp b/src/test/testutil.cpp deleted file mode 100644 index 591d0bf30..000000000 --- a/src/test/testutil.cpp +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2009-2016 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "testutil.h" - -#ifdef WIN32 -#include -#endif - -#include "fs.h" - -fs::path GetTempPath() { - return fs::temp_directory_path(); -} diff --git a/src/test/testutil.h b/src/test/testutil.h deleted file mode 100644 index cbe784d64..000000000 --- a/src/test/testutil.h +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2009-2016 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -/** - * Utility functions shared by unit tests - */ -#ifndef BITCOIN_TEST_TESTUTIL_H -#define BITCOIN_TEST_TESTUTIL_H - -#include "fs.h" - -fs::path GetTempPath(); - -#endif // BITCOIN_TEST_TESTUTIL_H From 87fe21f814c94403cba5ff3e304a861d915c70dc Mon Sep 17 00:00:00 2001 From: MeshCollider Date: Tue, 5 Sep 2017 20:48:32 +1200 Subject: [PATCH 152/382] Add translation note to CONTRIBUTING.md --- CONTRIBUTING.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4e67af627..56521a5f7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -81,6 +81,10 @@ Examples: Qt: Add feed bump button Trivial: Fix typo in init.cpp +Note that translations should not be submitted as pull requests, please see +[Translation Process](https://github.com/bitcoin/bitcoin/blob/master/doc/translation_process.md) +for more information on helping with translations. + If a pull request is specifically not to be considered for merging (yet) please prefix the title with [WIP] or use [Tasks Lists](https://help.github.com/articles/basic-writing-and-formatting-syntax/#task-lists) in the body of the pull request to indicate tasks are pending. From b3d6fc654770e3b4d2f82e8d77e531df9e522982 Mon Sep 17 00:00:00 2001 From: Cristian Mircea Messel Date: Tue, 5 Sep 2017 19:55:37 +0300 Subject: [PATCH 153/382] Improve signmessages functional test This patch improves branch coverage of the test, making sure a message can not be verified with the wrong address or signature. --- test/functional/signmessages.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/test/functional/signmessages.py b/test/functional/signmessages.py index 5fdfeceb7..52ba6a5ad 100755 --- a/test/functional/signmessages.py +++ b/test/functional/signmessages.py @@ -5,6 +5,7 @@ """Test RPC commands for signing and verifying messages.""" from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal class SignMessagesTest(BitcoinTestFramework): def set_test_params(self): @@ -14,20 +15,24 @@ def set_test_params(self): def run_test(self): message = 'This is just a test message' - # Test the signing with a privkey - privKey = 'cUeKHd5orzT3mz8P9pxyREHfsWtVfgsfDjiZZBcjUBAaGk1BTj7N' + self.log.info('test signing with priv_key') + priv_key = 'cUeKHd5orzT3mz8P9pxyREHfsWtVfgsfDjiZZBcjUBAaGk1BTj7N' address = 'mpLQjfK79b7CCV4VMJWEWAj5Mpx8Up5zxB' - signature = self.nodes[0].signmessagewithprivkey(privKey, message) - - # Verify the message + expected_signature = 'INbVnW4e6PeRmsv2Qgu8NuopvrVjkcxob+sX8OcZG0SALhWybUjzMLPdAsXI46YZGb0KQTRii+wWIQzRpG/U+S0=' + signature = self.nodes[0].signmessagewithprivkey(priv_key, message) + assert_equal(expected_signature, signature) assert(self.nodes[0].verifymessage(address, signature, message)) - # Test the signing with an address with wallet + self.log.info('test signing with an address with wallet') address = self.nodes[0].getnewaddress() signature = self.nodes[0].signmessage(address, message) - - # Verify the message assert(self.nodes[0].verifymessage(address, signature, message)) + self.log.info('test verifying with another address should not work') + other_address = self.nodes[0].getnewaddress() + other_signature = self.nodes[0].signmessage(other_address, message) + assert(not self.nodes[0].verifymessage(other_address, signature, message)) + assert(not self.nodes[0].verifymessage(address, other_signature, message)) + if __name__ == '__main__': SignMessagesTest().main() From 0311836f6927aec4ba5687ea12af35df3c509682 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Sun, 7 May 2017 14:10:19 -0400 Subject: [PATCH 154/382] Allow setting nMinimumChainWork on command line --- src/init.cpp | 17 +++++++++++++++++ src/net_processing.cpp | 2 +- src/test/util_tests.cpp | 25 +++++++++++++++++++++++++ src/utilstrencodings.cpp | 13 +++++++++++++ src/utilstrencodings.h | 6 ++++++ src/validation.cpp | 7 +++---- src/validation.h | 3 +++ 7 files changed, 68 insertions(+), 5 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 6114bbaa2..6cacb9c8d 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -354,6 +354,9 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-maxorphantx=", strprintf(_("Keep at most unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS)); strUsage += HelpMessageOpt("-maxmempool=", strprintf(_("Keep the transaction memory pool below megabytes (default: %u)"), DEFAULT_MAX_MEMPOOL_SIZE)); strUsage += HelpMessageOpt("-mempoolexpiry=", strprintf(_("Do not keep transactions in the mempool longer than hours (default: %u)"), DEFAULT_MEMPOOL_EXPIRY)); + if (showDebug) { + strUsage += HelpMessageOpt("-minimumchainwork=", strprintf("Minimum work assumed to exist on a valid chain in hex (default: %s, testnet: %s)", defaultChainParams->GetConsensus().nMinimumChainWork.GetHex(), testnetChainParams->GetConsensus().nMinimumChainWork.GetHex())); + } strUsage += HelpMessageOpt("-persistmempool", strprintf(_("Whether to save the mempool on shutdown and load on restart (default: %u)"), DEFAULT_PERSIST_MEMPOOL)); strUsage += HelpMessageOpt("-blockreconstructionextratxn=", strprintf(_("Extra transactions to keep in memory for compact block reconstructions (default: %u)"), DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN)); strUsage += HelpMessageOpt("-par=", strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"), @@ -970,6 +973,20 @@ bool AppInitParameterInteraction() else LogPrintf("Validating signatures for all blocks.\n"); + if (gArgs.IsArgSet("-minimumchainwork")) { + const std::string minChainWorkStr = gArgs.GetArg("-minimumchainwork", ""); + if (!IsHexNumber(minChainWorkStr)) { + return InitError(strprintf("Invalid non-hex (%s) minimum chain work value specified", minChainWorkStr)); + } + nMinimumChainWork = UintToArith256(uint256S(minChainWorkStr)); + } else { + nMinimumChainWork = UintToArith256(chainparams.GetConsensus().nMinimumChainWork); + } + LogPrintf("Setting nMinimumChainWork=%s\n", nMinimumChainWork.GetHex()); + if (nMinimumChainWork < UintToArith256(chainparams.GetConsensus().nMinimumChainWork)) { + LogPrintf("Warning: nMinimumChainWork set below default value of %s\n", chainparams.GetConsensus().nMinimumChainWork.GetHex()); + } + // mempool limits int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; int64_t nMempoolSizeMin = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000 * 40; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 596ae1139..3ee4e5596 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -466,7 +466,7 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vectorpindexBestKnownBlock == nullptr || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork || state->pindexBestKnownBlock->nChainWork < UintToArith256(consensusParams.nMinimumChainWork)) { + if (state->pindexBestKnownBlock == nullptr || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork || state->pindexBestKnownBlock->nChainWork < nMinimumChainWork) { // This peer has nothing interesting. return; } diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 567908696..6ec544290 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -253,6 +253,31 @@ BOOST_AUTO_TEST_CASE(util_IsHex) BOOST_CHECK(!IsHex("0x0000")); } +BOOST_AUTO_TEST_CASE(util_IsHexNumber) +{ + BOOST_CHECK(IsHexNumber("0x0")); + BOOST_CHECK(IsHexNumber("0")); + BOOST_CHECK(IsHexNumber("0x10")); + BOOST_CHECK(IsHexNumber("10")); + BOOST_CHECK(IsHexNumber("0xff")); + BOOST_CHECK(IsHexNumber("ff")); + BOOST_CHECK(IsHexNumber("0xFfa")); + BOOST_CHECK(IsHexNumber("Ffa")); + BOOST_CHECK(IsHexNumber("0x00112233445566778899aabbccddeeffAABBCCDDEEFF")); + BOOST_CHECK(IsHexNumber("00112233445566778899aabbccddeeffAABBCCDDEEFF")); + + BOOST_CHECK(!IsHexNumber("")); // empty string not allowed + BOOST_CHECK(!IsHexNumber("0x")); // empty string after prefix not allowed + BOOST_CHECK(!IsHexNumber("0x0 ")); // no spaces at end, + BOOST_CHECK(!IsHexNumber(" 0x0")); // or beginning, + BOOST_CHECK(!IsHexNumber("0x 0")); // or middle, + BOOST_CHECK(!IsHexNumber(" ")); // etc. + BOOST_CHECK(!IsHexNumber("0x0ga")); // invalid character + BOOST_CHECK(!IsHexNumber("x0")); // broken prefix + BOOST_CHECK(!IsHexNumber("0x0x00")); // two prefixes not allowed + +} + BOOST_AUTO_TEST_CASE(util_seed_insecure_rand) { SeedInsecureRand(true); diff --git a/src/utilstrencodings.cpp b/src/utilstrencodings.cpp index fd233f675..741680e93 100644 --- a/src/utilstrencodings.cpp +++ b/src/utilstrencodings.cpp @@ -65,6 +65,19 @@ bool IsHex(const std::string& str) return (str.size() > 0) && (str.size()%2 == 0); } +bool IsHexNumber(const std::string& str) +{ + size_t starting_location = 0; + if (str.size() > 2 && *str.begin() == '0' && *(str.begin()+1) == 'x') { + starting_location = 2; + } + for (auto c : str.substr(starting_location)) { + if (HexDigit(c) < 0) return false; + } + // Return false for empty string or "0x". + return (str.size() > starting_location); +} + std::vector ParseHex(const char* psz) { // convert hex dump to vector diff --git a/src/utilstrencodings.h b/src/utilstrencodings.h index 53da60e8f..192f33fb2 100644 --- a/src/utilstrencodings.h +++ b/src/utilstrencodings.h @@ -38,7 +38,13 @@ std::string SanitizeString(const std::string& str, int rule = SAFE_CHARS_DEFAULT std::vector ParseHex(const char* psz); std::vector ParseHex(const std::string& str); signed char HexDigit(char c); +/* Returns true if each character in str is a hex character, and has an even + * number of hex digits.*/ bool IsHex(const std::string& str); +/** +* Return true if the string is a hex number, optionally prefixed with "0x" +*/ +bool IsHexNumber(const std::string& str); std::vector DecodeBase64(const char* p, bool* pfInvalid = nullptr); std::string DecodeBase64(const std::string& str); std::string EncodeBase64(const unsigned char* pch, size_t len); diff --git a/src/validation.cpp b/src/validation.cpp index 3b9636839..0edc9bc32 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -83,6 +83,7 @@ int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE; bool fEnableReplacement = DEFAULT_ENABLE_REPLACEMENT; uint256 hashAssumeValid; +arith_uint256 nMinimumChainWork; CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE); CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE; @@ -1035,8 +1036,6 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams) bool IsInitialBlockDownload() { - const CChainParams& chainParams = Params(); - // Once this function has returned false, it must remain false. static std::atomic latchToFalse{false}; // Optimization: pre-test latch before taking the lock. @@ -1050,7 +1049,7 @@ bool IsInitialBlockDownload() return true; if (chainActive.Tip() == nullptr) return true; - if (chainActive.Tip()->nChainWork < UintToArith256(chainParams.GetConsensus().nMinimumChainWork)) + if (chainActive.Tip()->nChainWork < nMinimumChainWork) return true; if (chainActive.Tip()->GetBlockTime() < (GetTime() - nMaxTipAge)) return true; @@ -1670,7 +1669,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd if (it != mapBlockIndex.end()) { if (it->second->GetAncestor(pindex->nHeight) == pindex && pindexBestHeader->GetAncestor(pindex->nHeight) == pindex && - pindexBestHeader->nChainWork >= UintToArith256(chainparams.GetConsensus().nMinimumChainWork)) { + pindexBestHeader->nChainWork >= nMinimumChainWork) { // This block is a member of the assumed verified chain and an ancestor of the best header. // The equivalent time check discourages hash power from extorting the network via DOS attack // into accepting an invalid block through telling users they must manually set assumevalid. diff --git a/src/validation.h b/src/validation.h index d0f6cdc13..214d29173 100644 --- a/src/validation.h +++ b/src/validation.h @@ -186,6 +186,9 @@ extern bool fEnableReplacement; /** Block hash whose ancestors we will assume to have valid scripts without checking them. */ extern uint256 hashAssumeValid; +/** Minimum work we will assume exists on some valid chain. */ +extern arith_uint256 nMinimumChainWork; + /** Best header we've seen so far (used for getheaders queries' starting points). */ extern CBlockIndex *pindexBestHeader; From eac64bb7a3b6aba747403b23b3b1d3609843f8db Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Mon, 8 May 2017 09:59:00 -0400 Subject: [PATCH 155/382] [qa] Test nMinimumChainWork Nodes don't consider themselves out of "initial block download" until their active chain has more work than nMinimumChainWork. While in initial block download, nodes won't relay blocks to their peers, so test that this parameter functions as intended by verifying that block relay only succeeds past a given node once its nMinimumChainWork has been exceeded. --- test/functional/minchainwork.py | 81 +++++++++++++++++++++++++++++++++ test/functional/test_runner.py | 1 + 2 files changed, 82 insertions(+) create mode 100755 test/functional/minchainwork.py diff --git a/test/functional/minchainwork.py b/test/functional/minchainwork.py new file mode 100755 index 000000000..c7579d254 --- /dev/null +++ b/test/functional/minchainwork.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test logic for setting nMinimumChainWork on command line. + +Nodes don't consider themselves out of "initial block download" until +their active chain has more work than nMinimumChainWork. + +Nodes don't download blocks from a peer unless the peer's best known block +has more work than nMinimumChainWork. + +While in initial block download, nodes won't relay blocks to their peers, so +test that this parameter functions as intended by verifying that block relay +only succeeds past a given node once its nMinimumChainWork has been exceeded. +""" + +import time + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import sync_blocks, connect_nodes, assert_equal + +# 2 hashes required per regtest block (with no difficulty adjustment) +REGTEST_WORK_PER_BLOCK = 2 + +class MinimumChainWorkTest(BitcoinTestFramework): + def set_test_params(self): + self.setup_clean_chain = True + self.num_nodes = 3 + self.extra_args = [[], ["-minimumchainwork=0x65"], ["-minimumchainwork=0x65"]] + self.node_min_work = [0, 101, 101] + + def setup_network(self): + # This test relies on the chain setup being: + # node0 <- node1 <- node2 + # Before leaving IBD, nodes prefer to download blocks from outbound + # peers, so ensure that we're mining on an outbound peer and testing + # block relay to inbound peers. + self.setup_nodes() + for i in range(self.num_nodes-1): + connect_nodes(self.nodes[i+1], i) + + def run_test(self): + # Start building a chain on node0. node2 shouldn't be able to sync until node1's + # minchainwork is exceeded + starting_chain_work = REGTEST_WORK_PER_BLOCK # Genesis block's work + self.log.info("Testing relay across node %d (minChainWork = %d)", 1, self.node_min_work[1]) + + starting_blockcount = self.nodes[2].getblockcount() + + num_blocks_to_generate = int((self.node_min_work[1] - starting_chain_work) / REGTEST_WORK_PER_BLOCK) + self.log.info("Generating %d blocks on node0", num_blocks_to_generate) + hashes = self.nodes[0].generate(num_blocks_to_generate) + + self.log.info("Node0 current chain work: %s", self.nodes[0].getblockheader(hashes[-1])['chainwork']) + + # Sleep a few seconds and verify that node2 didn't get any new blocks + # or headers. We sleep, rather than sync_blocks(node0, node1) because + # it's reasonable either way for node1 to get the blocks, or not get + # them (since they're below node1's minchainwork). + time.sleep(3) + + self.log.info("Verifying node 2 has no more blocks than before") + self.log.info("Blockcounts: %s", [n.getblockcount() for n in self.nodes]) + # Node2 shouldn't have any new headers yet, because node1 should not + # have relayed anything. + assert_equal(len(self.nodes[2].getchaintips()), 1) + assert_equal(self.nodes[2].getchaintips()[0]['height'], 0) + + assert self.nodes[1].getbestblockhash() != self.nodes[0].getbestblockhash() + assert_equal(self.nodes[2].getblockcount(), starting_blockcount) + + self.log.info("Generating one more block") + self.nodes[0].generate(1) + + self.log.info("Verifying nodes are all synced") + self.sync_all() + self.log.info("Blockcounts: %s", [n.getblockcount() for n in self.nodes]) + +if __name__ == '__main__': + MinimumChainWorkTest().main() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 59c236ce1..8dbe6247e 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -121,6 +121,7 @@ 'bip65-cltv-p2p.py', 'uptime.py', 'resendwallettransactions.py', + 'minchainwork.py', ] EXTENDED_SCRIPTS = [ From dabee00ef1a7a2857c3318e898d3f63f79853048 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 5 Sep 2017 23:32:09 +0200 Subject: [PATCH 156/382] github-merge: Coalesce git fetches Fetch the destination branch as well as PR in one go. Saves a few seconds (as well as one ssh authentication, when using a yubikey) when using github-merge.py. --- contrib/devtools/github-merge.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/contrib/devtools/github-merge.py b/contrib/devtools/github-merge.py index 4b1bae610..2941d2cb6 100755 --- a/contrib/devtools/github-merge.py +++ b/contrib/devtools/github-merge.py @@ -197,9 +197,10 @@ def main(): print("ERROR: Cannot check out branch %s." % (branch), file=stderr) sys.exit(3) try: - subprocess.check_call([GIT,'fetch','-q',host_repo,'+refs/pull/'+pull+'/*:refs/heads/pull/'+pull+'/*']) + subprocess.check_call([GIT,'fetch','-q',host_repo,'+refs/pull/'+pull+'/*:refs/heads/pull/'+pull+'/*', + '+refs/heads/'+branch+':refs/heads/'+base_branch]) except subprocess.CalledProcessError as e: - print("ERROR: Cannot find pull request #%s on %s." % (pull,host_repo), file=stderr) + print("ERROR: Cannot find pull request #%s or branch %s on %s." % (pull,branch,host_repo), file=stderr) sys.exit(3) try: subprocess.check_call([GIT,'log','-q','-1','refs/heads/'+head_branch], stdout=devnull, stderr=stdout) @@ -211,11 +212,6 @@ def main(): except subprocess.CalledProcessError as e: print("ERROR: Cannot find merge of pull request #%s on %s." % (pull,host_repo), file=stderr) sys.exit(3) - try: - subprocess.check_call([GIT,'fetch','-q',host_repo,'+refs/heads/'+branch+':refs/heads/'+base_branch]) - except subprocess.CalledProcessError as e: - print("ERROR: Cannot find branch %s on %s." % (branch,host_repo), file=stderr) - sys.exit(3) subprocess.check_call([GIT,'checkout','-q',base_branch]) subprocess.call([GIT,'branch','-q','-D',local_merge_branch], stderr=devnull) subprocess.check_call([GIT,'checkout','-q','-b',local_merge_branch]) From a1ea1cfbd8d4fc976f0ab2423d395e03ded6eedd Mon Sep 17 00:00:00 2001 From: Chris Moore Date: Wed, 30 Aug 2017 00:32:54 -0700 Subject: [PATCH 157/382] qt: Use IsMine to validate custom change address (cherry picked from commit c41224dfd51c896341bbf2fa23e160bf5ffe27c3) --- src/qt/sendcoinsdialog.cpp | 10 ++++------ src/qt/walletmodel.cpp | 4 ++-- src/qt/walletmodel.h | 2 +- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index a01886c3e..c59111abd 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -789,10 +789,8 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text) } else // Valid address { - CKeyID keyid; - addr.GetKeyID(keyid); - if (!model->havePrivKey(keyid)) // Unknown change address - { + const CTxDestination dest = addr.Get(); + if (!model->IsSpendable(dest)) { ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address")); // confirmation dialog @@ -800,7 +798,7 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text) QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel); if(btnRetVal == QMessageBox::Yes) - CoinControlDialog::coinControl->destChange = addr.Get(); + CoinControlDialog::coinControl->destChange = dest; else { ui->lineEditCoinControlChange->setText(""); @@ -819,7 +817,7 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text) else ui->labelCoinControlChangeLabel->setText(tr("(no label)")); - CoinControlDialog::coinControl->destChange = addr.Get(); + CoinControlDialog::coinControl->destChange = dest; } } } diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index ba0e1da0c..5b63af617 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -561,9 +561,9 @@ bool WalletModel::getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const return wallet->GetPubKey(address, vchPubKeyOut); } -bool WalletModel::havePrivKey(const CKeyID &address) const +bool WalletModel::IsSpendable(const CTxDestination& dest) const { - return wallet->HaveKey(address); + return IsMine(*wallet, dest) & ISMINE_SPENDABLE; } bool WalletModel::getPrivKey(const CKeyID &address, CKey& vchPrivKeyOut) const diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 6be36a57e..05733f827 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -190,7 +190,7 @@ class WalletModel : public QObject UnlockContext requestUnlock(); bool getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const; - bool havePrivKey(const CKeyID &address) const; + bool IsSpendable(const CTxDestination& dest) const; bool getPrivKey(const CKeyID &address, CKey& vchPrivKeyOut) const; void getOutputs(const std::vector& vOutpoints, std::vector& vOutputs); bool isSpent(const COutPoint& outpoint) const; From 3b69a08c53c2ef738f32c270fdacf1e4ed5a4c97 Mon Sep 17 00:00:00 2001 From: MeshCollider Date: Wed, 6 Sep 2017 10:49:36 +1200 Subject: [PATCH 158/382] Fix division by zero in time remaining --- src/qt/modaloverlay.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/qt/modaloverlay.cpp b/src/qt/modaloverlay.cpp index a83f28503..4f357e297 100644 --- a/src/qt/modaloverlay.cpp +++ b/src/qt/modaloverlay.cpp @@ -99,15 +99,18 @@ void ModalOverlay::tipUpdate(int count, const QDateTime& blockDate, double nVeri progressDelta = progressStart-sample.second; timeDelta = blockProcessTime[0].first - sample.first; progressPerHour = progressDelta/(double)timeDelta*1000*3600; - remainingMSecs = remainingProgress / progressDelta * timeDelta; + remainingMSecs = (progressDelta > 0) ? remainingProgress / progressDelta * timeDelta : -1; break; } } // show progress increase per hour ui->progressIncreasePerH->setText(QString::number(progressPerHour*100, 'f', 2)+"%"); - // show expected remaining time - ui->expectedTimeLeft->setText(GUIUtil::formatNiceTimeOffset(remainingMSecs/1000.0)); + if(remainingMSecs >= 0) { + ui->expectedTimeLeft->setText(GUIUtil::formatNiceTimeOffset(remainingMSecs / 1000.0)); + } else { + ui->expectedTimeLeft->setText(QObject::tr("unknown")); + } static const int MAX_SAMPLES = 5000; if (blockProcessTime.count() > MAX_SAMPLES) @@ -169,4 +172,4 @@ void ModalOverlay::closeClicked() { showHide(true); userClosed = true; -} +} \ No newline at end of file From c8d38abd65d920ec28c68020bf910cfa79216bd5 Mon Sep 17 00:00:00 2001 From: MeshCollider Date: Wed, 6 Sep 2017 10:50:05 +1200 Subject: [PATCH 159/382] Refactor tipUpdate as per style guide --- src/qt/modaloverlay.cpp | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/qt/modaloverlay.cpp b/src/qt/modaloverlay.cpp index 4f357e297..e32a0bdda 100644 --- a/src/qt/modaloverlay.cpp +++ b/src/qt/modaloverlay.cpp @@ -82,30 +82,28 @@ void ModalOverlay::tipUpdate(int count, const QDateTime& blockDate, double nVeri blockProcessTime.push_front(qMakePair(currentDate.toMSecsSinceEpoch(), nVerificationProgress)); // show progress speed if we have more then one sample - if (blockProcessTime.size() >= 2) - { - double progressStart = blockProcessTime[0].second; + if (blockProcessTime.size() >= 2) { double progressDelta = 0; double progressPerHour = 0; qint64 timeDelta = 0; qint64 remainingMSecs = 0; double remainingProgress = 1.0 - nVerificationProgress; - for (int i = 1; i < blockProcessTime.size(); i++) - { + for (int i = 1; i < blockProcessTime.size(); i++) { QPair sample = blockProcessTime[i]; // take first sample after 500 seconds or last available one if (sample.first < (currentDate.toMSecsSinceEpoch() - 500 * 1000) || i == blockProcessTime.size() - 1) { - progressDelta = progressStart-sample.second; + progressDelta = blockProcessTime[0].second - sample.second; timeDelta = blockProcessTime[0].first - sample.first; - progressPerHour = progressDelta/(double)timeDelta*1000*3600; + progressPerHour = progressDelta / (double) timeDelta * 1000 * 3600; remainingMSecs = (progressDelta > 0) ? remainingProgress / progressDelta * timeDelta : -1; break; } } // show progress increase per hour - ui->progressIncreasePerH->setText(QString::number(progressPerHour*100, 'f', 2)+"%"); + ui->progressIncreasePerH->setText(QString::number(progressPerHour * 100, 'f', 2)+"%"); + // show expected remaining time if(remainingMSecs >= 0) { ui->expectedTimeLeft->setText(GUIUtil::formatNiceTimeOffset(remainingMSecs / 1000.0)); } else { @@ -113,8 +111,9 @@ void ModalOverlay::tipUpdate(int count, const QDateTime& blockDate, double nVeri } static const int MAX_SAMPLES = 5000; - if (blockProcessTime.count() > MAX_SAMPLES) - blockProcessTime.remove(MAX_SAMPLES, blockProcessTime.count()-MAX_SAMPLES); + if (blockProcessTime.count() > MAX_SAMPLES) { + blockProcessTime.remove(MAX_SAMPLES, blockProcessTime.count() - MAX_SAMPLES); + } } // show the last block date @@ -172,4 +171,4 @@ void ModalOverlay::closeClicked() { showHide(true); userClosed = true; -} \ No newline at end of file +} From b86a42077a134888c53bfd406b87bf0a39c78264 Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Tue, 5 Sep 2017 17:32:06 -0700 Subject: [PATCH 160/382] when clearing addrman clear mapInfo and mapAddr --- src/addrman.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/addrman.h b/src/addrman.h index 547088aed..18f306228 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -472,6 +472,8 @@ class CAddrMan nTried = 0; nNew = 0; nLastGood = 1; //Initially at 1 so that "never" is strictly worse. + mapInfo.clear(); + mapAddr.clear(); } CAddrMan() From 3a4401a6b899694d25b5ac89c8068e45711bab36 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Tue, 29 Aug 2017 20:06:30 +0200 Subject: [PATCH 161/382] [Qt] Terminate string *pszExePath after readlink and without using memset --- src/qt/guiutil.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index f3c5daebe..6d3ee5cb2 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -743,9 +743,10 @@ bool SetStartOnSystemStartup(bool fAutoStart) else { char pszExePath[MAX_PATH+1]; - memset(pszExePath, 0, sizeof(pszExePath)); - if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1) + ssize_t r = readlink("/proc/self/exe", pszExePath, sizeof(pszExePath) - 1); + if (r == -1) return false; + pszExePath[r] = '\0'; fs::create_directories(GetAutostartDir()); From 467cbbcbfc8876ae0955aaf82f2af83c352ad27f Mon Sep 17 00:00:00 2001 From: Lawrence Nahum Date: Mon, 21 Aug 2017 13:22:23 +0200 Subject: [PATCH 162/382] Add return value to DumpMempool --- src/validation.cpp | 6 ++++-- src/validation.h | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/validation.cpp b/src/validation.cpp index 3b9636839..ca0d6a871 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -4332,7 +4332,7 @@ bool LoadMempool(void) return true; } -void DumpMempool(void) +bool DumpMempool(void) { int64_t start = GetTimeMicros(); @@ -4352,7 +4352,7 @@ void DumpMempool(void) try { FILE* filestr = fsbridge::fopen(GetDataDir() / "mempool.dat.new", "wb"); if (!filestr) { - return; + return false; } CAutoFile file(filestr, SER_DISK, CLIENT_VERSION); @@ -4376,7 +4376,9 @@ void DumpMempool(void) LogPrintf("Dumped mempool: %gs to copy, %gs to dump\n", (mid-start)*MICRO, (last-mid)*MICRO); } catch (const std::exception& e) { LogPrintf("Failed to dump mempool: %s. Continuing anyway.\n", e.what()); + return false; } + return true; } //! Guess how far we are in the verification process at the given block index diff --git a/src/validation.h b/src/validation.h index d0f6cdc13..d7f7b99ef 100644 --- a/src/validation.h +++ b/src/validation.h @@ -475,7 +475,7 @@ static const unsigned int REJECT_HIGHFEE = 0x100; CBlockFileInfo* GetBlockFileInfo(size_t n); /** Dump the mempool to disk. */ -void DumpMempool(); +bool DumpMempool(); /** Load the mempool from disk. */ bool LoadMempool(); From 1aa97ee088ea03dd208be054c5ad9198c1f13329 Mon Sep 17 00:00:00 2001 From: Lawrence Nahum Date: Mon, 21 Aug 2017 13:23:18 +0200 Subject: [PATCH 163/382] Add savemempool RPC --- src/rpc/blockchain.cpp | 20 ++++++++++++++++++++ test/functional/mempool_persist.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index ef61e5a55..0acaf8aed 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1532,6 +1532,25 @@ UniValue getchaintxstats(const JSONRPCRequest& request) return ret; } +UniValue savemempool(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 0) { + throw std::runtime_error( + "savemempool\n" + "\nDumps the mempool to disk.\n" + "\nExamples:\n" + + HelpExampleCli("savemempool", "") + + HelpExampleRpc("savemempool", "") + ); + } + + if (!DumpMempool()) { + throw JSONRPCError(RPC_MISC_ERROR, "Unable to dump mempool to disk"); + } + + return NullUniValue; +} + static const CRPCCommand commands[] = { // category name actor (function) argNames // --------------------- ------------------------ ----------------------- ---------- @@ -1552,6 +1571,7 @@ static const CRPCCommand commands[] = { "blockchain", "gettxout", &gettxout, {"txid","n","include_mempool"} }, { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, {} }, { "blockchain", "pruneblockchain", &pruneblockchain, {"height"} }, + { "blockchain", "savemempool", &savemempool, {} }, { "blockchain", "verifychain", &verifychain, {"checklevel","nblocks"} }, { "blockchain", "preciousblock", &preciousblock, {"blockhash"} }, diff --git a/test/functional/mempool_persist.py b/test/functional/mempool_persist.py index 01f65b137..149a01870 100755 --- a/test/functional/mempool_persist.py +++ b/test/functional/mempool_persist.py @@ -28,8 +28,14 @@ - Restart node0 with -persistmempool. Verify that it has 5 transactions in its mempool. This tests that -persistmempool=0 does not overwrite a previously valid mempool stored on disk. + - Remove node0 mempool.dat and verify savemempool RPC recreates it + and verify that node1 can load it and has 5 transaction in its + mempool. + - Verify that savemempool throws when the RPC is called if + node1 can't write to disk. """ +import os import time from test_framework.test_framework import BitcoinTestFramework @@ -78,5 +84,27 @@ def run_test(self): self.start_node(0) wait_until(lambda: len(self.nodes[0].getrawmempool()) == 5) + mempooldat0 = os.path.join(self.options.tmpdir, 'node0', 'regtest', 'mempool.dat') + mempooldat1 = os.path.join(self.options.tmpdir, 'node1', 'regtest', 'mempool.dat') + self.log.debug("Remove the mempool.dat file. Verify that savemempool to disk via RPC re-creates it") + os.remove(mempooldat0) + self.nodes[0].savemempool() + assert os.path.isfile(mempooldat0) + + self.log.debug("Stop nodes, make node1 use mempool.dat from node0. Verify it has 5 transactions") + os.rename(mempooldat0, mempooldat1) + self.stop_nodes() + self.start_node(1, extra_args=[]) + wait_until(lambda: len(self.nodes[1].getrawmempool()) == 5) + + self.log.debug("Prevent bitcoind from writing mempool.dat to disk. Verify that `savemempool` fails") + # to test the exception we are setting bad permissions on a tmp file called mempool.dat.new + # which is an implementation detail that could change and break this test + mempooldotnew1 = mempooldat1 + '.new' + with os.fdopen(os.open(mempooldotnew1, os.O_CREAT, 0o000), 'w'): + pass + assert_raises_jsonrpc(-1, "Unable to dump mempool to disk", self.nodes[1].savemempool) + os.remove(mempooldotnew1) + if __name__ == '__main__': MempoolPersistTest().main() From 478d4fb79c878c560ed9179b24b48e884a3b0c13 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Wed, 6 Sep 2017 09:53:27 +0200 Subject: [PATCH 164/382] [docs] explain how to recompile only what bitcoind tests need `make` rebuilds the entire project. This is quite slow if e.g. you're making changes to one file and only wish to run the bitcoind tests. This commit adds an instruction to run `make -C src/test` (as opposed to `make src/test` and `make src/test/test_bitcoin`). --- src/test/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/test/README.md b/src/test/README.md index eeb04c6ff..dbaa9c27f 100644 --- a/src/test/README.md +++ b/src/test/README.md @@ -5,7 +5,10 @@ and tests weren't explicitly disabled. After configuring, they can be run with `make check`. -To run the bitcoind tests manually, launch `src/test/test_bitcoin`. +To run the bitcoind tests manually, launch `src/test/test_bitcoin`. To recompile +after a test file was modified, run `make` and then run the test again. If you +modify a non-test file, use `make -C src/test` to recompile only what's needed +to run the bitcoind tests. To add more bitcoind tests, add `BOOST_AUTO_TEST_CASE` functions to the existing .cpp files in the `test/` directory or add new .cpp files that From fdc329376c8a0fa4dffd0cd2599a494a64c38472 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Mon, 28 Aug 2017 09:24:17 +0200 Subject: [PATCH 165/382] Document assumptions that are being made to avoid NULL pointer dereferences --- src/qt/walletframe.cpp | 2 ++ src/validation.cpp | 2 ++ src/wallet/wallet.cpp | 1 + 3 files changed, 5 insertions(+) diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp index f3183320f..714a59431 100644 --- a/src/qt/walletframe.cpp +++ b/src/qt/walletframe.cpp @@ -7,6 +7,7 @@ #include "bitcoingui.h" #include "walletview.h" +#include #include #include @@ -69,6 +70,7 @@ bool WalletFrame::setCurrentWallet(const QString& name) WalletView *walletView = mapWalletViews.value(name); walletStack->setCurrentWidget(walletView); + assert(walletView); walletView->updateEncryptionStatus(); return true; } diff --git a/src/validation.cpp b/src/validation.cpp index 3b9636839..939921d67 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1710,6 +1710,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd // before the first had been spent. Since those coinbases are sufficiently buried its no longer possible to create further // duplicate transactions descending from the known pairs either. // If we're on the known chain at height greater than where BIP34 activated, we can save the db accesses needed for the BIP30 check. + assert(pindex->pprev); CBlockIndex *pindexBIP34height = pindex->pprev->GetAncestor(chainparams.GetConsensus().BIP34Height); //Only continue to enforce if we're below BIP34 activation height or the block hash at that height doesn't correspond. fEnforceBIP30 = fEnforceBIP30 && (!pindexBIP34height || !(pindexBIP34height->GetBlockHash() == chainparams.GetConsensus().BIP34Hash)); @@ -1849,6 +1850,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd if (!pblocktree->WriteTxIndex(vPos)) return AbortNode(state, "Failed to write transaction index"); + assert(pindex->phashBlock); // add this block to the view's block chain view.SetBestBlock(pindex->GetBlockHash()); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index d1d2060b0..36d6ce000 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -519,6 +519,7 @@ void CWallet::SyncMetaData(std::pair ran const uint256& hash = it->second; CWalletTx* copyTo = &mapWallet[hash]; if (copyFrom == copyTo) continue; + assert(copyFrom && "Oldest wallet transaction in range assumed to have been found."); if (!copyFrom->IsEquivalentTo(*copyTo)) continue; copyTo->mapValue = copyFrom->mapValue; copyTo->vOrderForm = copyFrom->vOrderForm; From faa8d9581affde35a8242c63fca8a52004d4f943 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Wed, 16 Aug 2017 08:52:24 -0700 Subject: [PATCH 166/382] [qa] TestNode: Add wait_until_stopped helper method --- test/functional/blockchain.py | 4 +-- test/functional/fundrawtransaction.py | 2 +- .../test_framework/test_framework.py | 8 ++--- test/functional/test_framework/test_node.py | 30 +++++++++++-------- test/functional/wallet-encryption.py | 2 +- 5 files changed, 24 insertions(+), 22 deletions(-) diff --git a/test/functional/blockchain.py b/test/functional/blockchain.py index 5d04de994..50be9262e 100755 --- a/test/functional/blockchain.py +++ b/test/functional/blockchain.py @@ -21,7 +21,7 @@ import http.client import subprocess -from test_framework.test_framework import (BitcoinTestFramework, BITCOIND_PROC_WAIT_TIMEOUT) +from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, assert_raises, @@ -141,7 +141,7 @@ def _test_stopatheight(self): except (ConnectionError, http.client.BadStatusLine): pass # The node already shut down before response self.log.debug('Node should stop at this height...') - self.nodes[0].process.wait(timeout=BITCOIND_PROC_WAIT_TIMEOUT) + self.nodes[0].wait_until_stopped() self.start_node(0) assert_equal(self.nodes[0].getblockcount(), 207) diff --git a/test/functional/fundrawtransaction.py b/test/functional/fundrawtransaction.py index 75a0dc5f9..71e88009b 100755 --- a/test/functional/fundrawtransaction.py +++ b/test/functional/fundrawtransaction.py @@ -4,7 +4,7 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test the fundrawtransaction RPC.""" -from test_framework.test_framework import BitcoinTestFramework, BITCOIND_PROC_WAIT_TIMEOUT +from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 103651f17..a53eb5179 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -43,8 +43,6 @@ class TestStatus(Enum): TEST_EXIT_FAILED = 1 TEST_EXIT_SKIPPED = 77 -BITCOIND_PROC_WAIT_TIMEOUT = 60 - class BitcoinTestFramework(object): """Base class for a bitcoin test script. @@ -263,8 +261,7 @@ def start_nodes(self, extra_args=None): def stop_node(self, i): """Stop a bitcoind test node""" self.nodes[i].stop_node() - while not self.nodes[i].is_node_stopped(): - time.sleep(0.1) + self.nodes[i].wait_until_stopped() def stop_nodes(self): """Stop multiple bitcoind test nodes""" @@ -274,8 +271,7 @@ def stop_nodes(self): for node in self.nodes: # Wait for nodes to stop - while not node.is_node_stopped(): - time.sleep(0.1) + node.wait_until_stopped() def assert_start_raises_init_error(self, i, extra_args=None, expected_msg=None): with tempfile.SpooledTemporaryFile(max_size=2**16) as log_stderr: diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index efb3ac9d1..f58a372a1 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -17,9 +17,12 @@ assert_equal, get_rpc_proxy, rpc_url, + wait_until, ) from .authproxy import JSONRPCException +BITCOIND_PROC_WAIT_TIMEOUT = 60 + class TestNode(): """A class for representing a bitcoind node under test. @@ -125,14 +128,20 @@ def is_node_stopped(self): if not self.running: return True return_code = self.process.poll() - if return_code is not None: - # process has stopped. Assert that it didn't return an error code. - assert_equal(return_code, 0) - self.running = False - self.process = None - self.log.debug("Node stopped") - return True - return False + if return_code is None: + return False + + # process has stopped. Assert that it didn't return an error code. + assert_equal(return_code, 0) + self.running = False + self.process = None + self.rpc_connected = False + self.rpc = None + self.log.debug("Node stopped") + return True + + def wait_until_stopped(self, timeout=BITCOIND_PROC_WAIT_TIMEOUT): + wait_until(self.is_node_stopped, timeout=timeout) def node_encrypt_wallet(self, passphrase): """"Encrypts the wallet. @@ -140,10 +149,7 @@ def node_encrypt_wallet(self, passphrase): This causes bitcoind to shutdown, so this method takes care of cleaning up resources.""" self.encryptwallet(passphrase) - while not self.is_node_stopped(): - time.sleep(0.1) - self.rpc = None - self.rpc_connected = False + self.wait_until_stopped() class TestNodeCLI(): """Interface to bitcoin-cli for an individual node""" diff --git a/test/functional/wallet-encryption.py b/test/functional/wallet-encryption.py index f63bb2ea5..ce1e7744e 100755 --- a/test/functional/wallet-encryption.py +++ b/test/functional/wallet-encryption.py @@ -6,7 +6,7 @@ import time -from test_framework.test_framework import BitcoinTestFramework, BITCOIND_PROC_WAIT_TIMEOUT +from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, assert_raises_jsonrpc, From 5c8ff0d448ffdc6340b195ddfa2128d5f21a839b Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 22 Aug 2017 18:02:33 -0700 Subject: [PATCH 167/382] Introduce wrappers around CBitcoinAddress This patch removes the need for the intermediary Base58 type CBitcoinAddress, by providing {Encode,Decode,IsValid}Destination function that directly operate on the conversion between strings and CTxDestination. --- src/base58.cpp | 22 ++++ src/base58.h | 5 + src/bitcoin-tx.cpp | 25 ++--- src/core_write.cpp | 5 +- src/qt/addresstablemodel.cpp | 22 ++-- src/qt/bitcoinaddressvalidator.cpp | 4 +- src/qt/coincontroldialog.cpp | 2 +- src/qt/guiutil.cpp | 5 +- src/qt/paymentserver.cpp | 15 ++- src/qt/sendcoinsdialog.cpp | 5 +- src/qt/signverifymessagedialog.cpp | 24 ++--- src/qt/test/wallettests.cpp | 8 +- src/qt/transactiondesc.cpp | 11 +- src/qt/transactionrecord.cpp | 4 +- src/qt/walletmodel.cpp | 13 ++- src/rpc/mining.cpp | 7 +- src/rpc/misc.cpp | 48 ++++----- src/rpc/rawtransaction.cpp | 19 ++-- src/script/standard.cpp | 4 + src/script/standard.h | 5 +- src/test/base58_tests.cpp | 32 +++--- src/test/key_tests.cpp | 27 +++-- src/wallet/rpcdump.cpp | 83 ++++++++------- src/wallet/rpcwallet.cpp | 159 +++++++++++++++-------------- src/wallet/wallet.cpp | 16 +-- src/wallet/walletdb.cpp | 6 +- 26 files changed, 299 insertions(+), 277 deletions(-) diff --git a/src/base58.cpp b/src/base58.cpp index 3802f953f..69692c95e 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -318,3 +318,25 @@ bool CBitcoinSecret::SetString(const std::string& strSecret) { return SetString(strSecret.c_str()); } + +std::string EncodeDestination(const CTxDestination& dest) +{ + CBitcoinAddress addr(dest); + if (!addr.IsValid()) return ""; + return addr.ToString(); +} + +CTxDestination DecodeDestination(const std::string& str) +{ + return CBitcoinAddress(str).Get(); +} + +bool IsValidDestinationString(const std::string& str, const CChainParams& params) +{ + return CBitcoinAddress(str).IsValid(params); +} + +bool IsValidDestinationString(const std::string& str) +{ + return CBitcoinAddress(str).IsValid(); +} diff --git a/src/base58.h b/src/base58.h index db12208f7..0a0ae6ae2 100644 --- a/src/base58.h +++ b/src/base58.h @@ -167,4 +167,9 @@ template class CBitcoinExtK typedef CBitcoinExtKeyBase CBitcoinExtKey; typedef CBitcoinExtKeyBase CBitcoinExtPubKey; +std::string EncodeDestination(const CTxDestination& dest); +CTxDestination DecodeDestination(const std::string& str); +bool IsValidDestinationString(const std::string& str); +bool IsValidDestinationString(const std::string& str, const CChainParams& params); + #endif // BITCOIN_BASE58_H diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index cff90d13a..d8d7934bf 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -271,11 +271,11 @@ static void MutateTxAddOutAddr(CMutableTransaction& tx, const std::string& strIn // extract and validate ADDRESS std::string strAddr = vStrInputParts[1]; - CBitcoinAddress addr(strAddr); - if (!addr.IsValid()) + CTxDestination destination = DecodeDestination(strAddr); + if (!IsValidDestination(destination)) { throw std::runtime_error("invalid TX output address"); - // build standard output script via GetScriptForDestination() - CScript scriptPubKey = GetScriptForDestination(addr.Get()); + } + CScript scriptPubKey = GetScriptForDestination(destination); // construct TxOut, append to transaction output list CTxOut txout(value, scriptPubKey); @@ -314,10 +314,8 @@ static void MutateTxAddOutPubKey(CMutableTransaction& tx, const std::string& str 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()); + // Get the ID for the script, and then construct a P2SH destination for it. + scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey)); } // construct TxOut, append to transaction output list @@ -381,10 +379,8 @@ static void MutateTxAddOutMultiSig(CMutableTransaction& tx, const std::string& s 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()); + // Get the ID for the script, and then construct a P2SH destination for it. + scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey)); } // construct TxOut, append to transaction output list @@ -444,11 +440,10 @@ static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& str } if (bSegWit) { - scriptPubKey = GetScriptForWitness(scriptPubKey); + scriptPubKey = GetScriptForWitness(scriptPubKey); } if (bScriptHash) { - CBitcoinAddress addr(scriptPubKey); - scriptPubKey = GetScriptForDestination(addr.Get()); + scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey)); } // construct TxOut, append to transaction output list diff --git a/src/core_write.cpp b/src/core_write.cpp index 1431fa0c9..e16db1365 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -148,8 +148,9 @@ void ScriptPubKeyToUniv(const CScript& scriptPubKey, out.pushKV("type", GetTxnOutputType(type)); UniValue a(UniValue::VARR); - for (const CTxDestination& addr : addresses) - a.push_back(CBitcoinAddress(addr).ToString()); + for (const CTxDestination& addr : addresses) { + a.push_back(EncodeDestination(addr)); + } out.pushKV("addresses", a); } diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index 2fa032abd..0eb7ec430 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -82,14 +82,14 @@ class AddressTablePriv LOCK(wallet->cs_wallet); for (const std::pair& item : wallet->mapAddressBook) { - const CBitcoinAddress& address = item.first; - bool fMine = IsMine(*wallet, address.Get()); + const CTxDestination& address = item.first; + bool fMine = IsMine(*wallet, address); AddressTableEntry::Type addressType = translateTransactionType( QString::fromStdString(item.second.purpose), fMine); const std::string& strName = item.second.name; cachedAddressTable.append(AddressTableEntry(addressType, QString::fromStdString(strName), - QString::fromStdString(address.ToString()))); + QString::fromStdString(EncodeDestination(address)))); } } // qLowerBound() and qUpperBound() require our cachedAddressTable list to be sorted in asc order @@ -246,7 +246,7 @@ bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, if(role == Qt::EditRole) { LOCK(wallet->cs_wallet); /* For SetAddressBook / DelAddressBook */ - CTxDestination curAddress = CBitcoinAddress(rec->address.toStdString()).Get(); + CTxDestination curAddress = DecodeDestination(rec->address.toStdString()); if(index.column() == Label) { // Do nothing, if old label == new label @@ -257,7 +257,7 @@ bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, } wallet->SetAddressBook(curAddress, value.toString().toStdString(), strPurpose); } else if(index.column() == Address) { - CTxDestination newAddress = CBitcoinAddress(value.toString().toStdString()).Get(); + CTxDestination newAddress = DecodeDestination(value.toString().toStdString()); // Refuse to set invalid address, set error status and return false if(boost::get(&newAddress)) { @@ -358,7 +358,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con // Check for duplicate addresses { LOCK(wallet->cs_wallet); - if(wallet->mapAddressBook.count(CBitcoinAddress(strAddress).Get())) + if(wallet->mapAddressBook.count(DecodeDestination(strAddress))) { editStatus = DUPLICATE_ADDRESS; return QString(); @@ -384,7 +384,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con return QString(); } } - strAddress = CBitcoinAddress(newKey.GetID()).ToString(); + strAddress = EncodeDestination(newKey.GetID()); } else { @@ -394,7 +394,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con // Add entry { LOCK(wallet->cs_wallet); - wallet->SetAddressBook(CBitcoinAddress(strAddress).Get(), strLabel, + wallet->SetAddressBook(DecodeDestination(strAddress), strLabel, (type == Send ? "send" : "receive")); } return QString::fromStdString(strAddress); @@ -412,7 +412,7 @@ bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent } { LOCK(wallet->cs_wallet); - wallet->DelAddressBook(CBitcoinAddress(rec->address.toStdString()).Get()); + wallet->DelAddressBook(DecodeDestination(rec->address.toStdString())); } return true; } @@ -423,8 +423,8 @@ QString AddressTableModel::labelForAddress(const QString &address) const { { LOCK(wallet->cs_wallet); - CBitcoinAddress address_parsed(address.toStdString()); - std::map::iterator mi = wallet->mapAddressBook.find(address_parsed.Get()); + CTxDestination destination = DecodeDestination(address.toStdString()); + std::map::iterator mi = wallet->mapAddressBook.find(destination); if (mi != wallet->mapAddressBook.end()) { return QString::fromStdString(mi->second.name); diff --git a/src/qt/bitcoinaddressvalidator.cpp b/src/qt/bitcoinaddressvalidator.cpp index d712705c4..4dd109280 100644 --- a/src/qt/bitcoinaddressvalidator.cpp +++ b/src/qt/bitcoinaddressvalidator.cpp @@ -89,9 +89,9 @@ QValidator::State BitcoinAddressCheckValidator::validate(QString &input, int &po { Q_UNUSED(pos); // Validate the passed Bitcoin address - CBitcoinAddress addr(input.toStdString()); - if (addr.IsValid()) + if (IsValidDestinationString(input.toStdString())) { return QValidator::Acceptable; + } return QValidator::Invalid; } diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 562c36179..3ca43eae2 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -660,7 +660,7 @@ void CoinControlDialog::updateView() QString sAddress = ""; if(ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey, outputAddress)) { - sAddress = QString::fromStdString(CBitcoinAddress(outputAddress).ToString()); + sAddress = QString::fromStdString(EncodeDestination(outputAddress)); // if listMode or change => show bitcoin address. In tree mode, address is not shown again for direct wallet address outputs if (!treeMode || (!(sAddress == sWalletAddress))) diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index f3c5daebe..ea5343c19 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -112,8 +112,9 @@ static std::string DummyAddress(const CChainParams ¶ms) sourcedata.insert(sourcedata.end(), dummydata, dummydata + sizeof(dummydata)); for(int i=0; i<256; ++i) { // Try every trailing byte std::string s = EncodeBase58(sourcedata.data(), sourcedata.data() + sourcedata.size()); - if (!CBitcoinAddress(s).IsValid()) + if (!IsValidDestinationString(s)) { return s; + } sourcedata[sourcedata.size()-1] += 1; } return ""; @@ -248,7 +249,7 @@ QString formatBitcoinURI(const SendCoinsRecipient &info) bool isDust(const QString& address, const CAmount& amount) { - CTxDestination dest = CBitcoinAddress(address.toStdString()).Get(); + CTxDestination dest = DecodeDestination(address.toStdString()); CScript script = GetScriptForDestination(dest); CTxOut txOut(amount, script); return IsDust(txOut, ::dustRelayFee); diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index d4137d280..169684cf6 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -218,17 +218,15 @@ void PaymentServer::ipcParseCommandLine(int argc, char* argv[]) SendCoinsRecipient r; if (GUIUtil::parseBitcoinURI(arg, &r) && !r.address.isEmpty()) { - CBitcoinAddress address(r.address.toStdString()); auto tempChainParams = CreateChainParams(CBaseChainParams::MAIN); - if (address.IsValid(*tempChainParams)) - { + if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) { SelectParams(CBaseChainParams::MAIN); - } - else { + } else { tempChainParams = CreateChainParams(CBaseChainParams::TESTNET); - if (address.IsValid(*tempChainParams)) + if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) { SelectParams(CBaseChainParams::TESTNET); + } } } } @@ -441,8 +439,7 @@ void PaymentServer::handleURIOrFile(const QString& s) SendCoinsRecipient recipient; if (GUIUtil::parseBitcoinURI(s, &recipient)) { - CBitcoinAddress address(recipient.address.toStdString()); - if (!address.IsValid()) { + if (!IsValidDestinationString(recipient.address.toStdString())) { Q_EMIT message(tr("URI handling"), tr("Invalid payment address %1").arg(recipient.address), CClientUIInterface::MSG_ERROR); } @@ -560,7 +557,7 @@ bool PaymentServer::processPaymentRequest(const PaymentRequestPlus& request, Sen CTxDestination dest; if (ExtractDestination(sendingTo.first, dest)) { // Append destination address - addresses.append(QString::fromStdString(CBitcoinAddress(dest).ToString())); + addresses.append(QString::fromStdString(EncodeDestination(dest))); } else if (!recipient.authenticatedMerchant.isEmpty()) { // Unauthenticated payment requests to custom bitcoin addresses are not supported diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index a056e858a..05c5ccbfe 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -777,19 +777,18 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text) CoinControlDialog::coinControl->destChange = CNoDestination(); ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}"); - CBitcoinAddress addr = CBitcoinAddress(text.toStdString()); + const CTxDestination dest = DecodeDestination(text.toStdString()); if (text.isEmpty()) // Nothing entered { ui->labelCoinControlChangeLabel->setText(""); } - else if (!addr.IsValid()) // Invalid address + else if (!IsValidDestination(dest)) // Invalid address { ui->labelCoinControlChangeLabel->setText(tr("Warning: Invalid Bitcoin address")); } else // Valid address { - const CTxDestination dest = addr.Get(); if (!model->IsSpendable(dest)) { ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address")); diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp index 0950ed023..4e37b8165 100644 --- a/src/qt/signverifymessagedialog.cpp +++ b/src/qt/signverifymessagedialog.cpp @@ -117,16 +117,14 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked() /* Clear old signature to ensure users don't get confused on error with an old signature displayed */ ui->signatureOut_SM->clear(); - CBitcoinAddress addr(ui->addressIn_SM->text().toStdString()); - if (!addr.IsValid()) - { + CTxDestination destination = DecodeDestination(ui->addressIn_SM->text().toStdString()); + if (!IsValidDestination(destination)) { ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_SM->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again.")); return; } - CKeyID keyID; - if (!addr.GetKeyID(keyID)) - { + const CKeyID* keyID = boost::get(&destination); + if (!keyID) { ui->addressIn_SM->setValid(false); ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_SM->setText(tr("The entered address does not refer to a key.") + QString(" ") + tr("Please check the address and try again.")); @@ -142,7 +140,7 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked() } CKey key; - if (!model->getPrivKey(keyID, key)) + if (!model->getPrivKey(*keyID, key)) { ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_SM->setText(tr("Private key for the entered address is not available.")); @@ -197,16 +195,13 @@ void SignVerifyMessageDialog::on_addressBookButton_VM_clicked() void SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked() { - CBitcoinAddress addr(ui->addressIn_VM->text().toStdString()); - if (!addr.IsValid()) - { + CTxDestination destination = DecodeDestination(ui->addressIn_VM->text().toStdString()); + if (!IsValidDestination(destination)) { ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_VM->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again.")); return; } - CKeyID keyID; - if (!addr.GetKeyID(keyID)) - { + if (!boost::get(&destination)) { ui->addressIn_VM->setValid(false); ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_VM->setText(tr("The entered address does not refer to a key.") + QString(" ") + tr("Please check the address and try again.")); @@ -237,8 +232,7 @@ void SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked() return; } - if (!(CBitcoinAddress(pubkey.GetID()) == addr)) - { + if (!(CTxDestination(pubkey.GetID()) == destination)) { ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_VM->setText(QString("") + tr("Message verification failed.") + QString("")); return; diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index ff1eb59f1..5031d7651 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -57,11 +57,11 @@ void ConfirmSend(QString* text = nullptr, bool cancel = false) } //! Send coins to address and return txid. -uint256 SendCoins(CWallet& wallet, SendCoinsDialog& sendCoinsDialog, const CBitcoinAddress& address, CAmount amount, bool rbf) +uint256 SendCoins(CWallet& wallet, SendCoinsDialog& sendCoinsDialog, const CTxDestination& address, CAmount amount, bool rbf) { QVBoxLayout* entries = sendCoinsDialog.findChild("entries"); SendCoinsEntry* entry = qobject_cast(entries->itemAt(0)->widget()); - entry->findChild("payTo")->setText(QString::fromStdString(address.ToString())); + entry->findChild("payTo")->setText(QString::fromStdString(EncodeDestination(address))); entry->findChild("payAmount")->setValue(amount); sendCoinsDialog.findChild("frameFee") ->findChild("frameFeeSelection") @@ -172,8 +172,8 @@ void TestSendCoins() // Send two transactions, and verify they are added to transaction list. TransactionTableModel* transactionTableModel = walletModel.getTransactionTableModel(); QCOMPARE(transactionTableModel->rowCount({}), 105); - uint256 txid1 = SendCoins(wallet, sendCoinsDialog, CBitcoinAddress(CKeyID()), 5 * COIN, false /* rbf */); - uint256 txid2 = SendCoins(wallet, sendCoinsDialog, CBitcoinAddress(CKeyID()), 10 * COIN, true /* rbf */); + uint256 txid1 = SendCoins(wallet, sendCoinsDialog, CKeyID(), 5 * COIN, false /* rbf */); + uint256 txid2 = SendCoins(wallet, sendCoinsDialog, CKeyID(), 10 * COIN, true /* rbf */); QCOMPARE(transactionTableModel->rowCount({}), 107); QVERIFY(FindTx(*transactionTableModel, txid1).isValid()); QVERIFY(FindTx(*transactionTableModel, txid2).isValid()); diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index bcacc47ef..f3fe4096a 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -91,9 +91,8 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco if (nNet > 0) { // Credit - if (CBitcoinAddress(rec->address).IsValid()) - { - CTxDestination address = CBitcoinAddress(rec->address).Get(); + if (IsValidDestinationString(rec->address)) { + CTxDestination address = DecodeDestination(rec->address); if (wallet->mapAddressBook.count(address)) { strHTML += "" + tr("From") + ": " + tr("unknown") + "
"; @@ -118,7 +117,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco // Online transaction std::string strAddress = wtx.mapValue["to"]; strHTML += "" + tr("To") + ": "; - CTxDestination dest = CBitcoinAddress(strAddress).Get(); + CTxDestination dest = DecodeDestination(strAddress); if (wallet->mapAddressBook.count(dest) && !wallet->mapAddressBook[dest].name.empty()) strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[dest].name) + " "; strHTML += GUIUtil::HtmlEscape(strAddress) + "
"; @@ -189,7 +188,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco strHTML += "" + tr("To") + ": "; if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].name.empty()) strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " "; - strHTML += GUIUtil::HtmlEscape(CBitcoinAddress(address).ToString()); + strHTML += GUIUtil::HtmlEscape(EncodeDestination(address)); if(toSelf == ISMINE_SPENDABLE) strHTML += " (own address)"; else if(toSelf & ISMINE_WATCH_ONLY) @@ -304,7 +303,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco { if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].name.empty()) strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " "; - strHTML += QString::fromStdString(CBitcoinAddress(address).ToString()); + strHTML += QString::fromStdString(EncodeDestination(address)); } strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatHtmlWithUnit(unit, vout.nValue); strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) & ISMINE_SPENDABLE ? tr("true") : tr("false")) + ""; diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 36d98ce49..d40ffd22c 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -55,7 +55,7 @@ QList TransactionRecord::decomposeTransaction(const CWallet * { // Received by Bitcoin Address sub.type = TransactionRecord::RecvWithAddress; - sub.address = CBitcoinAddress(address).ToString(); + sub.address = EncodeDestination(address); } else { @@ -127,7 +127,7 @@ QList TransactionRecord::decomposeTransaction(const CWallet * { // Sent to Bitcoin Address sub.type = TransactionRecord::SendToAddress; - sub.address = CBitcoinAddress(address).ToString(); + sub.address = EncodeDestination(address); } else { diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index a3f243a25..53b1c2967 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -188,8 +188,7 @@ void WalletModel::updateWatchOnlyFlag(bool fHaveWatchonly) bool WalletModel::validateAddress(const QString &address) { - CBitcoinAddress addressParsed(address.toStdString()); - return addressParsed.IsValid(); + return IsValidDestinationString(address.toStdString()); } WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl& coinControl) @@ -247,7 +246,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact setAddress.insert(rcp.address); ++nAddresses; - CScript scriptPubKey = GetScriptForDestination(CBitcoinAddress(rcp.address.toStdString()).Get()); + CScript scriptPubKey = GetScriptForDestination(DecodeDestination(rcp.address.toStdString())); CRecipient recipient = {scriptPubKey, rcp.amount, rcp.fSubtractFeeFromAmount}; vecSend.push_back(recipient); @@ -348,7 +347,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran if (!rcp.paymentRequest.IsInitialized()) { std::string strAddress = rcp.address.toStdString(); - CTxDestination dest = CBitcoinAddress(strAddress).Get(); + CTxDestination dest = DecodeDestination(strAddress); std::string strLabel = rcp.label.toStdString(); { LOCK(wallet->cs_wallet); @@ -464,7 +463,7 @@ static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet, const CTxDestination &address, const std::string &label, bool isMine, const std::string &purpose, ChangeType status) { - QString strAddress = QString::fromStdString(CBitcoinAddress(address).ToString()); + QString strAddress = QString::fromStdString(EncodeDestination(address)); QString strLabel = QString::fromStdString(label); QString strPurpose = QString::fromStdString(purpose); @@ -596,7 +595,7 @@ bool WalletModel::isSpent(const COutPoint& outpoint) const void WalletModel::listCoins(std::map >& mapCoins) const { for (auto& group : wallet->ListCoins()) { - auto& resultGroup = mapCoins[QString::fromStdString(CBitcoinAddress(group.first).ToString())]; + auto& resultGroup = mapCoins[QString::fromStdString(EncodeDestination(group.first))]; for (auto& coin : group.second) { resultGroup.emplace_back(std::move(coin)); } @@ -634,7 +633,7 @@ void WalletModel::loadReceiveRequests(std::vector& vReceiveRequests bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest) { - CTxDestination dest = CBitcoinAddress(sAddress).Get(); + CTxDestination dest = DecodeDestination(sAddress); std::stringstream ss; ss << nId; diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 73860f375..980988370 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -176,12 +176,13 @@ UniValue generatetoaddress(const JSONRPCRequest& request) nMaxTries = request.params[2].get_int(); } - CBitcoinAddress address(request.params[1].get_str()); - if (!address.IsValid()) + CTxDestination destination = DecodeDestination(request.params[1].get_str()); + if (!IsValidDestination(destination)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Error: Invalid address"); + } std::shared_ptr coinbaseScript = std::make_shared(); - coinbaseScript->reserveScript = GetScriptForDestination(address.Get()); + coinbaseScript->reserveScript = GetScriptForDestination(destination); return generateBlocks(coinbaseScript, nGenerate, nMaxTries, false); } diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 76eefabd2..91e3b05be 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -152,8 +152,9 @@ class DescribeAddressVisitor : public boost::static_visitor obj.push_back(Pair("script", GetTxnOutputType(whichType))); obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end()))); UniValue a(UniValue::VARR); - for (const CTxDestination& addr : addresses) - a.push_back(CBitcoinAddress(addr).ToString()); + for (const CTxDestination& addr : addresses) { + a.push_back(EncodeDestination(addr)); + } obj.push_back(Pair("addresses", a)); if (whichType == TX_MULTISIG) obj.push_back(Pair("sigsrequired", nRequired)); @@ -207,15 +208,14 @@ UniValue validateaddress(const JSONRPCRequest& request) LOCK(cs_main); #endif - CBitcoinAddress address(request.params[0].get_str()); - bool isValid = address.IsValid(); + CTxDestination dest = DecodeDestination(request.params[0].get_str()); + bool isValid = IsValidDestination(dest); UniValue ret(UniValue::VOBJ); ret.push_back(Pair("isvalid", isValid)); if (isValid) { - CTxDestination dest = address.Get(); - std::string currentAddress = address.ToString(); + std::string currentAddress = EncodeDestination(dest); ret.push_back(Pair("address", currentAddress)); CScript scriptPubKey = GetScriptForDestination(dest); @@ -230,10 +230,10 @@ UniValue validateaddress(const JSONRPCRequest& request) if (pwallet && pwallet->mapAddressBook.count(dest)) { ret.push_back(Pair("account", pwallet->mapAddressBook[dest].name)); } - CKeyID keyID; if (pwallet) { const auto& meta = pwallet->mapKeyMetadata; - auto it = address.GetKeyID(keyID) ? meta.find(keyID) : meta.end(); + const CKeyID *keyID = boost::get(&dest); + auto it = keyID ? meta.find(*keyID) : meta.end(); if (it == meta.end()) { it = meta.find(CScriptID(scriptPubKey)); } @@ -277,16 +277,15 @@ CScript _createmultisig_redeemScript(CWallet * const pwallet, const UniValue& pa const std::string& ks = keys[i].get_str(); #ifdef ENABLE_WALLET // Case 1: Bitcoin address and we have full public key: - CBitcoinAddress address(ks); - if (pwallet && address.IsValid()) { - CKeyID keyID; - if (!address.GetKeyID(keyID)) - throw std::runtime_error( - strprintf("%s does not refer to a key",ks)); + CTxDestination dest = DecodeDestination(ks); + if (pwallet && IsValidDestination(dest)) { + const CKeyID *keyID = boost::get(&dest); + if (!keyID) { + throw std::runtime_error(strprintf("%s does not refer to a key", ks)); + } CPubKey vchPubKey; - if (!pwallet->GetPubKey(keyID, vchPubKey)) { - throw std::runtime_error( - strprintf("no full public key for address %s",ks)); + if (!pwallet->GetPubKey(*keyID, vchPubKey)) { + throw std::runtime_error(strprintf("no full public key for address %s", ks)); } if (!vchPubKey.IsFullyValid()) throw std::runtime_error(" Invalid public key: "+ks); @@ -357,10 +356,9 @@ UniValue createmultisig(const JSONRPCRequest& request) // Construct using pay-to-script-hash: CScript inner = _createmultisig_redeemScript(pwallet, request.params); CScriptID innerID(inner); - CBitcoinAddress address(innerID); UniValue result(UniValue::VOBJ); - result.push_back(Pair("address", address.ToString())); + result.push_back(Pair("address", EncodeDestination(innerID))); result.push_back(Pair("redeemScript", HexStr(inner.begin(), inner.end()))); return result; @@ -395,13 +393,15 @@ UniValue verifymessage(const JSONRPCRequest& request) std::string strSign = request.params[1].get_str(); std::string strMessage = request.params[2].get_str(); - CBitcoinAddress addr(strAddress); - if (!addr.IsValid()) + CTxDestination destination = DecodeDestination(strAddress); + if (!IsValidDestination(destination)) { throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); + } - CKeyID keyID; - if (!addr.GetKeyID(keyID)) + const CKeyID *keyID = boost::get(&destination); + if (!keyID) { throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); + } bool fInvalid = false; std::vector vchSig = DecodeBase64(strSign.c_str(), &fInvalid); @@ -417,7 +417,7 @@ UniValue verifymessage(const JSONRPCRequest& request) if (!pubkey.RecoverCompact(ss.GetHash(), vchSig)) return false; - return (pubkey.GetID() == keyID); + return (pubkey.GetID() == *keyID); } UniValue signmessagewithprivkey(const JSONRPCRequest& request) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 27daefaf5..a0322f67b 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -384,7 +384,7 @@ UniValue createrawtransaction(const JSONRPCRequest& request) rawTx.vin.push_back(in); } - std::set setAddress; + std::set destinations; std::vector addrList = sendTo.getKeys(); for (const std::string& name_ : addrList) { @@ -394,15 +394,16 @@ UniValue createrawtransaction(const JSONRPCRequest& request) CTxOut out(0, CScript() << OP_RETURN << data); rawTx.vout.push_back(out); } else { - CBitcoinAddress address(name_); - if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ")+name_); + CTxDestination destination = DecodeDestination(name_); + if (!IsValidDestination(destination)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + name_); + } - if (setAddress.count(address)) - throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ")+name_); - setAddress.insert(address); + if (!destinations.insert(destination).second) { + throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_); + } - CScript scriptPubKey = GetScriptForDestination(address.Get()); + CScript scriptPubKey = GetScriptForDestination(destination); CAmount nAmount = AmountFromValue(sendTo[name_]); CTxOut out(nAmount, scriptPubKey); @@ -529,7 +530,7 @@ UniValue decodescript(const JSONRPCRequest& request) if (type.isStr() && type.get_str() != "scripthash") { // P2SH cannot be wrapped in a P2SH. If this script is already a P2SH, // don't return the address for a P2SH of the P2SH. - r.push_back(Pair("p2sh", CBitcoinAddress(CScriptID(script)).ToString())); + r.push_back(Pair("p2sh", EncodeDestination(CScriptID(script)))); } return r; diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 2aed39392..b6e2232ab 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -317,3 +317,7 @@ CScript GetScriptForWitness(const CScript& redeemscript) ret << OP_0 << ToByteVector(hash); return ret; } + +bool IsValidDestination(const CTxDestination& dest) { + return dest.which() != 0; +} diff --git a/src/script/standard.h b/src/script/standard.h index 761919226..8df143a3a 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -77,10 +77,13 @@ class CNoDestination { * * CNoDestination: no destination set * * CKeyID: TX_PUBKEYHASH destination * * CScriptID: TX_SCRIPTHASH destination - * A CTxDestination is the internal data type encoded in a CBitcoinAddress + * A CTxDestination is the internal data type encoded in a bitcoin address */ typedef boost::variant CTxDestination; +/** Check whether a CTxDestination is a CNoDestination. */ +bool IsValidDestination(const CTxDestination& dest); + /** Get the name of a txnouttype as a C string, or nullptr if unknown. */ const char* GetTxnOutputType(txnouttype t); diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index ee633249e..4829590c5 100644 --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -121,7 +121,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) { UniValue tests = read_json(std::string(json_tests::base58_keys_valid, json_tests::base58_keys_valid + sizeof(json_tests::base58_keys_valid))); CBitcoinSecret secret; - CBitcoinAddress addr; + CTxDestination destination; SelectParams(CBaseChainParams::MAIN); for (unsigned int idx = 0; idx < tests.size(); idx++) { @@ -145,7 +145,6 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) { bool isCompressed = find_value(metadata, "isCompressed").get_bool(); // Must be valid private key - // Note: CBitcoinSecret::SetString tests isValid, whereas CBitcoinAddress does not! BOOST_CHECK_MESSAGE(secret.SetString(exp_base58string), "!SetString:"+ strTest); BOOST_CHECK_MESSAGE(secret.IsValid(), "!IsValid:" + strTest); CKey privkey = secret.GetKey(); @@ -153,18 +152,17 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) BOOST_CHECK_MESSAGE(privkey.size() == exp_payload.size() && std::equal(privkey.begin(), privkey.end(), exp_payload.begin()), "key mismatch:" + strTest); // Private key must be invalid public key - addr.SetString(exp_base58string); - BOOST_CHECK_MESSAGE(!addr.IsValid(), "IsValid privkey as pubkey:" + strTest); + destination = DecodeDestination(exp_base58string); + BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid privkey as pubkey:" + strTest); } else { std::string exp_addrType = find_value(metadata, "addrType").get_str(); // "script" or "pubkey" // Must be valid public key - BOOST_CHECK_MESSAGE(addr.SetString(exp_base58string), "SetString:" + strTest); - BOOST_CHECK_MESSAGE(addr.IsValid(), "!IsValid:" + strTest); - BOOST_CHECK_MESSAGE(addr.IsScript() == (exp_addrType == "script"), "isScript mismatch" + strTest); - CTxDestination dest = addr.Get(); - BOOST_CHECK_MESSAGE(boost::apply_visitor(TestAddrTypeVisitor(exp_addrType), dest), "addrType mismatch" + strTest); + destination = DecodeDestination(exp_base58string); + BOOST_CHECK_MESSAGE(IsValidDestination(destination), "!IsValid:" + strTest); + BOOST_CHECK_MESSAGE((boost::get(&destination) != nullptr) == (exp_addrType == "script"), "isScript mismatch" + strTest); + BOOST_CHECK_MESSAGE(boost::apply_visitor(TestAddrTypeVisitor(exp_addrType), destination), "addrType mismatch" + strTest); // Public key must be invalid private key secret.SetString(exp_base58string); @@ -226,17 +224,11 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen) BOOST_ERROR("Bad addrtype: " << strTest); continue; } - CBitcoinAddress addrOut; - BOOST_CHECK_MESSAGE(addrOut.Set(dest), "encode dest: " + strTest); - BOOST_CHECK_MESSAGE(addrOut.ToString() == exp_base58string, "mismatch: " + strTest); + std::string address = EncodeDestination(dest); + BOOST_CHECK_MESSAGE(address == exp_base58string, "mismatch: " + strTest); } } - // Visiting a CNoDestination must fail - CBitcoinAddress dummyAddr; - CTxDestination nodest = CNoDestination(); - BOOST_CHECK(!dummyAddr.Set(nodest)); - SelectParams(CBaseChainParams::MAIN); } @@ -245,7 +237,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_invalid) { UniValue tests = read_json(std::string(json_tests::base58_keys_invalid, json_tests::base58_keys_invalid + sizeof(json_tests::base58_keys_invalid))); // Negative testcases CBitcoinSecret secret; - CBitcoinAddress addr; + CTxDestination destination; for (unsigned int idx = 0; idx < tests.size(); idx++) { UniValue test = tests[idx]; @@ -258,8 +250,8 @@ BOOST_AUTO_TEST_CASE(base58_keys_invalid) std::string exp_base58string = test[0].get_str(); // must be invalid as public and as private key - addr.SetString(exp_base58string); - BOOST_CHECK_MESSAGE(!addr.IsValid(), "IsValid pubkey:" + strTest); + destination = DecodeDestination(exp_base58string); + BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid pubkey:" + strTest); secret.SetString(exp_base58string); BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid privkey:" + strTest); } diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp index 559b3caf1..91c017541 100644 --- a/src/test/key_tests.cpp +++ b/src/test/key_tests.cpp @@ -16,17 +16,16 @@ #include -static const std::string strSecret1 ("5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj"); -static const std::string strSecret2 ("5KC4ejrDjv152FGwP386VD1i2NYc5KkfSMyv1nGy1VGDxGHqVY3"); -static const std::string strSecret1C ("Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw"); -static const std::string strSecret2C ("L3Hq7a8FEQwJkW1M2GNKDW28546Vp5miewcCzSqUD9kCAXrJdS3g"); -static const CBitcoinAddress addr1 ("1QFqqMUD55ZV3PJEJZtaKCsQmjLT6JkjvJ"); -static const CBitcoinAddress addr2 ("1F5y5E5FMc5YzdJtB9hLaUe43GDxEKXENJ"); -static const CBitcoinAddress addr1C("1NoJrossxPBKfCHuJXT4HadJrXRE9Fxiqs"); -static const CBitcoinAddress addr2C("1CRj2HyM1CXWzHAXLQtiGLyggNT9WQqsDs"); +static const std::string strSecret1 = "5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj"; +static const std::string strSecret2 = "5KC4ejrDjv152FGwP386VD1i2NYc5KkfSMyv1nGy1VGDxGHqVY3"; +static const std::string strSecret1C = "Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw"; +static const std::string strSecret2C = "L3Hq7a8FEQwJkW1M2GNKDW28546Vp5miewcCzSqUD9kCAXrJdS3g"; +static const std::string addr1 = "1QFqqMUD55ZV3PJEJZtaKCsQmjLT6JkjvJ"; +static const std::string addr2 = "1F5y5E5FMc5YzdJtB9hLaUe43GDxEKXENJ"; +static const std::string addr1C = "1NoJrossxPBKfCHuJXT4HadJrXRE9Fxiqs"; +static const std::string addr2C = "1CRj2HyM1CXWzHAXLQtiGLyggNT9WQqsDs"; - -static const std::string strAddressBad("1HV9Lc3sNHZxwj4Zk6fB38tEmBryq2cBiF"); +static const std::string strAddressBad = "1HV9Lc3sNHZxwj4Zk6fB38tEmBryq2cBiF"; BOOST_FIXTURE_TEST_SUITE(key_tests, BasicTestingSetup) @@ -74,10 +73,10 @@ BOOST_AUTO_TEST_CASE(key_test1) BOOST_CHECK(!key2C.VerifyPubKey(pubkey2)); BOOST_CHECK(key2C.VerifyPubKey(pubkey2C)); - BOOST_CHECK(addr1.Get() == CTxDestination(pubkey1.GetID())); - BOOST_CHECK(addr2.Get() == CTxDestination(pubkey2.GetID())); - BOOST_CHECK(addr1C.Get() == CTxDestination(pubkey1C.GetID())); - BOOST_CHECK(addr2C.Get() == CTxDestination(pubkey2C.GetID())); + BOOST_CHECK(DecodeDestination(addr1) == CTxDestination(pubkey1.GetID())); + BOOST_CHECK(DecodeDestination(addr2) == CTxDestination(pubkey2.GetID())); + BOOST_CHECK(DecodeDestination(addr1C) == CTxDestination(pubkey1C.GetID())); + BOOST_CHECK(DecodeDestination(addr2C) == CTxDestination(pubkey2C.GetID())); for (int n=0; n<16; n++) { diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index db6f6b48a..9539cc9f4 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -181,7 +181,7 @@ UniValue abortrescan(const JSONRPCRequest& request) return true; } -void ImportAddress(CWallet*, const CBitcoinAddress& address, const std::string& strLabel); +void ImportAddress(CWallet*, const CTxDestination& dest, const std::string& strLabel); void ImportScript(CWallet* const pwallet, const CScript& script, const std::string& strLabel, bool isRedeemScript) { if (!isRedeemScript && ::IsMine(*pwallet, script) == ISMINE_SPENDABLE) { @@ -198,7 +198,7 @@ void ImportScript(CWallet* const pwallet, const CScript& script, const std::stri if (!pwallet->HaveCScript(script) && !pwallet->AddCScript(script)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet"); } - ImportAddress(pwallet, CBitcoinAddress(CScriptID(script)), strLabel); + ImportAddress(pwallet, CScriptID(script), strLabel); } else { CTxDestination destination; if (ExtractDestination(script, destination)) { @@ -207,13 +207,13 @@ void ImportScript(CWallet* const pwallet, const CScript& script, const std::stri } } -void ImportAddress(CWallet* const pwallet, const CBitcoinAddress& address, const std::string& strLabel) +void ImportAddress(CWallet* const pwallet, const CTxDestination& dest, const std::string& strLabel) { - CScript script = GetScriptForDestination(address.Get()); + CScript script = GetScriptForDestination(dest); ImportScript(pwallet, script, strLabel, false); // add to address book or update label - if (address.IsValid()) - pwallet->SetAddressBook(address.Get(), strLabel, "receive"); + if (IsValidDestination(dest)) + pwallet->SetAddressBook(dest, strLabel, "receive"); } UniValue importaddress(const JSONRPCRequest& request) @@ -265,11 +265,12 @@ UniValue importaddress(const JSONRPCRequest& request) LOCK2(cs_main, pwallet->cs_wallet); - CBitcoinAddress address(request.params[0].get_str()); - if (address.IsValid()) { - if (fP2SH) + CTxDestination dest = DecodeDestination(request.params[0].get_str()); + if (IsValidDestination(dest)) { + if (fP2SH) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot use the p2sh flag with an address - use a script instead"); - ImportAddress(pwallet, address, strLabel); + } + ImportAddress(pwallet, dest, strLabel); } else if (IsHex(request.params[0].get_str())) { std::vector data(ParseHex(request.params[0].get_str())); ImportScript(pwallet, CScript(data.begin(), data.end()), strLabel, fP2SH); @@ -432,7 +433,7 @@ UniValue importpubkey(const JSONRPCRequest& request) LOCK2(cs_main, pwallet->cs_wallet); - ImportAddress(pwallet, CBitcoinAddress(pubKey.GetID()), strLabel); + ImportAddress(pwallet, pubKey.GetID(), strLabel); ImportScript(pwallet, GetScriptForRawPubKey(pubKey), strLabel, false); if (fRescan) @@ -506,7 +507,7 @@ UniValue importwallet(const JSONRPCRequest& request) assert(key.VerifyPubKey(pubkey)); CKeyID keyid = pubkey.GetID(); if (pwallet->HaveKey(keyid)) { - LogPrintf("Skipping import of %s (key already present)\n", CBitcoinAddress(keyid).ToString()); + LogPrintf("Skipping import of %s (key already present)\n", EncodeDestination(keyid)); continue; } int64_t nTime = DecodeDumpTime(vstr[1]); @@ -524,7 +525,7 @@ UniValue importwallet(const JSONRPCRequest& request) fLabel = true; } } - LogPrintf("Importing %s...\n", CBitcoinAddress(keyid).ToString()); + LogPrintf("Importing %s...\n", EncodeDestination(keyid)); if (!pwallet->AddKeyPubKey(key, pubkey)) { fGood = false; continue; @@ -573,14 +574,16 @@ UniValue dumpprivkey(const JSONRPCRequest& request) EnsureWalletIsUnlocked(pwallet); std::string strAddress = request.params[0].get_str(); - CBitcoinAddress address; - if (!address.SetString(strAddress)) + CTxDestination dest = DecodeDestination(strAddress); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); - CKeyID keyID; - if (!address.GetKeyID(keyID)) + } + const CKeyID *keyID = boost::get(&dest); + if (!keyID) { throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key"); + } CKey vchSecret; - if (!pwallet->GetKey(keyID, vchSecret)) { + if (!pwallet->GetKey(*keyID, vchSecret)) { throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known"); } return CBitcoinSecret(vchSecret).ToString(); @@ -659,7 +662,7 @@ UniValue dumpwallet(const JSONRPCRequest& request) for (std::vector >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) { const CKeyID &keyid = it->second; std::string strTime = EncodeDumpTime(it->first); - std::string strAddr = CBitcoinAddress(keyid).ToString(); + std::string strAddr = EncodeDestination(keyid); CKey key; if (pwallet->GetKey(keyid, key)) { file << strprintf("%s %s ", CBitcoinSecret(key).ToString(), strTime); @@ -715,14 +718,14 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6 // Parse the output. CScript script; - CBitcoinAddress address; + CTxDestination dest; if (!isScript) { - address = CBitcoinAddress(output); - if (!address.IsValid()) { + dest = DecodeDestination(output); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); } - script = GetScriptForDestination(address.Get()); + script = GetScriptForDestination(dest); } else { if (!IsHex(output)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid scriptPubKey"); @@ -780,8 +783,8 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet"); } - CBitcoinAddress redeemAddress = CBitcoinAddress(CScriptID(redeemScript)); - CScript redeemDestination = GetScriptForDestination(redeemAddress.Get()); + CTxDestination redeem_dest = CScriptID(redeemScript); + CScript redeemDestination = GetScriptForDestination(redeem_dest); if (::IsMine(*pwallet, redeemDestination) == ISMINE_SPENDABLE) { throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script"); @@ -794,8 +797,8 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6 } // add to address book or update label - if (address.IsValid()) { - pwallet->SetAddressBook(address.Get(), label, "receive"); + if (IsValidDestination(dest)) { + pwallet->SetAddressBook(dest, label, "receive"); } // Import private keys. @@ -854,27 +857,25 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key"); } - CBitcoinAddress pubKeyAddress = CBitcoinAddress(pubKey.GetID()); + CTxDestination pubkey_dest = pubKey.GetID(); // Consistency check. - if (!isScript && !(pubKeyAddress.Get() == address.Get())) { + if (!isScript && !(pubkey_dest == dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed"); } // Consistency check. if (isScript) { - CBitcoinAddress scriptAddress; CTxDestination destination; if (ExtractDestination(script, destination)) { - scriptAddress = CBitcoinAddress(destination); - if (!(scriptAddress.Get() == pubKeyAddress.Get())) { + if (!(destination == pubkey_dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed"); } } } - CScript pubKeyScript = GetScriptForDestination(pubKeyAddress.Get()); + CScript pubKeyScript = GetScriptForDestination(pubkey_dest); if (::IsMine(*pwallet, pubKeyScript) == ISMINE_SPENDABLE) { throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script"); @@ -887,8 +888,8 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6 } // add to address book or update label - if (pubKeyAddress.IsValid()) { - pwallet->SetAddressBook(pubKeyAddress.Get(), label, "receive"); + if (IsValidDestination(pubkey_dest)) { + pwallet->SetAddressBook(pubkey_dest, label, "receive"); } // TODO Is this necessary? @@ -927,21 +928,19 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6 CPubKey pubKey = key.GetPubKey(); assert(key.VerifyPubKey(pubKey)); - CBitcoinAddress pubKeyAddress = CBitcoinAddress(pubKey.GetID()); + CTxDestination pubkey_dest = pubKey.GetID(); // Consistency check. - if (!isScript && !(pubKeyAddress.Get() == address.Get())) { + if (!isScript && !(pubkey_dest == dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed"); } // Consistency check. if (isScript) { - CBitcoinAddress scriptAddress; CTxDestination destination; if (ExtractDestination(script, destination)) { - scriptAddress = CBitcoinAddress(destination); - if (!(scriptAddress.Get() == pubKeyAddress.Get())) { + if (!(destination == pubkey_dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed"); } } @@ -980,8 +979,8 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6 if (scriptPubKey.getType() == UniValue::VOBJ) { // add to address book or update label - if (address.IsValid()) { - pwallet->SetAddressBook(address.Get(), label, "receive"); + if (IsValidDestination(dest)) { + pwallet->SetAddressBook(dest, label, "receive"); } } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index e3639e468..637ca04e8 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -170,18 +170,18 @@ UniValue getnewaddress(const JSONRPCRequest& request) pwallet->SetAddressBook(keyID, strAccount, "receive"); - return CBitcoinAddress(keyID).ToString(); + return EncodeDestination(keyID); } -CBitcoinAddress GetAccountAddress(CWallet* const pwallet, std::string strAccount, bool bForceNew=false) +CTxDestination GetAccountAddress(CWallet* const pwallet, std::string strAccount, bool bForceNew=false) { CPubKey pubKey; if (!pwallet->GetAccountPubkey(pubKey, strAccount, bForceNew)) { throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); } - return CBitcoinAddress(pubKey.GetID()); + return pubKey.GetID(); } UniValue getaccountaddress(const JSONRPCRequest& request) @@ -213,7 +213,7 @@ UniValue getaccountaddress(const JSONRPCRequest& request) UniValue ret(UniValue::VSTR); - ret = GetAccountAddress(pwallet, strAccount).ToString(); + ret = EncodeDestination(GetAccountAddress(pwallet, strAccount)); return ret; } @@ -252,7 +252,7 @@ UniValue getrawchangeaddress(const JSONRPCRequest& request) CKeyID keyID = vchPubKey.GetID(); - return CBitcoinAddress(keyID).ToString(); + return EncodeDestination(keyID); } @@ -277,24 +277,25 @@ UniValue setaccount(const JSONRPCRequest& request) LOCK2(cs_main, pwallet->cs_wallet); - CBitcoinAddress address(request.params[0].get_str()); - if (!address.IsValid()) + CTxDestination dest = DecodeDestination(request.params[0].get_str()); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + } std::string strAccount; if (!request.params[1].isNull()) strAccount = AccountFromValue(request.params[1]); // Only add the account if the address is yours. - if (IsMine(*pwallet, address.Get())) { + if (IsMine(*pwallet, dest)) { // Detect when changing the account of an address that is the 'unused current key' of another account: - if (pwallet->mapAddressBook.count(address.Get())) { - std::string strOldAccount = pwallet->mapAddressBook[address.Get()].name; - if (address == GetAccountAddress(pwallet, strOldAccount)) { + if (pwallet->mapAddressBook.count(dest)) { + std::string strOldAccount = pwallet->mapAddressBook[dest].name; + if (dest == GetAccountAddress(pwallet, strOldAccount)) { GetAccountAddress(pwallet, strOldAccount, true); } } - pwallet->SetAddressBook(address.Get(), strAccount, "receive"); + pwallet->SetAddressBook(dest, strAccount, "receive"); } else throw JSONRPCError(RPC_MISC_ERROR, "setaccount can only be used with own address"); @@ -325,12 +326,13 @@ UniValue getaccount(const JSONRPCRequest& request) LOCK2(cs_main, pwallet->cs_wallet); - CBitcoinAddress address(request.params[0].get_str()); - if (!address.IsValid()) + CTxDestination dest = DecodeDestination(request.params[0].get_str()); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + } std::string strAccount; - std::map::iterator mi = pwallet->mapAddressBook.find(address.Get()); + std::map::iterator mi = pwallet->mapAddressBook.find(dest); if (mi != pwallet->mapAddressBook.end() && !(*mi).second.name.empty()) { strAccount = (*mi).second.name; } @@ -367,11 +369,12 @@ UniValue getaddressesbyaccount(const JSONRPCRequest& request) // Find all addresses that have the given account UniValue ret(UniValue::VARR); - for (const std::pair& item : pwallet->mapAddressBook) { - const CBitcoinAddress& address = item.first; + for (const std::pair& item : pwallet->mapAddressBook) { + const CTxDestination& dest = item.first; const std::string& strName = item.second.name; - if (strName == strAccount) - ret.push_back(address.ToString()); + if (strName == strAccount) { + ret.push_back(EncodeDestination(dest)); + } } return ret; } @@ -454,9 +457,10 @@ UniValue sendtoaddress(const JSONRPCRequest& request) ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); - CBitcoinAddress address(request.params[0].get_str()); - if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + CTxDestination dest = DecodeDestination(request.params[0].get_str()); + if (!IsValidDestination(dest)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); + } // Amount CAmount nAmount = AmountFromValue(request.params[1]); @@ -493,7 +497,7 @@ UniValue sendtoaddress(const JSONRPCRequest& request) EnsureWalletIsUnlocked(pwallet); - SendMoney(pwallet, address.Get(), nAmount, fSubtractFeeFromAmount, wtx, coin_control); + SendMoney(pwallet, dest, nAmount, fSubtractFeeFromAmount, wtx, coin_control); return wtx.GetHash().GetHex(); } @@ -533,16 +537,16 @@ UniValue listaddressgroupings(const JSONRPCRequest& request) UniValue jsonGroupings(UniValue::VARR); std::map balances = pwallet->GetAddressBalances(); - for (std::set grouping : pwallet->GetAddressGroupings()) { + for (const std::set& grouping : pwallet->GetAddressGroupings()) { UniValue jsonGrouping(UniValue::VARR); - for (CTxDestination address : grouping) + for (const CTxDestination& address : grouping) { UniValue addressInfo(UniValue::VARR); - addressInfo.push_back(CBitcoinAddress(address).ToString()); + addressInfo.push_back(EncodeDestination(address)); addressInfo.push_back(ValueFromAmount(balances[address])); { - if (pwallet->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwallet->mapAddressBook.end()) { - addressInfo.push_back(pwallet->mapAddressBook.find(CBitcoinAddress(address).Get())->second.name); + if (pwallet->mapAddressBook.find(address) != pwallet->mapAddressBook.end()) { + addressInfo.push_back(pwallet->mapAddressBook.find(address)->second.name); } } jsonGrouping.push_back(addressInfo); @@ -587,16 +591,18 @@ UniValue signmessage(const JSONRPCRequest& request) std::string strAddress = request.params[0].get_str(); std::string strMessage = request.params[1].get_str(); - CBitcoinAddress addr(strAddress); - if (!addr.IsValid()) + CTxDestination dest = DecodeDestination(strAddress); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); + } - CKeyID keyID; - if (!addr.GetKeyID(keyID)) + const CKeyID *keyID = boost::get(&dest); + if (!keyID) { throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); + } CKey key; - if (!pwallet->GetKey(keyID, key)) { + if (!pwallet->GetKey(*keyID, key)) { throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available"); } @@ -642,10 +648,11 @@ UniValue getreceivedbyaddress(const JSONRPCRequest& request) LOCK2(cs_main, pwallet->cs_wallet); // Bitcoin address - CBitcoinAddress address = CBitcoinAddress(request.params[0].get_str()); - if (!address.IsValid()) + CTxDestination dest = DecodeDestination(request.params[0].get_str()); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); - CScript scriptPubKey = GetScriptForDestination(address.Get()); + } + CScript scriptPubKey = GetScriptForDestination(dest); if (!IsMine(*pwallet, scriptPubKey)) { return ValueFromAmount(0); } @@ -915,9 +922,10 @@ UniValue sendfrom(const JSONRPCRequest& request) LOCK2(cs_main, pwallet->cs_wallet); std::string strAccount = AccountFromValue(request.params[0]); - CBitcoinAddress address(request.params[1].get_str()); - if (!address.IsValid()) + CTxDestination dest = DecodeDestination(request.params[1].get_str()); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + } CAmount nAmount = AmountFromValue(request.params[2]); if (nAmount <= 0) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); @@ -940,7 +948,7 @@ UniValue sendfrom(const JSONRPCRequest& request) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); CCoinControl no_coin_control; // This is a deprecated API - SendMoney(pwallet, address.Get(), nAmount, false, wtx, no_coin_control); + SendMoney(pwallet, dest, nAmount, false, wtx, no_coin_control); return wtx.GetHash().GetHex(); } @@ -1032,22 +1040,23 @@ UniValue sendmany(const JSONRPCRequest& request) } } - std::set setAddress; + std::set destinations; std::vector vecSend; CAmount totalAmount = 0; std::vector keys = sendTo.getKeys(); - for (const std::string& name_ : keys) - { - CBitcoinAddress address(name_); - if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ")+name_); + for (const std::string& name_ : keys) { + CTxDestination dest = DecodeDestination(name_); + if (!IsValidDestination(dest)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + name_); + } - if (setAddress.count(address)) - throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ")+name_); - setAddress.insert(address); + if (destinations.count(dest)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_); + } + destinations.insert(dest); - CScript scriptPubKey = GetScriptForDestination(address.Get()); + CScript scriptPubKey = GetScriptForDestination(dest); CAmount nAmount = AmountFromValue(sendTo[name_]); if (nAmount <= 0) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); @@ -1138,7 +1147,7 @@ UniValue addmultisigaddress(const JSONRPCRequest& request) pwallet->AddCScript(inner); pwallet->SetAddressBook(innerID, strAccount, "send"); - return CBitcoinAddress(innerID).ToString(); + return EncodeDestination(innerID); } class Witnessifier : public boost::static_visitor @@ -1226,12 +1235,12 @@ UniValue addwitnessaddress(const JSONRPCRequest& request) } } - CBitcoinAddress address(request.params[0].get_str()); - if (!address.IsValid()) + CTxDestination dest = DecodeDestination(request.params[0].get_str()); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + } Witnessifier w(pwallet); - CTxDestination dest = address.Get(); bool ret = boost::apply_visitor(w, dest); if (!ret) { throw JSONRPCError(RPC_WALLET_ERROR, "Public key or redeemscript not known to wallet, or the key is uncompressed"); @@ -1239,7 +1248,7 @@ UniValue addwitnessaddress(const JSONRPCRequest& request) pwallet->SetAddressBook(w.result, "", "receive"); - return CBitcoinAddress(w.result).ToString(); + return EncodeDestination(w.result); } struct tallyitem @@ -1274,7 +1283,7 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA filter = filter | ISMINE_WATCH_ONLY; // Tally - std::map mapTally; + std::map mapTally; for (const std::pair& pairWtx : pwallet->mapWallet) { const CWalletTx& wtx = pairWtx.second; @@ -1307,10 +1316,10 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA // Reply UniValue ret(UniValue::VARR); std::map mapAccountTally; - for (const std::pair& item : pwallet->mapAddressBook) { - const CBitcoinAddress& address = item.first; + for (const std::pair& item : pwallet->mapAddressBook) { + const CTxDestination& dest = item.first; const std::string& strAccount = item.second.name; - std::map::iterator it = mapTally.find(address); + std::map::iterator it = mapTally.find(dest); if (it == mapTally.end() && !fIncludeEmpty) continue; @@ -1336,7 +1345,7 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA UniValue obj(UniValue::VOBJ); if(fIsWatchonly) obj.push_back(Pair("involvesWatchonly", true)); - obj.push_back(Pair("address", address.ToString())); + obj.push_back(Pair("address", EncodeDestination(dest))); obj.push_back(Pair("account", strAccount)); obj.push_back(Pair("amount", ValueFromAmount(nAmount))); obj.push_back(Pair("confirmations", (nConf == std::numeric_limits::max() ? 0 : nConf))); @@ -1461,9 +1470,9 @@ UniValue listreceivedbyaccount(const JSONRPCRequest& request) static void MaybePushAddress(UniValue & entry, const CTxDestination &dest) { - CBitcoinAddress addr; - if (addr.Set(dest)) - entry.push_back(Pair("address", addr.ToString())); + if (IsValidDestination(dest)) { + entry.push_back(Pair("address", EncodeDestination(dest))); + } } /** @@ -2717,18 +2726,19 @@ UniValue listunspent(const JSONRPCRequest& request) nMaxDepth = request.params[1].get_int(); } - std::set setAddress; + std::set destinations; if (!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]; - CBitcoinAddress address(input.get_str()); - if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ")+input.get_str()); - if (setAddress.count(address)) - throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ")+input.get_str()); - setAddress.insert(address); + CTxDestination dest = DecodeDestination(input.get_str()); + if (!IsValidDestination(dest)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + input.get_str()); + } + if (!destinations.insert(dest).second) { + throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + input.get_str()); + } } } @@ -2770,7 +2780,7 @@ UniValue listunspent(const JSONRPCRequest& request) const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey; bool fValidAddress = ExtractDestination(scriptPubKey, address); - if (setAddress.size() && (!fValidAddress || !setAddress.count(address))) + if (destinations.size() && (!fValidAddress || !destinations.count(address))) continue; UniValue entry(UniValue::VOBJ); @@ -2778,7 +2788,7 @@ UniValue listunspent(const JSONRPCRequest& request) entry.push_back(Pair("vout", out.i)); if (fValidAddress) { - entry.push_back(Pair("address", CBitcoinAddress(address).ToString())); + entry.push_back(Pair("address", EncodeDestination(address))); if (pwallet->mapAddressBook.count(address)) { entry.push_back(Pair("account", pwallet->mapAddressBook[address].name)); @@ -2901,12 +2911,13 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) true, true); if (options.exists("changeAddress")) { - CBitcoinAddress address(options["changeAddress"].get_str()); + CTxDestination dest = DecodeDestination(options["changeAddress"].get_str()); - if (!address.IsValid()) + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "changeAddress must be a valid bitcoin address"); + } - coinControl.destChange = address.Get(); + coinControl.destChange = dest; } if (options.exists("changePosition")) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index d1d2060b0..584324feb 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -307,7 +307,7 @@ bool CWallet::LoadCScript(const CScript& redeemScript) * these. Do not add them to the wallet and warn. */ if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE) { - std::string strAddr = CBitcoinAddress(CScriptID(redeemScript)).ToString(); + std::string strAddr = EncodeDestination(CScriptID(redeemScript)); LogPrintf("%s: Warning: This wallet contains a redeemScript of size %i which exceeds maximum size %i thus can never be redeemed. Do not use address %s.\n", __func__, redeemScript.size(), MAX_SCRIPT_ELEMENT_SIZE, strAddr); return true; @@ -3072,9 +3072,9 @@ bool CWallet::SetAddressBook(const CTxDestination& address, const std::string& s } NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address) != ISMINE_NO, strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) ); - if (!strPurpose.empty() && !CWalletDB(*dbw).WritePurpose(CBitcoinAddress(address).ToString(), strPurpose)) + if (!strPurpose.empty() && !CWalletDB(*dbw).WritePurpose(EncodeDestination(address), strPurpose)) return false; - return CWalletDB(*dbw).WriteName(CBitcoinAddress(address).ToString(), strName); + return CWalletDB(*dbw).WriteName(EncodeDestination(address), strName); } bool CWallet::DelAddressBook(const CTxDestination& address) @@ -3083,7 +3083,7 @@ bool CWallet::DelAddressBook(const CTxDestination& address) LOCK(cs_wallet); // mapAddressBook // Delete destdata tuples associated with address - std::string strAddress = CBitcoinAddress(address).ToString(); + std::string strAddress = EncodeDestination(address); for (const std::pair &item : mapAddressBook[address].destdata) { CWalletDB(*dbw).EraseDestData(strAddress, item.first); @@ -3093,8 +3093,8 @@ bool CWallet::DelAddressBook(const CTxDestination& address) NotifyAddressBookChanged(this, address, "", ::IsMine(*this, address) != ISMINE_NO, "", CT_DELETED); - CWalletDB(*dbw).ErasePurpose(CBitcoinAddress(address).ToString()); - return CWalletDB(*dbw).EraseName(CBitcoinAddress(address).ToString()); + CWalletDB(*dbw).ErasePurpose(EncodeDestination(address)); + return CWalletDB(*dbw).EraseName(EncodeDestination(address)); } const std::string& CWallet::GetAccountName(const CScript& scriptPubKey) const @@ -3711,14 +3711,14 @@ bool CWallet::AddDestData(const CTxDestination &dest, const std::string &key, co return false; mapAddressBook[dest].destdata.insert(std::make_pair(key, value)); - return CWalletDB(*dbw).WriteDestData(CBitcoinAddress(dest).ToString(), key, value); + return CWalletDB(*dbw).WriteDestData(EncodeDestination(dest), key, value); } bool CWallet::EraseDestData(const CTxDestination &dest, const std::string &key) { if (!mapAddressBook[dest].destdata.erase(key)) return false; - return CWalletDB(*dbw).EraseDestData(CBitcoinAddress(dest).ToString(), key); + return CWalletDB(*dbw).EraseDestData(EncodeDestination(dest), key); } bool CWallet::LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value) diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 12da3cce6..52370a8eb 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -253,13 +253,13 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, { std::string strAddress; ssKey >> strAddress; - ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()].name; + ssValue >> pwallet->mapAddressBook[DecodeDestination(strAddress)].name; } else if (strType == "purpose") { std::string strAddress; ssKey >> strAddress; - ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()].purpose; + ssValue >> pwallet->mapAddressBook[DecodeDestination(strAddress)].purpose; } else if (strType == "tx") { @@ -493,7 +493,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, ssKey >> strAddress; ssKey >> strKey; ssValue >> strValue; - if (!pwallet->LoadDestData(CBitcoinAddress(strAddress).Get(), strKey, strValue)) + if (!pwallet->LoadDestData(DecodeDestination(strAddress), strKey, strValue)) { strErr = "Error reading wallet database: LoadDestData failed"; return false; From 864cd278741ab0e17e7d93eedee39417d4797b37 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 22 Aug 2017 23:46:51 -0700 Subject: [PATCH 168/382] Move CBitcoinAddress to base58.cpp --- src/base58.cpp | 24 ++++++++++++++++++++++++ src/base58.h | 24 ------------------------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/base58.cpp b/src/base58.cpp index 69692c95e..e093608aa 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -212,6 +212,30 @@ int CBase58Data::CompareTo(const CBase58Data& b58) const namespace { +/** base58-encoded Bitcoin addresses. + * Public-key-hash-addresses have version 0 (or 111 testnet). + * The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key. + * Script-hash-addresses have version 5 (or 196 testnet). + * The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script. + */ +class CBitcoinAddress : public CBase58Data { +public: + bool Set(const CKeyID &id); + bool Set(const CScriptID &id); + bool Set(const CTxDestination &dest); + bool IsValid() const; + bool IsValid(const CChainParams ¶ms) const; + + CBitcoinAddress() {} + CBitcoinAddress(const CTxDestination &dest) { Set(dest); } + CBitcoinAddress(const std::string& strAddress) { SetString(strAddress); } + CBitcoinAddress(const char* pszAddress) { SetString(pszAddress); } + + CTxDestination Get() const; + bool GetKeyID(CKeyID &keyID) const; + bool IsScript() const; +}; + class CBitcoinAddressVisitor : public boost::static_visitor { private: diff --git a/src/base58.h b/src/base58.h index 0a0ae6ae2..4b895ca02 100644 --- a/src/base58.h +++ b/src/base58.h @@ -95,30 +95,6 @@ class CBase58Data bool operator> (const CBase58Data& b58) const { return CompareTo(b58) > 0; } }; -/** base58-encoded Bitcoin addresses. - * Public-key-hash-addresses have version 0 (or 111 testnet). - * The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key. - * Script-hash-addresses have version 5 (or 196 testnet). - * The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script. - */ -class CBitcoinAddress : public CBase58Data { -public: - bool Set(const CKeyID &id); - bool Set(const CScriptID &id); - bool Set(const CTxDestination &dest); - bool IsValid() const; - bool IsValid(const CChainParams ¶ms) const; - - CBitcoinAddress() {} - CBitcoinAddress(const CTxDestination &dest) { Set(dest); } - CBitcoinAddress(const std::string& strAddress) { SetString(strAddress); } - CBitcoinAddress(const char* pszAddress) { SetString(pszAddress); } - - CTxDestination Get() const; - bool GetKeyID(CKeyID &keyID) const; - bool IsScript() const; -}; - /** * A base58-encoded secret key */ From 1444c2e7d0a243690b960c1fefe5f36bf5ca7e54 Mon Sep 17 00:00:00 2001 From: Adam Langley Date: Tue, 29 Aug 2017 22:26:12 -0700 Subject: [PATCH 169/382] Switch memory_cleanse implementation to BoringSSL's to ensure memory clearing even with link-time optimization. The implementation we currently use from OpenSSL prevents the compiler from optimizing away clensing operations on blocks of memory that are about to be released, but this protection is not extended to link-time optimization. This commit copies the solution cooked up by Google compiler engineers which uses inline assembly directives to instruct the compiler not to optimize out the call under any circumstances. As the code is in-lined, this has the added advantage of removing one more OpenSSL dependency. Regarding license compatibility, Google's contributions to BoringSSL library, including this code, is made available under the ISC license, which is MIT compatible. BoringSSL git commit: ad1907fe73334d6c696c8539646c21b11178f20f --- src/support/cleanse.cpp | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/support/cleanse.cpp b/src/support/cleanse.cpp index a2141b244..95899c9f0 100644 --- a/src/support/cleanse.cpp +++ b/src/support/cleanse.cpp @@ -5,9 +5,35 @@ #include "cleanse.h" -#include +#include +/* Compilers have a bad habit of removing "superfluous" memset calls that + * are trying to zero memory. For example, when memset()ing a buffer and + * then free()ing it, the compiler might decide that the memset is + * unobservable and thus can be removed. + * + * Previously we used OpenSSL which tried to stop this by a) implementing + * memset in assembly on x86 and b) putting the function in its own file + * for other platforms. + * + * This change removes those tricks in favour of using asm directives to + * scare the compiler away. As best as our compiler folks can tell, this is + * sufficient and will continue to be so. + * + * Adam Langley + * Commit: ad1907fe73334d6c696c8539646c21b11178f20f + * BoringSSL (LICENSE: ISC) + */ void memory_cleanse(void *ptr, size_t len) { - OPENSSL_cleanse(ptr, len); + std::memset(ptr, 0, len); + + /* As best as we can tell, this is sufficient to break any optimisations that + might try to eliminate "superfluous" memsets. If there's an easy way to + detect memset_s, it would be better to use that. */ +#if defined(_MSC_VER) + __asm; +#else + __asm__ __volatile__("" : : "r"(ptr) : "memory"); +#endif } From e91b9619232dc8999ae9657f117d55b7d241eadc Mon Sep 17 00:00:00 2001 From: flack Date: Sun, 9 Jul 2017 13:15:45 +0200 Subject: [PATCH 170/382] Create dependencies.md, and link dependencies file from README & build docs --- doc/README.md | 1 + doc/build-openbsd.md | 4 ++-- doc/build-osx.md | 2 ++ doc/build-unix.md | 2 +- doc/build-windows.md | 2 ++ doc/dependencies.md | 32 ++++++++++++++++++++++++++++++++ 6 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 doc/dependencies.md diff --git a/doc/README.md b/doc/README.md index 275ae67e5..988019869 100644 --- a/doc/README.md +++ b/doc/README.md @@ -37,6 +37,7 @@ Building --------------------- The following are developer notes on how to build Bitcoin on your native platform. They are not complete guides, but include notes on the necessary libraries, compile flags, etc. +- [Dependencies](dependencies.md) - [OS X Build Notes](build-osx.md) - [Unix Build Notes](build-unix.md) - [Windows Build Notes](build-windows.md) diff --git a/doc/build-openbsd.md b/doc/build-openbsd.md index f4a9826d8..dc1ba6e11 100644 --- a/doc/build-openbsd.md +++ b/doc/build-openbsd.md @@ -18,12 +18,12 @@ pkg_add automake # (select highest version, e.g. 1.15) pkg_add python # (select highest version, e.g. 3.5) ``` -The default C++ compiler that comes with OpenBSD 5.9 is g++ 4.2. This version is old (from 2007), and is not able to compile the current version of Bitcoin Core, primarily as it has no C++11 support, but even before there were issues. So here we will be installing a newer compiler. +See [dependencies.md](dependencies.md) for a complete overview. GCC ------- -You can install a newer version of gcc with: +The default C++ compiler that comes with OpenBSD 5.9 is g++ 4.2. This version is old (from 2007), and is not able to compile the current version of Bitcoin Core, primarily as it has no C++11 support, but even before there were issues. So here we will be installing a newer compiler: ```bash pkg_add g++ # (select newest 4.x version, e.g. 4.9.3) diff --git a/doc/build-osx.md b/doc/build-osx.md index 32d7dbd69..86c8f2255 100644 --- a/doc/build-osx.md +++ b/doc/build-osx.md @@ -18,6 +18,8 @@ Dependencies brew install automake berkeley-db4 libtool boost --c++11 miniupnpc openssl pkg-config protobuf qt libevent +See [dependencies.md](dependencies.md) for a complete overview. + If you want to build the disk image with `make deploy` (.dmg / optional), you need RSVG brew install librsvg diff --git a/doc/build-unix.md b/doc/build-unix.md index b7eae2a63..5d281c6ae 100644 --- a/doc/build-unix.md +++ b/doc/build-unix.md @@ -49,7 +49,7 @@ Optional dependencies: univalue | Utility | JSON parsing and encoding (bundled version will be used unless --with-system-univalue passed to configure) libzmq3 | ZMQ notification | Optional, allows generating ZMQ notifications (requires ZMQ version >= 4.x) -For the versions used in the release, see [release-process.md](release-process.md) under *Fetch and build inputs*. +For the versions used, see [dependencies.md](dependencies.md) Memory Requirements -------------------- diff --git a/doc/build-windows.md b/doc/build-windows.md index 9549a4b9d..2b1e0b5ae 100644 --- a/doc/build-windows.md +++ b/doc/build-windows.md @@ -59,6 +59,8 @@ A host toolchain (`build-essential`) is necessary because some dependency packages (such as `protobuf`) need to build host utilities that are used in the build process. +See also: [dependencies.md](dependencies.md). + ## Building for 64-bit Windows To build executables for Windows 64-bit, install the following dependencies: diff --git a/doc/dependencies.md b/doc/dependencies.md new file mode 100644 index 000000000..2574d8bb0 --- /dev/null +++ b/doc/dependencies.md @@ -0,0 +1,32 @@ +Dependencies +============ + +These are the dependencies currently used by Bitcoin Core. You can find instructions for installing them in the `build-*.md` file for your platform. + +| Dependency | Version used | Minimum Required | CVEs? | Shared | [Bundled Qt Library](https://doc.qt.io/qt-5/configure-options.html) | +| --- | --- | --- | --- | --- | --- | --- | +| openssl | [1.0.1k]](https://www.openssl.org/source) | | Yes | | | +| ccache | [3.3.4](https://ccache.samba.org/download.html) | | No | | | +| libevent | [2.1.8-stable](https://github.com/libevent/libevent/releases) | 2.0.22 | No | | | +| Qt | [5.7.1](https://download.qt.io/official_releases/qt/) | 4.7+ | No | | | +| Freetype | [2.7.1](http://download.savannah.gnu.org/releases/freetype) | | No | | | +| Boost | [1.64.0](http://www.boost.org/users/download/) | [1.47.0](https://github.com/bitcoin/bitcoin/pull/8920) | No | | | +| Protobuf | [2.6.3](https://github.com/google/protobuf/releases) | | No | | | +| Zeromq | [4.1.5](https://github.com/zeromq/libzmq/releases) | | No | | | +| miniupnpc | [2.0.20170509](http://miniupnp.free.fr/files) | | No | | | +| qrencode | [3.4.4](https://fukuchi.org/works/qrencode) | | No | | | +| berkeley-db | [4.8.30](http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html) | 4.8.x | No | | | +| dbus | [1.10.18](https://cgit.freedesktop.org/dbus/dbus/tree/NEWS?h=dbus-1.10) | | No | yes | | +| expat | [2.2.1](https://libexpat.github.io/) | | No | yes | | +| fontconfig | [2.12.1](https://www.freedesktop.org/software/fontconfig/release/) | | No | yes | | +| freetype | | | | | [no](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L38) (linux uses system) | +| zlib | [1.2.11](http://zlib.net/) | | | | no | +| libjpeg | | | | | [yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L75) | +| libpng | | | | | [yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L74) | +| PCRE | | | | | [yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L76) | +| xcb | | | | | [yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L94) (linux only) | +| xkbcommon | | | | | [yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L93) (linux only) | +| HarfBuzz-NG | | | | | ? | +| Python (tests) | | [3.4](https://www.python.org/downloads) | | | | +| GCC | | [4.7+](https://gcc.gnu.org/) | | | | +| Clang | | [3.3+](http://llvm.org/releases/download.html) (C++11 support) | | | | From fa40b0eb514e77928a13a007df170e5ede18ee3e Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Wed, 30 Aug 2017 07:54:44 -0700 Subject: [PATCH 171/382] travis: Assert default datadir isn't created, Run scripted diff only once --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index dadac3165..061683ac6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,9 +43,9 @@ install: - if [ -n "$PACKAGES" ]; then travis_retry sudo apt-get update; fi - if [ -n "$PACKAGES" ]; then travis_retry sudo apt-get install --no-install-recommends --no-upgrade -qq $PACKAGES; fi before_script: - - if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then contrib/devtools/commit-script-check.sh $TRAVIS_COMMIT_RANGE; fi - - unset CC; unset CXX + - if [ "$CHECK_DOC" = 1 -a "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then contrib/devtools/commit-script-check.sh $TRAVIS_COMMIT_RANGE; fi - if [ "$CHECK_DOC" = 1 ]; then contrib/devtools/check-doc.py; fi + - unset CC; unset CXX - mkdir -p depends/SDKs depends/sdk-sources - if [ -n "$OSX_SDK" -a ! -f depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then curl --location --fail $SDK_URL/MacOSX${OSX_SDK}.sdk.tar.gz -o depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz; fi - if [ -n "$OSX_SDK" -a -f depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then tar -C depends/SDKs -xf depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz; fi @@ -70,6 +70,7 @@ script: - if [ "$RUN_TESTS" = "true" ]; then travis_wait 30 make $MAKEJOBS check VERBOSE=1; fi - if [ "$TRAVIS_EVENT_TYPE" = "cron" ]; then extended="--extended --exclude pruning,dbcrash"; fi - if [ "$RUN_TESTS" = "true" ]; then test/functional/test_runner.py --coverage --quiet ${extended}; fi + - if [ -d ~/.bitcoin ]; then false; fi # Make sure default datadir does not exist after tests after_script: - echo $TRAVIS_COMMIT_RANGE - echo $TRAVIS_COMMIT_LOG From 8d0041e607a0e5bff52152f3d8af504f3703baa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= Date: Wed, 6 Sep 2017 23:47:53 +0100 Subject: [PATCH 172/382] Remove unused GetKeyID and IsScript methods from CBitcoinAddress --- src/base58.cpp | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/base58.cpp b/src/base58.cpp index e093608aa..3b907c20d 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -232,8 +232,6 @@ class CBitcoinAddress : public CBase58Data { CBitcoinAddress(const char* pszAddress) { SetString(pszAddress); } CTxDestination Get() const; - bool GetKeyID(CKeyID &keyID) const; - bool IsScript() const; }; class CBitcoinAddressVisitor : public boost::static_visitor @@ -295,21 +293,6 @@ CTxDestination CBitcoinAddress::Get() const return CNoDestination(); } -bool CBitcoinAddress::GetKeyID(CKeyID& keyID) const -{ - if (!IsValid() || vchVersion != Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS)) - return false; - uint160 id; - memcpy(&id, vchData.data(), 20); - keyID = CKeyID(id); - return true; -} - -bool CBitcoinAddress::IsScript() const -{ - return IsValid() && vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS); -} - void CBitcoinSecret::SetKey(const CKey& vchSecret) { assert(vchSecret.IsValid()); From 86e6dd4b630299eaa778ca241797b24758b6f8a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= Date: Wed, 6 Sep 2017 21:44:33 +0100 Subject: [PATCH 173/382] Remove duplicate destination decoding --- src/qt/transactiondesc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index f3fe4096a..74f5c774a 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -91,8 +91,8 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco if (nNet > 0) { // Credit - if (IsValidDestinationString(rec->address)) { - CTxDestination address = DecodeDestination(rec->address); + CTxDestination address = DecodeDestination(rec->address); + if (IsValidDestination(address)) { if (wallet->mapAddressBook.count(address)) { strHTML += "" + tr("From") + ": " + tr("unknown") + "
"; From 28f11e9406b185dc87144f1f29af0d93eb115b4e Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Thu, 6 Jul 2017 13:53:52 -0400 Subject: [PATCH 174/382] net: pass CConnman via pointer rather than reference There are a few too many edge-cases here to make this a scripted diff. The following commits will move a few functions into PeerLogicValidation, where the local connman instance can be used. This change prepares for that usage. --- src/net.cpp | 8 +- src/net.h | 6 +- src/net_processing.cpp | 168 ++++++++++++++++++++--------------------- src/net_processing.h | 4 +- src/test/DoS_tests.cpp | 22 +++--- 5 files changed, 104 insertions(+), 104 deletions(-) diff --git a/src/net.cpp b/src/net.cpp index 1eb867b48..5e29e6fee 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1114,7 +1114,7 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) { CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addr, CalculateKeyedNetGroup(addr), nonce, addr_bind, "", true); pnode->AddRef(); pnode->fWhitelisted = whitelisted; - GetNodeSignals().InitializeNode(pnode, *this); + GetNodeSignals().InitializeNode(pnode, this); LogPrint(BCLog::NET, "connection from %s accepted\n", addr.ToString()); @@ -1966,7 +1966,7 @@ bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai if (fAddnode) pnode->fAddnode = true; - GetNodeSignals().InitializeNode(pnode, *this); + GetNodeSignals().InitializeNode(pnode, this); { LOCK(cs_vNodes); vNodes.push_back(pnode); @@ -1996,7 +1996,7 @@ void CConnman::ThreadMessageHandler() continue; // Receive messages - bool fMoreNodeWork = GetNodeSignals().ProcessMessages(pnode, *this, flagInterruptMsgProc); + bool fMoreNodeWork = GetNodeSignals().ProcessMessages(pnode, this, flagInterruptMsgProc); fMoreWork |= (fMoreNodeWork && !pnode->fPauseSend); if (flagInterruptMsgProc) return; @@ -2004,7 +2004,7 @@ void CConnman::ThreadMessageHandler() // Send messages { LOCK(pnode->cs_sendProcessing); - GetNodeSignals().SendMessages(pnode, *this, flagInterruptMsgProc); + GetNodeSignals().SendMessages(pnode, this, flagInterruptMsgProc); } if (flagInterruptMsgProc) return; diff --git a/src/net.h b/src/net.h index 244930d8f..f170cdac2 100644 --- a/src/net.h +++ b/src/net.h @@ -441,9 +441,9 @@ struct CombinerAll // Signals for message handling struct CNodeSignals { - boost::signals2::signal&), CombinerAll> ProcessMessages; - boost::signals2::signal&), CombinerAll> SendMessages; - boost::signals2::signal InitializeNode; + boost::signals2::signal&), CombinerAll> ProcessMessages; + boost::signals2::signal&), CombinerAll> SendMessages; + boost::signals2::signal InitializeNode; boost::signals2::signal FinalizeNode; }; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index cc4487671..1e4713235 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -244,7 +244,7 @@ void UpdatePreferredDownload(CNode* node, CNodeState* state) nPreferredDownload += state->fPreferredDownload; } -void PushNodeVersion(CNode *pnode, CConnman& connman, int64_t nTime) +void PushNodeVersion(CNode *pnode, CConnman* connman, int64_t nTime) { ServiceFlags nLocalNodeServices = pnode->GetLocalServices(); uint64_t nonce = pnode->GetLocalNonce(); @@ -255,7 +255,7 @@ void PushNodeVersion(CNode *pnode, CConnman& connman, int64_t nTime) CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService(), addr.nServices)); CAddress addrMe = CAddress(CService(), nLocalNodeServices); - connman.PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalNodeServices, nTime, addrYou, addrMe, + connman->PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalNodeServices, nTime, addrYou, addrMe, nonce, strSubVersion, nNodeStartingHeight, ::fRelayTxes)); if (fLogIPs) { @@ -265,7 +265,7 @@ void PushNodeVersion(CNode *pnode, CConnman& connman, int64_t nTime) } } -void InitializeNode(CNode *pnode, CConnman& connman) { +void InitializeNode(CNode *pnode, CConnman* connman) { CAddress addr = pnode->addr; std::string addrName = pnode->GetAddrName(); NodeId nodeid = pnode->GetId(); @@ -404,7 +404,7 @@ void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) { } } -void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman& connman) { +void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman* connman) { AssertLockHeld(cs_main); CNodeState* nodestate = State(nodeid); if (!nodestate || !nodestate->fSupportsDesiredCmpctVersion) { @@ -419,20 +419,20 @@ void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman& connman) { return; } } - connman.ForNode(nodeid, [&connman](CNode* pfrom){ + 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)); + 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)); + connman->PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId()); return true; }); @@ -867,7 +867,7 @@ void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationSta !IsInitialBlockDownload() && mapBlocksInFlight.count(hash) == mapBlocksInFlight.size()) { if (it != mapBlockSource.end()) { - MaybeSetPeerAsAnnouncingHeaderAndIDs(it->second.first, *connman); + MaybeSetPeerAsAnnouncingHeaderAndIDs(it->second.first, connman); } } if (it != mapBlockSource.end()) @@ -912,16 +912,16 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) return true; } -static void RelayTransaction(const CTransaction& tx, CConnman& connman) +static void RelayTransaction(const CTransaction& tx, CConnman* connman) { CInv inv(MSG_TX, tx.GetHash()); - connman.ForEachNode([&inv](CNode* pnode) + connman->ForEachNode([&inv](CNode* pnode) { pnode->PushInventory(inv); }); } -static void RelayAddress(const CAddress& addr, bool fReachable, CConnman& connman) +static void RelayAddress(const CAddress& addr, bool fReachable, CConnman* connman) { unsigned int nRelayNodes = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s) @@ -929,7 +929,7 @@ static void RelayAddress(const CAddress& addr, bool fReachable, CConnman& connma // Use deterministic randomness to send to the same nodes for 24 hours // at a time so the addrKnowns of the chosen nodes prevent repeats uint64_t hashAddr = addr.GetHash(); - const CSipHasher hasher = connman.GetDeterministicRandomizer(RANDOMIZER_ID_ADDRESS_RELAY).Write(hashAddr << 32).Write((GetTime() + hashAddr) / (24*60*60)); + const CSipHasher hasher = connman->GetDeterministicRandomizer(RANDOMIZER_ID_ADDRESS_RELAY).Write(hashAddr << 32).Write((GetTime() + hashAddr) / (24*60*60)); FastRandomContext insecure_rand; std::array,2> best{{{0, nullptr}, {0, nullptr}}}; @@ -954,10 +954,10 @@ static void RelayAddress(const CAddress& addr, bool fReachable, CConnman& connma } }; - connman.ForEachNodeThen(std::move(sortfunc), std::move(pushfunc)); + connman->ForEachNodeThen(std::move(sortfunc), std::move(pushfunc)); } -void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParams, CConnman& connman, const std::atomic& interruptMsgProc) +void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParams, CConnman* connman, const std::atomic& interruptMsgProc) { std::deque::iterator it = pfrom->vRecvGetData.begin(); std::vector vNotFound; @@ -1019,7 +1019,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam // disconnect node in case we have reached the outbound limit for serving historical blocks // never disconnect whitelisted nodes static const int nOneWeek = 7 * 24 * 60 * 60; // assume > 1 week = historical - if (send && connman.OutboundTargetReached(true) && ( ((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() > nOneWeek)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted) + if (send && connman->OutboundTargetReached(true) && ( ((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() > nOneWeek)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted) { LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom->GetId()); @@ -1042,9 +1042,9 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam pblock = pblockRead; } if (inv.type == MSG_BLOCK) - connman.PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, *pblock)); + connman->PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, *pblock)); else if (inv.type == MSG_WITNESS_BLOCK) - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock)); else if (inv.type == MSG_FILTERED_BLOCK) { bool sendMerkleBlock = false; @@ -1057,7 +1057,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam } } if (sendMerkleBlock) { - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::MERKLEBLOCK, merkleBlock)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::MERKLEBLOCK, merkleBlock)); // CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see // This avoids hurting performance by pointlessly requiring a round-trip // Note that there is currently no way for a node to request any single transactions we didn't send here - @@ -1066,7 +1066,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam // however we MUST always provide at least what the remote peer needs typedef std::pair PairType; for (PairType& pair : merkleBlock.vMatchedTxn) - connman.PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, *pblock->vtx[pair.first])); + connman->PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, *pblock->vtx[pair.first])); } // else // no response @@ -1081,13 +1081,13 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam int nSendFlags = fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS; if (CanDirectFetch(consensusParams) && mi->second->nHeight >= chainActive.Height() - MAX_CMPCTBLOCK_DEPTH) { if ((fPeerWantsWitness || !fWitnessesPresentInARecentCompactBlock) && a_recent_compact_block && a_recent_compact_block->header.GetHash() == mi->second->GetBlockHash()) { - connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *a_recent_compact_block)); + connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *a_recent_compact_block)); } else { CBlockHeaderAndShortTxIDs cmpctblock(*pblock, fPeerWantsWitness); - connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock)); + connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock)); } } else { - connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCK, *pblock)); + connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCK, *pblock)); } } @@ -1099,7 +1099,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam // wait for other stuff first. std::vector vInv; vInv.push_back(CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash())); - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::INV, vInv)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::INV, vInv)); pfrom->hashContinue.SetNull(); } } @@ -1111,14 +1111,14 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam auto mi = mapRelay.find(inv.hash); int nSendFlags = (inv.type == MSG_TX ? SERIALIZE_TRANSACTION_NO_WITNESS : 0); if (mi != mapRelay.end()) { - connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *mi->second)); + connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *mi->second)); push = true; } else if (pfrom->timeLastMempoolReq) { auto txinfo = mempool.info(inv.hash); // To protect privacy, do not answer getdata using the mempool when // that TX couldn't have been INVed in reply to a MEMPOOL request. if (txinfo.tx && txinfo.nTime <= pfrom->timeLastMempoolReq) { - connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *txinfo.tx)); + connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *txinfo.tx)); push = true; } } @@ -1145,7 +1145,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam // do that because they want to know about (and store and rebroadcast and // risk analyze) the dependencies of transactions relevant to them, without // having to download the entire memory pool. - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::NOTFOUND, vNotFound)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::NOTFOUND, vNotFound)); } } @@ -1157,7 +1157,7 @@ uint32_t GetFetchFlags(CNode* pfrom) { return nFetchFlags; } -inline void static SendBlockTransactions(const CBlock& block, const BlockTransactionsRequest& req, CNode* pfrom, CConnman& connman) { +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()) { @@ -1171,10 +1171,10 @@ inline void static SendBlockTransactions(const CBlock& block, const BlockTransac 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)); + 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& interruptMsgProc) +bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman* connman, const std::atomic& interruptMsgProc) { LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->GetId()); if (gArgs.IsArgSet("-dropmessagestest") && GetRand(gArgs.GetArg("-dropmessagestest", 0)) == 0) @@ -1227,7 +1227,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // 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, std::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; @@ -1251,12 +1251,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr nServices = ServiceFlags(nServiceInt); if (!pfrom->fInbound) { - connman.SetServices(pfrom->addr, nServices); + connman->SetServices(pfrom->addr, nServices); } if (pfrom->nServicesExpected & ~nServices) { LogPrint(BCLog::NET, "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->GetId(), nServices, pfrom->nServicesExpected); - connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD, + 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; @@ -1277,7 +1277,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr { // disconnect from peers older than this proto version LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->GetId(), nVersion); - connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, + 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; @@ -1297,7 +1297,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr if (!vRecv.empty()) vRecv >> fRelay; // Disconnect if we connected to ourself - if (pfrom->fInbound && !connman.CheckIncomingNonce(nNonce)) + if (pfrom->fInbound && !connman->CheckIncomingNonce(nNonce)) { LogPrintf("connected to self at %s, disconnecting\n", pfrom->addr.ToString()); pfrom->fDisconnect = true; @@ -1313,7 +1313,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr if (pfrom->fInbound) PushNodeVersion(pfrom, connman, GetAdjustedTime()); - connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERACK)); + connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERACK)); pfrom->nServices = nServices; pfrom->SetAddrLocal(addrMe); @@ -1364,12 +1364,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr } // Get recent addresses - if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || connman.GetAddressCount() < 1000) + if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || connman->GetAddressCount() < 1000) { - connman.PushMessage(pfrom, CNetMsgMaker(nSendVersion).Make(NetMsgType::GETADDR)); + connman->PushMessage(pfrom, CNetMsgMaker(nSendVersion).Make(NetMsgType::GETADDR)); pfrom->fGetAddr = true; } - connman.MarkAddressGood(pfrom->addr); + connman->MarkAddressGood(pfrom->addr); } std::string remoteAddr; @@ -1388,7 +1388,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // 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)); + connman->PushMessage(pfrom, CNetMsgMaker(nSendVersion).Make("alert", finalAlert)); } // Feeler connections exist only to verify if address is online. @@ -1426,7 +1426,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // We send this to non-NODE NETWORK peers as well, because even // non-NODE NETWORK peers can announce blocks (such as pruning // nodes) - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDHEADERS)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDHEADERS)); } if (pfrom->nVersion >= SHORT_IDS_BLOCKS_VERSION) { // Tell our peer we are willing to provide version 1 or 2 cmpctblocks @@ -1437,9 +1437,9 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr bool fAnnounceUsingCMPCTBLOCK = false; uint64_t nCMPCTBLOCKVersion = 2; if (pfrom->GetLocalServices() & NODE_WITNESS) - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); nCMPCTBLOCKVersion = 1; - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); } pfrom->fSuccessfullyConnected = true; } @@ -1458,7 +1458,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr vRecv >> vAddr; // Don't want addr from older versions unless seeding - if (pfrom->nVersion < CADDR_TIME_VERSION && connman.GetAddressCount() > 1000) + if (pfrom->nVersion < CADDR_TIME_VERSION && connman->GetAddressCount() > 1000) return true; if (vAddr.size() > 1000) { @@ -1492,7 +1492,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr if (fReachable) vAddrOk.push_back(addr); } - connman.AddNewAddresses(vAddrOk, pfrom->addr, 2 * 60 * 60); + connman->AddNewAddresses(vAddrOk, pfrom->addr, 2 * 60 * 60); if (vAddr.size() < 1000) pfrom->fGetAddr = false; if (pfrom->fOneShot) @@ -1570,7 +1570,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // fell back to inv we probably have a reorg which we should get the headers for first, // we now only provide a getheaders response here. When we receive the headers, we will // then ask for the blocks we need. - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), inv.hash)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), inv.hash)); LogPrint(BCLog::NET, "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, inv.hash.ToString(), pfrom->GetId()); } } @@ -1776,7 +1776,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // 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)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::HEADERS, vHeaders)); } @@ -1957,7 +1957,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr pfrom->GetId(), FormatStateMessage(state)); if (state.GetRejectCode() > 0 && state.GetRejectCode() < REJECT_INTERNAL) // Never send AcceptToMemoryPool's internal codes over P2P - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(), + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash)); if (nDoS > 0) { Misbehaving(pfrom->GetId(), nDoS); @@ -1977,7 +1977,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr if (mapBlockIndex.find(cmpctblock.header.hashPrevBlock) == mapBlockIndex.end()) { // Doesn't connect (or is genesis), instead of DoSing in AcceptBlockHeader, request deeper headers if (!IsInitialBlockDownload()) - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256())); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256())); return true; } } @@ -2032,7 +2032,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // so we just grab the block via normal getdata std::vector vInv(1); vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom), cmpctblock.header.GetHash()); - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv)); } return true; } @@ -2076,7 +2076,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // Duplicate txindexes, the block is now in-flight, so just request it std::vector vInv(1); vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom), cmpctblock.header.GetHash()); - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv)); return true; } @@ -2093,7 +2093,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr fProcessBLOCKTXN = true; } else { req.blockhash = pindex->GetBlockHash(); - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETBLOCKTXN, req)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETBLOCKTXN, req)); } } else { // This block is either already in flight from a different @@ -2119,7 +2119,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // mempool will probably be useless - request the block normally std::vector vInv(1); vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom), cmpctblock.header.GetHash()); - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv)); return true; } else { // If this was an announce-cmpctblock, we want the same treatment as a header message @@ -2193,7 +2193,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // Might have collided, fall back to getdata now :( std::vector invs; invs.push_back(CInv(MSG_BLOCK | GetFetchFlags(pfrom), resp.blockhash)); - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, invs)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, invs)); } else { // Block is either okay, or possibly we received // READ_STATUS_CHECKBLOCK_FAILED. @@ -2274,7 +2274,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // nUnconnectingHeaders gets reset back to 0. if (mapBlockIndex.find(headers[0].hashPrevBlock) == mapBlockIndex.end() && nCount < MAX_BLOCKS_TO_ANNOUNCE) { nodestate->nUnconnectingHeaders++; - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256())); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256())); LogPrint(BCLog::NET, "received header %s: missing prev block %s, sending getheaders (%d) to end (peer=%d, nUnconnectingHeaders=%d)\n", headers[0].GetHash().ToString(), headers[0].hashPrevBlock.ToString(), @@ -2329,7 +2329,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue // from there instead. LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->GetId(), pfrom->nStartingHeight); - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexLast), uint256())); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexLast), uint256())); } bool fCanDirectFetch = CanDirectFetch(chainparams.GetConsensus()); @@ -2379,7 +2379,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // In any case, we want to download using a compact block, not a regular one vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash); } - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData)); } } } @@ -2440,7 +2440,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr pfrom->fSentAddr = true; pfrom->vAddrToSend.clear(); - std::vector vAddr = connman.GetAddresses(); + std::vector vAddr = connman->GetAddresses(); FastRandomContext insecure_rand; for (const CAddress &addr : vAddr) pfrom->PushAddress(addr, insecure_rand); @@ -2456,7 +2456,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr return true; } - if (connman.OutboundTargetReached(false) && !pfrom->fWhitelisted) + if (connman->OutboundTargetReached(false) && !pfrom->fWhitelisted) { LogPrint(BCLog::NET, "mempool request with bandwidth limit reached, disconnect peer=%d\n", pfrom->GetId()); pfrom->fDisconnect = true; @@ -2485,7 +2485,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // it, if the remote node sends a ping once per second and this node takes 5 // seconds to respond to each, the 5th ping the remote sends would appear to // return very quickly. - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::PONG, nonce)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::PONG, nonce)); } } @@ -2631,13 +2631,13 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr return true; } -static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman& connman) +static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman* connman) { AssertLockHeld(cs_main); CNodeState &state = *State(pnode->GetId()); for (const CBlockReject& reject : state.rejects) { - connman.PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, (std::string)NetMsgType::BLOCK, reject.chRejectCode, reject.strRejectReason, reject.hashBlock)); + connman->PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, (std::string)NetMsgType::BLOCK, reject.chRejectCode, reject.strRejectReason, reject.hashBlock)); } state.rejects.clear(); @@ -2653,7 +2653,7 @@ static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman& connman) LogPrintf("Warning: not banning local peer %s!\n", pnode->addr.ToString()); else { - connman.Ban(pnode->addr, BanReasonNodeMisbehaving); + connman->Ban(pnode->addr, BanReasonNodeMisbehaving); } } return true; @@ -2661,7 +2661,7 @@ static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman& connman) return false; } -bool ProcessMessages(CNode* pfrom, CConnman& connman, const std::atomic& interruptMsgProc) +bool ProcessMessages(CNode* pfrom, CConnman* connman, const std::atomic& interruptMsgProc) { const CChainParams& chainparams = Params(); // @@ -2695,7 +2695,7 @@ bool ProcessMessages(CNode* pfrom, CConnman& connman, const std::atomic& i // 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(); + pfrom->fPauseRecv = pfrom->nProcessQueueSize > connman->GetReceiveFloodSize(); fMoreWork = !pfrom->vProcessMsg.empty(); } CNetMessage& msg(msgs.front()); @@ -2744,7 +2744,7 @@ bool ProcessMessages(CNode* pfrom, CConnman& connman, const std::atomic& i } catch (const std::ios_base::failure& e) { - connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, std::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 @@ -2798,7 +2798,7 @@ class CompareInvMempoolOrder } }; -bool SendMessages(CNode* pto, CConnman& connman, const std::atomic& interruptMsgProc) +bool SendMessages(CNode* pto, CConnman* connman, const std::atomic& interruptMsgProc) { const Consensus::Params& consensusParams = Params().GetConsensus(); { @@ -2830,11 +2830,11 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic& interr pto->nPingUsecStart = GetTimeMicros(); if (pto->nVersion > BIP0031_VERSION) { pto->nPingNonceSent = nonce; - connman.PushMessage(pto, msgMaker.Make(NetMsgType::PING, nonce)); + connman->PushMessage(pto, msgMaker.Make(NetMsgType::PING, nonce)); } else { // Peer is too old to support ping command with nonce, pong will never arrive. pto->nPingNonceSent = 0; - connman.PushMessage(pto, msgMaker.Make(NetMsgType::PING)); + connman->PushMessage(pto, msgMaker.Make(NetMsgType::PING)); } } @@ -2869,14 +2869,14 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic& interr // receiver rejects addr messages larger than 1000 if (vAddr.size() >= 1000) { - connman.PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr)); + connman->PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr)); vAddr.clear(); } } } pto->vAddrToSend.clear(); if (!vAddr.empty()) - connman.PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr)); + connman->PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr)); // we only send the big addr message once if (pto->vAddrToSend.capacity() > 40) pto->vAddrToSend.shrink_to_fit(); @@ -2903,7 +2903,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic& interr if (pindexStart->pprev) pindexStart = pindexStart->pprev; LogPrint(BCLog::NET, "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->GetId(), pto->nStartingHeight); - connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexStart), uint256())); + connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexStart), uint256())); } } @@ -2912,7 +2912,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic& interr // transactions become unconfirmed and spams other nodes. if (!fReindex && !fImporting && !IsInitialBlockDownload()) { - GetMainSignals().Broadcast(nTimeBestReceived, &connman); + GetMainSignals().Broadcast(nTimeBestReceived, connman); } // @@ -2996,10 +2996,10 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic& interr LOCK(cs_most_recent_block); if (most_recent_block_hash == pBestIndex->GetBlockHash()) { if (state.fWantsCmpctWitness || !fWitnessesPresentInMostRecentCompactBlock) - connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *most_recent_compact_block)); + 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)); + connman->PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock)); } fGotBlockFromCache = true; } @@ -3009,7 +3009,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic& interr bool ret = ReadBlockFromDisk(block, pBestIndex, consensusParams); assert(ret); CBlockHeaderAndShortTxIDs cmpctblock(block, state.fWantsCmpctWitness); - connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock)); + connman->PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock)); } state.pindexBestHeaderSent = pBestIndex; } else if (state.fPreferHeaders) { @@ -3022,7 +3022,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic& interr LogPrint(BCLog::NET, "%s: sending header %s to peer=%d\n", __func__, vHeaders.front().GetHash().ToString(), pto->GetId()); } - connman.PushMessage(pto, msgMaker.Make(NetMsgType::HEADERS, vHeaders)); + connman->PushMessage(pto, msgMaker.Make(NetMsgType::HEADERS, vHeaders)); state.pindexBestHeaderSent = pBestIndex; } else fRevertToInv = true; @@ -3068,7 +3068,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic& interr for (const uint256& hash : pto->vInventoryBlockToSend) { vInv.push_back(CInv(MSG_BLOCK, hash)); if (vInv.size() == MAX_INV_SZ) { - connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); + connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); vInv.clear(); } } @@ -3114,7 +3114,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic& interr pto->filterInventoryKnown.insert(hash); vInv.push_back(inv); if (vInv.size() == MAX_INV_SZ) { - connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); + connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); vInv.clear(); } } @@ -3180,7 +3180,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic& interr } } if (vInv.size() == MAX_INV_SZ) { - connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); + connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); vInv.clear(); } pto->filterInventoryKnown.insert(hash); @@ -3188,7 +3188,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic& interr } } if (!vInv.empty()) - connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); + connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); // Detect whether we're stalling nNow = GetTimeMicros(); @@ -3283,7 +3283,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic& interr vGetData.push_back(inv); if (vGetData.size() >= 1000) { - connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData)); + connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData)); vGetData.clear(); } } else { @@ -3293,7 +3293,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic& interr pto->mapAskFor.erase(pto->mapAskFor.begin()); } if (!vGetData.empty()) - connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData)); + connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData)); // // Message: feefilter @@ -3310,7 +3310,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic& interr // We always have a fee filter of at least minRelayTxFee filterToSend = std::max(filterToSend, ::minRelayTxFee.GetFeePerK()); if (filterToSend != pto->lastSentFeeFilter) { - connman.PushMessage(pto, msgMaker.Make(NetMsgType::FEEFILTER, filterToSend)); + connman->PushMessage(pto, msgMaker.Make(NetMsgType::FEEFILTER, filterToSend)); pto->lastSentFeeFilter = filterToSend; } pto->nextSendTimeFeeFilter = PoissonNextSend(timeNow, AVG_FEEFILTER_BROADCAST_INTERVAL); diff --git a/src/net_processing.h b/src/net_processing.h index f4a43980a..461dc9a90 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -53,7 +53,7 @@ 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, const std::atomic& interrupt); +bool ProcessMessages(CNode* pfrom, CConnman* connman, const std::atomic& interrupt); /** * Send queued protocol messages to be sent to a give node. * @@ -62,6 +62,6 @@ bool ProcessMessages(CNode* pfrom, CConnman& connman, const std::atomic& i * @param[in] interrupt Interrupt condition for processing threads * @return True if there is more work to be done */ -bool SendMessages(CNode* pto, CConnman& connman, const std::atomic& interrupt); +bool SendMessages(CNode* pto, CConnman* connman, const std::atomic& interrupt); #endif // BITCOIN_NET_PROCESSING_H diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp index ffbeeb7d9..39a75f7c8 100644 --- a/src/test/DoS_tests.cpp +++ b/src/test/DoS_tests.cpp @@ -50,26 +50,26 @@ BOOST_AUTO_TEST_CASE(DoS_banning) CAddress addr1(ip(0xa0b0c001), NODE_NONE); CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 0, 0, CAddress(), "", true); dummyNode1.SetSendVersion(PROTOCOL_VERSION); - GetNodeSignals().InitializeNode(&dummyNode1, *connman); + GetNodeSignals().InitializeNode(&dummyNode1, connman); dummyNode1.nVersion = 1; dummyNode1.fSuccessfullyConnected = true; Misbehaving(dummyNode1.GetId(), 100); // Should get banned - SendMessages(&dummyNode1, *connman, interruptDummy); + SendMessages(&dummyNode1, connman, interruptDummy); BOOST_CHECK(connman->IsBanned(addr1)); BOOST_CHECK(!connman->IsBanned(ip(0xa0b0c001|0x0000ff00))); // Different IP, not banned CAddress addr2(ip(0xa0b0c002), NODE_NONE); CNode dummyNode2(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr2, 1, 1, CAddress(), "", true); dummyNode2.SetSendVersion(PROTOCOL_VERSION); - GetNodeSignals().InitializeNode(&dummyNode2, *connman); + GetNodeSignals().InitializeNode(&dummyNode2, connman); dummyNode2.nVersion = 1; dummyNode2.fSuccessfullyConnected = true; Misbehaving(dummyNode2.GetId(), 50); - SendMessages(&dummyNode2, *connman, interruptDummy); + SendMessages(&dummyNode2, connman, interruptDummy); BOOST_CHECK(!connman->IsBanned(addr2)); // 2 not banned yet... BOOST_CHECK(connman->IsBanned(addr1)); // ... but 1 still should be Misbehaving(dummyNode2.GetId(), 50); - SendMessages(&dummyNode2, *connman, interruptDummy); + SendMessages(&dummyNode2, connman, interruptDummy); BOOST_CHECK(connman->IsBanned(addr2)); } @@ -82,17 +82,17 @@ BOOST_AUTO_TEST_CASE(DoS_banscore) CAddress addr1(ip(0xa0b0c001), NODE_NONE); CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 3, 1, CAddress(), "", true); dummyNode1.SetSendVersion(PROTOCOL_VERSION); - GetNodeSignals().InitializeNode(&dummyNode1, *connman); + GetNodeSignals().InitializeNode(&dummyNode1, connman); dummyNode1.nVersion = 1; dummyNode1.fSuccessfullyConnected = true; Misbehaving(dummyNode1.GetId(), 100); - SendMessages(&dummyNode1, *connman, interruptDummy); + SendMessages(&dummyNode1, connman, interruptDummy); BOOST_CHECK(!connman->IsBanned(addr1)); Misbehaving(dummyNode1.GetId(), 10); - SendMessages(&dummyNode1, *connman, interruptDummy); + SendMessages(&dummyNode1, connman, interruptDummy); BOOST_CHECK(!connman->IsBanned(addr1)); Misbehaving(dummyNode1.GetId(), 1); - SendMessages(&dummyNode1, *connman, interruptDummy); + SendMessages(&dummyNode1, connman, interruptDummy); BOOST_CHECK(connman->IsBanned(addr1)); gArgs.ForceSetArg("-banscore", std::to_string(DEFAULT_BANSCORE_THRESHOLD)); } @@ -108,12 +108,12 @@ BOOST_AUTO_TEST_CASE(DoS_bantime) CAddress addr(ip(0xa0b0c001), NODE_NONE); CNode dummyNode(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr, 4, 4, CAddress(), "", true); dummyNode.SetSendVersion(PROTOCOL_VERSION); - GetNodeSignals().InitializeNode(&dummyNode, *connman); + GetNodeSignals().InitializeNode(&dummyNode, connman); dummyNode.nVersion = 1; dummyNode.fSuccessfullyConnected = true; Misbehaving(dummyNode.GetId(), 100); - SendMessages(&dummyNode, *connman, interruptDummy); + SendMessages(&dummyNode, connman, interruptDummy); BOOST_CHECK(connman->IsBanned(addr)); SetMockTime(nStartTime+60*60); From aece8a4637f0d097e4be497bc82d59b37244d245 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Sun, 16 Jul 2017 19:29:08 -0400 Subject: [PATCH 175/382] (finally) remove getinfo in favor of more module-specific infos --- doc/developer-notes.md | 4 +- src/rpc/misc.cpp | 89 ---------------------- src/wallet/wallet.h | 2 +- test/functional/bitcoin_cli.py | 12 ++- test/functional/p2p-versionbits-warning.py | 3 - test/functional/rpcnamedargs.py | 6 +- 6 files changed, 15 insertions(+), 101 deletions(-) diff --git a/doc/developer-notes.md b/doc/developer-notes.md index 4694175a9..8fdae2534 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -613,8 +613,8 @@ A few guidelines for introducing and reviewing new RPC interfaces: from there. - A RPC method must either be a wallet method or a non-wallet method. Do not - introduce new methods such as `getinfo` and `signrawtransaction` that differ - in behavior based on presence of a wallet. + introduce new methods such as `signrawtransaction` that differ in behavior + based on presence of a wallet. - *Rationale*: as well as complicating the implementation and interfering with the introduction of multi-wallet, wallet and non-wallet code should be diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 91e3b05be..2037ebe3c 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -31,94 +31,6 @@ #include -/** - * @note Do not add or change anything in the information returned by this - * method. `getinfo` exists for backwards-compatibility only. It combines - * information from wildly different sources in the program, which is a mess, - * and is thus planned to be deprecated eventually. - * - * Based on the source of the information, new information should be added to: - * - `getblockchaininfo`, - * - `getnetworkinfo` or - * - `getwalletinfo` - * - * Or alternatively, create a specific query method for the information. - **/ -UniValue getinfo(const JSONRPCRequest& request) -{ - if (request.fHelp || request.params.size() != 0) - throw std::runtime_error( - "getinfo\n" - "\nDEPRECATED. Returns an object containing various state info.\n" - "\nResult:\n" - "{\n" - " \"deprecation-warning\": \"...\" (string) warning that the getinfo command is deprecated and will be removed in 0.16\n" - " \"version\": xxxxx, (numeric) the server version\n" - " \"protocolversion\": xxxxx, (numeric) the protocol version\n" - " \"walletversion\": xxxxx, (numeric) the wallet version\n" - " \"balance\": xxxxxxx, (numeric) the total bitcoin balance of the wallet\n" - " \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n" - " \"timeoffset\": xxxxx, (numeric) the time offset\n" - " \"connections\": xxxxx, (numeric) the number of connections\n" - " \"proxy\": \"host:port\", (string, optional) the proxy used by the server\n" - " \"difficulty\": xxxxxx, (numeric) the current difficulty\n" - " \"testnet\": true|false, (boolean) if the server is using testnet or not\n" - " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since Unix epoch) of the oldest pre-generated key in the key pool\n" - " \"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 set in " + CURRENCY_UNIT + "/kB\n" - " \"relayfee\": x.xxxx, (numeric) minimum relay fee for transactions in " + CURRENCY_UNIT + "/kB\n" - " \"errors\": \"...\" (string) any error messages\n" - "}\n" - "\nExamples:\n" - + HelpExampleCli("getinfo", "") - + HelpExampleRpc("getinfo", "") - ); - -#ifdef ENABLE_WALLET - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); - - LOCK2(cs_main, pwallet ? &pwallet->cs_wallet : nullptr); -#else - LOCK(cs_main); -#endif - - proxyType proxy; - GetProxy(NET_IPV4, proxy); - - UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("deprecation-warning", "WARNING: getinfo is deprecated and will be fully removed in 0.16." - " Projects should transition to using getblockchaininfo, getnetworkinfo, and getwalletinfo before upgrading to 0.16")); - obj.push_back(Pair("version", CLIENT_VERSION)); - obj.push_back(Pair("protocolversion", PROTOCOL_VERSION)); -#ifdef ENABLE_WALLET - if (pwallet) { - obj.push_back(Pair("walletversion", pwallet->GetVersion())); - obj.push_back(Pair("balance", ValueFromAmount(pwallet->GetBalance()))); - } -#endif - obj.push_back(Pair("blocks", (int)chainActive.Height())); - obj.push_back(Pair("timeoffset", GetTimeOffset())); - if(g_connman) - obj.push_back(Pair("connections", (int)g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL))); - obj.push_back(Pair("proxy", (proxy.IsValid() ? proxy.proxy.ToStringIPPort() : std::string()))); - obj.push_back(Pair("difficulty", (double)GetDifficulty())); - obj.push_back(Pair("testnet", Params().NetworkIDString() == CBaseChainParams::TESTNET)); -#ifdef ENABLE_WALLET - if (pwallet) { - obj.push_back(Pair("keypoololdest", pwallet->GetOldestKeyPoolTime())); - obj.push_back(Pair("keypoolsize", (int)pwallet->GetKeyPoolSize())); - } - if (pwallet && pwallet->IsCrypted()) { - obj.push_back(Pair("unlocked_until", pwallet->nRelockTime)); - } - obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK()))); -#endif - obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()))); - obj.push_back(Pair("errors", GetWarnings("statusbar"))); - return obj; -} - #ifdef ENABLE_WALLET class DescribeAddressVisitor : public boost::static_visitor { @@ -651,7 +563,6 @@ UniValue echo(const JSONRPCRequest& request) static const CRPCCommand commands[] = { // category name actor (function) argNames // --------------------- ------------------------ ----------------------- ---------- - { "control", "getinfo", &getinfo, {} }, /* uses wallet if enabled */ { "control", "getmemoryinfo", &getmemoryinfo, {"mode"} }, { "util", "validateaddress", &validateaddress, {"address"} }, /* uses wallet if enabled */ { "util", "createmultisig", &createmultisig, {"nrequired","keys"} }, diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 73ad3bdec..51abe2b25 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -87,7 +87,7 @@ enum class FeeEstimateMode; /** (client) version numbers for particular wallet features */ enum WalletFeature { - FEATURE_BASE = 10500, // the earliest version new wallets supports (only useful for getinfo's clientversion output) + FEATURE_BASE = 10500, // the earliest version new wallets supports (only useful for getwalletinfo's clientversion output) FEATURE_WALLETCRYPT = 40000, // wallet encryption FEATURE_COMPRPUBKEY = 60000, // compressed public keys diff --git a/test/functional/bitcoin_cli.py b/test/functional/bitcoin_cli.py index 7acfede3f..04847252e 100755 --- a/test/functional/bitcoin_cli.py +++ b/test/functional/bitcoin_cli.py @@ -15,9 +15,15 @@ def set_test_params(self): def run_test(self): """Main test logic""" - self.log.info("Compare responses from getinfo RPC and `bitcoin-cli getinfo`") - cli_get_info = self.nodes[0].cli.getinfo() - rpc_get_info = self.nodes[0].getinfo() + self.log.info("Compare responses from gewalletinfo RPC and `bitcoin-cli getwalletinfo`") + cli_get_info = self.nodes[0].cli.getwalletinfo() + rpc_get_info = self.nodes[0].getwalletinfo() + + assert_equal(cli_get_info, rpc_get_info) + + self.log.info("Compare responses from getblockchaininfo RPC and `bitcoin-cli getblockchaininfo`") + cli_get_info = self.nodes[0].cli.getblockchaininfo() + rpc_get_info = self.nodes[0].getblockchaininfo() assert_equal(cli_get_info, rpc_get_info) diff --git a/test/functional/p2p-versionbits-warning.py b/test/functional/p2p-versionbits-warning.py index 5dfac6dd1..0848fcde6 100755 --- a/test/functional/p2p-versionbits-warning.py +++ b/test/functional/p2p-versionbits-warning.py @@ -87,7 +87,6 @@ def run_test(self): self.nodes[0].generate(VB_PERIOD - VB_THRESHOLD + 1) # Check that we're not getting any versionbit-related errors in # get*info() - assert(not VB_PATTERN.match(self.nodes[0].getinfo()["errors"])) assert(not VB_PATTERN.match(self.nodes[0].getmininginfo()["errors"])) assert(not VB_PATTERN.match(self.nodes[0].getnetworkinfo()["warnings"])) @@ -99,7 +98,6 @@ def run_test(self): # have gotten a different alert due to more than 51/100 blocks # being of unexpected version. # Check that get*info() shows some kind of error. - assert(WARN_UNKNOWN_RULES_MINED in self.nodes[0].getinfo()["errors"]) assert(WARN_UNKNOWN_RULES_MINED in self.nodes[0].getmininginfo()["errors"]) assert(WARN_UNKNOWN_RULES_MINED in self.nodes[0].getnetworkinfo()["warnings"]) @@ -115,7 +113,6 @@ def run_test(self): # Connecting one block should be enough to generate an error. self.nodes[0].generate(1) - assert(WARN_UNKNOWN_RULES_ACTIVE in self.nodes[0].getinfo()["errors"]) assert(WARN_UNKNOWN_RULES_ACTIVE in self.nodes[0].getmininginfo()["errors"]) assert(WARN_UNKNOWN_RULES_ACTIVE in self.nodes[0].getnetworkinfo()["warnings"]) self.stop_nodes() diff --git a/test/functional/rpcnamedargs.py b/test/functional/rpcnamedargs.py index da61cc66e..b3cc681da 100755 --- a/test/functional/rpcnamedargs.py +++ b/test/functional/rpcnamedargs.py @@ -16,10 +16,10 @@ def set_test_params(self): def run_test(self): node = self.nodes[0] - h = node.help(command='getinfo') - assert(h.startswith('getinfo\n')) + h = node.help(command='getblockchaininfo') + assert(h.startswith('getblockchaininfo\n')) - assert_raises_jsonrpc(-8, 'Unknown named parameter', node.help, random='getinfo') + assert_raises_jsonrpc(-8, 'Unknown named parameter', node.help, random='getblockchaininfo') h = node.getblockhash(height=0) node.getblock(blockhash=h) From 8ad663c1fa88d68843e45580deced56112343183 Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Thu, 6 Jul 2017 13:40:09 -0400 Subject: [PATCH 176/382] net: use an interface class rather than signals for message processing Drop boost signals in favor of a stateful class. This will allow the message processing loop to actually move to net_processing in a future step. --- src/init.cpp | 5 +- src/net.cpp | 20 +++---- src/net.h | 25 +++++---- src/net_processing.cpp | 113 ++++++++++++++++---------------------- src/net_processing.h | 36 ++++++------ src/test/DoS_tests.cpp | 22 ++++---- src/test/test_bitcoin.cpp | 6 +- src/test/test_bitcoin.h | 2 + 8 files changed, 103 insertions(+), 126 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index c70c0274b..9e8029971 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -195,11 +195,10 @@ void Shutdown() #endif MapPort(false); UnregisterValidationInterface(peerLogic.get()); - peerLogic.reset(); g_connman.reset(); + peerLogic.reset(); StopTorControl(); - UnregisterNodeSignals(GetNodeSignals()); if (fDumpMempoolLater && gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) { DumpMempool(); } @@ -1268,7 +1267,6 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) peerLogic.reset(new PeerLogicValidation(&connman)); RegisterValidationInterface(peerLogic.get()); - RegisterNodeSignals(GetNodeSignals()); // sanitize comments per BIP-0014, format user agent and check total size std::vector uacomments; @@ -1659,6 +1657,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) connOptions.nMaxFeeler = 1; connOptions.nBestHeight = chainActive.Height(); connOptions.uiInterface = &uiInterface; + connOptions.m_msgproc = peerLogic.get(); connOptions.nSendBufferMaxSize = 1000*gArgs.GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER); connOptions.nReceiveFloodSize = 1000*gArgs.GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER); diff --git a/src/net.cpp b/src/net.cpp index 5e29e6fee..52b66ccab 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -89,10 +89,6 @@ std::string strSubVersion; limitedmap mapAlreadyAskedFor(MAX_INV_SZ); -// Signals for message handling -static CNodeSignals g_signals; -CNodeSignals& GetNodeSignals() { return g_signals; } - void CConnman::AddOneShot(const std::string& strDest) { LOCK(cs_vOneShots); @@ -1114,7 +1110,7 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) { CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addr, CalculateKeyedNetGroup(addr), nonce, addr_bind, "", true); pnode->AddRef(); pnode->fWhitelisted = whitelisted; - GetNodeSignals().InitializeNode(pnode, this); + m_msgproc->InitializeNode(pnode, this); LogPrint(BCLog::NET, "connection from %s accepted\n", addr.ToString()); @@ -1966,7 +1962,7 @@ bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai if (fAddnode) pnode->fAddnode = true; - GetNodeSignals().InitializeNode(pnode, this); + m_msgproc->InitializeNode(pnode, this); { LOCK(cs_vNodes); vNodes.push_back(pnode); @@ -1996,16 +1992,16 @@ void CConnman::ThreadMessageHandler() continue; // Receive messages - bool fMoreNodeWork = GetNodeSignals().ProcessMessages(pnode, this, flagInterruptMsgProc); + bool fMoreNodeWork = m_msgproc->ProcessMessages(pnode, this, flagInterruptMsgProc); fMoreWork |= (fMoreNodeWork && !pnode->fPauseSend); if (flagInterruptMsgProc) return; - // Send messages { LOCK(pnode->cs_sendProcessing); - GetNodeSignals().SendMessages(pnode, this, flagInterruptMsgProc); + m_msgproc->SendMessages(pnode, this, flagInterruptMsgProc); } + if (flagInterruptMsgProc) return; } @@ -2324,6 +2320,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions) // // Start threads // + assert(m_msgproc); InterruptSocks5(false); interruptNet.reset(); flagInterruptMsgProc = false; @@ -2450,9 +2447,10 @@ void CConnman::DeleteNode(CNode* pnode) { assert(pnode); bool fUpdateConnectionTime = false; - GetNodeSignals().FinalizeNode(pnode->GetId(), fUpdateConnectionTime); - if(fUpdateConnectionTime) + m_msgproc->FinalizeNode(pnode->GetId(), fUpdateConnectionTime); + if(fUpdateConnectionTime) { addrman.Connected(pnode->addr); + } delete pnode; } diff --git a/src/net.h b/src/net.h index f170cdac2..4a503f6f3 100644 --- a/src/net.h +++ b/src/net.h @@ -33,7 +33,6 @@ #include #endif -#include class CScheduler; class CNode; @@ -116,7 +115,7 @@ struct CSerializedNetMsg std::string command; }; - +class NetEventsInterface; class CConnman { public: @@ -138,6 +137,7 @@ class CConnman int nMaxFeeler = 0; int nBestHeight = 0; CClientUIInterface* uiInterface = nullptr; + NetEventsInterface* m_msgproc = nullptr; unsigned int nSendBufferMaxSize = 0; unsigned int nReceiveFloodSize = 0; uint64_t nMaxOutboundTimeframe = 0; @@ -158,6 +158,7 @@ class CConnman nMaxFeeler = connOptions.nMaxFeeler; nBestHeight = connOptions.nBestHeight; clientInterface = connOptions.uiInterface; + m_msgproc = connOptions.m_msgproc; nSendBufferMaxSize = connOptions.nSendBufferMaxSize; nReceiveFloodSize = connOptions.nReceiveFloodSize; nMaxOutboundTimeframe = connOptions.nMaxOutboundTimeframe; @@ -398,6 +399,7 @@ class CConnman int nMaxFeeler; std::atomic nBestHeight; CClientUIInterface* clientInterface; + NetEventsInterface* m_msgproc; /** SipHasher seeds for deterministic randomness */ const uint64_t nSeed0, nSeed1; @@ -438,19 +440,18 @@ struct CombinerAll } }; -// Signals for message handling -struct CNodeSignals +/** + * Interface for message handling + */ +class NetEventsInterface { - boost::signals2::signal&), CombinerAll> ProcessMessages; - boost::signals2::signal&), CombinerAll> SendMessages; - boost::signals2::signal InitializeNode; - boost::signals2::signal FinalizeNode; +public: + virtual bool ProcessMessages(CNode* pnode, CConnman* connman, std::atomic& interrupt) = 0; + virtual bool SendMessages(CNode* pnode, CConnman* connman, std::atomic& interrupt) = 0; + virtual void InitializeNode(CNode* pnode, CConnman* connman) = 0; + virtual void FinalizeNode(NodeId id, bool& update_connection_time) = 0; }; - -CNodeSignals& GetNodeSignals(); - - enum { LOCAL_NONE, // unknown diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 1e4713235..9bd1adc57 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -123,11 +123,6 @@ namespace { std::deque> vRelayExpiration; } // namespace -////////////////////////////////////////////////////////////////////////////// -// -// Registration of network node signals. -// - namespace { struct CBlockReject { @@ -265,50 +260,6 @@ void PushNodeVersion(CNode *pnode, CConnman* connman, int64_t nTime) } } -void InitializeNode(CNode *pnode, CConnman* connman) { - CAddress addr = pnode->addr; - std::string addrName = pnode->GetAddrName(); - NodeId nodeid = pnode->GetId(); - { - LOCK(cs_main); - mapNodeState.emplace_hint(mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(addr, std::move(addrName))); - } - if(!pnode->fInbound) - PushNodeVersion(pnode, connman, GetTime()); -} - -void FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) { - fUpdateConnectionTime = false; - LOCK(cs_main); - CNodeState *state = State(nodeid); - assert(state != nullptr); - - if (state->fSyncStarted) - nSyncStarted--; - - if (state->nMisbehavior == 0 && state->fCurrentlyConnected) { - fUpdateConnectionTime = true; - } - - for (const QueuedBlock& entry : state->vBlocksInFlight) { - mapBlocksInFlight.erase(entry.hash); - } - EraseOrphansFor(nodeid); - nPreferredDownload -= state->fPreferredDownload; - nPeersWithValidatedDownloads -= (state->nBlocksInFlightValidHeaders != 0); - assert(nPeersWithValidatedDownloads >= 0); - - mapNodeState.erase(nodeid); - - if (mapNodeState.empty()) { - // Do a consistency check after the last peer is removed. - assert(mapBlocksInFlight.empty()); - assert(nPreferredDownload == 0); - assert(nPeersWithValidatedDownloads == 0); - } - LogPrint(BCLog::NET, "Cleared nodestate for peer=%d\n", nodeid); -} - // Requires cs_main. // 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 @@ -545,6 +496,50 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vectoraddr; + std::string addrName = pnode->GetAddrName(); + NodeId nodeid = pnode->GetId(); + { + LOCK(cs_main); + mapNodeState.emplace_hint(mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(addr, std::move(addrName))); + } + if(!pnode->fInbound) + PushNodeVersion(pnode, connman, GetTime()); +} + +void PeerLogicValidation::FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) { + fUpdateConnectionTime = false; + LOCK(cs_main); + CNodeState *state = State(nodeid); + assert(state != nullptr); + + if (state->fSyncStarted) + nSyncStarted--; + + if (state->nMisbehavior == 0 && state->fCurrentlyConnected) { + fUpdateConnectionTime = true; + } + + for (const QueuedBlock& entry : state->vBlocksInFlight) { + mapBlocksInFlight.erase(entry.hash); + } + EraseOrphansFor(nodeid); + nPreferredDownload -= state->fPreferredDownload; + nPeersWithValidatedDownloads -= (state->nBlocksInFlightValidHeaders != 0); + assert(nPeersWithValidatedDownloads >= 0); + + mapNodeState.erase(nodeid); + + if (mapNodeState.empty()) { + // Do a consistency check after the last peer is removed. + assert(mapBlocksInFlight.empty()); + assert(nPreferredDownload == 0); + assert(nPeersWithValidatedDownloads == 0); + } + LogPrint(BCLog::NET, "Cleared nodestate for peer=%d\n", nodeid); +} + bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) { LOCK(cs_main); CNodeState *state = State(nodeid); @@ -560,22 +555,6 @@ bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) { return true; } -void RegisterNodeSignals(CNodeSignals& nodeSignals) -{ - nodeSignals.ProcessMessages.connect(&ProcessMessages); - nodeSignals.SendMessages.connect(&SendMessages); - nodeSignals.InitializeNode.connect(&InitializeNode); - nodeSignals.FinalizeNode.connect(&FinalizeNode); -} - -void UnregisterNodeSignals(CNodeSignals& nodeSignals) -{ - nodeSignals.ProcessMessages.disconnect(&ProcessMessages); - nodeSignals.SendMessages.disconnect(&SendMessages); - nodeSignals.InitializeNode.disconnect(&InitializeNode); - nodeSignals.FinalizeNode.disconnect(&FinalizeNode); -} - ////////////////////////////////////////////////////////////////////////////// // // mapOrphanTransactions @@ -2661,7 +2640,7 @@ static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman* connman) return false; } -bool ProcessMessages(CNode* pfrom, CConnman* connman, const std::atomic& interruptMsgProc) +bool PeerLogicValidation::ProcessMessages(CNode* pfrom, CConnman* connman, std::atomic& interruptMsgProc) { const CChainParams& chainparams = Params(); // @@ -2798,7 +2777,7 @@ class CompareInvMempoolOrder } }; -bool SendMessages(CNode* pto, CConnman* connman, const std::atomic& interruptMsgProc) +bool PeerLogicValidation::SendMessages(CNode* pto, CConnman* connman, std::atomic& interruptMsgProc) { const Consensus::Params& consensusParams = Params().GetConsensus(); { diff --git a/src/net_processing.h b/src/net_processing.h index 461dc9a90..d79b74fcb 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -22,22 +22,32 @@ static const unsigned int DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN = 100; static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_BASE = 15 * 60 * 1000000; // 15 minutes static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER = 1000; // 1ms/header -/** Register with a network node to receive its signals */ -void RegisterNodeSignals(CNodeSignals& nodeSignals); -/** Unregister a network node */ -void UnregisterNodeSignals(CNodeSignals& nodeSignals); - -class PeerLogicValidation : public CValidationInterface { +class PeerLogicValidation : public CValidationInterface, public NetEventsInterface { private: CConnman* connman; public: - explicit PeerLogicValidation(CConnman* connmanIn); + explicit PeerLogicValidation(CConnman* connman); void BlockConnected(const std::shared_ptr& pblock, const CBlockIndex* pindexConnected, const std::vector& vtxConflicted) override; void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override; void BlockChecked(const CBlock& block, const CValidationState& state) override; void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr& pblock) override; + + + void InitializeNode(CNode* pnode, CConnman* connman) override; + void FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) override; + /** Process protocol messages received from a given node */ + bool ProcessMessages(CNode* pfrom, CConnman* connman, std::atomic& interrupt) override; + /** + * 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& interrupt) override; }; struct CNodeStateStats { @@ -52,16 +62,4 @@ bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats); /** Increase a node's misbehavior score. */ void Misbehaving(NodeId nodeid, int howmuch); -/** Process protocol messages received from a given node */ -bool ProcessMessages(CNode* pfrom, CConnman* connman, const std::atomic& 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, const std::atomic& interrupt); - #endif // BITCOIN_NET_PROCESSING_H diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp index 39a75f7c8..fc5ef84ad 100644 --- a/src/test/DoS_tests.cpp +++ b/src/test/DoS_tests.cpp @@ -50,26 +50,26 @@ BOOST_AUTO_TEST_CASE(DoS_banning) CAddress addr1(ip(0xa0b0c001), NODE_NONE); CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 0, 0, CAddress(), "", true); dummyNode1.SetSendVersion(PROTOCOL_VERSION); - GetNodeSignals().InitializeNode(&dummyNode1, connman); + peerLogic->InitializeNode(&dummyNode1, connman); dummyNode1.nVersion = 1; dummyNode1.fSuccessfullyConnected = true; Misbehaving(dummyNode1.GetId(), 100); // Should get banned - SendMessages(&dummyNode1, connman, interruptDummy); + peerLogic->SendMessages(&dummyNode1, connman, interruptDummy); BOOST_CHECK(connman->IsBanned(addr1)); BOOST_CHECK(!connman->IsBanned(ip(0xa0b0c001|0x0000ff00))); // Different IP, not banned CAddress addr2(ip(0xa0b0c002), NODE_NONE); CNode dummyNode2(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr2, 1, 1, CAddress(), "", true); dummyNode2.SetSendVersion(PROTOCOL_VERSION); - GetNodeSignals().InitializeNode(&dummyNode2, connman); + peerLogic->InitializeNode(&dummyNode2, connman); dummyNode2.nVersion = 1; dummyNode2.fSuccessfullyConnected = true; Misbehaving(dummyNode2.GetId(), 50); - SendMessages(&dummyNode2, connman, interruptDummy); + peerLogic->SendMessages(&dummyNode2, connman, interruptDummy); BOOST_CHECK(!connman->IsBanned(addr2)); // 2 not banned yet... BOOST_CHECK(connman->IsBanned(addr1)); // ... but 1 still should be Misbehaving(dummyNode2.GetId(), 50); - SendMessages(&dummyNode2, connman, interruptDummy); + peerLogic->SendMessages(&dummyNode2, connman, interruptDummy); BOOST_CHECK(connman->IsBanned(addr2)); } @@ -82,17 +82,17 @@ BOOST_AUTO_TEST_CASE(DoS_banscore) CAddress addr1(ip(0xa0b0c001), NODE_NONE); CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 3, 1, CAddress(), "", true); dummyNode1.SetSendVersion(PROTOCOL_VERSION); - GetNodeSignals().InitializeNode(&dummyNode1, connman); + peerLogic->InitializeNode(&dummyNode1, connman); dummyNode1.nVersion = 1; dummyNode1.fSuccessfullyConnected = true; Misbehaving(dummyNode1.GetId(), 100); - SendMessages(&dummyNode1, connman, interruptDummy); + peerLogic->SendMessages(&dummyNode1, connman, interruptDummy); BOOST_CHECK(!connman->IsBanned(addr1)); Misbehaving(dummyNode1.GetId(), 10); - SendMessages(&dummyNode1, connman, interruptDummy); + peerLogic->SendMessages(&dummyNode1, connman, interruptDummy); BOOST_CHECK(!connman->IsBanned(addr1)); Misbehaving(dummyNode1.GetId(), 1); - SendMessages(&dummyNode1, connman, interruptDummy); + peerLogic->SendMessages(&dummyNode1, connman, interruptDummy); BOOST_CHECK(connman->IsBanned(addr1)); gArgs.ForceSetArg("-banscore", std::to_string(DEFAULT_BANSCORE_THRESHOLD)); } @@ -108,12 +108,12 @@ BOOST_AUTO_TEST_CASE(DoS_bantime) CAddress addr(ip(0xa0b0c001), NODE_NONE); CNode dummyNode(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr, 4, 4, CAddress(), "", true); dummyNode.SetSendVersion(PROTOCOL_VERSION); - GetNodeSignals().InitializeNode(&dummyNode, connman); + peerLogic->InitializeNode(&dummyNode, connman); dummyNode.nVersion = 1; dummyNode.fSuccessfullyConnected = true; Misbehaving(dummyNode.GetId(), 100); - SendMessages(&dummyNode, connman, interruptDummy); + peerLogic->SendMessages(&dummyNode, connman, interruptDummy); BOOST_CHECK(connman->IsBanned(addr)); SetMockTime(nStartTime+60*60); diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 194f62ca1..045655983 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -48,7 +48,6 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName) BasicTestingSetup::~BasicTestingSetup() { ECC_Stop(); - g_connman.reset(); } TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(chainName) @@ -86,16 +85,17 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha threadGroup.create_thread(&ThreadScriptCheck); g_connman = std::unique_ptr(new CConnman(0x1337, 0x1337)); // Deterministic randomness for tests. connman = g_connman.get(); - RegisterNodeSignals(GetNodeSignals()); + peerLogic.reset(new PeerLogicValidation(connman)); } TestingSetup::~TestingSetup() { - UnregisterNodeSignals(GetNodeSignals()); threadGroup.interrupt_all(); threadGroup.join_all(); GetMainSignals().FlushBackgroundCallbacks(); GetMainSignals().UnregisterBackgroundSignalScheduler(); + g_connman.reset(); + peerLogic.reset(); UnloadBlockIndex(); delete pcoinsTip; delete pcoinsdbview; diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h index 2ddac2f07..6ada96f88 100644 --- a/src/test/test_bitcoin.h +++ b/src/test/test_bitcoin.h @@ -49,12 +49,14 @@ struct BasicTestingSetup { * Included are data directory, coins database, script check threads setup. */ class CConnman; +class PeerLogicValidation; struct TestingSetup: public BasicTestingSetup { CCoinsViewDB *pcoinsdbview; fs::path pathTemp; boost::thread_group threadGroup; CConnman* connman; CScheduler scheduler; + std::unique_ptr peerLogic; explicit TestingSetup(const std::string& chainName = CBaseChainParams::MAIN); ~TestingSetup(); From 80e2e9d0cec890c5d2f81360ebb81e81c07ccb8c Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Thu, 6 Jul 2017 14:08:23 -0400 Subject: [PATCH 177/382] net: drop unused connman param The copy in PeerLogicValidation can be used instead. --- src/net.cpp | 8 ++++---- src/net.h | 6 +++--- src/net_processing.cpp | 6 +++--- src/net_processing.h | 7 +++---- src/test/DoS_tests.cpp | 22 +++++++++++----------- 5 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/net.cpp b/src/net.cpp index 52b66ccab..587c9e511 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1110,7 +1110,7 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) { CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addr, CalculateKeyedNetGroup(addr), nonce, addr_bind, "", true); pnode->AddRef(); pnode->fWhitelisted = whitelisted; - m_msgproc->InitializeNode(pnode, this); + m_msgproc->InitializeNode(pnode); LogPrint(BCLog::NET, "connection from %s accepted\n", addr.ToString()); @@ -1962,7 +1962,7 @@ bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai if (fAddnode) pnode->fAddnode = true; - m_msgproc->InitializeNode(pnode, this); + m_msgproc->InitializeNode(pnode); { LOCK(cs_vNodes); vNodes.push_back(pnode); @@ -1992,14 +1992,14 @@ void CConnman::ThreadMessageHandler() continue; // Receive messages - bool fMoreNodeWork = m_msgproc->ProcessMessages(pnode, this, flagInterruptMsgProc); + bool fMoreNodeWork = m_msgproc->ProcessMessages(pnode, flagInterruptMsgProc); fMoreWork |= (fMoreNodeWork && !pnode->fPauseSend); if (flagInterruptMsgProc) return; // Send messages { LOCK(pnode->cs_sendProcessing); - m_msgproc->SendMessages(pnode, this, flagInterruptMsgProc); + m_msgproc->SendMessages(pnode, flagInterruptMsgProc); } if (flagInterruptMsgProc) diff --git a/src/net.h b/src/net.h index 4a503f6f3..ca2433aa5 100644 --- a/src/net.h +++ b/src/net.h @@ -446,9 +446,9 @@ struct CombinerAll class NetEventsInterface { public: - virtual bool ProcessMessages(CNode* pnode, CConnman* connman, std::atomic& interrupt) = 0; - virtual bool SendMessages(CNode* pnode, CConnman* connman, std::atomic& interrupt) = 0; - virtual void InitializeNode(CNode* pnode, CConnman* connman) = 0; + virtual bool ProcessMessages(CNode* pnode, std::atomic& interrupt) = 0; + virtual bool SendMessages(CNode* pnode, std::atomic& interrupt) = 0; + virtual void InitializeNode(CNode* pnode) = 0; virtual void FinalizeNode(NodeId id, bool& update_connection_time) = 0; }; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 9bd1adc57..b8900d988 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -496,7 +496,7 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vectoraddr; std::string addrName = pnode->GetAddrName(); NodeId nodeid = pnode->GetId(); @@ -2640,7 +2640,7 @@ static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman* connman) return false; } -bool PeerLogicValidation::ProcessMessages(CNode* pfrom, CConnman* connman, std::atomic& interruptMsgProc) +bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic& interruptMsgProc) { const CChainParams& chainparams = Params(); // @@ -2777,7 +2777,7 @@ class CompareInvMempoolOrder } }; -bool PeerLogicValidation::SendMessages(CNode* pto, CConnman* connman, std::atomic& interruptMsgProc) +bool PeerLogicValidation::SendMessages(CNode* pto, std::atomic& interruptMsgProc) { const Consensus::Params& consensusParams = Params().GetConsensus(); { diff --git a/src/net_processing.h b/src/net_processing.h index d79b74fcb..79745cdd4 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -35,19 +35,18 @@ class PeerLogicValidation : public CValidationInterface, public NetEventsInterfa void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr& pblock) override; - void InitializeNode(CNode* pnode, CConnman* connman) override; + void InitializeNode(CNode* pnode) override; void FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) override; /** Process protocol messages received from a given node */ - bool ProcessMessages(CNode* pfrom, CConnman* connman, std::atomic& interrupt) override; + bool ProcessMessages(CNode* pfrom, std::atomic& interrupt) override; /** * 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& interrupt) override; + bool SendMessages(CNode* pto, std::atomic& interrupt) override; }; struct CNodeStateStats { diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp index fc5ef84ad..b88ad5ed1 100644 --- a/src/test/DoS_tests.cpp +++ b/src/test/DoS_tests.cpp @@ -50,26 +50,26 @@ BOOST_AUTO_TEST_CASE(DoS_banning) CAddress addr1(ip(0xa0b0c001), NODE_NONE); CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 0, 0, CAddress(), "", true); dummyNode1.SetSendVersion(PROTOCOL_VERSION); - peerLogic->InitializeNode(&dummyNode1, connman); + peerLogic->InitializeNode(&dummyNode1); dummyNode1.nVersion = 1; dummyNode1.fSuccessfullyConnected = true; Misbehaving(dummyNode1.GetId(), 100); // Should get banned - peerLogic->SendMessages(&dummyNode1, connman, interruptDummy); + peerLogic->SendMessages(&dummyNode1, interruptDummy); BOOST_CHECK(connman->IsBanned(addr1)); BOOST_CHECK(!connman->IsBanned(ip(0xa0b0c001|0x0000ff00))); // Different IP, not banned CAddress addr2(ip(0xa0b0c002), NODE_NONE); CNode dummyNode2(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr2, 1, 1, CAddress(), "", true); dummyNode2.SetSendVersion(PROTOCOL_VERSION); - peerLogic->InitializeNode(&dummyNode2, connman); + peerLogic->InitializeNode(&dummyNode2); dummyNode2.nVersion = 1; dummyNode2.fSuccessfullyConnected = true; Misbehaving(dummyNode2.GetId(), 50); - peerLogic->SendMessages(&dummyNode2, connman, interruptDummy); + peerLogic->SendMessages(&dummyNode2, interruptDummy); BOOST_CHECK(!connman->IsBanned(addr2)); // 2 not banned yet... BOOST_CHECK(connman->IsBanned(addr1)); // ... but 1 still should be Misbehaving(dummyNode2.GetId(), 50); - peerLogic->SendMessages(&dummyNode2, connman, interruptDummy); + peerLogic->SendMessages(&dummyNode2, interruptDummy); BOOST_CHECK(connman->IsBanned(addr2)); } @@ -82,17 +82,17 @@ BOOST_AUTO_TEST_CASE(DoS_banscore) CAddress addr1(ip(0xa0b0c001), NODE_NONE); CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 3, 1, CAddress(), "", true); dummyNode1.SetSendVersion(PROTOCOL_VERSION); - peerLogic->InitializeNode(&dummyNode1, connman); + peerLogic->InitializeNode(&dummyNode1); dummyNode1.nVersion = 1; dummyNode1.fSuccessfullyConnected = true; Misbehaving(dummyNode1.GetId(), 100); - peerLogic->SendMessages(&dummyNode1, connman, interruptDummy); + peerLogic->SendMessages(&dummyNode1, interruptDummy); BOOST_CHECK(!connman->IsBanned(addr1)); Misbehaving(dummyNode1.GetId(), 10); - peerLogic->SendMessages(&dummyNode1, connman, interruptDummy); + peerLogic->SendMessages(&dummyNode1, interruptDummy); BOOST_CHECK(!connman->IsBanned(addr1)); Misbehaving(dummyNode1.GetId(), 1); - peerLogic->SendMessages(&dummyNode1, connman, interruptDummy); + peerLogic->SendMessages(&dummyNode1, interruptDummy); BOOST_CHECK(connman->IsBanned(addr1)); gArgs.ForceSetArg("-banscore", std::to_string(DEFAULT_BANSCORE_THRESHOLD)); } @@ -108,12 +108,12 @@ BOOST_AUTO_TEST_CASE(DoS_bantime) CAddress addr(ip(0xa0b0c001), NODE_NONE); CNode dummyNode(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr, 4, 4, CAddress(), "", true); dummyNode.SetSendVersion(PROTOCOL_VERSION); - peerLogic->InitializeNode(&dummyNode, connman); + peerLogic->InitializeNode(&dummyNode); dummyNode.nVersion = 1; dummyNode.fSuccessfullyConnected = true; Misbehaving(dummyNode.GetId(), 100); - peerLogic->SendMessages(&dummyNode, connman, interruptDummy); + peerLogic->SendMessages(&dummyNode, interruptDummy); BOOST_CHECK(connman->IsBanned(addr)); SetMockTime(nStartTime+60*60); From 769684132985cd67cbe8c7629a917aba6a333688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= Date: Thu, 24 Aug 2017 18:35:06 +0100 Subject: [PATCH 178/382] Fix style in -stdin and -stdinrpcpass handling --- src/bitcoin-cli.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index fca6083ea..3c94c99b3 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -296,19 +296,22 @@ int CommandLineRPC(int argc, char *argv[]) } std::string rpcPass; if (gArgs.GetBoolArg("-stdinrpcpass", false)) { - if(!std::getline(std::cin,rpcPass)) + if (!std::getline(std::cin, rpcPass)) { throw std::runtime_error("-stdinrpcpass specified but failed to read from standard input"); + } gArgs.ForceSetArg("-rpcpassword", rpcPass); } std::vector args = std::vector(&argv[1], &argv[argc]); if (gArgs.GetBoolArg("-stdin", false)) { // Read one arg per line from stdin and append std::string line; - while (std::getline(std::cin,line)) + while (std::getline(std::cin, line)) { args.push_back(line); + } } - if (args.size() < 1) + if (args.size() < 1) { throw std::runtime_error("too few parameters (need at least command)"); + } std::string strMethod = args[0]; args.erase(args.begin()); // Remove trailing method name from arguments vector From e1274947d4574bb83a020d0e178f0e5db7fc6282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= Date: Wed, 6 Sep 2017 16:52:25 +0100 Subject: [PATCH 179/382] [test] Improve assert_raises_jsonrpc docstring --- test/functional/test_framework/util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index a14cda07d..a5bc495df 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -62,13 +62,13 @@ def assert_raises_jsonrpc(code, message, fun, *args, **kwds): Calls function `fun` with arguments `args` and `kwds`. Catches a JSONRPCException and verifies that the error code and message are as expected. Throws AssertionError if - no JSONRPCException was returned or if the error code/message are not as expected. + no JSONRPCException was raised or if the error code/message are not as expected. Args: code (int), optional: the error code returned by the RPC call (defined in src/rpc/protocol.h). Set to None if checking the error code is not required. message (string), optional: [a substring of] the error string returned by the - RPC call. Set to None if checking the error string is not required + RPC call. Set to None if checking the error string is not required. fun (function): the function to call. This should be the name of an RPC. args*: positional arguments for the function. kwds**: named arguments for the function. From 5c18a84b9a49e3f9dacf0502dbf7d5d755f38da6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= Date: Wed, 6 Sep 2017 17:07:21 +0100 Subject: [PATCH 180/382] [test] Add support for custom arguments to TestNodeCLI --- test/functional/test_framework/test_node.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index f58a372a1..363025740 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -155,8 +155,16 @@ class TestNodeCLI(): """Interface to bitcoin-cli for an individual node""" def __init__(self, binary, datadir): + self.args = [] self.binary = binary self.datadir = datadir + self.input = None + + def __call__(self, *args, input=None): + # TestNodeCLI is callable with bitcoin-cli command-line args + self.args = [str(arg) for arg in args] + self.input = input + return self def __getattr__(self, command): def dispatcher(*args, **kwargs): @@ -169,9 +177,9 @@ def send_cli(self, command, *args, **kwargs): pos_args = [str(arg) for arg in args] named_args = [str(key) + "=" + str(value) for (key, value) in kwargs.items()] assert not (pos_args and named_args), "Cannot use positional arguments and named arguments in the same bitcoin-cli call" - p_args = [self.binary, "-datadir=" + self.datadir] + p_args = [self.binary, "-datadir=" + self.datadir] + self.args if named_args: p_args += ["-named"] p_args += [command] + pos_args + named_args - cli_output = subprocess.check_output(p_args, universal_newlines=True) + cli_output = subprocess.check_output(p_args, input=self.input, universal_newlines=True) return json.loads(cli_output, parse_float=decimal.Decimal) From 232e3e8471edb346c09f906c996b2f350cabc72a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= Date: Wed, 6 Sep 2017 16:35:57 +0100 Subject: [PATCH 181/382] [test] Add assert_raises_process_error to assert process errors --- test/functional/test_framework/util.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index a5bc495df..b2d8199d1 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -12,6 +12,7 @@ import os import random import re +from subprocess import CalledProcessError import time from . import coverage @@ -57,6 +58,30 @@ def assert_raises_message(exc, message, fun, *args, **kwds): else: raise AssertionError("No exception raised") +def assert_raises_process_error(returncode, output, fun, *args, **kwds): + """Execute a process and asserts the process return code and output. + + Calls function `fun` with arguments `args` and `kwds`. Catches a CalledProcessError + and verifies that the return code and output are as expected. Throws AssertionError if + no CalledProcessError was raised or if the return code and output are not as expected. + + Args: + returncode (int): the process return code. + output (string): [a substring of] the process output. + fun (function): the function to call. This should execute a process. + args*: positional arguments for the function. + kwds**: named arguments for the function. + """ + try: + fun(*args, **kwds) + except CalledProcessError as e: + if returncode != e.returncode: + raise AssertionError("Unexpected returncode %i" % e.returncode) + if output not in e.output: + raise AssertionError("Expected substring not found:" + e.output) + else: + raise AssertionError("No exception raised") + def assert_raises_jsonrpc(code, message, fun, *args, **kwds): """Run an RPC and verify that a specific JSONRPC exception code and message is raised. From ce379b47b92d6c04250c21719bb1abfb41c586ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= Date: Wed, 6 Sep 2017 16:36:13 +0100 Subject: [PATCH 182/382] [test] Replace check_output with low level version --- test/functional/test_framework/test_node.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 363025740..12dab57a0 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -181,5 +181,10 @@ def send_cli(self, command, *args, **kwargs): if named_args: p_args += ["-named"] p_args += [command] + pos_args + named_args - cli_output = subprocess.check_output(p_args, input=self.input, universal_newlines=True) - return json.loads(cli_output, parse_float=decimal.Decimal) + process = subprocess.Popen(p_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + cli_stdout, cli_stderr = process.communicate(input=self.input) + returncode = process.poll() + if returncode: + # Ignore cli_stdout, raise with cli_stderr + raise subprocess.CalledProcessError(returncode, self.binary, output=cli_stderr) + return json.loads(cli_stdout, parse_float=decimal.Decimal) From 29e1dfbd9793203479b8499c55ffec2086f5ab39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= Date: Fri, 25 Aug 2017 03:31:27 +0100 Subject: [PATCH 183/382] [test] Add bitcoin-cli -stdin and -stdinrpcpass functional tests --- test/functional/bitcoin_cli.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/test/functional/bitcoin_cli.py b/test/functional/bitcoin_cli.py index 04847252e..ffed5b0d3 100755 --- a/test/functional/bitcoin_cli.py +++ b/test/functional/bitcoin_cli.py @@ -4,7 +4,7 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test bitcoin-cli""" from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal +from test_framework.util import assert_equal, assert_raises_process_error, get_auth_cookie class TestBitcoinCli(BitcoinTestFramework): @@ -16,16 +16,24 @@ def run_test(self): """Main test logic""" self.log.info("Compare responses from gewalletinfo RPC and `bitcoin-cli getwalletinfo`") - cli_get_info = self.nodes[0].cli.getwalletinfo() - rpc_get_info = self.nodes[0].getwalletinfo() - - assert_equal(cli_get_info, rpc_get_info) + cli_response = self.nodes[0].cli.getwalletinfo() + rpc_response = self.nodes[0].getwalletinfo() + assert_equal(cli_response, rpc_response) self.log.info("Compare responses from getblockchaininfo RPC and `bitcoin-cli getblockchaininfo`") - cli_get_info = self.nodes[0].cli.getblockchaininfo() - rpc_get_info = self.nodes[0].getblockchaininfo() + cli_response = self.nodes[0].cli.getblockchaininfo() + rpc_response = self.nodes[0].getblockchaininfo() + assert_equal(cli_response, rpc_response) + + user, password = get_auth_cookie(self.nodes[0].datadir) + + self.log.info("Test -stdinrpcpass option") + assert_equal(0, self.nodes[0].cli('-rpcuser=%s' % user, '-stdinrpcpass', input=password).getblockcount()) + assert_raises_process_error(1, "incorrect rpcuser or rpcpassword", self.nodes[0].cli('-rpcuser=%s' % user, '-stdinrpcpass', input="foo").echo) - assert_equal(cli_get_info, rpc_get_info) + self.log.info("Test -stdin and -stdinrpcpass") + assert_equal(["foo", "bar"], self.nodes[0].cli('-rpcuser=%s' % user, '-stdin', '-stdinrpcpass', input=password + "\nfoo\nbar").echo()) + assert_raises_process_error(1, "incorrect rpcuser or rpcpassword", self.nodes[0].cli('-rpcuser=%s' % user, '-stdin', '-stdinrpcpass', input="foo").echo) if __name__ == '__main__': TestBitcoinCli().main() From 58d91af59e6417ad4dde0c5e1c9bfd18017d755d Mon Sep 17 00:00:00 2001 From: MeshCollider Date: Tue, 22 Aug 2017 21:02:25 +1200 Subject: [PATCH 184/382] Fix race for mapBlockIndex in AppInitMain --- src/init.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 3f68ea102..af1d4a085 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1631,9 +1631,16 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) // ********************************************************* Step 11: start node + int chain_active_height; + //// debug print - LogPrintf("mapBlockIndex.size() = %u\n", mapBlockIndex.size()); - LogPrintf("nBestHeight = %d\n", chainActive.Height()); + { + LOCK(cs_main); + LogPrintf("mapBlockIndex.size() = %u\n", mapBlockIndex.size()); + chain_active_height = chainActive.Height(); + } + LogPrintf("nBestHeight = %d\n", chain_active_height); + if (gArgs.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) StartTorControl(threadGroup, scheduler); @@ -1649,7 +1656,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) connOptions.nMaxOutbound = std::min(MAX_OUTBOUND_CONNECTIONS, connOptions.nMaxConnections); connOptions.nMaxAddnode = MAX_ADDNODE_CONNECTIONS; connOptions.nMaxFeeler = 1; - connOptions.nBestHeight = chainActive.Height(); + connOptions.nBestHeight = chain_active_height; connOptions.uiInterface = &uiInterface; connOptions.nSendBufferMaxSize = 1000*gArgs.GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER); connOptions.nReceiveFloodSize = 1000*gArgs.GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER); From 35aeabec62cf10f3c0de297b1189f0a669b69d6e Mon Sep 17 00:00:00 2001 From: MeshCollider Date: Thu, 24 Aug 2017 13:45:26 +1200 Subject: [PATCH 185/382] Make fReindex atomic to avoid race --- src/validation.cpp | 4 ++-- src/validation.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/validation.cpp b/src/validation.cpp index d1a8b8460..0d4fdf79a 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -66,7 +66,7 @@ CWaitableCriticalSection csBestBlock; CConditionVariable cvBlockChange; int nScriptCheckThreads = 0; std::atomic_bool fImporting(false); -bool fReindex = false; +std::atomic_bool fReindex(false); bool fTxIndex = false; bool fHavePruned = false; bool fPruneMode = false; @@ -3523,7 +3523,7 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) // Check whether we need to continue reindexing bool fReindexing = false; pblocktree->ReadReindexing(fReindexing); - fReindex |= fReindexing; + if(fReindexing) fReindex = true; // Check whether we have a transaction index pblocktree->ReadFlag("txindex", fTxIndex); diff --git a/src/validation.h b/src/validation.h index d0f6cdc13..10511fce3 100644 --- a/src/validation.h +++ b/src/validation.h @@ -167,7 +167,7 @@ extern const std::string strMessageMagic; extern CWaitableCriticalSection csBestBlock; extern CConditionVariable cvBlockChange; extern std::atomic_bool fImporting; -extern bool fReindex; +extern std::atomic_bool fReindex; extern int nScriptCheckThreads; extern bool fTxIndex; extern bool fIsBareMultisigStd; From 731065b114452ff770320d09639448b3c9a74b0a Mon Sep 17 00:00:00 2001 From: MeshCollider Date: Thu, 24 Aug 2017 13:58:28 +1200 Subject: [PATCH 186/382] Consistent parameter names in txdb.h --- src/txdb.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/txdb.h b/src/txdb.h index d1cd5a425..ab3a9f7e3 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -115,12 +115,12 @@ class CBlockTreeDB : public CDBWrapper void operator=(const CBlockTreeDB&); public: bool WriteBatchSync(const std::vector >& fileInfo, int nLastFile, const std::vector& blockinfo); - bool ReadBlockFileInfo(int nFile, CBlockFileInfo &fileinfo); + bool ReadBlockFileInfo(int nFile, CBlockFileInfo &info); bool ReadLastBlockFile(int &nFile); - bool WriteReindexing(bool fReindex); - bool ReadReindexing(bool &fReindex); + bool WriteReindexing(bool fReindexing); + bool ReadReindexing(bool &fReindexing); bool ReadTxIndex(const uint256 &txid, CDiskTxPos &pos); - bool WriteTxIndex(const std::vector > &list); + bool WriteTxIndex(const std::vector > &vect); bool WriteFlag(const std::string &name, bool fValue); bool ReadFlag(const std::string &name, bool &fValue); bool LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function insertBlockIndex); From 3f0ee3e5011430461c7c61f4f2d406bf3cd8b54b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Sun, 25 Jun 2017 04:16:21 +0200 Subject: [PATCH 187/382] Proper indentation for CheckTxInputs and other minor fixes --- src/consensus/tx_verify.cpp | 76 +++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp index 0671cbc13..25647ba6a 100644 --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -13,7 +13,7 @@ #include "chain.h" #include "coins.h" #include "utilmoneystr.h" - + bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) { if (tx.nLockTime == 0) @@ -207,44 +207,46 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight) { - // This doesn't trigger the DoS code on purpose; if it did, it would make it easier - // for an attacker to attempt to split the network. - if (!inputs.HaveInputs(tx)) - return state.Invalid(false, 0, "", "Inputs unavailable"); - - CAmount nValueIn = 0; - CAmount nFees = 0; - for (unsigned int i = 0; i < tx.vin.size(); i++) - { - const COutPoint &prevout = tx.vin[i].prevout; - const Coin& coin = inputs.AccessCoin(prevout); - assert(!coin.IsSpent()); - - // If prev is coinbase, check that it's matured - if (coin.IsCoinBase()) { - if (nSpendHeight - coin.nHeight < COINBASE_MATURITY) - return state.Invalid(false, - REJECT_INVALID, "bad-txns-premature-spend-of-coinbase", - strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight)); - } - - // Check for negative or overflow input values - nValueIn += coin.out.nValue; - if (!MoneyRange(coin.out.nValue) || !MoneyRange(nValueIn)) - return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange"); + // This doesn't trigger the DoS code on purpose; if it did, it would make it easier + // for an attacker to attempt to split the network. + if (!inputs.HaveInputs(tx)) { + return state.Invalid(false, 0, "", "Inputs unavailable"); + } + + CAmount nValueIn = 0; + CAmount nFees = 0; + for (unsigned int i = 0; i < tx.vin.size(); ++i) { + const COutPoint &prevout = tx.vin[i].prevout; + const Coin& coin = inputs.AccessCoin(prevout); + assert(!coin.IsSpent()); + + // If prev is coinbase, check that it's matured + if (coin.IsCoinBase() && nSpendHeight - coin.nHeight < COINBASE_MATURITY) { + return state.Invalid(false, + REJECT_INVALID, "bad-txns-premature-spend-of-coinbase", + strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight)); + } + // Check for negative or overflow input values + nValueIn += coin.out.nValue; + if (!MoneyRange(coin.out.nValue) || !MoneyRange(nValueIn)) { + return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange"); } + } + + if (nValueIn < tx.GetValueOut()) { + return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false, + strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(tx.GetValueOut()))); + } - if (nValueIn < tx.GetValueOut()) - return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false, - strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(tx.GetValueOut()))); - - // Tally transaction fees - CAmount nTxFee = nValueIn - tx.GetValueOut(); - if (nTxFee < 0) - return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-negative"); - nFees += nTxFee; - if (!MoneyRange(nFees)) - return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange"); + // Tally transaction fees + CAmount nTxFee = nValueIn - tx.GetValueOut(); + if (nTxFee < 0) { + return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-negative"); + } + nFees += nTxFee; + if (!MoneyRange(nFees)) { + return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange"); + } return true; } From 061297f0ac5ddedf0e8457a5660e01a742b15d55 Mon Sep 17 00:00:00 2001 From: jjz Date: Tue, 5 Sep 2017 06:03:42 +0800 Subject: [PATCH 188/382] Ensure that data types are consistent 1. nStatus of CBlockIndex is consistent with the definition of Enum(BlockStatus) 2. The BlockHeader is consistent with the type of variable defined in CBlockHeader --- src/chain.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/chain.h b/src/chain.h index ef7e6f955..f1036e5d9 100644 --- a/src/chain.h +++ b/src/chain.h @@ -204,14 +204,14 @@ class CBlockIndex unsigned int nChainTx; //! Verification status of this block. See enum BlockStatus - unsigned int nStatus; + uint32_t nStatus; //! block header - int nVersion; + int32_t nVersion; uint256 hashMerkleRoot; - unsigned int nTime; - unsigned int nBits; - unsigned int nNonce; + uint32_t nTime; + uint32_t nBits; + uint32_t nNonce; //! (memory only) Sequential id assigned to distinguish order in which blocks are received. int32_t nSequenceId; From d2be7b25b55a80842265a74f5fee48e5e6539446 Mon Sep 17 00:00:00 2001 From: James Evans Date: Sat, 22 Jul 2017 18:49:45 +0200 Subject: [PATCH 189/382] Typo in optionsdialog.ui Tooltip displayed ampersand incorrectly, & should be in text. --- src/qt/forms/optionsdialog.ui | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index 14078b9ee..e31bfee05 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -199,10 +199,10 @@ - Accept connections from outside + Accept connections from outside. - Allow incoming connections + Allow incomin&g connections @@ -399,7 +399,7 @@ Connect to the Bitcoin network through a separate SOCKS5 proxy for Tor hidden services. - Use separate SOCKS5 proxy to reach peers via Tor hidden services: + Use separate SOCKS&5 proxy to reach peers via Tor hidden services: @@ -507,10 +507,10 @@ - &Hide the icon from the system tray. + Hide the icon from the system tray. - Hide tray icon + &Hide tray icon @@ -610,7 +610,7 @@ Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |. - Third party transaction URLs + &Third party transaction URLs thirdPartyTxUrls From f151f5f50f05727cfd3445d8f678a8a2ac6c8409 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Thu, 7 Sep 2017 10:11:44 -0700 Subject: [PATCH 190/382] [macOS] remove Growl support, remove unused code --- src/qt/macnotificationhandler.h | 7 +--- src/qt/macnotificationhandler.mm | 14 ------- src/qt/notificator.cpp | 66 -------------------------------- src/qt/notificator.h | 3 -- 4 files changed, 2 insertions(+), 88 deletions(-) diff --git a/src/qt/macnotificationhandler.h b/src/qt/macnotificationhandler.h index d4749b3d5..3a005c3c4 100644 --- a/src/qt/macnotificationhandler.h +++ b/src/qt/macnotificationhandler.h @@ -7,20 +7,17 @@ #include -/** Macintosh-specific notification handler (supports UserNotificationCenter and Growl). +/** Macintosh-specific notification handler (supports UserNotificationCenter). */ class MacNotificationHandler : public QObject { Q_OBJECT public: - /** shows a 10.8+ UserNotification in the UserNotificationCenter + /** shows a macOS 10.8+ UserNotification in the UserNotificationCenter */ void showNotification(const QString &title, const QString &text); - /** executes AppleScript */ - void sendAppleScript(const QString &script); - /** check if OS can handle UserNotifications */ bool hasUserNotificationCenterSupport(void); static MacNotificationHandler *instance(); diff --git a/src/qt/macnotificationhandler.mm b/src/qt/macnotificationhandler.mm index 4c96d08c8..1b16c5f52 100644 --- a/src/qt/macnotificationhandler.mm +++ b/src/qt/macnotificationhandler.mm @@ -47,20 +47,6 @@ - (NSString *)__bundleIdentifier } } -// sendAppleScript just take a QString and executes it as apple script -void MacNotificationHandler::sendAppleScript(const QString &script) -{ - QByteArray utf8 = script.toUtf8(); - char* cString = (char *)utf8.constData(); - NSString *scriptApple = [[NSString alloc] initWithUTF8String:cString]; - - NSAppleScript *as = [[NSAppleScript alloc] initWithSource:scriptApple]; - NSDictionary *err = nil; - [as executeAndReturnError:&err]; - [as release]; - [scriptApple release]; -} - bool MacNotificationHandler::hasUserNotificationCenterSupport(void) { Class possibleClass = NSClassFromString(@"NSUserNotificationCenter"); diff --git a/src/qt/notificator.cpp b/src/qt/notificator.cpp index a7a7a4ce1..937928315 100644 --- a/src/qt/notificator.cpp +++ b/src/qt/notificator.cpp @@ -60,22 +60,6 @@ Notificator::Notificator(const QString &_programName, QSystemTrayIcon *_trayIcon if( MacNotificationHandler::instance()->hasUserNotificationCenterSupport()) { mode = UserNotificationCenter; } - else { - // Check if Growl is installed (based on Qt's tray icon implementation) - CFURLRef cfurl; - OSStatus status = LSGetApplicationForInfo(kLSUnknownType, kLSUnknownCreator, CFSTR("growlTicket"), kLSRolesAll, 0, &cfurl); - if (status != kLSApplicationNotFoundErr) { - CFBundleRef bundle = CFBundleCreate(0, cfurl); - if (CFStringCompare(CFBundleGetIdentifier(bundle), CFSTR("com.Growl.GrowlHelperApp"), kCFCompareCaseInsensitive | kCFCompareBackwards) == kCFCompareEqualTo) { - if (CFStringHasSuffix(CFURLGetString(cfurl), CFSTR("/Growl.app/"))) - mode = Growl13; - else - mode = Growl12; - } - CFRelease(cfurl); - CFRelease(bundle); - } - } #endif } @@ -241,52 +225,6 @@ void Notificator::notifySystray(Class cls, const QString &title, const QString & // Based on Qt's tray icon implementation #ifdef Q_OS_MAC -void Notificator::notifyGrowl(Class cls, const QString &title, const QString &text, const QIcon &icon) -{ - const QString script( - "tell application \"%5\"\n" - " set the allNotificationsList to {\"Notification\"}\n" // -- Make a list of all the notification types (all) - " set the enabledNotificationsList to {\"Notification\"}\n" // -- Make a list of the notifications (enabled) - " register as application \"%1\" all notifications allNotificationsList default notifications enabledNotificationsList\n" // -- Register our script with Growl - " notify with name \"Notification\" title \"%2\" description \"%3\" application name \"%1\"%4\n" // -- Send a Notification - "end tell" - ); - - QString notificationApp(QApplication::applicationName()); - if (notificationApp.isEmpty()) - notificationApp = "Application"; - - QPixmap notificationIconPixmap; - if (icon.isNull()) { // If no icon specified, set icon based on class - QStyle::StandardPixmap sicon = QStyle::SP_MessageBoxQuestion; - switch (cls) - { - case Information: sicon = QStyle::SP_MessageBoxInformation; break; - case Warning: sicon = QStyle::SP_MessageBoxWarning; break; - case Critical: sicon = QStyle::SP_MessageBoxCritical; break; - } - notificationIconPixmap = QApplication::style()->standardPixmap(sicon); - } - else { - QSize size = icon.actualSize(QSize(48, 48)); - notificationIconPixmap = icon.pixmap(size); - } - - QString notificationIcon; - QTemporaryFile notificationIconFile; - if (!notificationIconPixmap.isNull() && notificationIconFile.open()) { - QImageWriter writer(¬ificationIconFile, "PNG"); - if (writer.write(notificationIconPixmap.toImage())) - notificationIcon = QString(" image from location \"file://%1\"").arg(notificationIconFile.fileName()); - } - - QString quotedTitle(title), quotedText(text); - quotedTitle.replace("\\", "\\\\").replace("\"", "\\"); - quotedText.replace("\\", "\\\\").replace("\"", "\\"); - QString growlApp(this->mode == Notificator::Growl13 ? "Growl" : "GrowlHelperApp"); - MacNotificationHandler::instance()->sendAppleScript(script.arg(notificationApp, quotedTitle, quotedText, notificationIcon, growlApp)); -} - void Notificator::notifyMacUserNotificationCenter(Class cls, const QString &title, const QString &text, const QIcon &icon) { // icon is not supported by the user notification center yet. OSX will use the app icon. MacNotificationHandler::instance()->showNotification(title, text); @@ -310,10 +248,6 @@ void Notificator::notify(Class cls, const QString &title, const QString &text, c case UserNotificationCenter: notifyMacUserNotificationCenter(cls, title, text, icon); break; - case Growl12: - case Growl13: - notifyGrowl(cls, title, text, icon); - break; #endif default: if(cls == Critical) diff --git a/src/qt/notificator.h b/src/qt/notificator.h index f92b791d4..67f2b1df6 100644 --- a/src/qt/notificator.h +++ b/src/qt/notificator.h @@ -58,8 +58,6 @@ public Q_SLOTS: None, /**< Ignore informational notifications, and show a modal pop-up dialog for Critical notifications. */ Freedesktop, /**< Use DBus org.freedesktop.Notifications */ QSystemTray, /**< Use QSystemTray::showMessage */ - Growl12, /**< Use the Growl 1.2 notification system (Mac only) */ - Growl13, /**< Use the Growl 1.3 notification system (Mac only) */ UserNotificationCenter /**< Use the 10.8+ User Notification Center (Mac only) */ }; QString programName; @@ -72,7 +70,6 @@ public Q_SLOTS: #endif void notifySystray(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout); #ifdef Q_OS_MAC - void notifyGrowl(Class cls, const QString &title, const QString &text, const QIcon &icon); void notifyMacUserNotificationCenter(Class cls, const QString &title, const QString &text, const QIcon &icon); #endif }; From 2525b972af6645ca239ac1078cffb132b402bfbb Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Thu, 7 Sep 2017 14:26:20 -0400 Subject: [PATCH 191/382] net: stop both net/net_processing before destroying them This should avoid either attempting to use an invalid reference/pointer to the other. --- src/init.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/init.cpp b/src/init.cpp index 9e8029971..810368b9d 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -194,9 +194,13 @@ void Shutdown() } #endif MapPort(false); + + // Because these depend on each-other, we make sure that neither can be + // using the other before destroying them. UnregisterValidationInterface(peerLogic.get()); - g_connman.reset(); + g_connman->Stop(); peerLogic.reset(); + g_connman.reset(); StopTorControl(); if (fDumpMempoolLater && gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) { From aa2e0f09ec94dd0908f792ebc2249859ad174586 Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Thu, 7 Sep 2017 16:17:58 -0400 Subject: [PATCH 192/382] travis: filter out pyenv --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index dadac3165..3d9c6bbaa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,6 +38,7 @@ env: before_install: - export PATH=$(echo $PATH | tr ':' "\n" | sed '/\/opt\/python/d' | tr "\n" ":" | sed "s|::|:|g") + - export PATH=$(echo $PATH | tr ':' "\n" | sed '/\/opt\/pyenv/d' | tr "\n" ":" | sed "s|::|:|g") install: - if [ -n "$DPKG_ADD_ARCH" ]; then sudo dpkg --add-architecture "$DPKG_ADD_ARCH" ; fi - if [ -n "$PACKAGES" ]; then travis_retry sudo apt-get update; fi From 77aa9e59ea0e6a000a0aea5dec4ef9585356147d Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 6 Jul 2017 15:01:27 +0200 Subject: [PATCH 193/382] test: Check RPC argument mapping Parse the dispatch tables from the server implementation files, and the conversion table from the client. Perform the following consistency checks: - Arguments defined in conversion table, must be present in dispatch table. If not, it was probably forgotten to add them to the dispatch table, and they will not work. - Arguments defined in conversion table must have the same names as in the dispatch table. If not, they will not work. - All aliases for an argument must either be present in the conversion table, or not. Anything in between means an oversight and some aliases won't work. Any of these results in an error. It also performs a consistency check to see if the same named argument is sometimes converted, and sometimes not. E.g. one RPC call might have a 'verbose' argument that is converted, another RPC call might have one that is not converted. This is not necessarily wrong, but points at a possible error (as well as makes the API harder to memorize) - so it is emitted as a warning (could upgrade this to error). --- .travis.yml | 1 + contrib/devtools/check-rpc-mappings.py | 158 +++++++++++++++++++++++++ 2 files changed, 159 insertions(+) create mode 100755 contrib/devtools/check-rpc-mappings.py diff --git a/.travis.yml b/.travis.yml index 061683ac6..e81d1a727 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,6 +45,7 @@ install: before_script: - if [ "$CHECK_DOC" = 1 -a "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then contrib/devtools/commit-script-check.sh $TRAVIS_COMMIT_RANGE; fi - if [ "$CHECK_DOC" = 1 ]; then contrib/devtools/check-doc.py; fi + - if [ "$CHECK_DOC" = 1 ]; then contrib/devtools/check-rpc-mappings.py .; fi - unset CC; unset CXX - mkdir -p depends/SDKs depends/sdk-sources - if [ -n "$OSX_SDK" -a ! -f depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then curl --location --fail $SDK_URL/MacOSX${OSX_SDK}.sdk.tar.gz -o depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz; fi diff --git a/contrib/devtools/check-rpc-mappings.py b/contrib/devtools/check-rpc-mappings.py new file mode 100755 index 000000000..d2698de04 --- /dev/null +++ b/contrib/devtools/check-rpc-mappings.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Check RPC argument consistency.""" + +from collections import defaultdict +import os +import re +import sys + +# Source files (relative to root) to scan for dispatch tables +SOURCES = [ + "src/rpc/server.cpp", + "src/rpc/blockchain.cpp", + "src/rpc/mining.cpp", + "src/rpc/misc.cpp", + "src/rpc/net.cpp", + "src/rpc/rawtransaction.cpp", + "src/wallet/rpcwallet.cpp", +] +# Source file (relative to root) containing conversion mapping +SOURCE_CLIENT = 'src/rpc/client.cpp' +# Argument names that should be ignored in consistency checks +IGNORE_DUMMY_ARGS = {'dummy', 'arg0', 'arg1', 'arg2', 'arg3', 'arg4', 'arg5', 'arg6', 'arg7', 'arg8', 'arg9'} + +class RPCCommand: + def __init__(self, name, args): + self.name = name + self.args = args + +class RPCArgument: + def __init__(self, names, idx): + self.names = names + self.idx = idx + self.convert = False + +def parse_string(s): + assert s[0] == '"' + assert s[-1] == '"' + return s[1:-1] + +def process_commands(fname): + """Find and parse dispatch table in implementation file `fname`.""" + cmds = [] + in_rpcs = False + with open(fname, "r") as f: + for line in f: + line = line.rstrip() + if not in_rpcs: + if re.match("static const CRPCCommand .*\[\] =", line): + in_rpcs = True + else: + if line.startswith('};'): + in_rpcs = False + elif '{' in line and '"' in line: + m = re.search('{ *("[^"]*"), *("[^"]*"), *&([^,]*), *{([^}]*)} *},', line) + assert m, 'No match to table expression: %s' % line + name = parse_string(m.group(2)) + args_str = m.group(4).strip() + if args_str: + args = [RPCArgument(parse_string(x.strip()).split('|'), idx) for idx, x in enumerate(args_str.split(','))] + else: + args = [] + cmds.append(RPCCommand(name, args)) + assert not in_rpcs, "Something went wrong with parsing the C++ file: update the regexps" + return cmds + +def process_mapping(fname): + """Find and parse conversion table in implementation file `fname`.""" + cmds = [] + in_rpcs = False + with open(fname, "r") as f: + for line in f: + line = line.rstrip() + if not in_rpcs: + if line == 'static const CRPCConvertParam vRPCConvertParams[] =': + in_rpcs = True + else: + if line.startswith('};'): + in_rpcs = False + elif '{' in line and '"' in line: + m = re.search('{ *("[^"]*"), *([0-9]+) *, *("[^"]*") *},', line) + assert m, 'No match to table expression: %s' % line + name = parse_string(m.group(1)) + idx = int(m.group(2)) + argname = parse_string(m.group(3)) + cmds.append((name, idx, argname)) + assert not in_rpcs + return cmds + +def main(): + root = sys.argv[1] + + # Get all commands from dispatch tables + cmds = [] + for fname in SOURCES: + cmds += process_commands(os.path.join(root, fname)) + + cmds_by_name = {} + for cmd in cmds: + cmds_by_name[cmd.name] = cmd + + # Get current convert mapping for client + client = SOURCE_CLIENT + mapping = set(process_mapping(os.path.join(root, client))) + + print('* Checking consistency between dispatch tables and vRPCConvertParams') + + # Check mapping consistency + errors = 0 + for (cmdname, argidx, argname) in mapping: + try: + rargnames = cmds_by_name[cmdname].args[argidx].names + except IndexError: + print('ERROR: %s argument %i (named %s in vRPCConvertParams) is not defined in dispatch table' % (cmdname, argidx, argname)) + errors += 1 + continue + if argname not in rargnames: + print('ERROR: %s argument %i is named %s in vRPCConvertParams but %s in dispatch table' % (cmdname, argidx, argname, rargnames), file=sys.stderr) + errors += 1 + + # Check for conflicts in vRPCConvertParams conversion + # All aliases for an argument must either be present in the + # conversion table, or not. Anything in between means an oversight + # and some aliases won't work. + for cmd in cmds: + for arg in cmd.args: + convert = [((cmd.name, arg.idx, argname) in mapping) for argname in arg.names] + if any(convert) != all(convert): + print('ERROR: %s argument %s has conflicts in vRPCConvertParams conversion specifier %s' % (cmd.name, arg.names, convert)) + errors += 1 + arg.convert = all(convert) + + # Check for conversion difference by argument name. + # It is preferable for API consistency that arguments with the same name + # have the same conversion, so bin by argument name. + all_methods_by_argname = defaultdict(list) + converts_by_argname = defaultdict(list) + for cmd in cmds: + for arg in cmd.args: + for argname in arg.names: + all_methods_by_argname[argname].append(cmd.name) + converts_by_argname[argname].append(arg.convert) + + for argname, convert in converts_by_argname.items(): + if all(convert) != any(convert): + if argname in IGNORE_DUMMY_ARGS: + # these are testing or dummy, don't warn for them + continue + print('WARNING: conversion mismatch for argument named %s (%s)' % + (argname, list(zip(all_methods_by_argname[argname], converts_by_argname[argname])))) + + sys.exit(errors > 0) + + +if __name__ == '__main__': + main() From 592404f03f2b734351d734f0c9ca1fdce997321b Mon Sep 17 00:00:00 2001 From: MeshCollider Date: Tue, 11 Jul 2017 21:37:53 +1200 Subject: [PATCH 194/382] Changing &vec[0] to vec.data(), what 9804 missed --- src/bench/crypto_hash.cpp | 2 +- src/qt/signverifymessagedialog.cpp | 2 +- src/qt/test/paymentservertests.cpp | 14 +++++----- src/rpc/misc.cpp | 2 +- src/script/interpreter.cpp | 2 +- src/test/bip32_tests.cpp | 2 +- src/test/bloom_tests.cpp | 4 +-- src/test/crypto_tests.cpp | 44 +++++++++++++++--------------- src/test/getarg_tests.cpp | 2 +- src/test/skiplist_tests.cpp | 4 +-- src/test/test_bitcoin_fuzzy.cpp | 2 +- src/uint256.cpp | 2 +- src/wallet/crypter.cpp | 11 ++++---- src/wallet/rpcwallet.cpp | 2 +- src/wallet/test/crypto_tests.cpp | 2 +- 15 files changed, 48 insertions(+), 49 deletions(-) diff --git a/src/bench/crypto_hash.cpp b/src/bench/crypto_hash.cpp index 2914a36c7..410a08e51 100644 --- a/src/bench/crypto_hash.cpp +++ b/src/bench/crypto_hash.cpp @@ -47,7 +47,7 @@ static void SHA256_32b(benchmark::State& state) std::vector in(32,0); while (state.KeepRunning()) { for (int i = 0; i < 1000000; i++) { - CSHA256().Write(in.data(), in.size()).Finalize(&in[0]); + CSHA256().Write(in.data(), in.size()).Finalize(in.data()); } } } diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp index 0950ed023..85f4c6703 100644 --- a/src/qt/signverifymessagedialog.cpp +++ b/src/qt/signverifymessagedialog.cpp @@ -164,7 +164,7 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked() ui->statusLabel_SM->setStyleSheet("QLabel { color: green; }"); ui->statusLabel_SM->setText(QString("") + tr("Message signed.") + QString("")); - ui->signatureOut_SM->setText(QString::fromStdString(EncodeBase64(&vchSig[0], vchSig.size()))); + ui->signatureOut_SM->setText(QString::fromStdString(EncodeBase64(vchSig.data(), vchSig.size()))); } void SignVerifyMessageDialog::on_copySignatureButton_SM_clicked() diff --git a/src/qt/test/paymentservertests.cpp b/src/qt/test/paymentservertests.cpp index c7830071e..273bd1048 100644 --- a/src/qt/test/paymentservertests.cpp +++ b/src/qt/test/paymentservertests.cpp @@ -24,7 +24,7 @@ X509 *parse_b64der_cert(const char* cert_data) { std::vector data = DecodeBase64(cert_data); assert(data.size() > 0); - const unsigned char* dptr = &data[0]; + const unsigned char* dptr = data.data(); X509 *cert = d2i_X509(nullptr, &dptr, data.size()); assert(cert); return cert; @@ -43,7 +43,7 @@ static SendCoinsRecipient handleRequest(PaymentServer* server, std::vector >(witness.stack.begin(), witness.stack.end() - 1); uint256 hashScriptPubKey; CSHA256().Write(&scriptPubKey[0], scriptPubKey.size()).Finalize(hashScriptPubKey.begin()); - if (memcmp(hashScriptPubKey.begin(), &program[0], 32)) { + if (memcmp(hashScriptPubKey.begin(), program.data(), 32)) { return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH); } } else if (program.size() == 20) { diff --git a/src/test/bip32_tests.cpp b/src/test/bip32_tests.cpp index c851ab284..e123c26ad 100644 --- a/src/test/bip32_tests.cpp +++ b/src/test/bip32_tests.cpp @@ -91,7 +91,7 @@ void RunTest(const TestVector &test) { std::vector seed = ParseHex(test.strHexMaster); CExtKey key; CExtPubKey pubkey; - key.SetMaster(&seed[0], seed.size()); + key.SetMaster(seed.data(), seed.size()); pubkey = key.Neuter(); for (const TestDerivation &derive : test.vDerive) { unsigned char data[74]; diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp index 2085b5cb2..9274ceefc 100644 --- a/src/test/bloom_tests.cpp +++ b/src/test/bloom_tests.cpp @@ -154,8 +154,8 @@ BOOST_AUTO_TEST_CASE(bloom_match) COutPoint prevOutPoint(uint256S("0x90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 0); { std::vector data(32 + sizeof(unsigned int)); - memcpy(&data[0], prevOutPoint.hash.begin(), 32); - memcpy(&data[32], &prevOutPoint.n, sizeof(unsigned int)); + memcpy(data.data(), prevOutPoint.hash.begin(), 32); + memcpy(data.data()+32, &prevOutPoint.n, sizeof(unsigned int)); filter.insert(data); } BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match manually serialized COutPoint"); diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index 391ad14ff..c748b2448 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -58,12 +58,12 @@ void TestRIPEMD160(const std::string &in, const std::string &hexout) { TestVecto void TestHMACSHA256(const std::string &hexkey, const std::string &hexin, const std::string &hexout) { std::vector key = ParseHex(hexkey); - TestVector(CHMAC_SHA256(&key[0], key.size()), ParseHex(hexin), ParseHex(hexout)); + TestVector(CHMAC_SHA256(key.data(), key.size()), ParseHex(hexin), ParseHex(hexout)); } void TestHMACSHA512(const std::string &hexkey, const std::string &hexin, const std::string &hexout) { std::vector key = ParseHex(hexkey); - TestVector(CHMAC_SHA512(&key[0], key.size()), ParseHex(hexin), ParseHex(hexout)); + TestVector(CHMAC_SHA512(key.data(), key.size()), ParseHex(hexin), ParseHex(hexout)); } void TestAES128(const std::string &hexkey, const std::string &hexin, const std::string &hexout) @@ -76,13 +76,13 @@ void TestAES128(const std::string &hexkey, const std::string &hexin, const std:: assert(key.size() == 16); assert(in.size() == 16); assert(correctout.size() == 16); - AES128Encrypt enc(&key[0]); + AES128Encrypt enc(key.data()); buf.resize(correctout.size()); buf2.resize(correctout.size()); - enc.Encrypt(&buf[0], &in[0]); + enc.Encrypt(buf.data(), in.data()); BOOST_CHECK_EQUAL(HexStr(buf), HexStr(correctout)); - AES128Decrypt dec(&key[0]); - dec.Decrypt(&buf2[0], &buf[0]); + AES128Decrypt dec(key.data()); + dec.Decrypt(buf2.data(), buf.data()); BOOST_CHECK_EQUAL(HexStr(buf2), HexStr(in)); } @@ -96,12 +96,12 @@ void TestAES256(const std::string &hexkey, const std::string &hexin, const std:: assert(key.size() == 32); assert(in.size() == 16); assert(correctout.size() == 16); - AES256Encrypt enc(&key[0]); + AES256Encrypt enc(key.data()); buf.resize(correctout.size()); - enc.Encrypt(&buf[0], &in[0]); + enc.Encrypt(buf.data(), in.data()); BOOST_CHECK(buf == correctout); - AES256Decrypt dec(&key[0]); - dec.Decrypt(&buf[0], &buf[0]); + AES256Decrypt dec(key.data()); + dec.Decrypt(buf.data(), buf.data()); BOOST_CHECK(buf == in); } @@ -114,16 +114,16 @@ void TestAES128CBC(const std::string &hexkey, const std::string &hexiv, bool pad std::vector realout(in.size() + AES_BLOCKSIZE); // Encrypt the plaintext and verify that it equals the cipher - AES128CBCEncrypt enc(&key[0], &iv[0], pad); - int size = enc.Encrypt(&in[0], in.size(), &realout[0]); + AES128CBCEncrypt enc(key.data(), iv.data(), pad); + int size = enc.Encrypt(in.data(), in.size(), realout.data()); realout.resize(size); BOOST_CHECK(realout.size() == correctout.size()); BOOST_CHECK_MESSAGE(realout == correctout, HexStr(realout) + std::string(" != ") + hexout); // Decrypt the cipher and verify that it equals the plaintext std::vector decrypted(correctout.size()); - AES128CBCDecrypt dec(&key[0], &iv[0], pad); - size = dec.Decrypt(&correctout[0], correctout.size(), &decrypted[0]); + AES128CBCDecrypt dec(key.data(), iv.data(), pad); + size = dec.Decrypt(correctout.data(), correctout.size(), decrypted.data()); decrypted.resize(size); BOOST_CHECK(decrypted.size() == in.size()); BOOST_CHECK_MESSAGE(decrypted == in, HexStr(decrypted) + std::string(" != ") + hexin); @@ -133,12 +133,12 @@ void TestAES128CBC(const std::string &hexkey, const std::string &hexiv, bool pad { std::vector sub(i, in.end()); std::vector subout(sub.size() + AES_BLOCKSIZE); - int _size = enc.Encrypt(&sub[0], sub.size(), &subout[0]); + int _size = enc.Encrypt(sub.data(), sub.size(), subout.data()); if (_size != 0) { subout.resize(_size); std::vector subdecrypted(subout.size()); - _size = dec.Decrypt(&subout[0], subout.size(), &subdecrypted[0]); + _size = dec.Decrypt(subout.data(), subout.size(), subdecrypted.data()); subdecrypted.resize(_size); BOOST_CHECK(decrypted.size() == in.size()); BOOST_CHECK_MESSAGE(subdecrypted == sub, HexStr(subdecrypted) + std::string(" != ") + HexStr(sub)); @@ -155,16 +155,16 @@ void TestAES256CBC(const std::string &hexkey, const std::string &hexiv, bool pad std::vector realout(in.size() + AES_BLOCKSIZE); // Encrypt the plaintext and verify that it equals the cipher - AES256CBCEncrypt enc(&key[0], &iv[0], pad); - int size = enc.Encrypt(&in[0], in.size(), &realout[0]); + AES256CBCEncrypt enc(key.data(), iv.data(), pad); + int size = enc.Encrypt(in.data(), in.size(), realout.data()); realout.resize(size); BOOST_CHECK(realout.size() == correctout.size()); BOOST_CHECK_MESSAGE(realout == correctout, HexStr(realout) + std::string(" != ") + hexout); // Decrypt the cipher and verify that it equals the plaintext std::vector decrypted(correctout.size()); - AES256CBCDecrypt dec(&key[0], &iv[0], pad); - size = dec.Decrypt(&correctout[0], correctout.size(), &decrypted[0]); + AES256CBCDecrypt dec(key.data(), iv.data(), pad); + size = dec.Decrypt(correctout.data(), correctout.size(), decrypted.data()); decrypted.resize(size); BOOST_CHECK(decrypted.size() == in.size()); BOOST_CHECK_MESSAGE(decrypted == in, HexStr(decrypted) + std::string(" != ") + hexin); @@ -174,12 +174,12 @@ void TestAES256CBC(const std::string &hexkey, const std::string &hexiv, bool pad { std::vector sub(i, in.end()); std::vector subout(sub.size() + AES_BLOCKSIZE); - int _size = enc.Encrypt(&sub[0], sub.size(), &subout[0]); + int _size = enc.Encrypt(sub.data(), sub.size(), subout.data()); if (_size != 0) { subout.resize(_size); std::vector subdecrypted(subout.size()); - _size = dec.Decrypt(&subout[0], subout.size(), &subdecrypted[0]); + _size = dec.Decrypt(subout.data(), subout.size(), subdecrypted.data()); subdecrypted.resize(_size); BOOST_CHECK(decrypted.size() == in.size()); BOOST_CHECK_MESSAGE(subdecrypted == sub, HexStr(subdecrypted) + std::string(" != ") + HexStr(sub)); diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp index 18a7e5993..40f0ecd5f 100644 --- a/src/test/getarg_tests.cpp +++ b/src/test/getarg_tests.cpp @@ -27,7 +27,7 @@ static void ResetArgs(const std::string& strArg) for (std::string& s : vecArg) vecChar.push_back(s.c_str()); - gArgs.ParseParameters(vecChar.size(), &vecChar[0]); + gArgs.ParseParameters(vecChar.size(), vecChar.data()); } BOOST_AUTO_TEST_CASE(boolarg) diff --git a/src/test/skiplist_tests.cpp b/src/test/skiplist_tests.cpp index e3654e67a..164cbd873 100644 --- a/src/test/skiplist_tests.cpp +++ b/src/test/skiplist_tests.cpp @@ -39,7 +39,7 @@ BOOST_AUTO_TEST_CASE(skiplist_test) BOOST_CHECK(vIndex[SKIPLIST_LENGTH - 1].GetAncestor(from) == &vIndex[from]); BOOST_CHECK(vIndex[from].GetAncestor(to) == &vIndex[to]); - BOOST_CHECK(vIndex[from].GetAncestor(0) == &vIndex[0]); + BOOST_CHECK(vIndex[from].GetAncestor(0) == vIndex.data()); } } @@ -64,7 +64,7 @@ BOOST_AUTO_TEST_CASE(getlocator_test) for (unsigned int i=0; i= TEST_ID_END) return 0; diff --git a/src/uint256.cpp b/src/uint256.cpp index c4c7b716f..736a0d4fe 100644 --- a/src/uint256.cpp +++ b/src/uint256.cpp @@ -14,7 +14,7 @@ template base_blob::base_blob(const std::vector& vch) { assert(vch.size() == sizeof(data)); - memcpy(data, &vch[0], sizeof(data)); + memcpy(data, vch.data(), sizeof(data)); } template diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp index dcce88ced..8db3bfd69 100644 --- a/src/wallet/crypter.cpp +++ b/src/wallet/crypter.cpp @@ -27,8 +27,7 @@ int CCrypter::BytesToKeySHA512AES(const std::vector& chSalt, cons CSHA512 di; di.Write((const unsigned char*)strKeyData.c_str(), strKeyData.size()); - if(chSalt.size()) - di.Write(&chSalt[0], chSalt.size()); + di.Write(chSalt.data(), chSalt.size()); di.Finalize(buf); for(int i = 0; i != count - 1; i++) @@ -82,7 +81,7 @@ bool CCrypter::Encrypt(const CKeyingMaterial& vchPlaintext, std::vector& vchCiphertext, CKeyingM vchPlaintext.resize(nLen); AES256CBCDecrypt dec(vchKey.data(), vchIV.data(), true); - nLen = dec.Decrypt(&vchCiphertext[0], vchCiphertext.size(), &vchPlaintext[0]); + nLen = dec.Decrypt(vchCiphertext.data(), vchCiphertext.size(), &vchPlaintext[0]); if(nLen == 0) return false; vchPlaintext.resize(nLen); @@ -113,7 +112,7 @@ static bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMateri { CCrypter cKeyCrypter; std::vector chIV(WALLET_CRYPTO_IV_SIZE); - memcpy(&chIV[0], &nIV, WALLET_CRYPTO_IV_SIZE); + memcpy(chIV.data(), &nIV, WALLET_CRYPTO_IV_SIZE); if(!cKeyCrypter.SetKey(vMasterKey, chIV)) return false; return cKeyCrypter.Encrypt(*((const CKeyingMaterial*)&vchPlaintext), vchCiphertext); @@ -123,7 +122,7 @@ static bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector chIV(WALLET_CRYPTO_IV_SIZE); - memcpy(&chIV[0], &nIV, WALLET_CRYPTO_IV_SIZE); + memcpy(chIV.data(), &nIV, WALLET_CRYPTO_IV_SIZE); if(!cKeyCrypter.SetKey(vMasterKey, chIV)) return false; return cKeyCrypter.Decrypt(vchCiphertext, *((CKeyingMaterial*)&vchPlaintext)); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 4ea53c413..ceaab00ba 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -605,7 +605,7 @@ UniValue signmessage(const JSONRPCRequest& request) if (!key.SignCompact(ss.GetHash(), vchSig)) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed"); - return EncodeBase64(&vchSig[0], vchSig.size()); + return EncodeBase64(vchSig.data(), vchSig.size()); } UniValue getreceivedbyaddress(const JSONRPCRequest& request) diff --git a/src/wallet/test/crypto_tests.cpp b/src/wallet/test/crypto_tests.cpp index cbd74b6f9..f4dabc50c 100644 --- a/src/wallet/test/crypto_tests.cpp +++ b/src/wallet/test/crypto_tests.cpp @@ -80,7 +80,7 @@ BOOST_AUTO_TEST_CASE(passphrase) { std::string hash(GetRandHash().ToString()); std::vector vchSalt(8); - GetRandBytes(&vchSalt[0], vchSalt.size()); + GetRandBytes(vchSalt.data(), vchSalt.size()); uint32_t rounds = InsecureRand32(); if (rounds > 30000) rounds = 30000; From 9c76ba18cdfe01fe9266e0dcf6714dbe0abbb837 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Tue, 29 Aug 2017 11:48:25 -0400 Subject: [PATCH 195/382] [wallet] Rename InitLoadWallet() to OpenWallets() Rationale: - this init function can now open multiple wallets (hence Wallet->Wallets) - This is named as the antonym to CloseWallets(), which carries out the opposite action. --- src/init.cpp | 2 +- src/wallet/init.cpp | 2 +- src/wallet/init.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 7c99dc74e..387bd2e2f 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1567,7 +1567,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) // ********************************************************* Step 8: load wallet #ifdef ENABLE_WALLET - if (!InitLoadWallet()) + if (!OpenWallets()) return false; #else LogPrintf("No wallet support compiled in!\n"); diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index 18365b1b7..95b188b62 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -228,7 +228,7 @@ bool WalletVerify() return true; } -bool InitLoadWallet() +bool OpenWallets() { if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { LogPrintf("Wallet disabled!\n"); diff --git a/src/wallet/init.h b/src/wallet/init.h index fa2251506..1821e5e70 100644 --- a/src/wallet/init.h +++ b/src/wallet/init.h @@ -20,6 +20,6 @@ bool WalletParameterInteraction(); bool WalletVerify(); //! Load wallet databases. -bool InitLoadWallet(); +bool OpenWallets(); #endif // BITCOIN_WALLET_INIT_H From 1b9cee66e1c50cb6f110793ec5dc4c6a291cce36 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Tue, 29 Aug 2017 11:51:02 -0400 Subject: [PATCH 196/382] [wallet] Rename WalletVerify() to VerifyWallets() This function can now verify multiple wallets. --- src/init.cpp | 2 +- src/wallet/init.cpp | 2 +- src/wallet/init.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 387bd2e2f..ce368a6bb 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1246,7 +1246,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) // ********************************************************* Step 5: verify wallet database integrity #ifdef ENABLE_WALLET - if (!WalletVerify()) + if (!VerifyWallets()) return false; #endif // ********************************************************* Step 6: network initialization diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index 95b188b62..f39201e79 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -171,7 +171,7 @@ bool WalletParameterInteraction() return true; } -bool WalletVerify() +bool VerifyWallets() { if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) return true; diff --git a/src/wallet/init.h b/src/wallet/init.h index 1821e5e70..a66b35943 100644 --- a/src/wallet/init.h +++ b/src/wallet/init.h @@ -17,7 +17,7 @@ bool WalletParameterInteraction(); //! Responsible for reading and validating the -wallet arguments and verifying the wallet database. // This function will perform salvage on the wallet if requested, as long as only one wallet is // being loaded (CWallet::ParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet). -bool WalletVerify(); +bool VerifyWallets(); //! Load wallet databases. bool OpenWallets(); From 2da5eafa47cdf81107bd3e71a709d404ebb6dcdb Mon Sep 17 00:00:00 2001 From: John Newbery Date: Mon, 28 Aug 2017 12:31:53 -0400 Subject: [PATCH 197/382] [wallet] Add FlushWallets() function to wallet/init.cpp --- src/init.cpp | 8 ++------ src/wallet/init.cpp | 6 ++++++ src/wallet/init.h | 3 +++ 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index ce368a6bb..6d250a25c 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -189,9 +189,7 @@ void Shutdown() StopRPC(); StopHTTPServer(); #ifdef ENABLE_WALLET - for (CWalletRef pwallet : vpwallets) { - pwallet->Flush(false); - } + FlushWallets(false); #endif MapPort(false); UnregisterValidationInterface(peerLogic.get()); @@ -246,9 +244,7 @@ void Shutdown() pblocktree = nullptr; } #ifdef ENABLE_WALLET - for (CWalletRef pwallet : vpwallets) { - pwallet->Flush(true); - } + FlushWallets(true); #endif #if ENABLE_ZMQ diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index f39201e79..f58f3bc54 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -245,3 +245,9 @@ bool OpenWallets() return true; } + +void FlushWallets(bool shutdown) { + for (CWalletRef pwallet : vpwallets) { + pwallet->Flush(shutdown); + } +} diff --git a/src/wallet/init.h b/src/wallet/init.h index a66b35943..5445c72d1 100644 --- a/src/wallet/init.h +++ b/src/wallet/init.h @@ -22,4 +22,7 @@ bool VerifyWallets(); //! Load wallet databases. bool OpenWallets(); +//! Flush all wallets in preparation for shutdown. +//! Call with shutdown = true to actually shutdown the wallet. +void FlushWallets(bool shutdown); #endif // BITCOIN_WALLET_INIT_H From 77fe07c1591395b4279f9f2ff3c36c6bde11fbb7 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Mon, 28 Aug 2017 12:53:56 -0400 Subject: [PATCH 198/382] [wallet] Add StopWallets() function to wallet/init.cpp --- src/init.cpp | 4 ++-- src/wallet/init.cpp | 10 ++++++++-- src/wallet/init.h | 6 ++++-- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 6d250a25c..d1ffe126d 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -189,7 +189,7 @@ void Shutdown() StopRPC(); StopHTTPServer(); #ifdef ENABLE_WALLET - FlushWallets(false); + FlushWallets(); #endif MapPort(false); UnregisterValidationInterface(peerLogic.get()); @@ -244,7 +244,7 @@ void Shutdown() pblocktree = nullptr; } #ifdef ENABLE_WALLET - FlushWallets(true); + StopWallets(); #endif #if ENABLE_ZMQ diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index f58f3bc54..2b5c6b173 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -246,8 +246,14 @@ bool OpenWallets() return true; } -void FlushWallets(bool shutdown) { +void FlushWallets() { for (CWalletRef pwallet : vpwallets) { - pwallet->Flush(shutdown); + pwallet->Flush(false); + } +} + +void StopWallets() { + for (CWalletRef pwallet : vpwallets) { + pwallet->Flush(true); } } diff --git a/src/wallet/init.h b/src/wallet/init.h index 5445c72d1..c06b05847 100644 --- a/src/wallet/init.h +++ b/src/wallet/init.h @@ -23,6 +23,8 @@ bool VerifyWallets(); bool OpenWallets(); //! Flush all wallets in preparation for shutdown. -//! Call with shutdown = true to actually shutdown the wallet. -void FlushWallets(bool shutdown); +void FlushWallets(); + +//! Stop all wallets. Wallets will be flushed first. +void StopWallets(); #endif // BITCOIN_WALLET_INIT_H From 062d63102eb1e90168d28589de8bf182e9a6a7d3 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Mon, 28 Aug 2017 12:41:33 -0400 Subject: [PATCH 199/382] [wallet] Add CloseWallets() function to wallet/init.cpp --- src/init.cpp | 5 +---- src/wallet/init.cpp | 7 +++++++ src/wallet/init.h | 4 ++++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index d1ffe126d..3f528a1f5 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -265,10 +265,7 @@ void Shutdown() UnregisterAllValidationInterfaces(); GetMainSignals().UnregisterBackgroundSignalScheduler(); #ifdef ENABLE_WALLET - for (CWalletRef pwallet : vpwallets) { - delete pwallet; - } - vpwallets.clear(); + CloseWallets(); #endif globalVerifyHandle.reset(); ECC_Stop(); diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index 2b5c6b173..84318eac0 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -257,3 +257,10 @@ void StopWallets() { pwallet->Flush(true); } } + +void CloseWallets() { + for (CWalletRef pwallet : vpwallets) { + delete pwallet; + } + vpwallets.clear(); +} diff --git a/src/wallet/init.h b/src/wallet/init.h index c06b05847..1be2ef927 100644 --- a/src/wallet/init.h +++ b/src/wallet/init.h @@ -27,4 +27,8 @@ void FlushWallets(); //! Stop all wallets. Wallets will be flushed first. void StopWallets(); + +//! Close all wallets. +void CloseWallets(); + #endif // BITCOIN_WALLET_INIT_H From 290f3c56d9dd8a519920939a4fc440da832c1c63 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Mon, 28 Aug 2017 13:33:59 -0400 Subject: [PATCH 200/382] [wallet] Add RegisterWalletRPC() function to wallet/init.cpp --- src/init.cpp | 2 +- src/wallet/init.cpp | 8 ++++++++ src/wallet/init.h | 5 +++++ src/wallet/rpcwallet.cpp | 3 --- src/wallet/rpcwallet.h | 3 +++ 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 3f528a1f5..9022b4a4c 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1017,7 +1017,7 @@ bool AppInitParameterInteraction() RegisterAllCoreRPCCommands(tableRPC); #ifdef ENABLE_WALLET - RegisterWalletRPCCommands(tableRPC); + RegisterWalletRPC(tableRPC); #endif nConnectTimeout = gArgs.GetArg("-timeout", DEFAULT_CONNECT_TIMEOUT); diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index 84318eac0..9fd038150 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -10,6 +10,7 @@ #include "utilmoneystr.h" #include "validation.h" #include "wallet/wallet.h" +#include "wallet/rpcwallet.h" std::string GetWalletHelpString(bool showDebug) { @@ -171,6 +172,13 @@ bool WalletParameterInteraction() return true; } +void RegisterWalletRPC(CRPCTable &t) +{ + if (gArgs.GetBoolArg("-disablewallet", false)) return; + + RegisterWalletRPCCommands(t); +} + bool VerifyWallets() { if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) diff --git a/src/wallet/init.h b/src/wallet/init.h index 1be2ef927..588357119 100644 --- a/src/wallet/init.h +++ b/src/wallet/init.h @@ -8,12 +8,17 @@ #include +class CRPCTable; + //! Return the wallets help message. std::string GetWalletHelpString(bool showDebug); //! Wallets parameter interaction bool WalletParameterInteraction(); +//! Register wallet RPCs. +void RegisterWalletRPC(CRPCTable &tableRPC); + //! Responsible for reading and validating the -wallet arguments and verifying the wallet database. // This function will perform salvage on the wallet if requested, as long as only one wallet is // being loaded (CWallet::ParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet). diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 4ea53c413..6ef3599a1 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3217,9 +3217,6 @@ static const CRPCCommand commands[] = void RegisterWalletRPCCommands(CRPCTable &t) { - if (gArgs.GetBoolArg("-disablewallet", false)) - return; - for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) t.appendCommand(commands[vcidx].name, &commands[vcidx]); } diff --git a/src/wallet/rpcwallet.h b/src/wallet/rpcwallet.h index db0808b93..14e51610d 100644 --- a/src/wallet/rpcwallet.h +++ b/src/wallet/rpcwallet.h @@ -5,7 +5,10 @@ #ifndef BITCOIN_WALLET_RPCWALLET_H #define BITCOIN_WALLET_RPCWALLET_H +#include + class CRPCTable; +class CWallet; class JSONRPCRequest; void RegisterWalletRPCCommands(CRPCTable &t); From 43b0e81d0f8f5d235e1cdaa2ee128b67259f7109 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Tue, 29 Aug 2017 11:47:06 -0400 Subject: [PATCH 201/382] [wallet] Add StartWallets() function to wallet/init.cpp --- src/init.cpp | 5 +---- src/wallet/init.cpp | 6 ++++++ src/wallet/init.h | 4 ++++ 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 9022b4a4c..fc31d3d4d 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -44,7 +44,6 @@ #include "validationinterface.h" #ifdef ENABLE_WALLET #include "wallet/init.h" -#include "wallet/wallet.h" #endif #include "warnings.h" #include @@ -1691,9 +1690,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) uiInterface.InitMessage(_("Done loading")); #ifdef ENABLE_WALLET - for (CWalletRef pwallet : vpwallets) { - pwallet->postInitProcess(scheduler); - } + StartWallets(scheduler); #endif return !fRequestShutdown; diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index 9fd038150..1e00735ed 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -254,6 +254,12 @@ bool OpenWallets() return true; } +void StartWallets(CScheduler& scheduler) { + for (CWalletRef pwallet : vpwallets) { + pwallet->postInitProcess(scheduler); + } +} + void FlushWallets() { for (CWalletRef pwallet : vpwallets) { pwallet->Flush(false); diff --git a/src/wallet/init.h b/src/wallet/init.h index 588357119..0d5e5f0cd 100644 --- a/src/wallet/init.h +++ b/src/wallet/init.h @@ -9,6 +9,7 @@ #include class CRPCTable; +class CScheduler; //! Return the wallets help message. std::string GetWalletHelpString(bool showDebug); @@ -27,6 +28,9 @@ bool VerifyWallets(); //! Load wallet databases. bool OpenWallets(); +//! Complete startup of wallets. +void StartWallets(CScheduler& scheduler); + //! Flush all wallets in preparation for shutdown. void FlushWallets(); From 5d2a3995e7035b3607a11660a2c8330a548f733d Mon Sep 17 00:00:00 2001 From: John Newbery Date: Thu, 7 Sep 2017 16:22:11 -0700 Subject: [PATCH 202/382] [trivial] fixup comment for VerifyWallets() --- src/wallet/init.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/init.h b/src/wallet/init.h index 0d5e5f0cd..0b3ee2dda 100644 --- a/src/wallet/init.h +++ b/src/wallet/init.h @@ -22,7 +22,7 @@ void RegisterWalletRPC(CRPCTable &tableRPC); //! Responsible for reading and validating the -wallet arguments and verifying the wallet database. // This function will perform salvage on the wallet if requested, as long as only one wallet is -// being loaded (CWallet::ParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet). +// being loaded (WalletParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet). bool VerifyWallets(); //! Load wallet databases. From d4c18f7330b1a967b8144df3d49a857514310a55 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Tue, 5 Sep 2017 15:37:45 -0700 Subject: [PATCH 203/382] Bump wallet version number to 159900 --- src/wallet/wallet.cpp | 4 ++-- src/wallet/wallet.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index d1d2060b0..7cc25eb79 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3832,8 +3832,8 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) // Create new keyUser and set as default key if (gArgs.GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET) && !walletInstance->IsHDEnabled()) { - // ensure this wallet.dat can only be opened by clients supporting HD with chain split - walletInstance->SetMinVersion(FEATURE_HD_SPLIT); + // ensure this wallet.dat can only be opened by clients supporting HD with chain split and expects no default key + walletInstance->SetMinVersion(FEATURE_NO_DEFAULT_KEY); // generate a new master key CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey(); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 73ad3bdec..5bd0bcd32 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -96,6 +96,8 @@ enum WalletFeature FEATURE_HD_SPLIT = 139900, // Wallet with HD chain split (change outputs will use m/0'/1'/k) + FEATURE_NO_DEFAULT_KEY = 159900, // Wallet without a default key written + FEATURE_LATEST = FEATURE_COMPRPUBKEY // HD is optional, use FEATURE_COMPRPUBKEY as latest version }; From 713a92073b3b391f48d4fa146bd334893ec94b8f Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Tue, 5 Sep 2017 15:54:11 -0700 Subject: [PATCH 204/382] Remove usehd option and warn when it is used Removed the -usehd option so wallets cannot be made to be non-hd anymore. A warning will be displayed when the option is set. --- src/wallet/init.cpp | 1 - src/wallet/wallet.cpp | 18 +++++++----------- test/functional/keypool-topup.py | 2 +- test/functional/wallet-hd.py | 8 +------- test/functional/wallet.py | 3 +-- 5 files changed, 10 insertions(+), 22 deletions(-) diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index 18365b1b7..f5ca2a78b 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -29,7 +29,6 @@ std::string GetWalletHelpString(bool showDebug) strUsage += HelpMessageOpt("-salvagewallet", _("Attempt to recover private keys from a corrupt wallet on startup")); strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE)); strUsage += HelpMessageOpt("-txconfirmtarget=", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), DEFAULT_TX_CONFIRM_TARGET)); - strUsage += HelpMessageOpt("-usehd", _("Use hierarchical deterministic key generation (HD) after BIP32. Only has effect during wallet creation/first start") + " " + strprintf(_("(default: %u)"), DEFAULT_USE_HD_WALLET)); strUsage += HelpMessageOpt("-walletrbf", strprintf(_("Send transactions with full-RBF opt-in enabled (default: %u)"), DEFAULT_WALLET_RBF)); strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format on startup")); strUsage += HelpMessageOpt("-wallet=", _("Specify wallet file (within data directory)") + " " + strprintf(_("(default: %s)"), DEFAULT_WALLET_DAT)); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 7cc25eb79..10a1c7fa9 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3829,17 +3829,13 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) if (fFirstRun) { - // Create new keyUser and set as default key - if (gArgs.GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET) && !walletInstance->IsHDEnabled()) { + // ensure this wallet.dat can only be opened by clients supporting HD with chain split and expects no default key + walletInstance->SetMinVersion(FEATURE_NO_DEFAULT_KEY); - // ensure this wallet.dat can only be opened by clients supporting HD with chain split and expects no default key - walletInstance->SetMinVersion(FEATURE_NO_DEFAULT_KEY); - - // generate a new master key - CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey(); - if (!walletInstance->SetHDMasterKey(masterPubKey)) - throw std::runtime_error(std::string(__func__) + ": Storing master key failed"); - } + // generate a new master key + CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey(); + if (!walletInstance->SetHDMasterKey(masterPubKey)) + throw std::runtime_error(std::string(__func__) + ": Storing master key failed"); // Top up the keypool if (!walletInstance->TopUpKeyPool()) { @@ -3852,7 +3848,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) else if (gArgs.IsArgSet("-usehd")) { bool useHD = gArgs.GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET); if (walletInstance->IsHDEnabled() && !useHD) { - InitError(strprintf(_("Error loading %s: You can't disable HD on an already existing HD wallet"), walletFile)); + InitError(strprintf(_("Error loading %s: You can't disable HD on an already existing HD wallet or create new non-HD wallets."), walletFile)); return nullptr; } if (!walletInstance->IsHDEnabled() && useHD) { diff --git a/test/functional/keypool-topup.py b/test/functional/keypool-topup.py index b87433a9c..160a0f7ae 100755 --- a/test/functional/keypool-topup.py +++ b/test/functional/keypool-topup.py @@ -23,7 +23,7 @@ class KeypoolRestoreTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 - self.extra_args = [['-usehd=0'], ['-usehd=1', '-keypool=100', '-keypoolmin=20']] + self.extra_args = [[], ['-keypool=100', '-keypoolmin=20']] def run_test(self): self.tmpdir = self.options.tmpdir diff --git a/test/functional/wallet-hd.py b/test/functional/wallet-hd.py index a6b96b745..3af04c227 100755 --- a/test/functional/wallet-hd.py +++ b/test/functional/wallet-hd.py @@ -15,17 +15,11 @@ class WalletHDTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 - self.extra_args = [['-usehd=0'], ['-usehd=1', '-keypool=0']] + self.extra_args = [[], ['-keypool=0']] def run_test (self): tmpdir = self.options.tmpdir - # Make sure can't switch off usehd after wallet creation - self.stop_node(1) - self.assert_start_raises_init_error(1, ['-usehd=0'], 'already existing HD wallet') - self.start_node(1) - connect_nodes_bi(self.nodes, 0, 1) - # Make sure we use hd, keep masterkeyid masterkeyid = self.nodes[1].getwalletinfo()['hdmasterkeyid'] assert_equal(len(masterkeyid), 40) diff --git a/test/functional/wallet.py b/test/functional/wallet.py index 27089e884..a643684c7 100755 --- a/test/functional/wallet.py +++ b/test/functional/wallet.py @@ -10,10 +10,9 @@ class WalletTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 4 self.setup_clean_chain = True - self.extra_args = [['-usehd={:d}'.format(i%2==0)] for i in range(4)] def setup_network(self): - self.add_nodes(4, self.extra_args) + self.add_nodes(4) self.start_node(0) self.start_node(1) self.start_node(2) From 24697c40ee6739b812259140042d426c81179977 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 7 Sep 2017 18:16:34 +0200 Subject: [PATCH 205/382] rpc: update cli for estimatefee argument rename The first argument of estimatesmartfee was renamed from nblocks to conf_target in 06bcdb8da64502a64df03f3c89fbc6ccb72cd349. Update the client-side table as well. --- src/rpc/client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 417945378..7c1c4c55a 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -116,7 +116,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "getrawmempool", 0, "verbose" }, { "estimatefee", 0, "nblocks" }, { "estimatesmartfee", 0, "nblocks" }, - { "estimaterawfee", 0, "nblocks" }, + { "estimaterawfee", 0, "conf_target" }, { "estimaterawfee", 1, "threshold" }, { "prioritisetransaction", 1, "dummy" }, { "prioritisetransaction", 2, "fee_delta" }, From 5acd82de9ad6df8cab922da66d49b8ff2bd35439 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 7 Sep 2017 19:15:45 +0200 Subject: [PATCH 206/382] rpc: make estimatesmartfee argument naming consistent with documentation Part of this was a reversion in ec6902d0ea2bbe75179684fc71849d5e34647a14. --- src/rpc/client.cpp | 2 +- src/rpc/mining.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 7c1c4c55a..406ad2f6e 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -115,7 +115,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "keypoolrefill", 0, "newsize" }, { "getrawmempool", 0, "verbose" }, { "estimatefee", 0, "nblocks" }, - { "estimatesmartfee", 0, "nblocks" }, + { "estimatesmartfee", 0, "conf_target" }, { "estimaterawfee", 0, "conf_target" }, { "estimaterawfee", 1, "threshold" }, { "prioritisetransaction", 1, "dummy" }, diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 980988370..e2a25fd52 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -980,7 +980,7 @@ static const CRPCCommand commands[] = { "generating", "generatetoaddress", &generatetoaddress, {"nblocks","address","maxtries"} }, { "util", "estimatefee", &estimatefee, {"nblocks"} }, - { "util", "estimatesmartfee", &estimatesmartfee, {"nblocks", "estimate_mode"} }, + { "util", "estimatesmartfee", &estimatesmartfee, {"conf_target", "estimate_mode"} }, { "hidden", "estimaterawfee", &estimaterawfee, {"conf_target", "threshold"} }, }; From 91c39e38d9f59331b77ec9017ea147725a32f2d1 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Thu, 7 Sep 2017 14:14:55 -0700 Subject: [PATCH 207/382] Update CONTRIBUTRING.md to reduce unnecesarry review workload --- CONTRIBUTING.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 56521a5f7..aed6d7954 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -157,6 +157,14 @@ behaviour of code within the pull request (bugs must be preserved as is). Project maintainers aim for a quick turnaround on refactoring pull requests, so where possible keep them short, uncomplex and easy to verify. +Pull requests that refactor the code should not be made by new contributors. It +requires a certain level of experience to know where the code belongs to and to +understand the full ramification (including rebase effort of open pull requests). + +Trivial pull requests or pull requests that refactor the code with no clear +benefits may be immediately closed by the maintainers to reduce unnecessary +workload on reviewing. + "Decision Making" Process ------------------------- From 7d03418aeab039775f8790718080ad10774fc4d0 Mon Sep 17 00:00:00 2001 From: MeshCollider Date: Fri, 8 Sep 2017 19:21:52 +1200 Subject: [PATCH 208/382] Add -usehd to excluded args in check-doc.py --- contrib/devtools/check-doc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/devtools/check-doc.py b/contrib/devtools/check-doc.py index 3279dc361..10c3bab03 100755 --- a/contrib/devtools/check-doc.py +++ b/contrib/devtools/check-doc.py @@ -22,7 +22,7 @@ REGEX_ARG = re.compile(r'(?:map(?:Multi)?Args(?:\.count\(|\[)|Get(?:Bool)?Arg\()\"(\-[^\"]+?)\"') REGEX_DOC = re.compile(r'HelpMessageOpt\(\"(\-[^\"=]+?)(?:=|\")') # list unsupported, deprecated and duplicate args as they need no documentation -SET_DOC_OPTIONAL = set(['-rpcssl', '-benchmark', '-h', '-help', '-socks', '-tor', '-debugnet', '-whitelistalwaysrelay', '-prematurewitness', '-walletprematurewitness', '-promiscuousmempoolflags', '-blockminsize', '-dbcrashratio', '-forcecompactdb']) +SET_DOC_OPTIONAL = set(['-rpcssl', '-benchmark', '-h', '-help', '-socks', '-tor', '-debugnet', '-whitelistalwaysrelay', '-prematurewitness', '-walletprematurewitness', '-promiscuousmempoolflags', '-blockminsize', '-dbcrashratio', '-forcecompactdb', '-usehd']) def main(): used = check_output(CMD_GREP_ARGS, shell=True) From b8d91e03a9f5eebf20c3a2822bd39bb77f1f7597 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Thu, 7 Sep 2017 13:52:38 +0200 Subject: [PATCH 209/382] [Docs] Fix broken Markdown table in dependencies.md. Cleanups. Use the correct capitalization for the dependencies Sort dependencies Fix header formatting. Minor style cleanups. --- doc/dependencies.md | 47 ++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/doc/dependencies.md b/doc/dependencies.md index 2574d8bb0..964c03ba8 100644 --- a/doc/dependencies.md +++ b/doc/dependencies.md @@ -3,30 +3,29 @@ Dependencies These are the dependencies currently used by Bitcoin Core. You can find instructions for installing them in the `build-*.md` file for your platform. -| Dependency | Version used | Minimum Required | CVEs? | Shared | [Bundled Qt Library](https://doc.qt.io/qt-5/configure-options.html) | -| --- | --- | --- | --- | --- | --- | --- | -| openssl | [1.0.1k]](https://www.openssl.org/source) | | Yes | | | +| Dependency | Version used | Minimum required | CVEs | Shared | [Bundled Qt library](https://doc.qt.io/qt-5/configure-options.html) | +| --- | --- | --- | --- | --- | --- | +| Berkeley DB | [4.8.30](http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html) | 4.8.x | No | | | +| Boost | [1.64.0](http://www.boost.org/users/download/) | [1.47.0](https://github.com/bitcoin/bitcoin/pull/8920) | No | | | | ccache | [3.3.4](https://ccache.samba.org/download.html) | | No | | | +| Clang | | [3.3+](http://llvm.org/releases/download.html) (C++11 support) | | | | +| D-Bus | [1.10.18](https://cgit.freedesktop.org/dbus/dbus/tree/NEWS?h=dbus-1.10) | | No | Yes | | +| Expat | [2.2.1](https://libexpat.github.io/) | | No | Yes | | +| fontconfig | [2.12.1](https://www.freedesktop.org/software/fontconfig/release/) | | No | Yes | | +| FreeType | [2.7.1](http://download.savannah.gnu.org/releases/freetype) | | No | | | +| GCC | | [4.7+](https://gcc.gnu.org/) | | | | +| HarfBuzz-NG | | | | | | | libevent | [2.1.8-stable](https://github.com/libevent/libevent/releases) | 2.0.22 | No | | | -| Qt | [5.7.1](https://download.qt.io/official_releases/qt/) | 4.7+ | No | | | -| Freetype | [2.7.1](http://download.savannah.gnu.org/releases/freetype) | | No | | | -| Boost | [1.64.0](http://www.boost.org/users/download/) | [1.47.0](https://github.com/bitcoin/bitcoin/pull/8920) | No | | | -| Protobuf | [2.6.3](https://github.com/google/protobuf/releases) | | No | | | -| Zeromq | [4.1.5](https://github.com/zeromq/libzmq/releases) | | No | | | -| miniupnpc | [2.0.20170509](http://miniupnp.free.fr/files) | | No | | | -| qrencode | [3.4.4](https://fukuchi.org/works/qrencode) | | No | | | -| berkeley-db | [4.8.30](http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html) | 4.8.x | No | | | -| dbus | [1.10.18](https://cgit.freedesktop.org/dbus/dbus/tree/NEWS?h=dbus-1.10) | | No | yes | | -| expat | [2.2.1](https://libexpat.github.io/) | | No | yes | | -| fontconfig | [2.12.1](https://www.freedesktop.org/software/fontconfig/release/) | | No | yes | | -| freetype | | | | | [no](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L38) (linux uses system) | -| zlib | [1.2.11](http://zlib.net/) | | | | no | -| libjpeg | | | | | [yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L75) | -| libpng | | | | | [yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L74) | -| PCRE | | | | | [yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L76) | -| xcb | | | | | [yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L94) (linux only) | -| xkbcommon | | | | | [yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L93) (linux only) | -| HarfBuzz-NG | | | | | ? | +| libjpeg | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L75) | +| libpng | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L74) | +| MiniUPnPc | [2.0.20170509](http://miniupnp.free.fr/files) | | No | | | +| OpenSSL | [1.0.1k](https://www.openssl.org/source) | | Yes | | | +| PCRE | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L76) | +| protobuf | [2.6.3](https://github.com/google/protobuf/releases) | | No | | | | Python (tests) | | [3.4](https://www.python.org/downloads) | | | | -| GCC | | [4.7+](https://gcc.gnu.org/) | | | | -| Clang | | [3.3+](http://llvm.org/releases/download.html) (C++11 support) | | | | +| qrencode | [3.4.4](https://fukuchi.org/works/qrencode) | | No | | | +| Qt | [5.7.1](https://download.qt.io/official_releases/qt/) | 4.7+ | No | | | +| XCB | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L94) (Linux only) | +| xkbcommon | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L93) (Linux only) | +| ZeroMQ | [4.1.5](https://github.com/zeromq/libzmq/releases) | | No | | | +| zlib | [1.2.11](http://zlib.net/) | | | | No | From f38c05104848db63ad0df85c24eea466dbff8536 Mon Sep 17 00:00:00 2001 From: fanquake Date: Fri, 8 Sep 2017 16:29:01 +0800 Subject: [PATCH 210/382] [depends] Don't build libevent sample code --- depends/packages/libevent.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/depends/packages/libevent.mk b/depends/packages/libevent.mk index 00231d75d..5f622f8e6 100644 --- a/depends/packages/libevent.mk +++ b/depends/packages/libevent.mk @@ -9,7 +9,7 @@ define $(package)_preprocess_cmds endef define $(package)_set_vars - $(package)_config_opts=--disable-shared --disable-openssl --disable-libevent-regress + $(package)_config_opts=--disable-shared --disable-openssl --disable-libevent-regress --disable-samples $(package)_config_opts_release=--disable-debug-mode $(package)_config_opts_linux=--with-pic endef From c626dcb50eed496462fd4ac3e05bf79164749ebe Mon Sep 17 00:00:00 2001 From: MeshCollider Date: Sun, 10 Sep 2017 11:19:46 +1200 Subject: [PATCH 211/382] Make fUseCrypto atomic --- src/wallet/crypter.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h index f1e8a2565..0948de42e 100644 --- a/src/wallet/crypter.h +++ b/src/wallet/crypter.h @@ -9,6 +9,8 @@ #include "serialize.h" #include "support/allocators/secure.h" +#include + const unsigned int WALLET_CRYPTO_KEY_SIZE = 32; const unsigned int WALLET_CRYPTO_SALT_SIZE = 8; const unsigned int WALLET_CRYPTO_IV_SIZE = 16; @@ -118,7 +120,7 @@ class CCryptoKeyStore : public CBasicKeyStore //! if fUseCrypto is true, mapKeys must be empty //! if fUseCrypto is false, vMasterKey must be empty - bool fUseCrypto; + std::atomic fUseCrypto; //! keeps track of whether Unlock has run a thorough check before bool fDecryptionThoroughlyChecked; From b73628d5a8e259a7f1fee5c4a245b463990f05a5 Mon Sep 17 00:00:00 2001 From: MeshCollider Date: Sun, 10 Sep 2017 16:26:20 +1200 Subject: [PATCH 212/382] Make sure ~/.bitcoin doesn't exist before build --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index f1b10d3bd..7cc320ddd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -47,6 +47,7 @@ before_script: - if [ "$CHECK_DOC" = 1 -a "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then contrib/devtools/commit-script-check.sh $TRAVIS_COMMIT_RANGE; fi - if [ "$CHECK_DOC" = 1 ]; then contrib/devtools/check-doc.py; fi - unset CC; unset CXX + - rm -rf ~/.bitcoin - mkdir -p depends/SDKs depends/sdk-sources - if [ -n "$OSX_SDK" -a ! -f depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then curl --location --fail $SDK_URL/MacOSX${OSX_SDK}.sdk.tar.gz -o depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz; fi - if [ -n "$OSX_SDK" -a -f depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then tar -C depends/SDKs -xf depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz; fi From d601f16621e55c2f174afea2c5d7d1c9a0c0b969 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Mon, 11 Sep 2017 13:40:31 +1000 Subject: [PATCH 213/382] Fix invalid memory access in CScript::operator+= --- src/script/script.h | 1 + src/test/script_tests.cpp | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/script/script.h b/src/script/script.h index 587f2d26e..2a9206054 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -420,6 +420,7 @@ class CScript : public CScriptBase CScript& operator+=(const CScript& b) { + reserve(size() + b.size()); insert(end(), b.begin(), b.end()); return *this; } diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 17374edcc..011a5db79 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -1451,4 +1451,21 @@ BOOST_AUTO_TEST_CASE(script_HasValidOps) BOOST_CHECK(!script.HasValidOps()); } +BOOST_AUTO_TEST_CASE(script_can_append_self) +{ + CScript s, d; + + s = ScriptFromHex("00"); + s += s; + d = ScriptFromHex("0000"); + BOOST_CHECK(s == d); + + // check doubling a script that's large enough to require reallocation + static const char hex[] = "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"; + s = CScript() << ParseHex(hex) << OP_CHECKSIG; + d = CScript() << ParseHex(hex) << OP_CHECKSIG << ParseHex(hex) << OP_CHECKSIG; + s += s; + BOOST_CHECK(s == d); +} + BOOST_AUTO_TEST_SUITE_END() From df10edfd03fe4c9d1eca43a17ac4c54a14ac4832 Mon Sep 17 00:00:00 2001 From: MeshCollider Date: Sat, 9 Sep 2017 00:20:00 +1200 Subject: [PATCH 214/382] More user-friendly error message when partially signing --- src/rpc/rawtransaction.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index a0322f67b..b2fc6a357 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -873,7 +873,12 @@ UniValue signrawtransaction(const JSONRPCRequest& request) ScriptError serror = SCRIPT_ERR_OK; if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) { - TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); + if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) { + // Unable to sign input and verification failed (possible attempt to partially sign). + TxInErrorToJSON(txin, vErrors, "Unable to sign input, invalid stack size (possibly missing key)"); + } else { + TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); + } } } bool fComplete = vErrors.empty(); From 605918272cd806d0ee77a391d00360b925a5fab3 Mon Sep 17 00:00:00 2001 From: Marko Bencun Date: Mon, 11 Sep 2017 16:13:46 +0200 Subject: [PATCH 215/382] add m_added_nodes to connman options --- src/init.cpp | 1 + src/net.cpp | 5 ----- src/net.h | 2 ++ 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 6230eafb6..c611c07e4 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1656,6 +1656,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) connOptions.m_msgproc = peerLogic.get(); connOptions.nSendBufferMaxSize = 1000*gArgs.GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER); connOptions.nReceiveFloodSize = 1000*gArgs.GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER); + connOptions.m_added_nodes = gArgs.GetArgs("-addnode"); connOptions.nMaxOutboundTimeframe = nMaxOutboundTimeframe; connOptions.nMaxOutboundLimit = nMaxOutboundLimit; diff --git a/src/net.cpp b/src/net.cpp index 587c9e511..042bb0bb0 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1897,11 +1897,6 @@ std::vector CConnman::GetAddedNodeInfo() void CConnman::ThreadOpenAddedConnections() { - { - LOCK(cs_vAddedNodes); - vAddedNodes = gArgs.GetArgs("-addnode"); - } - while (true) { CSemaphoreGrant grant(*semAddnode); diff --git a/src/net.h b/src/net.h index ca2433aa5..92d829dd4 100644 --- a/src/net.h +++ b/src/net.h @@ -147,6 +147,7 @@ class CConnman std::vector vBinds, vWhiteBinds; bool m_use_addrman_outgoing = true; std::vector m_specified_outgoing; + std::vector m_added_nodes; }; void Init(const Options& connOptions) { @@ -164,6 +165,7 @@ class CConnman nMaxOutboundTimeframe = connOptions.nMaxOutboundTimeframe; nMaxOutboundLimit = connOptions.nMaxOutboundLimit; vWhitelistedRange = connOptions.vWhitelistedRange; + vAddedNodes = connOptions.m_added_nodes; } CConnman(uint64_t seed0, uint64_t seed1); From 35e5c2269c9a9dc71e4141d15837683d5b3363a1 Mon Sep 17 00:00:00 2001 From: Marko Bencun Date: Mon, 11 Sep 2017 16:13:52 +0200 Subject: [PATCH 216/382] remove unused IsArgSet check Forgotten in 506b700dcb5dd5a7c1d8ffa7c77043a93e4e10de --- src/init.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index c611c07e4..25a415109 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1687,9 +1687,8 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) connOptions.vWhitelistedRange.push_back(subnet); } - if (gArgs.IsArgSet("-seednode")) { - connOptions.vSeedNodes = gArgs.GetArgs("-seednode"); - } + connOptions.vSeedNodes = gArgs.GetArgs("-seednode"); + // Initiate outbound connections unless connect=0 connOptions.m_use_addrman_outgoing = !gArgs.IsArgSet("-connect"); if (!connOptions.m_use_addrman_outgoing) { From ba206d2c63a8d3cbd4a8dd47e9ef126af1bb3bb9 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Thu, 24 Aug 2017 11:36:07 -0400 Subject: [PATCH 217/382] Deprecate confusing blockmaxsize, fix getmininginfo output * This removes block-size-limiting code in favor of GBT clients doing the limiting themselves (if at all). * -blockmaxsize is deprecated and only used to calculate an implied blockmaxweight, addressing confusion from multiple users. * getmininginfo's currentblocksize return value was returning garbage values, and has been removed, also removing a GetSerializeSize call in some block generation inner loops and potentially addressing some performance edge cases. --- src/init.cpp | 11 ++++++++++- src/miner.cpp | 38 ++------------------------------------ src/miner.h | 4 +--- src/policy/policy.h | 2 -- src/rpc/mining.cpp | 2 -- src/validation.h | 1 - test/functional/mining.py | 1 - 7 files changed, 13 insertions(+), 46 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 6230eafb6..3166098ca 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -484,7 +484,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageGroup(_("Block creation options:")); strUsage += HelpMessageOpt("-blockmaxweight=", strprintf(_("Set maximum BIP141 block weight (default: %d)"), DEFAULT_BLOCK_MAX_WEIGHT)); - strUsage += HelpMessageOpt("-blockmaxsize=", strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE)); + strUsage += HelpMessageOpt("-blockmaxsize=", _("Set maximum BIP141 block weight to this * 4. Deprecated, use blockmaxweight")); strUsage += HelpMessageOpt("-blockmintxfee=", 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=", "Override block version to test forking scenarios"); @@ -785,6 +785,15 @@ void InitParameterInteraction() if (gArgs.SoftSetBoolArg("-whitelistrelay", true)) LogPrintf("%s: parameter interaction: -whitelistforcerelay=1 -> setting -whitelistrelay=1\n", __func__); } + + if (gArgs.IsArgSet("-blockmaxsize")) { + unsigned int max_size = gArgs.GetArg("-blockmaxsize", 0); + if (gArgs.SoftSetArg("blockmaxweight", strprintf("%d", max_size * WITNESS_SCALE_FACTOR))) { + LogPrintf("%s: parameter interaction: -blockmaxsize=%d -> setting -blockmaxweight=%d (-blockmaxsize is deprecated!)\n", __func__, max_size, max_size * WITNESS_SCALE_FACTOR); + } else { + LogPrintf("%s: Ignoring blockmaxsize setting which is overridden by blockmaxweight", __func__); + } + } } static std::string ResolveErrMsg(const char * const optname, const std::string& strBind) diff --git a/src/miner.cpp b/src/miner.cpp index 249ea172a..a9989f4b1 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -43,7 +43,6 @@ // its ancestors. uint64_t nLastBlockTx = 0; -uint64_t nLastBlockSize = 0; uint64_t nLastBlockWeight = 0; int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev) @@ -64,7 +63,6 @@ int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParam BlockAssembler::Options::Options() { blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE); nBlockMaxWeight = DEFAULT_BLOCK_MAX_WEIGHT; - nBlockMaxSize = DEFAULT_BLOCK_MAX_SIZE; } BlockAssembler::BlockAssembler(const CChainParams& params, const Options& options) : chainparams(params) @@ -72,10 +70,6 @@ BlockAssembler::BlockAssembler(const CChainParams& params, const Options& option blockMinFeeRate = options.blockMinFeeRate; // Limit weight to between 4K and MAX_BLOCK_WEIGHT-4K for sanity: nBlockMaxWeight = std::max(4000, std::min(MAX_BLOCK_WEIGHT - 4000, options.nBlockMaxWeight)); - // Limit size to between 1K and MAX_BLOCK_SERIALIZED_SIZE-1K for sanity: - nBlockMaxSize = std::max(1000, std::min(MAX_BLOCK_SERIALIZED_SIZE - 1000, options.nBlockMaxSize)); - // Whether we need to account for byte usage (in addition to weight usage) - fNeedSizeAccounting = (nBlockMaxSize < MAX_BLOCK_SERIALIZED_SIZE - 1000); } static BlockAssembler::Options DefaultOptions(const CChainParams& params) @@ -85,20 +79,7 @@ static BlockAssembler::Options DefaultOptions(const CChainParams& params) // If only one is given, only restrict the specified resource. // If both are given, restrict both. BlockAssembler::Options options; - options.nBlockMaxWeight = DEFAULT_BLOCK_MAX_WEIGHT; - options.nBlockMaxSize = DEFAULT_BLOCK_MAX_SIZE; - bool fWeightSet = false; - if (gArgs.IsArgSet("-blockmaxweight")) { - options.nBlockMaxWeight = gArgs.GetArg("-blockmaxweight", DEFAULT_BLOCK_MAX_WEIGHT); - options.nBlockMaxSize = MAX_BLOCK_SERIALIZED_SIZE; - fWeightSet = true; - } - if (gArgs.IsArgSet("-blockmaxsize")) { - options.nBlockMaxSize = gArgs.GetArg("-blockmaxsize", DEFAULT_BLOCK_MAX_SIZE); - if (!fWeightSet) { - options.nBlockMaxWeight = options.nBlockMaxSize * WITNESS_SCALE_FACTOR; - } - } + options.nBlockMaxWeight = gArgs.GetArg("-blockmaxweight", DEFAULT_BLOCK_MAX_WEIGHT); if (gArgs.IsArgSet("-blockmintxfee")) { CAmount n = 0; ParseMoney(gArgs.GetArg("-blockmintxfee", ""), n); @@ -116,7 +97,6 @@ void BlockAssembler::resetBlock() inBlock.clear(); // Reserve space for coinbase tx - nBlockSize = 1000; nBlockWeight = 4000; nBlockSigOpsCost = 400; fIncludeWitness = false; @@ -176,7 +156,6 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc int64_t nTime1 = GetTimeMicros(); nLastBlockTx = nBlockTx; - nLastBlockSize = nBlockSize; nLastBlockWeight = nBlockWeight; // Create coinbase transaction. @@ -191,8 +170,7 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc pblocktemplate->vchCoinbaseCommitment = GenerateCoinbaseCommitment(*pblock, pindexPrev, chainparams.GetConsensus()); pblocktemplate->vTxFees[0] = -nFees; - uint64_t nSerializeSize = GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION); - LogPrintf("CreateNewBlock(): total size: %u block weight: %u txs: %u fees: %ld sigops %d\n", nSerializeSize, GetBlockWeight(*pblock), nBlockTx, nFees, nBlockSigOpsCost); + LogPrintf("CreateNewBlock(): block weight: %u txs: %u fees: %ld sigops %d\n", GetBlockWeight(*pblock), nBlockTx, nFees, nBlockSigOpsCost); // Fill in header pblock->hashPrevBlock = pindexPrev->GetBlockHash(); @@ -239,22 +217,13 @@ bool BlockAssembler::TestPackage(uint64_t packageSize, int64_t packageSigOpsCost // - transaction finality (locktime) // - premature witness (in case segwit transactions are added to mempool before // segwit activation) -// - serialized size (in case -blockmaxsize is in use) bool BlockAssembler::TestPackageTransactions(const CTxMemPool::setEntries& package) { - uint64_t nPotentialBlockSize = nBlockSize; // only used with fNeedSizeAccounting for (const CTxMemPool::txiter it : package) { if (!IsFinalTx(it->GetTx(), nHeight, nLockTimeCutoff)) return false; if (!fIncludeWitness && it->GetTx().HasWitness()) return false; - if (fNeedSizeAccounting) { - uint64_t nTxSize = ::GetSerializeSize(it->GetTx(), SER_NETWORK, PROTOCOL_VERSION); - if (nPotentialBlockSize + nTxSize >= nBlockMaxSize) { - return false; - } - nPotentialBlockSize += nTxSize; - } } return true; } @@ -264,9 +233,6 @@ void BlockAssembler::AddToBlock(CTxMemPool::txiter iter) pblock->vtx.emplace_back(iter->GetSharedTx()); pblocktemplate->vTxFees.push_back(iter->GetFee()); pblocktemplate->vTxSigOpsCost.push_back(iter->GetSigOpCost()); - if (fNeedSizeAccounting) { - nBlockSize += ::GetSerializeSize(iter->GetTx(), SER_NETWORK, PROTOCOL_VERSION); - } nBlockWeight += iter->GetTxWeight(); ++nBlockTx; nBlockSigOpsCost += iter->GetSigOpCost(); diff --git a/src/miner.h b/src/miner.h index abd2ff619..683f4fe08 100644 --- a/src/miner.h +++ b/src/miner.h @@ -139,13 +139,11 @@ class BlockAssembler // Configuration parameters for the block size bool fIncludeWitness; - unsigned int nBlockMaxWeight, nBlockMaxSize; - bool fNeedSizeAccounting; + unsigned int nBlockMaxWeight; CFeeRate blockMinFeeRate; // Information on the current status of the block uint64_t nBlockWeight; - uint64_t nBlockSize; uint64_t nBlockTx; uint64_t nBlockSigOpsCost; CAmount nFees; diff --git a/src/policy/policy.h b/src/policy/policy.h index c06820f84..702471383 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -16,8 +16,6 @@ class CCoinsViewCache; class CTxOut; -/** Default for -blockmaxsize, which controls the maximum size of block the mining code will create **/ -static const unsigned int DEFAULT_BLOCK_MAX_SIZE = 750000; /** 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 **/ diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 980988370..c54dc7407 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -196,7 +196,6 @@ UniValue getmininginfo(const JSONRPCRequest& request) "\nResult:\n" "{\n" " \"blocks\": nnn, (numeric) The current block\n" - " \"currentblocksize\": nnn, (numeric) The last block size\n" " \"currentblockweight\": nnn, (numeric) The last block weight\n" " \"currentblocktx\": nnn, (numeric) The last block transaction\n" " \"difficulty\": xxx.xxxxx (numeric) The current difficulty\n" @@ -215,7 +214,6 @@ UniValue getmininginfo(const JSONRPCRequest& request) UniValue obj(UniValue::VOBJ); obj.push_back(Pair("blocks", (int)chainActive.Height())); - obj.push_back(Pair("currentblocksize", (uint64_t)nLastBlockSize)); obj.push_back(Pair("currentblockweight", (uint64_t)nLastBlockWeight)); obj.push_back(Pair("currentblocktx", (uint64_t)nLastBlockTx)); obj.push_back(Pair("difficulty", (double)GetDifficulty())); diff --git a/src/validation.h b/src/validation.h index 23d4b3558..aa4d7abb4 100644 --- a/src/validation.h +++ b/src/validation.h @@ -161,7 +161,6 @@ extern CTxMemPool mempool; typedef std::unordered_map BlockMap; extern BlockMap mapBlockIndex; extern uint64_t nLastBlockTx; -extern uint64_t nLastBlockSize; extern uint64_t nLastBlockWeight; extern const std::string strMessageMagic; extern CWaitableCriticalSection csBestBlock; diff --git a/test/functional/mining.py b/test/functional/mining.py index 93f983889..c8fb1062b 100755 --- a/test/functional/mining.py +++ b/test/functional/mining.py @@ -38,7 +38,6 @@ def run_test(self): mining_info = node.getmininginfo() assert_equal(mining_info['blocks'], 200) assert_equal(mining_info['chain'], 'regtest') - assert_equal(mining_info['currentblocksize'], 0) assert_equal(mining_info['currentblocktx'], 0) assert_equal(mining_info['currentblockweight'], 0) assert_equal(mining_info['difficulty'], Decimal('4.656542373906925E-10')) From 3dc263c9b9068ee9793b6c7a0023eff16d70fb8f Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Fri, 25 Aug 2017 12:06:13 -0400 Subject: [PATCH 218/382] Use a sensible default for blockmaxweight No sensible user will ever keep the default settings here, so not having sensible defaults only serves to screw users who are paying less attention, which makes for terrible defaults. --- src/policy/policy.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/policy/policy.h b/src/policy/policy.h index 702471383..ef71dd73b 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -17,7 +17,7 @@ class CCoinsViewCache; class CTxOut; /** Default for -blockmaxweight, which controls the range of block weights the mining code will create **/ -static const unsigned int DEFAULT_BLOCK_MAX_WEIGHT = 3000000; +static const unsigned int DEFAULT_BLOCK_MAX_WEIGHT = MAX_BLOCK_WEIGHT - 4000; /** 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 */ From 6f703e9bf11c5f2fcb0fca0e4243fce6b4b9d35a Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Fri, 25 Aug 2017 11:27:03 -0400 Subject: [PATCH 219/382] Add release notes describing blockmaxweight deprecation --- doc/release-notes.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/doc/release-notes.md b/doc/release-notes.md index aa1d1bea1..46fbae042 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -56,6 +56,22 @@ frequently tested on them. Notable changes =============== +Miner block size limiting deprecated +------------------------------------ + +Though blockmaxweight has been preferred for limiting the size of blocks returned by +getblocktemplate since 0.13.0, blockmaxsize remained as an option for those who wished +to limit their block size directly. Using this option resulted in a few UI issues as +well as non-optimal fee selection and ever-so-slightly worse performance, and has thus +now been deprecated. Further, the blockmaxsize option is now used only to calculate an +implied blockmaxweight, instead of limiting block size directly. Any miners who wish +to limit their blocks by size, instead of by weight, will have to do so manually by +removing transactions from their block template directly. + +Low-level RPC changes +---------------------- +- The "currentblocksize" value in getmininginfo has been removed. + Credits ======= From 0b1b9148cd77092d2851eeed5c8c6d5ce117452a Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Mon, 11 Sep 2017 15:24:45 -0400 Subject: [PATCH 220/382] Remove countMaskInv caching in bench framework We were saving a div by caching the inverse as a float, but this ended up requiring a int -> float -> int conversion, which takes almost as much time as the difference between float mul and div. There are lots of other more pressing issues with the bench framework which probably require simply removing the adaptive iteration count stuff anyway. --- src/bench/bench.cpp | 6 ++---- src/bench/bench.h | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/bench/bench.cpp b/src/bench/bench.cpp index 849d924af..7b307d6f4 100644 --- a/src/bench/bench.cpp +++ b/src/bench/bench.cpp @@ -55,13 +55,13 @@ bool benchmark::State::KeepRunning() else { now = gettimedouble(); double elapsed = now - lastTime; - double elapsedOne = elapsed * countMaskInv; + double elapsedOne = elapsed / (countMask + 1); if (elapsedOne < minTime) minTime = elapsedOne; if (elapsedOne > maxTime) maxTime = elapsedOne; // We only use relative values, so don't have to handle 64-bit wrap-around specially nowCycles = perf_cpucycles(); - uint64_t elapsedOneCycles = (nowCycles - lastCycles) * countMaskInv; + uint64_t elapsedOneCycles = (nowCycles - lastCycles) / (countMask + 1); if (elapsedOneCycles < minCycles) minCycles = elapsedOneCycles; if (elapsedOneCycles > maxCycles) maxCycles = elapsedOneCycles; @@ -69,7 +69,6 @@ bool benchmark::State::KeepRunning() // If the execution was much too fast (1/128th of maxElapsed), increase the count mask by 8x and restart timing. // The restart avoids including the overhead of this code in the measurement. countMask = ((countMask<<3)|7) & ((1LL<<60)-1); - countMaskInv = 1./(countMask+1); count = 0; minTime = std::numeric_limits::max(); maxTime = std::numeric_limits::min(); @@ -81,7 +80,6 @@ bool benchmark::State::KeepRunning() uint64_t newCountMask = ((countMask<<1)|1) & ((1LL<<60)-1); if ((count & newCountMask)==0) { countMask = newCountMask; - countMaskInv = 1./(countMask+1); } } } diff --git a/src/bench/bench.h b/src/bench/bench.h index 1f36f2a4b..79109eaa5 100644 --- a/src/bench/bench.h +++ b/src/bench/bench.h @@ -41,7 +41,7 @@ namespace benchmark { std::string name; double maxElapsed; double beginTime; - double lastTime, minTime, maxTime, countMaskInv; + double lastTime, minTime, maxTime; uint64_t count; uint64_t countMask; uint64_t beginCycles; @@ -55,7 +55,6 @@ namespace benchmark { minCycles = std::numeric_limits::max(); maxCycles = std::numeric_limits::min(); countMask = 1; - countMaskInv = 1./(countMask + 1); } bool KeepRunning(); }; From 53a6590f496b25174c740927243bf8307541b0b9 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Mon, 11 Sep 2017 15:43:49 -0400 Subject: [PATCH 221/382] Make float <-> int casts explicit outside of test, qt, CFeeRate --- src/policy/fees.cpp | 2 +- src/rpc/net.cpp | 2 +- src/txmempool.h | 2 +- src/wallet/wallet.cpp | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp index b9476407c..ca774cd74 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -1043,5 +1043,5 @@ CAmount FeeFilterRounder::round(CAmount currentMinFee) if ((it != feeset.begin() && insecure_rand.rand32() % 3 != 0) || it == feeset.end()) { it--; } - return *it; + return static_cast(*it); } diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 7faf21604..e0be81704 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -146,7 +146,7 @@ UniValue getpeerinfo(const JSONRPCRequest& request) obj.push_back(Pair("timeoffset", stats.nTimeOffset)); if (stats.dPingTime > 0.0) obj.push_back(Pair("pingtime", stats.dPingTime)); - if (stats.dMinPing < std::numeric_limits::max()/1e6) + if (stats.dMinPing < static_cast(std::numeric_limits::max())/1e6) obj.push_back(Pair("minping", stats.dMinPing)); if (stats.dPingWait > 0.0) obj.push_back(Pair("pingwait", stats.dPingWait)); diff --git a/src/txmempool.h b/src/txmempool.h index b07886579..929d22358 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -507,7 +507,7 @@ class CTxMemPool * check does nothing. */ void check(const CCoinsViewCache *pcoins) const; - void setSanityCheck(double dFrequency = 1.0) { nCheckFrequency = dFrequency * 4294967295.0; } + void setSanityCheck(double dFrequency = 1.0) { nCheckFrequency = static_cast(dFrequency * 4294967295.0); } // addUnchecked must updated state for all ancestors of a given transaction, // to track size/count of descendant transactions. First version of diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index d376de233..1d163a2bb 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -390,11 +390,11 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, { int64_t nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); - pMasterKey.second.nDeriveIterations = pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime))); + pMasterKey.second.nDeriveIterations = static_cast(pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime)))); nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); - pMasterKey.second.nDeriveIterations = (pMasterKey.second.nDeriveIterations + pMasterKey.second.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2; + pMasterKey.second.nDeriveIterations = (pMasterKey.second.nDeriveIterations + static_cast(pMasterKey.second.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime)))) / 2; if (pMasterKey.second.nDeriveIterations < 25000) pMasterKey.second.nDeriveIterations = 25000; @@ -595,11 +595,11 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) CCrypter crypter; int64_t nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod); - kMasterKey.nDeriveIterations = 2500000 / ((double)(GetTimeMillis() - nStartTime)); + kMasterKey.nDeriveIterations = static_cast(2500000 / ((double)(GetTimeMillis() - nStartTime))); nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod); - kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2; + kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + static_cast(kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime)))) / 2; if (kMasterKey.nDeriveIterations < 25000) kMasterKey.nDeriveIterations = 25000; From 1789e4675b17f274fcb0761321e6fd249a102f40 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Mon, 11 Sep 2017 15:47:09 -0400 Subject: [PATCH 222/382] Force explicit double -> int conversion for CFeeRate constructor This resolves an issue where estimatesmartfee would return 999 sat/byte instead of 1000, due to floating point loss of precision Thanks to sipa for suggesting is_integral. --- src/policy/feerate.h | 7 ++++++- src/policy/fees.cpp | 4 ++-- src/test/mempool_tests.cpp | 6 +++--- src/txmempool.cpp | 4 ++-- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/policy/feerate.h b/src/policy/feerate.h index 7e519e3ef..3449cdd69 100644 --- a/src/policy/feerate.h +++ b/src/policy/feerate.h @@ -20,10 +20,15 @@ class CFeeRate { private: CAmount nSatoshisPerK; // unit is satoshis-per-1,000-bytes + public: /** Fee rate of 0 satoshis per kB */ CFeeRate() : nSatoshisPerK(0) { } - explicit CFeeRate(const CAmount& _nSatoshisPerK): nSatoshisPerK(_nSatoshisPerK) { } + template + CFeeRate(const I _nSatoshisPerK): nSatoshisPerK(_nSatoshisPerK) { + // We've previously had bugs creep in from silent double->int conversion... + static_assert(std::is_integral::value, "CFeeRate should be used without floats"); + } /** Constructor for a fee rate in satoshis per kB. The size in bytes must not exceed (2^63 - 1)*/ CFeeRate(const CAmount& nFeePaid, size_t nBytes); /** diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp index ca774cd74..8056f385a 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -714,7 +714,7 @@ CFeeRate CBlockPolicyEstimator::estimateRawFee(int confTarget, double successThr if (median < 0) return CFeeRate(0); - return CFeeRate(median); + return CFeeRate(llround(median)); } unsigned int CBlockPolicyEstimator::HighestTargetTracked(FeeEstimateHorizon horizon) const @@ -901,7 +901,7 @@ CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, FeeCalculation if (median < 0) return CFeeRate(0); // error condition - return CFeeRate(median); + return CFeeRate(llround(median)); } diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp index 51b28d09f..116210a29 100644 --- a/src/test/mempool_tests.cpp +++ b/src/test/mempool_tests.cpp @@ -559,15 +559,15 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest) // ... we should keep the same min fee until we get a block pool.removeForBlock(vtx, 1); SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE); - BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/2); + BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/2.0)); // ... then feerate should drop 1/2 each halflife SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2); - BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 5 / 2).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/4); + BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 5 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/4.0)); // ... with a 1/2 halflife when mempool is < 1/2 its target size SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4); - BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 9 / 2).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/8); + BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 9 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/8.0)); // ... with a 1/4 halflife when mempool is < 1/4 its target size SetMockTime(42 + 7*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4); diff --git a/src/txmempool.cpp b/src/txmempool.cpp index f68d67764..776d3f36c 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -981,7 +981,7 @@ const CTxMemPool::setEntries & CTxMemPool::GetMemPoolChildren(txiter entry) cons CFeeRate CTxMemPool::GetMinFee(size_t sizelimit) const { LOCK(cs); if (!blockSinceLastRollingFeeBump || rollingMinimumFeeRate == 0) - return CFeeRate(rollingMinimumFeeRate); + return CFeeRate(llround(rollingMinimumFeeRate)); int64_t time = GetTime(); if (time > lastRollingFeeUpdate + 10) { @@ -999,7 +999,7 @@ CFeeRate CTxMemPool::GetMinFee(size_t sizelimit) const { return CFeeRate(0); } } - return std::max(CFeeRate(rollingMinimumFeeRate), incrementalRelayFee); + return std::max(CFeeRate(llround(rollingMinimumFeeRate)), incrementalRelayFee); } void CTxMemPool::trackPackageRemoved(const CFeeRate& rate) { From 05cae8aefd160ed83206a6d0f15cc29fd8653755 Mon Sep 17 00:00:00 2001 From: Marko Bencun Date: Thu, 20 Jul 2017 11:32:47 +0200 Subject: [PATCH 223/382] range-based loops and const qualifications in net.cpp Plus a use of std::copy() instead of manual copying. --- src/net.cpp | 71 ++++++++++++++++++++++++++++------------------------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/src/net.cpp b/src/net.cpp index 587c9e511..cfc544de1 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -135,11 +135,10 @@ static std::vector convertSeed6(const std::vector &vSeedsIn const int64_t nOneWeek = 7*24*60*60; std::vector vSeedsOut; vSeedsOut.reserve(vSeedsIn.size()); - for (std::vector::const_iterator i(vSeedsIn.begin()); i != vSeedsIn.end(); ++i) - { + for (const auto& seed_in : vSeedsIn) { struct in6_addr ip; - memcpy(&ip, i->addr, sizeof(ip)); - CAddress addr(CService(ip, i->port), NODE_NETWORK); + memcpy(&ip, seed_in.addr, sizeof(ip)); + CAddress addr(CService(ip, seed_in.port), NODE_NETWORK); addr.nTime = GetTime() - GetRand(nOneWeek) - nOneWeek; vSeedsOut.push_back(addr); } @@ -299,18 +298,22 @@ bool IsReachable(const CNetAddr& addr) CNode* CConnman::FindNode(const CNetAddr& ip) { LOCK(cs_vNodes); - for (CNode* pnode : vNodes) - if ((CNetAddr)pnode->addr == ip) - return (pnode); + for (CNode* pnode : vNodes) { + if ((CNetAddr)pnode->addr == ip) { + return pnode; + } + } return nullptr; } CNode* CConnman::FindNode(const CSubNet& subNet) { LOCK(cs_vNodes); - for (CNode* pnode : vNodes) - if (subNet.Match((CNetAddr)pnode->addr)) - return (pnode); + for (CNode* pnode : vNodes) { + if (subNet.Match((CNetAddr)pnode->addr)) { + return pnode; + } + } return nullptr; } @@ -319,7 +322,7 @@ CNode* CConnman::FindNode(const std::string& addrName) LOCK(cs_vNodes); for (CNode* pnode : vNodes) { if (pnode->GetAddrName() == addrName) { - return (pnode); + return pnode; } } return nullptr; @@ -328,9 +331,11 @@ CNode* CConnman::FindNode(const std::string& addrName) CNode* CConnman::FindNode(const CService& addr) { LOCK(cs_vNodes); - for (CNode* pnode : vNodes) - if ((CService)pnode->addr == addr) - return (pnode); + for (CNode* pnode : vNodes) { + if ((CService)pnode->addr == addr) { + return pnode; + } + } return nullptr; } @@ -474,10 +479,9 @@ void CConnman::ClearBanned() bool CConnman::IsBanned(CNetAddr ip) { LOCK(cs_setBanned); - for (banmap_t::iterator it = setBanned.begin(); it != setBanned.end(); it++) - { - CSubNet subNet = (*it).first; - CBanEntry banEntry = (*it).second; + for (const auto& it : setBanned) { + CSubNet subNet = it.first; + CBanEntry banEntry = it.second; if (subNet.Match(ip) && GetTime() < banEntry.nBanUntil) { return true; @@ -952,7 +956,7 @@ bool CConnman::AttemptToEvictConnection() { LOCK(cs_vNodes); - for (CNode *node : vNodes) { + for (const CNode* node : vNodes) { if (node->fWhitelisted) continue; if (!node->fInbound) @@ -1030,9 +1034,9 @@ bool CConnman::AttemptToEvictConnection() // Disconnect from the network group with the most connections NodeId evicted = vEvictionCandidates.front().id; LOCK(cs_vNodes); - for(std::vector::const_iterator it(vNodes.begin()); it != vNodes.end(); ++it) { - if ((*it)->GetId() == evicted) { - (*it)->fDisconnect = true; + for (CNode* pnode : vNodes) { + if (pnode->GetId() == evicted) { + pnode->fDisconnect = true; return true; } } @@ -1056,9 +1060,9 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) { bool whitelisted = hListenSocket.whitelisted || IsWhitelistedRange(addr); { LOCK(cs_vNodes); - for (CNode* pnode : vNodes) - if (pnode->fInbound) - nInbound++; + for (const CNode* pnode : vNodes) { + if (pnode->fInbound) nInbound++; + } } if (hSocket == INVALID_SOCKET) @@ -1850,8 +1854,7 @@ std::vector CConnman::GetAddedNodeInfo() { LOCK(cs_vAddedNodes); ret.reserve(vAddedNodes.size()); - for (const std::string& strAddNode : vAddedNodes) - lAddresses.push_back(strAddNode); + std::copy(vAddedNodes.cbegin(), vAddedNodes.cend(), std::back_inserter(lAddresses)); } @@ -2488,9 +2491,8 @@ std::vector CConnman::GetAddresses() bool CConnman::AddNode(const std::string& strNode) { LOCK(cs_vAddedNodes); - for(std::vector::const_iterator it = vAddedNodes.begin(); it != vAddedNodes.end(); ++it) { - if (strNode == *it) - return false; + for (const std::string& it : vAddedNodes) { + if (strNode == it) return false; } vAddedNodes.push_back(strNode); @@ -2516,9 +2518,11 @@ size_t CConnman::GetNodeCount(NumConnections flags) return vNodes.size(); int nNum = 0; - for(std::vector::const_iterator it = vNodes.begin(); it != vNodes.end(); ++it) - if (flags & ((*it)->fInbound ? CONNECTIONS_IN : CONNECTIONS_OUT)) + for (const auto& pnode : vNodes) { + if (flags & (pnode->fInbound ? CONNECTIONS_IN : CONNECTIONS_OUT)) { nNum++; + } + } return nNum; } @@ -2528,8 +2532,7 @@ void CConnman::GetNodeStats(std::vector& vstats) vstats.clear(); LOCK(cs_vNodes); vstats.reserve(vNodes.size()); - for(std::vector::iterator it = vNodes.begin(); it != vNodes.end(); ++it) { - CNode* pnode = *it; + for (CNode* pnode : vNodes) { vstats.emplace_back(); pnode->copyStats(vstats.back()); } From fa2c3b6cf73e776147b5a714fd3f0dc0652c4b34 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Tue, 12 Sep 2017 09:34:12 +0200 Subject: [PATCH 224/382] doc: Bump manpages to 0.15.99 --- doc/man/bitcoin-cli.1 | 27 ++++++++-- doc/man/bitcoin-qt.1 | 115 +++++++++++++++++++++++------------------- doc/man/bitcoin-tx.1 | 11 ++-- doc/man/bitcoind.1 | 115 +++++++++++++++++++++++------------------- 4 files changed, 156 insertions(+), 112 deletions(-) diff --git a/doc/man/bitcoin-cli.1 b/doc/man/bitcoin-cli.1 index 0493241b1..678763844 100644 --- a/doc/man/bitcoin-cli.1 +++ b/doc/man/bitcoin-cli.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH BITCOIN-CLI "1" "February 2017" "bitcoin-cli v0.14.99.0" "User Commands" +.TH BITCOIN-CLI "1" "September 2017" "bitcoin-cli v0.15.99.0" "User Commands" .SH NAME -bitcoin-cli \- manual page for bitcoin-cli v0.14.99.0 +bitcoin-cli \- manual page for bitcoin-cli v0.15.99.0 .SH DESCRIPTION -Bitcoin Core RPC client version v0.14.99.0 +Bitcoin Core RPC client version v0.15.99.0 .SS "Usage:" .TP bitcoin\-cli [options] [params] @@ -64,12 +64,29 @@ Password for JSON\-RPC connections .HP \fB\-rpcclienttimeout=\fR .IP -Timeout during HTTP requests (default: 900) +Timeout in seconds during HTTP requests, or 0 for no timeout. (default: +900) +.HP +\fB\-stdinrpcpass\fR +.TP +Read RPC password from standard input as a single line. +When combined +.IP +with \fB\-stdin\fR, the first line from standard input is used for the +RPC password. .HP \fB\-stdin\fR .IP Read extra arguments from standard input, one per line until EOF/Ctrl\-D -(recommended for sensitive information such as passphrases) +(recommended for sensitive information such as passphrases). +When combined with \fB\-stdinrpcpass\fR, the first line from standard +input is used for the RPC password. +.HP +\fB\-rpcwallet=\fR +.IP +Send RPC for non\-default wallet on RPC server (argument is wallet +filename in bitcoind directory, required if bitcoind/\-Qt runs +with multiple wallets) .SH COPYRIGHT Copyright (C) 2009-2017 The Bitcoin Core developers diff --git a/doc/man/bitcoin-qt.1 b/doc/man/bitcoin-qt.1 index ce252612e..ae35d50ac 100644 --- a/doc/man/bitcoin-qt.1 +++ b/doc/man/bitcoin-qt.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH BITCOIN-QT "1" "February 2017" "bitcoin-qt v0.14.99.0" "User Commands" +.TH BITCOIN-QT "1" "September 2017" "bitcoin-qt v0.15.99.0" "User Commands" .SH NAME -bitcoin-qt \- manual page for bitcoin-qt v0.14.99.0 +bitcoin-qt \- manual page for bitcoin-qt v0.15.99.0 .SH DESCRIPTION -Bitcoin Core version v0.14.99.0 (64\-bit) +Bitcoin Core version v0.15.99.0 (64\-bit) Usage: .IP bitcoin\-qt [command\-line options] @@ -32,9 +32,9 @@ block hash) 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: -00000000000000000013176bf8d7dfeab4e1db31dc93bc311b436e82ab226b90, +0000000000000000003b9ce759c2a087d52abc4266f8f4ebd6d768b89defa50a, testnet: -00000000000128796ee387cf110ccb9d2f36cffaf7f73079c995377c65ac0dcc) +0000000002e9e7b00e1f6dc5123a04aad68dd0f0968d8c7aa45f6640795c37b1) .HP \fB\-conf=\fR .IP @@ -46,7 +46,7 @@ Specify data directory .HP \fB\-dbcache=\fR .IP -Set database cache size in megabytes (4 to 16384, default: 300) +Set database cache size in megabytes (4 to 16384, default: 450) .HP \fB\-loadblock=\fR .IP @@ -65,6 +65,10 @@ Keep the transaction memory pool below megabytes (default: 300) Do not keep transactions in the mempool longer than hours (default: 336) .HP +\fB\-persistmempool\fR +.IP +Whether to save the mempool on shutdown and load on restart (default: 1) +.HP \fB\-blockreconstructionextratxn=\fR .IP Extra transactions to keep in memory for compact block reconstructions @@ -131,8 +135,8 @@ for IPv6 .HP \fB\-connect=\fR .IP -Connect only to the specified node(s); \fB\-noconnect\fR or \fB\-connect\fR=\fI\,0\/\fR alone to -disable automatic connections +Connect only to the specified node(s); \fB\-connect\fR=\fI\,0\/\fR disables automatic +connections .HP \fB\-discover\fR .IP @@ -146,7 +150,7 @@ Allow DNS lookups for \fB\-addnode\fR, \fB\-seednode\fR and \fB\-connect\fR (def \fB\-dnsseed\fR .IP Query for peer addresses via DNS lookup, if low on addresses (default: 1 -unless \fB\-connect\fR/\-noconnect) +unless \fB\-connect\fR used) .HP \fB\-externalip=\fR .IP @@ -158,8 +162,7 @@ Always query for peer addresses via DNS lookup (default: 0) .HP \fB\-listen\fR .IP -Accept connections from outside (default: 1 if no \fB\-proxy\fR or -\fB\-connect\fR/\-noconnect) +Accept connections from outside (default: 1 if no \fB\-proxy\fR or \fB\-connect\fR) .HP \fB\-listenonion\fR .IP @@ -214,11 +217,6 @@ Connect through SOCKS5 proxy Randomize credentials for every proxy connection. This enables Tor stream isolation (default: 1) .HP -\fB\-rpcserialversion\fR -.IP -Sets the serialization of raw transaction or block hex returned in -non\-verbose mode, non\-segwit(0) or segwit(1) (default: 1) -.HP \fB\-seednode=\fR .IP Connect to a node to retrieve peer addresses, and disconnect @@ -253,16 +251,6 @@ times. 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 .HP -\fB\-whitelistrelay\fR -.IP -Accept relayed transactions received from whitelisted peers even when -not relaying transactions (default: 1) -.HP -\fB\-whitelistforcerelay\fR -.IP -Force relay of transactions from whitelisted peers even if they violate -local relay policy (default: 1) -.HP \fB\-maxuploadtarget=\fR .IP Tries to keep outbound traffic under the given target (in MiB per 24h), @@ -276,13 +264,21 @@ Do not load the wallet and disable wallet RPC calls .HP \fB\-keypool=\fR .IP -Set key pool size to (default: 100) +Set key pool size to (default: 1000) .HP \fB\-fallbackfee=\fR .IP A fee rate (in BTC/kB) that will be used when fee estimation has insufficient data (default: 0.0002) .HP +\fB\-discardfee=\fR +.IP +The fee rate (in BTC/kB) that indicates your tolerance for discarding +change by adding it to the fee (default: 0.0001). Note: An output +is discarded if it is dust at this rate, but we will always +discard up to the dust relay fee and a discard fee above that is +limited by the fee estimate for the longest target +.HP \fB\-mintxfee=\fR .IP Fees (in BTC/kB) smaller than this are considered zero fee for @@ -309,11 +305,6 @@ Spend unconfirmed change when sending transactions (default: 1) If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: 6) .HP -\fB\-usehd\fR -.IP -Use hierarchical deterministic key generation (HD) after BIP32. Only has -effect during wallet creation/first start (default: 1) -.HP \fB\-walletrbf\fR .IP Send transactions with full\-RBF opt\-in enabled (default: 0) @@ -370,10 +361,16 @@ Append comment to the user agent string .IP Output debugging information (default: 0, supplying is optional). If is not supplied or if = 1, -output all debugging information. can be: addrman, -alert, bench, cmpctblock, coindb, db, http, libevent, lock, -mempool, mempoolrej, net, proxy, prune, rand, reindex, rpc, -selectcoins, tor, zmq, qt. +output all debugging information. can be: net, tor, +mempool, http, bench, zmq, db, rpc, estimatefee, addrman, +selectcoins, reindex, cmpctblock, rand, prune, proxy, mempoolrej, +libevent, coindb, qt, leveldb. +.HP +\fB\-debugexclude=\fR +.IP +Exclude debugging information for a category. Can be used in conjunction +with \fB\-debug\fR=\fI\,1\/\fR to output debug logs for all categories except one +or more specified categories. .HP \fB\-help\-debug\fR .IP @@ -387,11 +384,6 @@ Include IP addresses in debug output (default: 0) .IP Prepend debug output with timestamp (default: 1) .HP -\fB\-minrelaytxfee=\fR -.IP -Fees (in BTC/kB) smaller than this are considered zero fee for relaying, -mining and transaction creation (default: 0.00001) -.HP \fB\-maxtxfee=\fR .IP Maximum total fees (in BTC) to use in a single wallet transaction or raw @@ -431,21 +423,32 @@ Maximum size of data in data carrier transactions we relay and mine \fB\-mempoolreplacement\fR .IP Enable transaction replacement in the memory pool (default: 1) +.HP +\fB\-minrelaytxfee=\fR +.IP +Fees (in BTC/kB) smaller than this are considered zero fee for relaying, +mining and transaction creation (default: 0.00001) +.HP +\fB\-whitelistrelay\fR +.IP +Accept relayed transactions received from whitelisted peers even when +not relaying transactions (default: 1) +.HP +\fB\-whitelistforcerelay\fR +.IP +Force relay of transactions from whitelisted peers even if they violate +local relay policy (default: 1) .PP Block creation options: .HP \fB\-blockmaxweight=\fR .IP -Set maximum BIP141 block weight (default: 3000000) +Set maximum BIP141 block weight (default: 3996000) .HP \fB\-blockmaxsize=\fR .IP -Set maximum block size in bytes (default: 750000) -.HP -\fB\-blockprioritysize=\fR -.IP -Set maximum size of high\-priority/low\-fee transactions in bytes -(default: 0) +Set maximum BIP141 block weight to this * 4. Deprecated, use +blockmaxweight .HP \fB\-blockmintxfee=\fR .IP @@ -462,11 +465,14 @@ Accept command line and JSON\-RPC commands .IP Accept public REST requests (default: 0) .HP -\fB\-rpcbind=\fR +\fB\-rpcbind=\fR[:port] .IP -Bind to given address to listen for JSON\-RPC connections. Use -[host]:port notation for IPv6. This option can be specified -multiple times (default: bind to all interfaces) +Bind to given address to listen for JSON\-RPC connections. This option is +ignored unless \fB\-rpcallowip\fR is also passed. Port is optional and +overrides \fB\-rpcport\fR. Use [host]:port notation for IPv6. This +option can be specified multiple times (default: 127.0.0.1 and +::1 i.e., localhost, or if \fB\-rpcallowip\fR has been specified, +0.0.0.0 and :: i.e., all addresses) .HP \fB\-rpccookiefile=\fR .IP @@ -501,6 +507,11 @@ 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 .HP +\fB\-rpcserialversion\fR +.IP +Sets the serialization of raw transaction or block hex returned in +non\-verbose mode, non\-segwit(0) or segwit(1) (default: 1) +.HP \fB\-rpcthreads=\fR .IP Set the number of threads to service RPC calls (default: 4) diff --git a/doc/man/bitcoin-tx.1 b/doc/man/bitcoin-tx.1 index 98adf2f5b..8b72fbde0 100644 --- a/doc/man/bitcoin-tx.1 +++ b/doc/man/bitcoin-tx.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH BITCOIN-TX "1" "February 2017" "bitcoin-tx v0.14.99.0" "User Commands" +.TH BITCOIN-TX "1" "September 2017" "bitcoin-tx v0.15.99.0" "User Commands" .SH NAME -bitcoin-tx \- manual page for bitcoin-tx v0.14.99.0 +bitcoin-tx \- manual page for bitcoin-tx v0.15.99.0 .SH DESCRIPTION -Bitcoin Core bitcoin\-tx utility version v0.14.99.0 +Bitcoin Core bitcoin\-tx utility version v0.15.99.0 .SS "Usage:" .TP bitcoin\-tx [options] [commands] @@ -63,6 +63,11 @@ nversion=N .IP Set TX version to N .IP +replaceable(=N) +.IP +Set RBF opt\-in sequence number for input N (if not provided, opt\-in all +available inputs) +.IP outaddr=VALUE:ADDRESS .IP Add address\-based output to TX diff --git a/doc/man/bitcoind.1 b/doc/man/bitcoind.1 index fb066e0c6..baf747436 100644 --- a/doc/man/bitcoind.1 +++ b/doc/man/bitcoind.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH BITCOIND "1" "February 2017" "bitcoind v0.14.99.0" "User Commands" +.TH BITCOIND "1" "September 2017" "bitcoind v0.15.99.0" "User Commands" .SH NAME -bitcoind \- manual page for bitcoind v0.14.99.0 +bitcoind \- manual page for bitcoind v0.15.99.0 .SH DESCRIPTION -Bitcoin Core Daemon version v0.14.99.0 +Bitcoin Core Daemon version v0.15.99.0 .SS "Usage:" .TP bitcoind [options] @@ -33,9 +33,9 @@ block hash) 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: -00000000000000000013176bf8d7dfeab4e1db31dc93bc311b436e82ab226b90, +0000000000000000003b9ce759c2a087d52abc4266f8f4ebd6d768b89defa50a, testnet: -00000000000128796ee387cf110ccb9d2f36cffaf7f73079c995377c65ac0dcc) +0000000002e9e7b00e1f6dc5123a04aad68dd0f0968d8c7aa45f6640795c37b1) .HP \fB\-conf=\fR .IP @@ -51,7 +51,7 @@ Specify data directory .HP \fB\-dbcache=\fR .IP -Set database cache size in megabytes (4 to 16384, default: 300) +Set database cache size in megabytes (4 to 16384, default: 450) .HP \fB\-loadblock=\fR .IP @@ -70,6 +70,10 @@ Keep the transaction memory pool below megabytes (default: 300) Do not keep transactions in the mempool longer than hours (default: 336) .HP +\fB\-persistmempool\fR +.IP +Whether to save the mempool on shutdown and load on restart (default: 1) +.HP \fB\-blockreconstructionextratxn=\fR .IP Extra transactions to keep in memory for compact block reconstructions @@ -136,8 +140,8 @@ for IPv6 .HP \fB\-connect=\fR .IP -Connect only to the specified node(s); \fB\-noconnect\fR or \fB\-connect\fR=\fI\,0\/\fR alone to -disable automatic connections +Connect only to the specified node(s); \fB\-connect\fR=\fI\,0\/\fR disables automatic +connections .HP \fB\-discover\fR .IP @@ -151,7 +155,7 @@ Allow DNS lookups for \fB\-addnode\fR, \fB\-seednode\fR and \fB\-connect\fR (def \fB\-dnsseed\fR .IP Query for peer addresses via DNS lookup, if low on addresses (default: 1 -unless \fB\-connect\fR/\-noconnect) +unless \fB\-connect\fR used) .HP \fB\-externalip=\fR .IP @@ -163,8 +167,7 @@ Always query for peer addresses via DNS lookup (default: 0) .HP \fB\-listen\fR .IP -Accept connections from outside (default: 1 if no \fB\-proxy\fR or -\fB\-connect\fR/\-noconnect) +Accept connections from outside (default: 1 if no \fB\-proxy\fR or \fB\-connect\fR) .HP \fB\-listenonion\fR .IP @@ -219,11 +222,6 @@ Connect through SOCKS5 proxy Randomize credentials for every proxy connection. This enables Tor stream isolation (default: 1) .HP -\fB\-rpcserialversion\fR -.IP -Sets the serialization of raw transaction or block hex returned in -non\-verbose mode, non\-segwit(0) or segwit(1) (default: 1) -.HP \fB\-seednode=\fR .IP Connect to a node to retrieve peer addresses, and disconnect @@ -258,16 +256,6 @@ times. 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 .HP -\fB\-whitelistrelay\fR -.IP -Accept relayed transactions received from whitelisted peers even when -not relaying transactions (default: 1) -.HP -\fB\-whitelistforcerelay\fR -.IP -Force relay of transactions from whitelisted peers even if they violate -local relay policy (default: 1) -.HP \fB\-maxuploadtarget=\fR .IP Tries to keep outbound traffic under the given target (in MiB per 24h), @@ -281,13 +269,21 @@ Do not load the wallet and disable wallet RPC calls .HP \fB\-keypool=\fR .IP -Set key pool size to (default: 100) +Set key pool size to (default: 1000) .HP \fB\-fallbackfee=\fR .IP A fee rate (in BTC/kB) that will be used when fee estimation has insufficient data (default: 0.0002) .HP +\fB\-discardfee=\fR +.IP +The fee rate (in BTC/kB) that indicates your tolerance for discarding +change by adding it to the fee (default: 0.0001). Note: An output +is discarded if it is dust at this rate, but we will always +discard up to the dust relay fee and a discard fee above that is +limited by the fee estimate for the longest target +.HP \fB\-mintxfee=\fR .IP Fees (in BTC/kB) smaller than this are considered zero fee for @@ -314,11 +310,6 @@ Spend unconfirmed change when sending transactions (default: 1) If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: 6) .HP -\fB\-usehd\fR -.IP -Use hierarchical deterministic key generation (HD) after BIP32. Only has -effect during wallet creation/first start (default: 1) -.HP \fB\-walletrbf\fR .IP Send transactions with full\-RBF opt\-in enabled (default: 0) @@ -375,10 +366,16 @@ Append comment to the user agent string .IP Output debugging information (default: 0, supplying is optional). If is not supplied or if = 1, -output all debugging information. can be: addrman, -alert, bench, cmpctblock, coindb, db, http, libevent, lock, -mempool, mempoolrej, net, proxy, prune, rand, reindex, rpc, -selectcoins, tor, zmq. +output all debugging information. can be: net, tor, +mempool, http, bench, zmq, db, rpc, estimatefee, addrman, +selectcoins, reindex, cmpctblock, rand, prune, proxy, mempoolrej, +libevent, coindb, qt, leveldb. +.HP +\fB\-debugexclude=\fR +.IP +Exclude debugging information for a category. Can be used in conjunction +with \fB\-debug\fR=\fI\,1\/\fR to output debug logs for all categories except one +or more specified categories. .HP \fB\-help\-debug\fR .IP @@ -392,11 +389,6 @@ Include IP addresses in debug output (default: 0) .IP Prepend debug output with timestamp (default: 1) .HP -\fB\-minrelaytxfee=\fR -.IP -Fees (in BTC/kB) smaller than this are considered zero fee for relaying, -mining and transaction creation (default: 0.00001) -.HP \fB\-maxtxfee=\fR .IP Maximum total fees (in BTC) to use in a single wallet transaction or raw @@ -436,21 +428,32 @@ Maximum size of data in data carrier transactions we relay and mine \fB\-mempoolreplacement\fR .IP Enable transaction replacement in the memory pool (default: 1) +.HP +\fB\-minrelaytxfee=\fR +.IP +Fees (in BTC/kB) smaller than this are considered zero fee for relaying, +mining and transaction creation (default: 0.00001) +.HP +\fB\-whitelistrelay\fR +.IP +Accept relayed transactions received from whitelisted peers even when +not relaying transactions (default: 1) +.HP +\fB\-whitelistforcerelay\fR +.IP +Force relay of transactions from whitelisted peers even if they violate +local relay policy (default: 1) .PP Block creation options: .HP \fB\-blockmaxweight=\fR .IP -Set maximum BIP141 block weight (default: 3000000) +Set maximum BIP141 block weight (default: 3996000) .HP \fB\-blockmaxsize=\fR .IP -Set maximum block size in bytes (default: 750000) -.HP -\fB\-blockprioritysize=\fR -.IP -Set maximum size of high\-priority/low\-fee transactions in bytes -(default: 0) +Set maximum BIP141 block weight to this * 4. Deprecated, use +blockmaxweight .HP \fB\-blockmintxfee=\fR .IP @@ -467,11 +470,14 @@ Accept command line and JSON\-RPC commands .IP Accept public REST requests (default: 0) .HP -\fB\-rpcbind=\fR +\fB\-rpcbind=\fR[:port] .IP -Bind to given address to listen for JSON\-RPC connections. Use -[host]:port notation for IPv6. This option can be specified -multiple times (default: bind to all interfaces) +Bind to given address to listen for JSON\-RPC connections. This option is +ignored unless \fB\-rpcallowip\fR is also passed. Port is optional and +overrides \fB\-rpcport\fR. Use [host]:port notation for IPv6. This +option can be specified multiple times (default: 127.0.0.1 and +::1 i.e., localhost, or if \fB\-rpcallowip\fR has been specified, +0.0.0.0 and :: i.e., all addresses) .HP \fB\-rpccookiefile=\fR .IP @@ -506,6 +512,11 @@ 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 .HP +\fB\-rpcserialversion\fR +.IP +Sets the serialization of raw transaction or block hex returned in +non\-verbose mode, non\-segwit(0) or segwit(1) (default: 1) +.HP \fB\-rpcthreads=\fR .IP Set the number of threads to service RPC calls (default: 4) From fa65dcdda07746d2e5149367a193e361249873b5 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Tue, 12 Sep 2017 10:04:27 +0200 Subject: [PATCH 225/382] doc: Update release notes for 0.16.0 --- doc/release-notes.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/doc/release-notes.md b/doc/release-notes.md index 46fbae042..04fb0f333 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -8,7 +8,7 @@ Bitcoin Core version *version* is now available from: This is a new major version release, including new features, various bugfixes and performance improvements, as well as updated translations. -Please report bugs using the issue tracker at github: +Please report bugs using the issue tracker at GitHub: @@ -21,7 +21,7 @@ How to Upgrade If you are running an older version, shut it down. Wait until it has completely shut down (which might take a few minutes for older versions), then run the -installer (on Windows) or just copy over /Applications/Bitcoin-Qt (on Mac) +installer (on Windows) or just copy over `/Applications/Bitcoin-Qt` (on Mac) or `bitcoind`/`bitcoin-qt` (on Linux). The first time you run version 0.15.0, your chainstate database will be converted to a @@ -68,9 +68,20 @@ implied blockmaxweight, instead of limiting block size directly. Any miners who to limit their blocks by size, instead of by weight, will have to do so manually by removing transactions from their block template directly. +HD-wallets by default +--------------------- +Due to a backward-incompatible change in the wallet database, wallets created +with version 0.16.0 will be rejected by previous versions. Also, version 0.16.0 +will only create hierarchical deterministic (HD) wallets. + Low-level RPC changes ---------------------- - The "currentblocksize" value in getmininginfo has been removed. +- The deprecated RPC `getinfo` was removed. It is recommended that the more specific RPCs are used: + * `getblockchaininfo` + * `getnetworkinfo` + * `getwalletinfo` + * `getmininginfo` Credits ======= From fadf31ef02b35f438ce2cf7048830f6974cdb515 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Tue, 12 Sep 2017 12:44:23 +0200 Subject: [PATCH 226/382] wallet: Display non-HD error on first run --- src/wallet/wallet.cpp | 8 ++++++-- src/wallet/wallet.h | 2 -- test/functional/wallet-hd.py | 6 ++++++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index d376de233..cbc045fa1 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3827,6 +3827,10 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) if (fFirstRun) { // ensure this wallet.dat can only be opened by clients supporting HD with chain split and expects no default key + if (!gArgs.GetBoolArg("-usehd", true)) { + InitError(strprintf(_("Error creating %s: You can't create non-HD wallets with this version."), walletFile)); + return nullptr; + } walletInstance->SetMinVersion(FEATURE_NO_DEFAULT_KEY); // generate a new master key @@ -3843,9 +3847,9 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) walletInstance->SetBestChain(chainActive.GetLocator()); } else if (gArgs.IsArgSet("-usehd")) { - bool useHD = gArgs.GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET); + bool useHD = gArgs.GetBoolArg("-usehd", true); if (walletInstance->IsHDEnabled() && !useHD) { - InitError(strprintf(_("Error loading %s: You can't disable HD on an already existing HD wallet or create new non-HD wallets."), walletFile)); + InitError(strprintf(_("Error loading %s: You can't disable HD on an already existing HD wallet"), walletFile)); return nullptr; } if (!walletInstance->IsHDEnabled() && useHD) { diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 542e9bd5c..c4af192f3 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -65,8 +65,6 @@ static const unsigned int DEFAULT_TX_CONFIRM_TARGET = 6; static const bool DEFAULT_WALLET_RBF = false; static const bool DEFAULT_WALLETBROADCAST = true; static const bool DEFAULT_DISABLE_WALLET = false; -//! if set, all keys will be derived by using BIP32 -static const bool DEFAULT_USE_HD_WALLET = true; extern const char * DEFAULT_WALLET_DAT; diff --git a/test/functional/wallet-hd.py b/test/functional/wallet-hd.py index 3af04c227..5ef3bf5bf 100755 --- a/test/functional/wallet-hd.py +++ b/test/functional/wallet-hd.py @@ -20,6 +20,12 @@ def set_test_params(self): def run_test (self): tmpdir = self.options.tmpdir + # Make sure can't switch off usehd after wallet creation + self.stop_node(1) + self.assert_start_raises_init_error(1, ['-usehd=0'], 'already existing HD wallet') + self.start_node(1) + connect_nodes_bi(self.nodes, 0, 1) + # Make sure we use hd, keep masterkeyid masterkeyid = self.nodes[1].getwalletinfo()['hdmasterkeyid'] assert_equal(len(masterkeyid), 40) From fd849e1b039bcb5856aa705269437211194cdfee Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Tue, 12 Sep 2017 12:30:26 -0400 Subject: [PATCH 227/382] Change AcceptToMemoryPool function signature Combine fLimitFree and fOverrideMempoolLimit into a single boolean: bypass_limits. This is used to indicate that mempool limiting based on feerate should be bypassed. It is used when readding transactions from a reorg and then the mempool is trimmed to size after all transactions are added and they can be evaluated in the context of their descendants. No changes to behavior. --- src/net_processing.cpp | 5 +++-- src/rpc/rawtransaction.cpp | 4 ++-- src/test/txvalidationcache_tests.cpp | 3 ++- src/validation.cpp | 27 +++++++++++++++------------ src/validation.h | 6 +++--- src/wallet/wallet.cpp | 3 ++- 6 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/net_processing.cpp b/src/net_processing.cpp index b8900d988..7fced41d4 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -1788,7 +1788,8 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr std::list lRemovedTxn; - if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, ptx, true, &fMissingInputs, &lRemovedTxn)) { + if (!AlreadyHave(inv) && + AcceptToMemoryPool(mempool, state, ptx, &fMissingInputs, &lRemovedTxn, false /* bypass_limits */, 0 /* nAbsurdFee */)) { mempool.check(pcoinsTip); RelayTransaction(tx, connman); for (unsigned int i = 0; i < tx.vout.size(); i++) { @@ -1826,7 +1827,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr if (setMisbehaving.count(fromPeer)) continue; - if (AcceptToMemoryPool(mempool, stateDummy, porphanTx, true, &fMissingInputs2, &lRemovedTxn)) { + if (AcceptToMemoryPool(mempool, stateDummy, porphanTx, &fMissingInputs2, &lRemovedTxn, false /* bypass_limits */, 0 /* nAbsurdFee */)) { LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString()); RelayTransaction(orphanTx, connman); for (unsigned int i = 0; i < orphanTx.vout.size(); i++) { diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index a0322f67b..8142fc571 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -937,8 +937,8 @@ UniValue sendrawtransaction(const JSONRPCRequest& request) // push to local node and sync with wallets CValidationState state; bool fMissingInputs; - bool fLimitFree = true; - if (!AcceptToMemoryPool(mempool, state, std::move(tx), fLimitFree, &fMissingInputs, nullptr, false, nMaxRawTxFee)) { + if (!AcceptToMemoryPool(mempool, state, std::move(tx), &fMissingInputs, + nullptr /* plTxnReplaced */, false /* bypass_limits */, nMaxRawTxFee)) { if (state.IsInvalid()) { throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason())); } else { diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp index 2d25cb96c..82ca93e7d 100644 --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -29,7 +29,8 @@ ToMemPool(CMutableTransaction& tx) LOCK(cs_main); CValidationState state; - return AcceptToMemoryPool(mempool, state, MakeTransactionRef(tx), false, nullptr, nullptr, true, 0); + return AcceptToMemoryPool(mempool, state, MakeTransactionRef(tx), nullptr /* pfMissingInputs */, + nullptr /* plTxnReplaced */, true /* bypass_limits */, 0 /* nAbsurdFee */); } BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup) diff --git a/src/validation.cpp b/src/validation.cpp index 0bd1ec672..6bfa80ea7 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -384,7 +384,9 @@ void UpdateMempoolForReorg(DisconnectedBlockTransactions &disconnectpool, bool f while (it != disconnectpool.queuedTx.get().rend()) { // ignore validation errors in resurrected transactions CValidationState stateDummy; - if (!fAddToMempool || (*it)->IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, *it, false, nullptr, nullptr, true)) { + if (!fAddToMempool || (*it)->IsCoinBase() || + !AcceptToMemoryPool(mempool, stateDummy, *it, nullptr /* pfMissingInputs */, + nullptr /* plTxnReplaced */, true /* bypass_limits */, 0 /* nAbsurdFee */)) { // If the transaction doesn't make it in to the mempool, remove any // transactions that depend on it (which would now be orphans). mempool.removeRecursive(**it, MemPoolRemovalReason::REORG); @@ -443,9 +445,9 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationSt return CheckInputs(tx, state, view, true, flags, cacheSigStore, true, txdata); } -static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx, bool fLimitFree, +static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx, bool* pfMissingInputs, int64_t nAcceptTime, std::list* plTxnReplaced, - bool fOverrideMempoolLimit, const CAmount& nAbsurdFee, std::vector& coins_to_uncache) + bool bypass_limits, const CAmount& nAbsurdFee, std::vector& coins_to_uncache) { const CTransaction& tx = *ptx; const uint256 hash = tx.GetHash(); @@ -623,7 +625,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool } // No transactions are allowed below minRelayTxFee except from disconnected blocks - if (fLimitFree && nModifiedFees < ::minRelayTxFee.GetFee(nSize)) { + if (!bypass_limits && nModifiedFees < ::minRelayTxFee.GetFee(nSize)) { return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "min relay fee not met"); } @@ -865,7 +867,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool pool.addUnchecked(hash, entry, setAncestors, validForFeeEstimation); // trim mempool and check if tx was trimmed - if (!fOverrideMempoolLimit) { + if (!bypass_limits) { LimitMempoolSize(pool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); if (!pool.exists(hash)) return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool full"); @@ -878,12 +880,12 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool } /** (try to) add transaction to memory pool with a specified acceptance time **/ -static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree, +static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool* pfMissingInputs, int64_t nAcceptTime, std::list* plTxnReplaced, - bool fOverrideMempoolLimit, const CAmount nAbsurdFee) + bool bypass_limits, const CAmount nAbsurdFee) { std::vector coins_to_uncache; - bool res = AcceptToMemoryPoolWorker(chainparams, pool, state, tx, fLimitFree, pfMissingInputs, nAcceptTime, plTxnReplaced, fOverrideMempoolLimit, nAbsurdFee, coins_to_uncache); + bool res = AcceptToMemoryPoolWorker(chainparams, pool, state, tx, pfMissingInputs, nAcceptTime, plTxnReplaced, bypass_limits, nAbsurdFee, coins_to_uncache); if (!res) { for (const COutPoint& hashTx : coins_to_uncache) pcoinsTip->Uncache(hashTx); @@ -894,12 +896,12 @@ static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPo return res; } -bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree, +bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool* pfMissingInputs, std::list* plTxnReplaced, - bool fOverrideMempoolLimit, const CAmount nAbsurdFee) + bool bypass_limits, const CAmount nAbsurdFee) { const CChainParams& chainparams = Params(); - return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, fLimitFree, pfMissingInputs, GetTime(), plTxnReplaced, fOverrideMempoolLimit, nAbsurdFee); + return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, pfMissingInputs, GetTime(), plTxnReplaced, bypass_limits, nAbsurdFee); } /** Return transaction in txOut, and if it was found inside a block, its hash is placed in hashBlock */ @@ -4306,7 +4308,8 @@ bool LoadMempool(void) CValidationState state; if (nTime + nExpiryTimeout > nNow) { LOCK(cs_main); - AcceptToMemoryPoolWithTime(chainparams, mempool, state, tx, true, nullptr, nTime, nullptr, false, 0); + AcceptToMemoryPoolWithTime(chainparams, mempool, state, tx, nullptr /* pfMissingInputs */, nTime, + nullptr /* plTxnReplaced */, false /* bypass_limits */, 0 /* nAbsurdFee */); if (state.IsValid()) { ++count; } else { diff --git a/src/validation.h b/src/validation.h index aa4d7abb4..d52154f8d 100644 --- a/src/validation.h +++ b/src/validation.h @@ -301,9 +301,9 @@ void PruneBlockFilesManual(int nManualPruneHeight); /** (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, std::list* plTxnReplaced = nullptr, - bool fOverrideMempoolLimit=false, const CAmount nAbsurdFee=0); +bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, + bool* pfMissingInputs, std::list* plTxnReplaced, + bool bypass_limits, const CAmount nAbsurdFee); /** Convert CValidationState to a human-readable message for logging */ std::string FormatStateMessage(const CValidationState &state); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index d376de233..575eca0d7 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -4014,5 +4014,6 @@ int CMerkleTx::GetBlocksToMaturity() const bool CMerkleTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state) { - return ::AcceptToMemoryPool(mempool, state, tx, true, nullptr, nullptr, false, nAbsurdFee); + return ::AcceptToMemoryPool(mempool, state, tx, nullptr /* pfMissingInputs */, + nullptr /* plTxnReplaced */, false /* bypass_limits */, nAbsurdFee); } From 04f78ab5b94424be9cdef86971b7a92de79effa4 Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Tue, 12 Sep 2017 12:35:35 -0400 Subject: [PATCH 228/382] Do not reject based on mempool min fee when bypass_limits is set. This should have always been the case, but we will correctly trim to size after a reorg which is when bypass_limits is set. --- src/validation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/validation.cpp b/src/validation.cpp index 6bfa80ea7..347b84734 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -620,7 +620,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool strprintf("%d", nSigOpsCost)); CAmount mempoolRejectFee = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize); - if (mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) { + if (!bypass_limits && mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) { return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nFees, mempoolRejectFee)); } From bf64c3cb34571cfcd2ec4cce29bbf9c49ec3700c Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Tue, 12 Sep 2017 12:40:06 -0400 Subject: [PATCH 229/382] Ignore transactions added to mempool during a reorg for fee estimation purposes. --- src/validation.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/validation.cpp b/src/validation.cpp index 347b84734..ab031d76c 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -857,11 +857,12 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool } pool.RemoveStaged(allConflicting, false, MemPoolRemovalReason::REPLACED); - // 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); + // This transaction should only count for fee estimation if: + // - it isn't a BIP 125 replacement transaction (may not be widely supported) + // - it's not being readded during a reorg which bypasses typical mempool fee limits + // - the node is not behind + // - the transaction is not dependent on any other transactions in the mempool + bool validForFeeEstimation = !fReplacementTransaction && !bypass_limits && IsCurrentForFeeEstimation() && pool.HasNoInputsOf(tx); // Store transaction in memory pool.addUnchecked(hash, entry, setAncestors, validForFeeEstimation); From fa4fad9d86ab76061efaba3477585ff606e9720c Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Tue, 12 Sep 2017 23:03:12 +0200 Subject: [PATCH 230/382] travis: Revert default datadir check --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7cc320ddd..ef7c3f8d0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -47,7 +47,6 @@ before_script: - if [ "$CHECK_DOC" = 1 -a "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then contrib/devtools/commit-script-check.sh $TRAVIS_COMMIT_RANGE; fi - if [ "$CHECK_DOC" = 1 ]; then contrib/devtools/check-doc.py; fi - unset CC; unset CXX - - rm -rf ~/.bitcoin - mkdir -p depends/SDKs depends/sdk-sources - if [ -n "$OSX_SDK" -a ! -f depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then curl --location --fail $SDK_URL/MacOSX${OSX_SDK}.sdk.tar.gz -o depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz; fi - if [ -n "$OSX_SDK" -a -f depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then tar -C depends/SDKs -xf depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz; fi @@ -72,7 +71,6 @@ script: - if [ "$RUN_TESTS" = "true" ]; then travis_wait 30 make $MAKEJOBS check VERBOSE=1; fi - if [ "$TRAVIS_EVENT_TYPE" = "cron" ]; then extended="--extended --exclude pruning,dbcrash"; fi - if [ "$RUN_TESTS" = "true" ]; then test/functional/test_runner.py --coverage --quiet ${extended}; fi - - if [ -d ~/.bitcoin ]; then false; fi # Make sure default datadir does not exist after tests after_script: - echo $TRAVIS_COMMIT_RANGE - echo $TRAVIS_COMMIT_LOG From fadd0c16b6bd62e9d663d906755320ae089e02d3 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Tue, 12 Sep 2017 17:20:26 +0200 Subject: [PATCH 231/382] [qa] zapwallettxes: Wait up to 3s for mempool reload --- test/functional/zapwallettxes.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/test/functional/zapwallettxes.py b/test/functional/zapwallettxes.py index c001517a6..83b11035c 100755 --- a/test/functional/zapwallettxes.py +++ b/test/functional/zapwallettxes.py @@ -15,9 +15,11 @@ been zapped. """ from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import (assert_equal, - assert_raises_jsonrpc, - ) +from test_framework.util import ( + assert_equal, + assert_raises_jsonrpc, + wait_until, +) class ZapWalletTXesTest (BitcoinTestFramework): def set_test_params(self): @@ -56,6 +58,8 @@ def run_test(self): self.stop_node(0) self.start_node(0, ["-persistmempool=1", "-zapwallettxes=2"]) + wait_until(lambda: self.nodes[0].getmempoolinfo()['size'] == 1, timeout=3) + assert_equal(self.nodes[0].gettransaction(txid1)['txid'], txid1) assert_equal(self.nodes[0].gettransaction(txid2)['txid'], txid2) From fb1f3258225a7052f4fc9e3f2accc3811c53e221 Mon Sep 17 00:00:00 2001 From: Cristian Mircea Messel Date: Wed, 13 Sep 2017 00:41:14 +0300 Subject: [PATCH 232/382] Add listwallets RPC test to multiwallet.py --- test/functional/multiwallet.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/functional/multiwallet.py b/test/functional/multiwallet.py index e5453e9aa..b4e15a332 100755 --- a/test/functional/multiwallet.py +++ b/test/functional/multiwallet.py @@ -18,6 +18,8 @@ def set_test_params(self): self.extra_args = [['-wallet=w1', '-wallet=w2', '-wallet=w3']] def run_test(self): + assert_equal(set(self.nodes[0].listwallets()), {"w1", "w2", "w3"}) + self.stop_node(0) # should not initialize if there are duplicate wallets From d552ed678c2632bafeab695c639f5fe185dc9853 Mon Sep 17 00:00:00 2001 From: Paul Berg Date: Wed, 13 Sep 2017 07:24:42 +0000 Subject: [PATCH 233/382] Put back inadvertently removed copyright notices In an abundance of caution this restores "Bitcoin Developers" to the COPYING file in case there were contributors before that point in time that would object to the current label. It's harmless and more pedantically correct. [Change extracted from the Bitcoin-abc repository, commit message by gmaxwell] Signed-off-by: Gregory Maxwell --- COPYING | 1 + 1 file changed, 1 insertion(+) diff --git a/COPYING b/COPYING index c6203c0f7..45d51c3c7 100644 --- a/COPYING +++ b/COPYING @@ -1,6 +1,7 @@ The MIT License (MIT) Copyright (c) 2009-2017 The Bitcoin Core developers +Copyright (c) 2009-2017 Bitcoin Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From a7820422e0b182db6f1df8904242e5d76d6c73fa Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Wed, 13 Sep 2017 09:17:15 -0400 Subject: [PATCH 234/382] qa: Treat mininode p2p exceptions as fatal --- test/functional/test_framework/mininode.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py index 2607b9b07..c6ee9b219 100755 --- a/test/functional/test_framework/mininode.py +++ b/test/functional/test_framework/mininode.py @@ -1502,6 +1502,7 @@ def deliver(self, conn, message): except: print("ERROR delivering %s (%s)" % (repr(message), sys.exc_info()[0])) + raise def get_deliver_sleep_time(self): with mininode_lock: @@ -1701,13 +1702,10 @@ def handle_close(self): self.cb.on_close(self) def handle_read(self): - try: - t = self.recv(8192) - if len(t) > 0: - self.recvbuf += t - self.got_data() - except: - pass + t = self.recv(8192) + if len(t) > 0: + self.recvbuf += t + self.got_data() def readable(self): return True @@ -1773,8 +1771,10 @@ def got_data(self): self.got_message(t) else: logger.warning("Received unknown command from %s:%d: '%s' %s" % (self.dstaddr, self.dstport, command, repr(msg))) + raise ValueError("Unknown command: '%s'" % (command)) except Exception as e: logger.exception('got_data:', repr(e)) + raise def send_message(self, message, pushbuf=False): if self.state != "connected" and not pushbuf: From f97ab35fa9687fd5c110ad6cca5be5b4a5c2142d Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Wed, 13 Sep 2017 09:21:27 -0400 Subject: [PATCH 235/382] qa: Fix bug introduced in p2p-segwit.py Changing __init__() -> set_test_params() in the tests should not have applied to NodeConnCB-derived objects. --- test/functional/p2p-segwit.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/functional/p2p-segwit.py b/test/functional/p2p-segwit.py index 943bc2c6d..a9ef47559 100755 --- a/test/functional/p2p-segwit.py +++ b/test/functional/p2p-segwit.py @@ -32,8 +32,8 @@ def get_virtual_size(witness_block): return vsize class TestNode(NodeConnCB): - def set_test_params(self): - self.num_nodes = 3 + def __init__(self): + super().__init__() self.getdataset = set() def on_getdata(self, conn, message): From 1817398b397afebcc857c40a16d201c84878cb89 Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Wed, 13 Sep 2017 13:24:38 -0400 Subject: [PATCH 236/382] mininode: add an optimistic write and disable nagle Because the poll/select loop may pause for 100msec before actually doing a send, and we have no way to force the loop awake, try sending from the calling thread if the queue is empty. Also, disable nagle as all sends should be either full messages or unfinished sends. This shaves an average of ~1 minute or so off of my accumulated runtime, and 10-15 seconds off of actual runtime. --- test/functional/test_framework/mininode.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py index 2607b9b07..03b7c6e50 100755 --- a/test/functional/test_framework/mininode.py +++ b/test/functional/test_framework/mininode.py @@ -1654,6 +1654,7 @@ def __init__(self, dstaddr, dstport, rpc, callback, net="regtest", services=NODE self.dstaddr = dstaddr self.dstport = dstport self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) self.sendbuf = b"" self.recvbuf = b"" self.ver_send = 209 @@ -1792,7 +1793,14 @@ def send_message(self, message, pushbuf=False): tmsg += h[:4] tmsg += data with mininode_lock: - self.sendbuf += tmsg + if (len(self.sendbuf) == 0 and not pushbuf): + try: + sent = self.send(tmsg) + self.sendbuf = tmsg[sent:] + except BlockingIOError: + self.sendbuf = tmsg + else: + self.sendbuf += tmsg self.last_sent = time.time() def got_message(self, message): From dd365612fdd14e4dadc731e1b2f5d8564b9f23f1 Mon Sep 17 00:00:00 2001 From: Evan Klitzke Date: Mon, 7 Aug 2017 14:24:17 -0700 Subject: [PATCH 237/382] Add a lint check for trailing whitespace. This adds a new CHECK_DOC check that looks for newly introduced trailing whitespace. Existing trailing whitespace (of which there is plenty!) will not trigger an error. This is written in a generic way so that new lint-*.sh scripts can be added to contrib/devtools/, as I'd like to contribute additional lint checks in the future. --- .travis.yml | 1 + contrib/devtools/lint-all.sh | 22 ++++++++++++++ contrib/devtools/lint-whitespace.sh | 47 +++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100755 contrib/devtools/lint-all.sh create mode 100755 contrib/devtools/lint-whitespace.sh diff --git a/.travis.yml b/.travis.yml index 4fa659472..0de7ca6f7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -49,6 +49,7 @@ before_script: - if [ "$CHECK_DOC" = 1 -a "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then contrib/devtools/commit-script-check.sh $TRAVIS_COMMIT_RANGE; fi - if [ "$CHECK_DOC" = 1 ]; then contrib/devtools/check-doc.py; fi - if [ "$CHECK_DOC" = 1 ]; then contrib/devtools/check-rpc-mappings.py .; fi + - if [ "$CHECK_DOC" = 1 ]; then contrib/devtools/lint-all.sh; fi - unset CC; unset CXX - mkdir -p depends/SDKs depends/sdk-sources - if [ -n "$OSX_SDK" -a ! -f depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then curl --location --fail $SDK_URL/MacOSX${OSX_SDK}.sdk.tar.gz -o depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz; fi diff --git a/contrib/devtools/lint-all.sh b/contrib/devtools/lint-all.sh new file mode 100755 index 000000000..b6d86959c --- /dev/null +++ b/contrib/devtools/lint-all.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# +# Copyright (c) 2017 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# +# This script runs all contrib/devtools/lint-*.sh files, and fails if any exit +# with a non-zero status code. + +set -u + +SCRIPTDIR=$(dirname "${BASH_SOURCE[0]}") +LINTALL=$(basename "${BASH_SOURCE[0]}") + +for f in "${SCRIPTDIR}"/lint-*.sh; do + if [ "$(basename "$f")" != "$LINTALL" ]; then + if ! "$f"; then + echo "^---- failure generated from $f" + exit 1 + fi + fi +done diff --git a/contrib/devtools/lint-whitespace.sh b/contrib/devtools/lint-whitespace.sh new file mode 100755 index 000000000..59e5c9299 --- /dev/null +++ b/contrib/devtools/lint-whitespace.sh @@ -0,0 +1,47 @@ +#!/bin/bash +# +# Copyright (c) 2017 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# +# Check for new lines in diff that introduce trailing whitespace. + +# We can't run this check unless we know the commit range for the PR. +if [ -z "${TRAVIS_COMMIT_RANGE}" ]; then + exit 0 +fi + +showdiff() { + if ! git diff -U0 "${TRAVIS_COMMIT_RANGE}" --; then + echo "Failed to get a diff" + exit 1 + fi +} + +# Do a first pass, and if no trailing whitespace was found then exit early. +if ! showdiff | grep -E -q '^\+.*\s+$'; then + exit +fi + +echo "This diff appears to have added new lines with trailing whitespace." +echo "The following changes were suspected:" + +FILENAME="" +SEEN=0 + +while read -r line; do + if [[ "$line" =~ ^diff ]]; then + FILENAME="$line" + SEEN=0 + else + if [ "$SEEN" -eq 0 ]; then + # The first time a file is seen with trailing whitespace, we print the + # filename (preceded by a newline). + echo + echo "$FILENAME" + SEEN=1 + fi + echo "$line" + fi +done < <(showdiff | grep -E '^(diff --git |\+.*\s+$)') +exit 1 From 1f379b1f062bd3ee67a75fe2eb14ad8783f283a5 Mon Sep 17 00:00:00 2001 From: MeshCollider Date: Mon, 11 Sep 2017 19:38:06 +1200 Subject: [PATCH 238/382] Add tab char lint check and exclude imported dependencies --- contrib/devtools/lint-whitespace.sh | 91 +++++++++++++++++++++-------- 1 file changed, 66 insertions(+), 25 deletions(-) diff --git a/contrib/devtools/lint-whitespace.sh b/contrib/devtools/lint-whitespace.sh index 59e5c9299..6150dd3f6 100755 --- a/contrib/devtools/lint-whitespace.sh +++ b/contrib/devtools/lint-whitespace.sh @@ -8,40 +8,81 @@ # We can't run this check unless we know the commit range for the PR. if [ -z "${TRAVIS_COMMIT_RANGE}" ]; then - exit 0 + echo "Cannot run lint-whitespace.sh without commit range. To run locally, use:" + echo "TRAVIS_COMMIT_RANGE='' .lint-whitespace.sh" + echo "For example:" + echo "TRAVIS_COMMIT_RANGE='47ba2c3...ee50c9e' .lint-whitespace.sh" + exit 1 fi showdiff() { - if ! git diff -U0 "${TRAVIS_COMMIT_RANGE}" --; then + if ! git diff -U0 "${TRAVIS_COMMIT_RANGE}" -- "." ":(exclude)src/leveldb/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/"; then echo "Failed to get a diff" exit 1 fi } -# Do a first pass, and if no trailing whitespace was found then exit early. -if ! showdiff | grep -E -q '^\+.*\s+$'; then - exit -fi +showcodediff() { + if ! git diff -U0 "${TRAVIS_COMMIT_RANGE}" -- *.cpp *.h *.md *.py *.sh ":(exclude)src/leveldb/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/"; then + echo "Failed to get a diff" + exit 1 + fi +} -echo "This diff appears to have added new lines with trailing whitespace." -echo "The following changes were suspected:" +RET=0 -FILENAME="" -SEEN=0 +# Check if trailing whitespace was found in the diff. +if showdiff | grep -E -q '^\+.*\s+$'; then + echo "This diff appears to have added new lines with trailing whitespace." + echo "The following changes were suspected:" + FILENAME="" + SEEN=0 + while read -r line; do + if [[ "$line" =~ ^diff ]]; then + FILENAME="$line" + SEEN=0 + elif [[ "$line" =~ ^@@ ]]; then + LINENUMBER="$line" + else + if [ "$SEEN" -eq 0 ]; then + # The first time a file is seen with trailing whitespace, we print the + # filename (preceded by a newline). + echo + echo "$FILENAME" + echo "$LINENUMBER" + SEEN=1 + fi + echo "$line" + fi + done < <(showdiff | grep -E '^(diff --git |@@|\+.*\s+$)') + RET=1 +fi -while read -r line; do - if [[ "$line" =~ ^diff ]]; then - FILENAME="$line" - SEEN=0 - else - if [ "$SEEN" -eq 0 ]; then - # The first time a file is seen with trailing whitespace, we print the - # filename (preceded by a newline). - echo - echo "$FILENAME" - SEEN=1 +# Check if tab characters were found in the diff. +if showcodediff | grep -P -q '^\+.*\t'; then + echo "This diff appears to have added new lines with tab characters instead of spaces." + echo "The following changes were suspected:" + FILENAME="" + SEEN=0 + while read -r line; do + if [[ "$line" =~ ^diff ]]; then + FILENAME="$line" + SEEN=0 + elif [[ "$line" =~ ^@@ ]]; then + LINENUMBER="$line" + else + if [ "$SEEN" -eq 0 ]; then + # The first time a file is seen with a tab character, we print the + # filename (preceded by a newline). + echo + echo "$FILENAME" + echo "$LINENUMBER" + SEEN=1 + fi + echo "$line" fi - echo "$line" - fi -done < <(showdiff | grep -E '^(diff --git |\+.*\s+$)') -exit 1 + done < <(showcodediff | grep -P '^(diff --git |@@|\+.*\t)') + RET=1 +fi + +exit $RET From 77939f27f7dc42640ebfb9fe52490a2ddacc3ad4 Mon Sep 17 00:00:00 2001 From: MeshCollider Date: Thu, 14 Sep 2017 15:18:55 +1200 Subject: [PATCH 239/382] Fix uninitialized g_connman crash in Shutdown() --- src/init.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/init.cpp b/src/init.cpp index a997f9740..6b9699213 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -194,7 +194,7 @@ void Shutdown() // Because these depend on each-other, we make sure that neither can be // using the other before destroying them. UnregisterValidationInterface(peerLogic.get()); - g_connman->Stop(); + if(g_connman) g_connman->Stop(); peerLogic.reset(); g_connman.reset(); From 86700d3d056caf54b091a6673dd6dabb65fac1f2 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 14 Sep 2017 14:51:28 +0200 Subject: [PATCH 240/382] doc: add release-notes for 0.15.0 to master Tree-SHA512: 0f72b10ac60c3da0847fb9c3b20015566c3bd05f121fa588527828de66cb87e2fe18d463f3cc92b582ab12e4c9362fdd71f3f9a7b014fedb463925d3fa51a7f7 --- doc/release-notes/release-notes-0.15.0.md | 878 ++++++++++++++++++++++ 1 file changed, 878 insertions(+) create mode 100644 doc/release-notes/release-notes-0.15.0.md diff --git a/doc/release-notes/release-notes-0.15.0.md b/doc/release-notes/release-notes-0.15.0.md new file mode 100644 index 000000000..29816cacf --- /dev/null +++ b/doc/release-notes/release-notes-0.15.0.md @@ -0,0 +1,878 @@ +Bitcoin Core version *0.15.0* is now available from: + + + +This is a new major version release, including new features, various bugfixes +and performance improvements, as well as updated translations. + +Please report bugs using the issue tracker at GitHub: + + + +To receive security and update notifications, please subscribe to: + + + +How to Upgrade +============== + +If you are running an older version, shut it down. Wait until it has completely +shut down (which might take a few minutes for older versions), then run the +installer (on Windows) or just copy over `/Applications/Bitcoin-Qt` (on Mac) +or `bitcoind`/`bitcoin-qt` (on Linux). + +The first time you run version 0.15.0, your chainstate database will be converted to a +new format, which will take anywhere from a few minutes to half an hour, +depending on the speed of your machine. + +The file format of `fee_estimates.dat` changed in version 0.15.0. Hence, a +downgrade from version 0.15.0 or upgrade to version 0.15.0 will cause all fee +estimates to be discarded. + +Note that the block database format also changed in version 0.8.0 and there is no +automatic upgrade code from before version 0.8 to version 0.15.0. Upgrading +directly from 0.7.x and earlier without redownloading the blockchain is not supported. +However, as usual, old wallet versions are still supported. + +Downgrading warning +------------------- + +The chainstate database for this release is not compatible with previous +releases, so if you run 0.15 and then decide to switch back to any +older version, you will need to run the old release with the `-reindex-chainstate` +option to rebuild the chainstate data structures in the old format. + +If your node has pruning enabled, this will entail re-downloading and +processing the entire blockchain. + +Compatibility +============== + +Bitcoin Core is extensively tested on multiple operating systems using +the Linux kernel, macOS 10.8+, and Windows Vista and later. Windows XP is not supported. + +Bitcoin Core should also work on most other Unix-like systems but is not +frequently tested on them. + +Notes for 0.15.0 +================ + +Current SegWit support +---------------------- + +Version 0.15.0 supports adding a segregated witness address via the `addwitnessaddress` RPC, but +please note that this is a testing/expert RPC, which does not guarantee recovery from backup. Only use +this RPC if you know what you are doing. More complete wallet support for segregated witness is coming +in a next version. + +Rescanning with encrypted wallets +--------------------------------- + +As in previous versions, when using an encrypted HD wallet, the keypool cannot be topped up without unlocking +the wallet. This means that currently, in order to recover from a backup of an encrypted HD wallet, the user +must unlock the wallet with a really long timeout and manually trigger a rescan, otherwise they risk missing +some keys when auto-topup cannot run. Unfortunately there is no `rescan` RPC in this version, that will be +included in a future version, so for now a rescan can be triggered using one of the `import*` commands, using +a dummy address generated by another (trusted) wallet. + +Notable changes +=============== + +Performance Improvements +------------------------ + +Version 0.15 contains a number of significant performance improvements, which make +Initial Block Download, startup, transaction and block validation much faster: + +- The chainstate database (which is used for tracking UTXOs) has been changed + from a per-transaction model to a per-output model (See [PR 10195](https://github.com/bitcoin/bitcoin/pull/10195)). Advantages of this model + are that it: + - avoids the CPU overhead of deserializing and serializing the unused outputs; + - has more predictable memory usage; + - uses simpler code; + - is adaptable to various future cache flushing strategies. + + As a result, validating the blockchain during Initial Block Download (IBD) and reindex + is ~30-40% faster, uses 10-20% less memory, and flushes to disk far less frequently. + The only downside is that the on-disk database is 15% larger. During the conversion from the previous format + a few extra gigabytes may be used. +- Earlier versions experienced a spike in memory usage while flushing UTXO updates to disk. + As a result, only half of the available memory was actually used as cache, and the other half was + reserved to accommodate flushing. This is no longer the case (See [PR 10148](https://github.com/bitcoin/bitcoin/pull/10148)), and the entirety of + the available cache (see `-dbcache`) is now actually used as cache. This reduces the flushing + frequency by a factor 2 or more. +- In previous versions, signature validation for transactions has been cached when the + transaction is accepted to the mempool. Version 0.15 extends this to cache the entire script + validity (See [PR 10192](https://github.com/bitcoin/bitcoin/pull/10192)). This means that if a transaction in a block has already been accepted to the + mempool, the scriptSig does not need to be re-evaluated. Empirical tests show that + this results in new block validation being 40-50% faster. +- LevelDB has been upgraded to version 1.20 (See [PR 10544](https://github.com/bitcoin/bitcoin/pull/10544)). This version contains hardware acceleration for CRC + on architectures supporting SSE 4.2. As a result, synchronization and block validation are now faster. +- SHA256 hashing has been optimized for architectures supporting SSE 4 (See [PR 10821](https://github.com/bitcoin/bitcoin/pull/10821)). SHA256 is around + 50% faster on supported hardware, which results in around 5% faster IBD and block + validation. In version 0.15, SHA256 hardware optimization is disabled in release builds by + default, but can be enabled by using `--enable-experimental-asm` when building. +- Refill of the keypool no longer flushes the wallet between each key which resulted in a ~20x speedup in creating a new wallet. Part of this speedup was used to increase the default keypool to 1000 keys to make recovery more robust. (See [PR 10831](https://github.com/bitcoin/bitcoin/pull/10831)). + +Fee Estimation Improvements +--------------------------- + +Fee estimation has been significantly improved in version 0.15, with more accurate fee estimates used by the wallet and a wider range of options for advanced users of the `estimatesmartfee` and `estimaterawfee` RPCs (See [PR 10199](https://github.com/bitcoin/bitcoin/pull/10199)). + +### Changes to internal logic and wallet behavior + +- Internally, estimates are now tracked on 3 different time horizons. This allows for longer targets and means estimates adjust more quickly to changes in conditions. +- Estimates can now be *conservative* or *economical*. *Conservative* estimates use longer time horizons to produce an estimate which is less susceptible to rapid changes in fee conditions. *Economical* estimates use shorter time horizons and will be more affected by short-term changes in fee conditions. Economical estimates may be considerably lower during periods of low transaction activity (for example over weekends), but may result in transactions being unconfirmed if prevailing fees increase rapidly. +- By default, the wallet will use conservative fee estimates to increase the reliability of transactions being confirmed within the desired target. For transactions that are marked as replaceable, the wallet will use an economical estimate by default, since the fee can be 'bumped' if the fee conditions change rapidly (See [PR 10589](https://github.com/bitcoin/bitcoin/pull/10589)). +- Estimates can now be made for confirmation targets up to 1008 blocks (one week). +- More data on historical fee rates is stored, leading to more precise fee estimates. +- Transactions which leave the mempool due to eviction or other non-confirmed reasons are now taken into account by the fee estimation logic, leading to more accurate fee estimates. +- The fee estimation logic will make sure enough data has been gathered to return a meaningful estimate. If there is insufficient data, a fallback default fee is used. + +### Changes to fee estimate RPCs + +- The `estimatefee` RPC is now deprecated in favor of using only `estimatesmartfee` (which is the implementation used by the GUI) +- The `estimatesmartfee` RPC interface has been changed (See [PR 10707](https://github.com/bitcoin/bitcoin/pull/10707)): + - The `nblocks` argument has been renamed to `conf_target` (to be consistent with other RPC methods). + - An `estimate_mode` argument has been added. This argument takes one of the following strings: `CONSERVATIVE`, `ECONOMICAL` or `UNSET` (which defaults to `CONSERVATIVE`). + - The RPC return object now contains an `errors` member, which returns errors encountered during processing. + - If Bitcoin Core has not been running for long enough and has not seen enough blocks or transactions to produce an accurate fee estimation, an error will be returned (previously a value of -1 was used to indicate an error, which could be confused for a feerate). +- A new `estimaterawfee` RPC is added to provide raw fee data. External clients can query and use this data in their own fee estimation logic. + +Multi-wallet support +-------------------- + +Bitcoin Core now supports loading multiple, separate wallets (See [PR 8694](https://github.com/bitcoin/bitcoin/pull/8694), [PR 10849](https://github.com/bitcoin/bitcoin/pull/10849)). The wallets are completely separated, with individual balances, keys and received transactions. + +Multi-wallet is enabled by using more than one `-wallet` argument when starting Bitcoin, either on the command line or in the Bitcoin config file. + +**In Bitcoin-Qt, only the first wallet will be displayed and accessible for creating and signing transactions.** GUI selectable multiple wallets will be supported in a future version. However, even in 0.15 other loaded wallets will remain synchronized to the node's current tip in the background. This can be useful if running a pruned node, since loading a wallet where the most recent sync is beyond the pruned height results in having to download and revalidate the whole blockchain. Continuing to synchronize all wallets in the background avoids this problem. + +Bitcoin Core 0.15.0 contains the following changes to the RPC interface and `bitcoin-cli` for multi-wallet: + +* When running Bitcoin Core with a single wallet, there are **no** changes to the RPC interface or `bitcoin-cli`. All RPC calls and `bitcoin-cli` commands continue to work as before. +* When running Bitcoin Core with multi-wallet, all *node-level* RPC methods continue to work as before. HTTP RPC requests should be send to the normal `:/` endpoint, and `bitcoin-cli` commands should be run as before. A *node-level* RPC method is any method which does not require access to the wallet. +* When running Bitcoin Core with multi-wallet, *wallet-level* RPC methods must specify the wallet for which they're intended in every request. HTTP RPC requests should be send to the `:/wallet//` endpoint, for example `127.0.0.1:8332/wallet/wallet1.dat/`. `bitcoin-cli` commands should be run with a `-rpcwallet` option, for example `bitcoin-cli -rpcwallet=wallet1.dat getbalance`. +* A new *node-level* `listwallets` RPC method is added to display which wallets are currently loaded. The names returned by this method are the same as those used in the HTTP endpoint and for the `rpcwallet` argument. + +Note that while multi-wallet is now fully supported, the RPC multi-wallet interface should be considered unstable for version 0.15.0, and there may backwards-incompatible changes in future versions. + +Replace-by-fee control in the GUI +--------------------------------- + +Bitcoin Core has supported creating opt-in replace-by-fee (RBF) transactions +since version 0.12.0, and since version 0.14.0 has included a `bumpfee` RPC method to +replace unconfirmed opt-in RBF transactions with a new transaction that pays +a higher fee. + +In version 0.15, creating an opt-in RBF transaction and replacing the unconfirmed +transaction with a higher-fee transaction are both supported in the GUI (See [PR 9592](https://github.com/bitcoin/bitcoin/pull/9592)). + +Removal of Coin Age Priority +---------------------------- + +In previous versions of Bitcoin Core, a portion of each block could be reserved for transactions based on the age and value of UTXOs they spent. This concept (Coin Age Priority) is a policy choice by miners, and there are no consensus rules around the inclusion of Coin Age Priority transactions in blocks. In practice, only a few miners continue to use Coin Age Priority for transaction selection in blocks. Bitcoin Core 0.15 removes all remaining support for Coin Age Priority (See [PR 9602](https://github.com/bitcoin/bitcoin/pull/9602)). This has the following implications: + +- The concept of *free transactions* has been removed. High Coin Age Priority transactions would previously be allowed to be relayed even if they didn't attach a miner fee. This is no longer possible since there is no concept of Coin Age Priority. The `-limitfreerelay` and `-relaypriority` options which controlled relay of free transactions have therefore been removed. +- The `-sendfreetransactions` option has been removed, since almost all miners do not include transactions which do not attach a transaction fee. +- The `-blockprioritysize` option has been removed. +- The `estimatepriority` and `estimatesmartpriority` RPCs have been removed. +- The `getmempoolancestors`, `getmempooldescendants`, `getmempoolentry` and `getrawmempool` RPCs no longer return `startingpriority` and `currentpriority`. +- The `prioritisetransaction` RPC no longer takes a `priority_delta` argument, which is replaced by a `dummy` argument for backwards compatibility with clients using positional arguments. The RPC is still used to change the apparent fee-rate of the transaction by using the `fee_delta` argument. +- `-minrelaytxfee` can now be set to 0. If `minrelaytxfee` is set, then fees smaller than `minrelaytxfee` (per kB) are rejected from relaying, mining and transaction creation. This defaults to 1000 satoshi/kB. +- The `-printpriority` option has been updated to only output the fee rate and hash of transactions included in a block by the mining code. + +Mempool Persistence Across Restarts +----------------------------------- + +Version 0.14 introduced mempool persistence across restarts (the mempool is saved to a `mempool.dat` file in the data directory prior to shutdown and restores the mempool when the node is restarted). Version 0.15 allows this feature to be switched on or off using the `-persistmempool` command-line option (See [PR 9966](https://github.com/bitcoin/bitcoin/pull/9966)). By default, the option is set to true, and the mempool is saved on shutdown and reloaded on startup. If set to false, the `mempool.dat` file will not be loaded on startup or saved on shutdown. + +New RPC methods +--------------- + +Version 0.15 introduces several new RPC methods: + +- `abortrescan` stops current wallet rescan, e.g. when triggered by an `importprivkey` call (See [PR 10208](https://github.com/bitcoin/bitcoin/pull/10208)). +- `combinerawtransaction` accepts a JSON array of raw transactions and combines them into a single raw transaction (See [PR 10571](https://github.com/bitcoin/bitcoin/pull/10571)). +- `estimaterawfee` returns raw fee data so that customized logic can be implemented to analyze the data and calculate estimates. See [Fee Estimation Improvements](#fee-estimation-improvements) for full details on changes to the fee estimation logic and interface. +- `getchaintxstats` returns statistics about the total number and rate of transactions + in the chain (See [PR 9733](https://github.com/bitcoin/bitcoin/pull/9733)). +- `listwallets` lists wallets which are currently loaded. See the *Multi-wallet* section + of these release notes for full details (See [Multi-wallet support](#multi-wallet-support)). +- `uptime` returns the total runtime of the `bitcoind` server since its last start (See [PR 10400](https://github.com/bitcoin/bitcoin/pull/10400)). + +Low-level RPC changes +--------------------- + +- When using Bitcoin Core in multi-wallet mode, RPC requests for wallet methods must specify + the wallet that they're intended for. See [Multi-wallet support](#multi-wallet-support) for full details. + +- The new database model no longer stores information about transaction + versions of unspent outputs (See [Performance improvements](#performance-improvements)). This means that: + - The `gettxout` RPC no longer has a `version` field in the response. + - The `gettxoutsetinfo` RPC reports `hash_serialized_2` instead of `hash_serialized`, + which does not commit to the transaction versions of unspent outputs, but does + commit to the height and coinbase information. + - The `getutxos` REST path no longer reports the `txvers` field in JSON format, + and always reports 0 for transaction versions in the binary format + +- The `estimatefee` RPC is deprecated. Clients should switch to using the `estimatesmartfee` RPC, which returns better fee estimates. See [Fee Estimation Improvements](#fee-estimation-improvements) for full details on changes to the fee estimation logic and interface. + +- The `gettxoutsetinfo` response now contains `disk_size` and `bogosize` instead of + `bytes_serialized`. The first is a more accurate estimate of actual disk usage, but + is not deterministic. The second is unrelated to disk usage, but is a + database-independent metric of UTXO set size: it counts every UTXO entry as 50 + the + length of its scriptPubKey (See [PR 10426](https://github.com/bitcoin/bitcoin/pull/10426)). + +- `signrawtransaction` can no longer be used to combine multiple transactions into a single transaction. Instead, use the new `combinerawtransaction` RPC (See [PR 10571](https://github.com/bitcoin/bitcoin/pull/10571)). + +- `fundrawtransaction` no longer accepts a `reserveChangeKey` option. This option used to allow RPC users to fund a raw transaction using an key from the keypool for the change address without removing it from the available keys in the keypool. The key could then be re-used for a `getnewaddress` call, which could potentially result in confusing or dangerous behaviour (See [PR 10784](https://github.com/bitcoin/bitcoin/pull/10784)). + +- `estimatepriority` and `estimatesmartpriority` have been removed. See [Removal of Coin Age Priority](#removal-of-coin-age-priority). + +- The `listunspent` RPC now takes a `query_options` argument (see [PR 8952](https://github.com/bitcoin/bitcoin/pull/8952)), which is a JSON object + containing one or more of the following members: + - `minimumAmount` - a number specifying the minimum value of each UTXO + - `maximumAmount` - a number specifying the maximum value of each UTXO + - `maximumCount` - a number specifying the minimum number of UTXOs + - `minimumSumAmount` - a number specifying the minimum sum value of all UTXOs + +- The `getmempoolancestors`, `getmempooldescendants`, `getmempoolentry` and `getrawmempool` RPCs no longer return `startingpriority` and `currentpriority`. See [Removal of Coin Age Priority](#removal-of-coin-age-priority). + +- The `dumpwallet` RPC now returns the full absolute path to the dumped wallet. It + used to return no value, even if successful (See [PR 9740](https://github.com/bitcoin/bitcoin/pull/9740)). + +- In the `getpeerinfo` RPC, the return object for each peer now returns an `addrbind` member, which contains the ip address and port of the connection to the peer. This is in addition to the `addrlocal` member which contains the ip address and port of the local node as reported by the peer (See [PR 10478](https://github.com/bitcoin/bitcoin/pull/10478)). + +- The `disconnectnode` RPC can now disconnect a node specified by node ID (as well as by IP address/port). To disconnect a node based on node ID, call the RPC with the new `nodeid` argument (See [PR 10143](https://github.com/bitcoin/bitcoin/pull/10143)). + +- The second argument in `prioritisetransaction` has been renamed from `priority_delta` to `dummy` since Bitcoin Core no longer has a concept of coin age priority. The `dummy` argument has no functional effect, but is retained for positional argument compatibility. See [Removal of Coin Age Priority](#removal-of-coin-age-priority). + +- The `resendwallettransactions` RPC throws an error if the `-walletbroadcast` option is set to false (See [PR 10995](https://github.com/bitcoin/bitcoin/pull/10995)). + +- The second argument in the `submitblock` RPC argument has been renamed from `parameters` to `dummy`. This argument never had any effect, and the renaming is simply to communicate this fact to the user (See [PR 10191](https://github.com/bitcoin/bitcoin/pull/10191)) + (Clients should, however, use positional arguments for `submitblock` in order to be compatible with BIP 22.) + +- The `verbose` argument of `getblock` has been renamed to `verbosity` and now takes an integer from 0 to 2. Verbose level 0 is equivalent to `verbose=false`. Verbose level 1 is equivalent to `verbose=true`. Verbose level 2 will give the full transaction details of each transaction in the output as given by `getrawtransaction`. The old behavior of using the `verbose` named argument and a boolean value is still maintained for compatibility. + +- Error codes have been updated to be more accurate for the following error cases (See [PR 9853](https://github.com/bitcoin/bitcoin/pull/9853)): + - `getblock` now returns RPC_MISC_ERROR if the block can't be found on disk (for + example if the block has been pruned). Previously returned RPC_INTERNAL_ERROR. + - `pruneblockchain` now returns RPC_MISC_ERROR if the blocks cannot be pruned + because the node is not in pruned mode. Previously returned RPC_METHOD_NOT_FOUND. + - `pruneblockchain` now returns RPC_INVALID_PARAMETER if the blocks cannot be pruned + because the supplied timestamp is too late. Previously returned RPC_INTERNAL_ERROR. + - `pruneblockchain` now returns RPC_MISC_ERROR if the blocks cannot be pruned + because the blockchain is too short. Previously returned RPC_INTERNAL_ERROR. + - `setban` now returns RPC_CLIENT_INVALID_IP_OR_SUBNET if the supplied IP address + or subnet is invalid. Previously returned RPC_CLIENT_NODE_ALREADY_ADDED. + - `setban` now returns RPC_CLIENT_INVALID_IP_OR_SUBNET if the user tries to unban + a node that has not previously been banned. Previously returned RPC_MISC_ERROR. + - `removeprunedfunds` now returns RPC_WALLET_ERROR if `bitcoind` is unable to remove + the transaction. Previously returned RPC_INTERNAL_ERROR. + - `removeprunedfunds` now returns RPC_INVALID_PARAMETER if the transaction does not + exist in the wallet. Previously returned RPC_INTERNAL_ERROR. + - `fundrawtransaction` now returns RPC_INVALID_ADDRESS_OR_KEY if an invalid change + address is provided. Previously returned RPC_INVALID_PARAMETER. + - `fundrawtransaction` now returns RPC_WALLET_ERROR if `bitcoind` is unable to create + the transaction. The error message provides further details. Previously returned + RPC_INTERNAL_ERROR. + - `bumpfee` now returns RPC_INVALID_PARAMETER if the provided transaction has + descendants in the wallet. Previously returned RPC_MISC_ERROR. + - `bumpfee` now returns RPC_INVALID_PARAMETER if the provided transaction has + descendants in the mempool. Previously returned RPC_MISC_ERROR. + - `bumpfee` now returns RPC_WALLET_ERROR if the provided transaction has + has been mined or conflicts with a mined transaction. Previously returned + RPC_INVALID_ADDRESS_OR_KEY. + - `bumpfee` now returns RPC_WALLET_ERROR if the provided transaction is not + BIP 125 replaceable. Previously returned RPC_INVALID_ADDRESS_OR_KEY. + - `bumpfee` now returns RPC_WALLET_ERROR if the provided transaction has already + been bumped by a different transaction. Previously returned RPC_INVALID_REQUEST. + - `bumpfee` now returns RPC_WALLET_ERROR if the provided transaction contains + inputs which don't belong to this wallet. Previously returned RPC_INVALID_ADDRESS_OR_KEY. + - `bumpfee` now returns RPC_WALLET_ERROR if the provided transaction has multiple change + outputs. Previously returned RPC_MISC_ERROR. + - `bumpfee` now returns RPC_WALLET_ERROR if the provided transaction has no change + output. Previously returned RPC_MISC_ERROR. + - `bumpfee` now returns RPC_WALLET_ERROR if the fee is too high. Previously returned + RPC_MISC_ERROR. + - `bumpfee` now returns RPC_WALLET_ERROR if the fee is too low. Previously returned + RPC_MISC_ERROR. + - `bumpfee` now returns RPC_WALLET_ERROR if the change output is too small to bump the + fee. Previously returned RPC_MISC_ERROR. + +0.15.0 Change log +================= + +### RPC and other APIs +- #9485 `61a640e` ZMQ example using python3 and asyncio (mcelrath) +- #9894 `0496e15` remove 'label' filter for rpc command help (instagibbs) +- #9853 `02bd6e9` Fix error codes from various RPCs (jnewbery) +- #9842 `598ef9c` Fix RPC failure testing (continuation of #9707) (jnewbery) +- #10038 `d34995a` Add mallocinfo mode to `getmemoryinfo` RPC (laanwj) +- #9500 `3568b30` [Qt][RPC] Autocomplete commands for 'help' command in debug console (achow101) +- #10056 `e6156a0` [zmq] Call va_end() on va_start()ed args (kallewoof) +- #10086 `7438cea` Trivial: move rpcserialversion into RPC option group (jlopp) +- #10150 `350b224` [rpc] Add logging rpc (jnewbery) +- #10208 `393160c` [wallet] Rescan abortability (kallewoof) +- #10143 `a987def` [net] Allow disconnectnode RPC to be called with node id (jnewbery) +- #10281 `0e8499c` doc: Add RPC interface guidelines (laanwj) +- #9733 `d4732f3` Add getchaintxstats RPC (sipa) +- #10310 `f4b15e2` [doc] Add hint about getmempoolentry to getrawmempool help (kallewoof) +- #8704 `96c850c` [RPC] Transaction details in getblock (achow101) +- #8952 `9390845` Add query options to listunspent RPC call (pedrobranco) +- #10413 `08ac35a` Fix docs (there's no rpc command setpaytxfee) (RHavar) +- #8384 `e317c0d` Add witness data output to TxInError messages (instagibbs) +- #9571 `4677151` RPC: getblockchaininfo returns BIP signaling statistics (pinheadmz) +- #10450 `ef2d062` Fix bumpfee rpc "errors" return value (ryanofsky) +- #10475 `39039b1` [RPC] getmempoolinfo mempoolminfee is a BTC/KB feerate (instagibbs) +- #10478 `296928e` rpc: Add listen address to incoming connections in `getpeerinfo` (laanwj) +- #10403 `08d0390` Fix importmulti failure to return rescan errors (ryanofsky) +- #9740 `9fec4da` Add friendly output to dumpwallet (aideca) +- #10426 `16f6c98` Replace bytes_serialized with bogosize (sipa) +- #10252 `980deaf` RPC/Mining: Restore API compatibility for prioritisetransaction (luke-jr) +- #9672 `46311e7` Opt-into-RBF for RPC & bitcoin-tx (luke-jr) +- #10481 `9c248e3` Decodehextx scripts sanity check (achow101) +- #10488 `fa1f106` Note that the prioritizetransaction dummy value is deprecated, and has no meaning (TheBlueMatt) +- #9738 `c94b89e` gettxoutproof() should return consistent result (jnewbery) +- #10191 `00350bd` [trivial] Rename unused RPC arguments 'dummy' (jnewbery) +- #10627 `b62b4c8` fixed listunspent rpc convert parameter (tnakagawa) +- #10412 `bef02fb` Improve wallet rescan API (ryanofsky) +- #10400 `1680ee0` [RPC] Add an uptime command that displays the amount of time (in seconds) bitcoind has been running (rvelhote) +- #10683 `d81bec7` rpc: Move the `generate` RPC call to rpcwallet (laanwj) +- #10710 `30bc0f6` REST/RPC example update (Mirobit) +- #10747 `9edda0c` [rpc] fix verbose argument for getblock in bitcoin-cli (jnewbery) +- #10589 `104f5f2` More economical fee estimates for RBF and RPC options to control (morcos) +- #10543 `b27b004` Change API to estimaterawfee (morcos) +- #10807 `afd2fca` getbalance example covers at least 6 confirms (instagibbs) +- #10707 `75b5643` Better API for estimatesmartfee RPC (morcos) +- #10784 `9e8d6a3` Do not allow users to get keys from keypool without reserving them (TheBlueMatt) +- #10857 `d445a2c` [RPC] Add a deprecation warning to getinfo's output (achow101) +- #10571 `adf170d` [RPC]Move transaction combining from signrawtransaction to new RPC (achow101) +- #10783 `041dad9` [RPC] Various rpc argument fixes (instagibbs) +- #9622 `6ef3c7e` [rpc] listsinceblock should include lost transactions when parameter is a reorg'd block (kallewoof) +- #10799 `8537187` Prevent user from specifying conflicting parameters to fundrawtx (TheBlueMatt) +- #10931 `0b11a07` Fix misleading "Method not found" multiwallet errors (ryanofsky) +- #10788 `f66c596` [RPC] Fix addwitnessaddress by replacing ismine with producesignature (achow101) +- #10999 `627c3c0` Fix amounts formatting in `decoderawtransaction` (laanwj) +- #11002 `4268426` [wallet] return correct error code from resendwallettransaction (jnewbery) +- #11029 `96a63a3` [RPC] trivial: gettxout no longer shows version of tx (FelixWeis) +- #11083 `6c2b008` Fix combinerawtransaction RPC help result section (jonasnick) +- #11027 `07164bb` [RPC] Only return hex field once in getrawtransaction (achow101) +- #10698 `5af6572` Be consistent in calling transactions "replaceable" for Opt-In RBF (TheBlueMatt) + +### Block and transaction handling +- #9801 `a8c5751` Removed redundant parameter from mempool.PrioritiseTransaction (gubatron) +- #9819 `1efc99c` Remove harmless read of unusued priority estimates (morcos) +- #9822 `b7547fa` Remove block file location upgrade code (benma) +- #9602 `30ff3a2` Remove coin age priority and free transactions - implementation (morcos) +- #9548 `47510ad` Remove min reasonable fee (morcos) +- #10249 `c73af54` Switch CCoinsMap from boost to std unordered_map (sipa) +- #9966 `2a183de` Control mempool persistence using a command line parameter (jnewbery) +- #10199 `318ea50` Better fee estimates (morcos) +- #10196 `bee3529` Bugfix: PrioritiseTransaction updates the mempool tx counter (sdaftuar) +- #10195 `1088b02` Switch chainstate db and cache to per-txout model (sipa) +- #10284 `c2ab38b` Always log debug information for fee calculation in CreateTransaction (morcos) +- #10503 `efbcf2b` Use REJECT_DUPLICATE for already known and conflicted txn (sipa) +- #10537 `b3eb0d6` Few Minor per-utxo assert-semantics re-adds and tweak (TheBlueMatt) +- #10626 `8c841a3` doc: Remove outdated minrelaytxfee comment (MarcoFalke) +- #10559 `234ffc6` Change semantics of HaveCoinInCache to match HaveCoin (morcos) +- #10581 `7878353` Simplify return values of GetCoin/HaveCoin(InCache) (sipa) +- #10684 `a381f6a` Remove no longer used mempool.exists(outpoint) (morcos) +- #10148 `d4e551a` Use non-atomic flushing with block replay (sipa) +- #10685 `30c2130` Clarify CCoinsViewMemPool documentation (TheBlueMatt) +- #10558 `90a002e` Address nits from per-utxo change (morcos) +- #10706 `6859ad2` Improve wallet fee logic and fix GUI bugs (morcos) +- #10526 `754aa02` Force on-the-fly compaction during pertxout upgrade (sipa) +- #10985 `d896d5c` Add undocumented -forcecompactdb to force LevelDB compactions (sipa) +- #10292 `e4bbd3d` Improved efficiency in COutPoint constructors (mm-s) +- #10290 `8d6d43e` Add -stopatheight for benchmarking (sipa) + +### P2P protocol and network code +- #9726 `7639d38` netbase: Do not print an error on connection timeouts through proxy (laanwj) +- #9805 `5b583ef` Add seed.btc.petertodd.org to mainnet DNS seeds (petertodd) +- #9861 `22f609f` Trivial: Debug log ambiguity fix for peer addrs (keystrike) +- #9774 `90cb2a2` Enable host lookups for -proxy and -onion parameters (jmcorgan) +- #9558 `7b585cf` Clarify assumptions made about when BlockCheck is called (TheBlueMatt) +- #10135 `e19586a` [p2p] Send the correct error code in reject messages (jnewbery) +- #9665 `eab00d9` Use cached [compact] blocks to respond to getdata messages (TheBlueMatt) +- #10215 `a077a90` Check interruptNet during dnsseed lookups (TheBlueMatt) +- #10234 `faf2dea` [net] listbanned RPC and QT should show correct banned subnets (jnewbery) +- #10134 `314ebdf` [qa] Fixes segwit block relay test after inv-direct-fetch was disabled (sdaftuar) +- #10351 `3f57c55` removed unused code in INV message (Greg-Griffith) +- #10061 `ae78609` [net] Added SetSocketNoDelay() utility function (tjps) +- #10408 `28c6e8d` Net: Improvements to Tor control port parser (str4d) +- #10460 `5c63d66` Broadcast address every day, not 9 hours (sipa) +- #10471 `400fdd0` Denote functions CNode::GetRecvVersion() and CNode::GetRefCount() as const (pavlosantoniou) +- #10345 `67700b3` [P2P] Timeout for headers sync (sdaftuar) +- #10564 `8d9f45e` Return early in IsBanned (gmaxwell) +- #10587 `de8db47` Net: Fix resource leak in ReadBinaryFile(...) (practicalswift) +- #9549 `b33ca14` [net] Avoid possibility of NULL pointer dereference in MarkBlockAsInFlight(...) (practicalswift) +- #10446 `2772dc9` net: avoid extra dns query per seed (theuni) +- #10824 `9dd6a2b` Avoid unnecessary work in SetNetworkActive (promag) +- #10948 `df3a6f4` p2p: Hardcoded seeds update pre-0.15 branch (laanwj) +- #10977 `02f4c4a` [net] Fix use of uninitialized value in getnetworkinfo(const JSONRPCRequest&) (practicalswift) +- #10982 `c8b62c7` Disconnect network service bits 6 and 8 until Aug 1, 2018 (TheBlueMatt) +- #11012 `0e5cff6` Make sure to clean up mapBlockSource if we've already seen the block (theuni) + +### Validation +- #9725 `67023e9` CValidationInterface Cleanups (TheBlueMatt) +- #10178 `2584925` Remove CValidationInterface::UpdatedTransaction (TheBlueMatt) +- #10201 `a6548a4` pass Consensus::Params& to functions in validation.cpp and make them static (mariodian) +- #10297 `431a548` Simplify DisconnectBlock arguments/return value (sipa) +- #10464 `f94b7d5` Introduce static DoWarning (simplify UpdateTip) (jtimon) +- #10569 `2e7d8f8` Fix stopatheight (achow101) +- #10192 `2935b46` Cache full script execution results in addition to signatures (TheBlueMatt) +- #10179 `21ed30a` Give CValidationInterface Support for calling notifications on the CScheduler Thread (TheBlueMatt) +- #10557 `66270a4` Make check to distinguish between orphan txs and old txs more efficient (morcos) +- #10775 `7c2400c` nCheckDepth chain height fix (romanornr) +- #10821 `16240f4` Add SSE4 optimized SHA256 (sipa) +- #10854 `04d395e` Avoid using sizes on non-fixed-width types to derive protocol constants (gmaxwell) +- #10945 `2a50b11` Update defaultAssumeValid according to release-process.md (gmaxwell) +- #10986 `2361208` Update chain transaction statistics (sipa) +- #11028 `6bdf4b3` Avoid masking of difficulty adjustment errors by checkpoints (sipa) +- #9533 `cb598cf` Allow non-power-of-2 signature cache sizes (sipa) +- #9208 `acd9957` Improve DisconnectTip performance (sdaftuar) +- #10618 `f90603a` Remove confusing MAX_BLOCK_BASE_SIZE (gmaxwell) +- #10758 `bd92424` Fix some chainstate-init-order bugs (TheBlueMatt) +- #10550 `b7296bc` Don't return stale data from CCoinsViewCache::Cursor() (ryanofsky) +- #10998 `2507fd5` Fix upgrade cancel warnings (TheBlueMatt) +- #9868 `cbdb473` Abstract out the command line options for block assembly (sipa) + +### Build system +- #9727 `5f0556d` Remove fallbacks for boost_filesystem < v3 (laanwj) +- #9788 `50a2265` gitian: bump descriptors for master (theuni) +- #9794 `7ca2f54` Minor update to qrencode package builder (mitchellcash) +- #9514 `2cc0df1` release: Windows signing script (theuni) +- #9921 `8b789d8` build: Probe MSG_DONTWAIT in the same way as MSG_NOSIGNAL (laanwj) +- #10011 `32d1b34` build: Fix typo s/HAVE_DONTWAIT/HAVE_MSG_DONTWAIT (laanwj) +- #9946 `90dd9e6` Fix build errors if spaces in path or parent directory (pinheadmz) +- #10136 `81da4c7` build: Disable Wshadow warning (laanwj) +- #10166 `64962ae` Ignore Doxyfile generated from Doxyfile.in template (paveljanik) +- #10239 `0416ea9` Make Boost use std::atomic internally (sipa) +- #10228 `27faa6c` build: regenerate bitcoin-config.h as necessary (theuni) +- #10273 `8979f45` [scripts] Minor improvements to `macdeployqtplus` script (chrisgavin) +- #10325 `a26280b` 0.15.0 Depends Updates (fanquake) +- #10328 `79aeff6` Update contrib/debian to latest Ubuntu PPA upload (TheBlueMatt) +- #7522 `d25449f` Bugfix: Only use git for build info if the repository is actually the right one (luke-jr) +- #10489 `e654d61` build: silence gcc7's implicit fallthrough warning (theuni) +- #10549 `ad1a13e` Avoid printing generic and duplicated "checking for QT" during ./configure (drizzt) +- #10628 `8465b68` [depends] expat 2.2.1 (fanquake) +- #10806 `db825d2` build: verify that the assembler can handle crc32 functions (theuni) +- #10766 `b4d03be` Building Environment: Set ARFLAGS to cr (ReneNyffenegger) +- #10803 `91edda8` Explicitly search for bdb5.3 (pstratem) +- #10855 `81560b0` random: only use getentropy on openbsd (theuni) +- #10508 `1caafa6` Run Qt wallet tests on travis (ryanofsky) +- #10851 `e222618` depends: fix fontconfig with newer glibc (theuni) +- #10971 `88b1e4b` build: fix missing sse42 in depends builds (theuni) +- #11097 `129b03f` gitian: quick hack to fix version string in releases (theuni) +- #10039 `919aaf6` Fix compile errors with Qt 5.3.2 and Boost 1.55.0 (ryanofsky) +- #10168 `7032021` Fix build warning from #error text (jnewbery) +- #10301 `318392c` Check if sys/random.h is required for getentropy (jameshilliard) + +### GUI +- #9724 `1a9fd5c` Qt/Intro: Add explanation of IBD process (luke-jr) +- #9834 `b00ba62` qt: clean up initialize/shutdown signals (benma) +- #9481 `ce01e62` [Qt] Show more significant warning if we fall back to the default fee (jonasschnelli) +- #9974 `b9f930b` Add basic Qt wallet test (ryanofsky) +- #9690 `a387d3a` Change 'Clear' button string to 'Reset' (da2x) +- #9592 `9c7b7cf` [Qt] Add checkbox in the GUI to opt-in to RBF when creating a transaction (ryanofsky) +- #10098 `2b477e6` Make qt wallet test compatible with qt4 (ryanofsky) +- #9890 `1fa4ae6` Add a button to open the config file in a text editor (ericshawlinux) +- #10156 `51833a1` Fix for issues with startup and multiple monitors on windows (AllanDoensen) +- #10177 `de01da7` Changed "Send" button default status from true to false (KibbledJiveElkZoo) +- #10221 `e96486c` Stop treating coinbase outputs differently in GUI: show them at 1conf (TheBlueMatt) +- #10231 `987a6c0` [Qt] Reduce a significant cs_main lock freeze (jonasschnelli) +- #10242 `f6f3b58` [qt] Don't call method on null WalletModel object (ryanofsky) +- #10093 `a3e756b` [Qt] Don't add arguments of sensitive command to console window (jonasschnelli) +- #10362 `95546c8` [GUI] Add OSX keystroke to RPCConsole info (spencerlievens) +- #9697 `962cd3f` [Qt] simple fee bumper with user verification (jonasschnelli) +- #10390 `e477516` [wallet] remove minimum total fee option (instagibbs) +- #10420 `4314544` Add Qt tests for wallet spends & bumpfee (ryanofsky) +- #10454 `c1c9a95` Fix broken q4 test build (ryanofsky) +- #10449 `64beb13` Overhaul Qt fee bumper (jonasschnelli) +- #10582 `7c72fb9` Pass in smart fee slider value to coin control dialog (morcos) +- #10673 `4c72cc3` [qt] Avoid potential null pointer dereference in TransactionView::exportClicked() (practicalswift) +- #10769 `8fdd23a` [Qt] replace fee slider with a Dropdown, extend conf. targets (jonasschnelli) +- #10870 `412b466` [Qt] Use wallet 0 in rpc console if running with multiple wallets (jonasschnelli) +- #10988 `a9dd111` qt: Increase BLOCK_CHAIN_SIZE constants (laanwj) +- #10644 `e292140` Slightly overhaul NSI pixmaps (jonasschnelli) +- #10660 `0c3542e` Allow to cancel the txdb upgrade via splashscreen keypress 'q' (jonasschnelli) + +### Wallet +- #9359 `f7ec7cf` Add test for CWalletTx::GetImmatureCredit() returning stale values (ryanofsky) +- #9576 `56ab672` [wallet] Remove redundant initialization (practicalswift) +- #9333 `fa625b0` Document CWalletTx::mapValue entries and remove erase of nonexistent "version" entry (ryanofsky) +- #9906 `72fb515` Disallow copy constructor CReserveKeys (instagibbs) +- #9369 `3178b2c` Factor out CWallet::nTimeSmart computation into a method (ryanofsky) +- #9830 `afcd7c0` Add safe flag to listunspent result (NicolasDorier) +- #9993 `c49355c` Initialize nRelockTime (pstratem) +- #9818 `3d857f3` Save watch only key timestamps when reimporting keys (ryanofsky) +- #9294 `f34cdcb` Use internal HD chain for change outputs (hd split) (jonasschnelli) +- #10164 `e183ea2` Wallet: reduce excess logic InMempool() (kewde) +- #10186 `c9ff4f8` Remove SYNC_TRANSACTION_NOT_IN_BLOCK magic number (jnewbery) +- #10226 `64c45aa` wallet: Use boost to more portably ensure -wallet specifies only a filename (luke-jr) +- #9827 `c91ca0a` Improve ScanForWalletTransactions return value (ryanofsky) +- #9951 `fa1ac28` Wallet database handling abstractions/simplifications (laanwj) +- #10265 `c29a0d4` [wallet] [moveonly] Check non-null pindex before potentially referencing (kallewoof) +- #10283 `a550f6e` Cleanup: reduce to one GetMinimumFee call signature (morcos) +- #10294 `e2b99b1` [Wallet] unset change position when there is no change (instagibbs) +- #10115 `d3dce0e` Avoid reading the old hd master key during wallet encryption (TheBlueMatt) +- #10341 `18c9deb` rpc/wallet: Workaround older UniValue which returns a std::string temporary for get_str (luke-jr) +- #10308 `94e5227` [wallet] Securely erase potentially sensitive keys/values (tjps) +- #10257 `ea1fd43` [test] Add test for getmemoryinfo (jimmysong) +- #10295 `ce8176d` [qt] Move some WalletModel functions into CWallet (ryanofsky) +- #10506 `7cc2c67` Fix bumpfee test after #10449 (ryanofsky) +- #10500 `098b01d` Avoid CWalletTx copies in GetAddressBalances and GetAddressGroupings (ryanofsky) +- #10455 `0747d33` Simplify feebumper minimum fee code slightly (ryanofsky) +- #10522 `2805d60` [wallet] Remove unused variables (practicalswift) +- #8694 `177433a` Basic multiwallet support (luke-jr) +- #10598 `7a74f88` Supress struct/class mismatch warnings introduced in #10284 (paveljanik) +- #9343 `209eef6` Don't create change at dust limit (morcos) +- #10744 `ed88e31` Use method name via __func__ macro (darksh1ne) +- #10712 `e8b9523` Add change output if necessary to reduce excess fee (morcos) +- #10816 `1c011ff` Properly forbid -salvagewallet and -zapwallettxes for multi wallet (morcos) +- #10235 `5cfdda2` Track keypool entries as internal vs external in memory (TheBlueMatt) +- #10330 `bf0a08b` [wallet] fix zapwallettxes interaction with persistent mempool (jnewbery) +- #10831 `0b01935` Batch flushing operations to the walletdb during top up and increase keypool size (gmaxwell) +- #10795 `7b6e8bc` No longer ever reuse keypool indexes (TheBlueMatt) +- #10849 `bde4f93` Multiwallet: simplest endpoint support (jonasschnelli) +- #10817 `9022aa3` Redefine Dust and add a discard_rate (morcos) +- #10883 `bf3b742` Rename -usewallet to -rpcwallet (morcos) +- #10604 `420238d` [wallet] [tests] Add listwallets RPC, include wallet name in `getwalletinfo` and add multiwallet test (jnewbery) +- #10885 `70888a3` Reject invalid wallets (promag) +- #10949 `af56397` Clarify help message for -discardfee (morcos) +- #10942 `2e857bb` Eliminate fee overpaying edge case when subtracting fee from recipients (morcos) +- #10995 `fa64636` Fix resendwallettransactions assert failure if -walletbroadcast=0 (TheBlueMatt) +- #11022 `653a46d` Basic keypool topup (jnewbery) +- #11081 `9fe1f6b` Add length check for CExtKey deserialization (jonasschnelli, guidovranken) +- #11044 `4ef8374` [wallet] Keypool topup cleanups (jnewbery) +- #11145 `e51bb71` Fix rounding bug in calculation of minimum change (morcos) +- #9605 `779f2f9` Use CScheduler for wallet flushing, remove ThreadFlushWalletDB (TheBlueMatt) +- #10108 `4e3efd4` ApproximateBestSubset should take inputs by reference, not value (RHavar) + +### Tests and QA +- #9744 `8efd1c8` Remove unused module from rpc-tests (34ro) +- #9657 `7ff4a53` Improve rpc-tests.py (jnewbery) +- #9766 `7146d96` Add --exclude option to rpc-tests.py (jnewbery) +- #9577 `d6064a8` Fix docstrings in qa tests (jnewbery) +- #9823 `a13a417` qa: Set correct path for binaries in rpc tests (MarcoFalke) +- #9847 `6206252` Extra test vector for BIP32 (sipa) +- #9350 `88c2ae3` [Trivial] Adding label for amount inside of tx_valid/tx_invalid.json (Christewart) +- #9888 `36afd4d` travis: Verify commits only for one target (MarcoFalke) +- #9904 `58861ad` test: Fail if InitBlockIndex fails (laanwj) +- #9828 `67c5cc1` Avoid -Wshadow warnings in wallet_tests (ryanofsky) +- #9832 `48c3429` [qa] assert_start_raises_init_error (NicolasDorier) +- #9739 `9d5fcbf` Fix BIP68 activation test (jnewbery) +- #9547 `d32581c` bench: Assert that division by zero is unreachable (practicalswift) +- #9843 `c78adbf` Fix segwit getblocktemplate test (jnewbery) +- #9929 `d5ce14e` tests: Delete unused function _rpchost_to_args (laanwj) +- #9555 `19be26a` [test] Avoid reading a potentially uninitialized variable in tx_invalid-test (transaction_tests.cpp) (practicalswift) +- #9945 `ac23a7c` Improve logging in bctest.py if there is a formatting mismatch (jnewbery) +- #9768 `8910b47` [qa] Add logging to test_framework.py (jnewbery) +- #9972 `21833f9` Fix extended rpc tests broken by #9768 (jnewbery) +- #9977 `857d1e1` QA: getblocktemplate_longpoll.py should always use >0 fee tx (sdaftuar) +- #9970 `3cc13ea` Improve readability of segwit.py, smartfees.py (sdaftuar) +- #9497 `2c781fb` CCheckQueue Unit Tests (JeremyRubin) +- #10024 `9225de2` [trivial] Use log.info() instead of print() in remaining functional test cases (jnewbery) +- #9956 `3192e52` Reorganise qa directory (jnewbery) +- #10017 `02d64bd` combine_logs.py - aggregates log files from multiple bitcoinds during functional tests (jnewbery) +- #10047 `dfef6b6` [tests] Remove unused variables and imports (practicalswift) +- #9701 `a230b05` Make bumpfee tests less fragile (ryanofsky) +- #10053 `ca20923` [test] Allow functional test cases to be skipped (jnewbery) +- #10052 `a0b1e57` [test] Run extended tests once daily in Travis (jnewbery) +- #10069 `1118493` [QA] Fix typo in fundrawtransaction test (NicolasDorier) +- #10083 `c044f03` [QA] Renaming rawTx into rawtx (NicolasDorier) +- #10073 `b1a4f27` Actually run assumevalid.py (jnewbery) +- #9780 `c412fd8` Suppress noisy output from qa tests in Travis (jnewbery) +- #10096 `79af9fb` Check that all test scripts in test/functional are being run (jnewbery) +- #10076 `5b029aa` [qa] combine_logs: Use ordered list for logfiles (MarcoFalke) +- #10107 `f2734c2` Remove unused variable. Remove accidental trailing semicolons in Python code (practicalswift) +- #10109 `8ac8041` Remove SingleNodeConnCB (jnewbery) +- #10114 `edc62c9` [tests] sync_with_ping should assert that ping hasn't timed out (jnewbery) +- #10128 `427d2fd` Speed Up CuckooCache tests (JeremyRubin) +- #10072 `12af74b` Remove sources of unreliablility in extended functional tests (jnewbery) +- #10077 `ebfd653` [qa] Add setnetworkactive smoke test (MarcoFalke) +- #10152 `080d7c7` [trivial] remove unused line in Travis config (jnewbery) +- #10159 `df1ca9e` [tests] color test results and sort alphabetically (jnewbery) +- #10124 `88799ea` [test] Suppress test logging spam (jnewbery) +- #10142 `ed09dd3` Run bitcoin_test-qt under minimal QPA platform (ryanofsky) +- #9949 `a27dbc5` [bench] Avoid function call arguments which are pointers to uninitialized values (practicalswift) +- #10187 `b44adf9` tests: Fix test_runner return value in case of skipped test (laanwj) +- #10197 `d86bb07` [tests] Functional test warnings (jnewbery) +- #10219 `9111df9` Tests: Order Python Tests Differently (jimmysong) +- #10229 `f3db4c6` Tests: Add test for getdifficulty (jimmysong) +- #10224 `2723bcd` [test] Add test for getaddednodeinfo (jimmysong) +- #10023 `c530c15` [tests] remove maxblocksinflight.py (functionality covered by other test) (jnewbery) +- #10097 `1b25b6d` Move zmq test skipping logic into individual test case (jnewbery) +- #10272 `54e2d87` [Tests] Prevent warning: variable 'x' is uninitialized (paveljanik) +- #10225 `e0a7e19` [test] Add aborttrescan tests (kallewoof) +- #10278 `8254a8a` [test] Add Unit Test for GetListenPort (jimmysong) +- #10280 `47535d7` [test] Unit test amount.h/amount.cpp (jimmysong) +- #10256 `80c3a73` [test] Add test for gettxout to wallet.py (jimmysong) +- #10264 `492d22f` [test] Add tests for getconnectioncount, getnettotals and ping (jimmysong) +- #10169 `8f3e384` [tests] Remove func test code duplication (jnewbery) +- #10198 `dc8fc0c` [tests] Remove is_network_split from functional test framework (jnewbery) +- #10255 `3c5e6c9` [test] Add test for listaddressgroupings (jimmysong) +- #10137 `75171f0` Remove unused import. Remove accidental trailing semicolons (practicalswift) +- #10307 `83073de` [tests] allow zmq test to be run in out-of-tree builds (jnewbery) +- #10344 `e927483` [tests] Fix abandonconflict.py intermittency (jnewbery) +- #10318 `170bc2c` [tests] fix wait_for_inv() (jnewbery) +- #10171 `fff72de` [tests] Add node methods to test framework (jnewbery) +- #10352 `23d78c4` test: Add elapsed time to RPC tracing (laanwj) +- #10342 `6a796b2` [tests] Improve mempool_persist test (jnewbery) +- #10287 `776ba23` [tests] Update Unit Test for addrman.h/addrman.cpp (jimmysong) +- #10365 `7ee5236` [tests] increase timeouts in sendheaders test (jnewbery) +- #10361 `f6241b3` qa: disablewallet: Check that wallet is really disabled (MarcoFalke) +- #10371 `4b766fc` [tests] Clean up addrman_tests.cpp (jimmysong) +- #10253 `87abe20` [test] Add test for getnetworkhashps (jimmysong) +- #10376 `8bd16ee` [tests] fix disconnect_ban intermittency (jnewbery) +- #10374 `5411997` qa: Warn when specified test is not found (MarcoFalke) +- #10405 `0542978` tests: Correct testcase in script_tests.json for large number OP_EQUAL (laanwj) +- #10429 `6b99daf` tests: fix spurious addrman test failure (theuni) +- #10433 `8e57256` [tests] improve tmpdir structure (jnewbery) +- #10415 `217b416` [tests] Speed up fuzzing by ~200x when using afl-fuzz (practicalswift) +- #10445 `b4b057a` Add test for empty chain and reorg consistency for gettxoutsetinfo (gmaxwell) +- #10423 `1aefc94` [tests] skipped tests should clean up after themselves (jnewbery) +- #10359 `329fc1d` [tests] functional tests should call BitcoinTestFramework start/stop node methods (jnewbery) +- #10514 `e103b3f` Bugfix: missing == 0 after randrange (sipa) +- #10515 `c871f32` [test] Add test for getchaintxstats (jimmysong) +- #10509 `bea5b00` Remove xvfb configuration from travis (ryanofsky) +- #10535 `30853e1` [qa] fundrawtx: Fix shutdown race (MarcoFalke) +- #9909 `300f8e7` tests: Add FindEarliestAtLeast test for edge cases (ryanofsky) +- #10331 `75e898c` Share config between util and functional tests (jnewbery) +- #10321 `e801084` Use FastRandomContext for all tests (sipa) +- #10524 `6c2d81f` [tests] Remove printf(...) (practicalswift) +- #10547 `71ab6e5` [tests] Use FastRandomContext instead of boost::random::{mt19937,uniform_int_distribution} (practicalswift) +- #10551 `6702617` [Tests] Wallet encryption functional tests (achow101) +- #10555 `643fa0b` [tests] various improvements to zmq_test.py (jnewbery) +- #10533 `d083bd9` [tests] Use cookie auth instead of rpcuser and rpcpassword (achow101) +- #10632 `c68a9a6` qa: Add stopatheight test (MarcoFalke) +- #10636 `4bc853b` [qa] util: Check return code after closing bitcoind proc (MarcoFalke) +- #10662 `e0a7801` Initialize randomness in benchmarks (achow101) +- #10612 `7c87a9c` The young person's guide to the test_framework (jnewbery) +- #10659 `acb1153` [qa] blockchain: Pass on closed connection during generate call (MarcoFalke) +- #10690 `416af3e` [qa] Bugfix: allow overriding extra_args in ComparisonTestFramework (sdaftuar) +- #10556 `65cc7aa` Move stop/start functions from utils.py into BitcoinTestFramework (jnewbery) +- #10704 `dd07f47` [tests] nits in dbcrash.py (jnewbery) +- #10743 `be82498` [test] don't run dbcrash.py on Travis (jnewbery) +- #10761 `d3b5870` [tests] fix replace_by_fee.py (jnewbery) +- #10759 `1d4805c` Fix multi_rpc test for hosts that dont default to utf8 (TheBlueMatt) +- #10190 `e4f226a` [tests] mining functional tests (including regression test for submitblock) (jnewbery) +- #10739 `1fc783f` test: Move variable `state` down where it is used (paveljanik) +- #9980 `fee0d80` Fix mem access violation merkleblock (Christewart) +- #10893 `0c173a1` [QA] Avoid running multiwallet.py twice (jonasschnelli) +- #10927 `9d5e8f9` test: Make sure wallet.backup is created in temp path (laanwj) +- #10899 `f29d5db` [test] Qt: Use _putenv_s instead of setenv on Windows builds (brianmcmichael) +- #10912 `5c8eb79` [tests] Fix incorrect memory_cleanse(…) call in crypto_tests.cpp (practicalswift) +- #11001 `fa8a063` [tests] Test disconnecting unsupported service bits logic (jnewbery) +- #10695 `929fd72` [qa] Rewrite BIP65/BIP66 functional tests (sdaftuar) +- #10963 `ecd2135` [bench] Restore format state of cout after printing with std::fixed/setprecision (practicalswift) +- #11025 `e5d26e4` qa: Fix inv race in example_test (MarcoFalke) +- #10765 `2c811e0` Tests: address placement should be deterministic by default (ReneNyffenegger) +- #11000 `ac016e1` test: Add resendwallettransactions functional tests (promag) +- #11032 `aeb3175` [qa] Fix block message processing error in sendheaders.py (sdaftuar) +- #10105 `0b9fb68` [tests] fixup - make all Travis test runs quiet, non just cron job runs (jnewbery) +- #10222 `6ce7337` [tests] test_runner - check unicode (jnewbery) +- #10327 `35da2ae` [tests] remove import-abort-rescan.py (jnewbery) +- #11023 `bf74d37` [tests] Add option to attach a python debugger if functional test fails (jnewbery) +- #10565 `8c2098a` [coverage] Remove subtrees and benchmarks from coverage report (achow101) + +### Miscellaneous +- #9871 `be8ba2c` Add a tree sha512 hash to merge commits (sipa) +- #9821 `d19d45a` util: Specific GetOSRandom for Linux/FreeBSD/OpenBSD (laanwj) +- #9903 `ba80a68` Docs: add details to -rpcclienttimeout doc (ian-kelling) +- #9910 `53c300f` Docs: correct and elaborate -rpcbind doc (ian-kelling) +- #9905 `01b7cda` [contrib] gh-merge: Move second sha512 check to the end (MarcoFalke) +- #9880 `4df8213` Verify Tree-SHA512s in merge commits, enforce sigs are not SHA1 (TheBlueMatt) +- #9932 `00c13ea` Fix verify-commits on travis and always check top commit's tree (TheBlueMatt) +- #9952 `6996e06` Add historical release notes for 0.14.0 (laanwj) +- #9940 `fa99663` Fix verify-commits on OSX, update for new bad Tree-SHA512, point travis to different keyservers (TheBlueMatt) +- #9963 `8040ae6` util: Properly handle errors during log message formatting (laanwj) +- #9984 `cce056d` devtools: Make github-merge compute SHA512 from git, instead of worktree (laanwj) +- #9995 `8bcf934` [doc] clarify blockchain size and pruning (askmike) +- #9734 `0c17afc` Add updating of chainTxData to release process (sipa) +- #10063 `530fcbd` add missing spaces so that markdown recognizes headline (flack) +- #10085 `db1ae54` Docs: remove 'noconnect' option (jlopp) +- #10090 `8e4f7e7` Update bitcoin.conf with example for pruning (coinables) +- #9424 `1a5aaab` Change LogAcceptCategory to use uint32_t rather than sets of strings (gmaxwell) +- #10036 `fbf36ca` Fix init README format to render correctly on github (jlopp) +- #10058 `a2cd0b0` No need to use OpenSSL malloc/free (tjps) +- #10123 `471ed00` Allow debug logs to be excluded from specified component (jnewbery) +- #10104 `fadf078` linearize script: Option to use RPC cookie (achow101) +- #10162 `a3a2160` [trivial] Log calls to getblocktemplate (jnewbery) +- #10155 `928695b` build: Deduplicate version numbers (laanwj) +- #10211 `a86255b` [doc] Contributor fixes & new "finding reviewers" section (kallewoof) +- #10250 `1428f30` Fix some empty vector references (sipa) +- #10270 `95f5e44` Remove Clang workaround for Boost 1.46 (fanquake) +- #10263 `cb007e4` Trivial: fix fee estimate write error log message (CryptAxe) +- #9670 `bd9ec0e` contrib: github-merge improvements (laanwj) +- #10260 `1d75597` [doc] Minor corrections to osx dependencies (fanquake) +- #10189 `750c5a5` devtools/net: add a verifier for scriptable changes. Use it to make CNode::id private (theuni) +- #10322 `bc64b5a` Use hardware timestamps in RNG seeding (sipa) +- #10381 `7f2b9e0` Shadowing warnings are not enabled by default, update doc accordingly (paveljanik) +- #10380 `b6ee855` [doc] Removing comments about dirty entries on txmempool (madeo) +- #10383 `d0c37ee` [logging] log system time and mock time (jnewbery) +- #10404 `b45a52a` doc: Add logging to FinalizeNode() (sdaftuar) +- #10388 `526e839` Output line to debug.log when IsInitialBlockDownload latches to false (morcos) +- #10372 `15254e9` Add perf counter data to GetStrongRandBytes state in scheduler (TheBlueMatt) +- #10461 `55b72f3` Update style guide (sipa) +- #10486 `10e8c0a` devtools: Retry after signing fails in github-merge (laanwj) +- #10447 `f259263` Make bitcoind invalid argument error message specific (laanwj) +- #10495 `6a38b79` contrib: Update location of seeds.txt (laanwj) +- #10469 `b6b150b` Fixing typo in rpcdump.cpp help message (keystrike) +- #10451 `27b9931` contrib/init/bitcoind.openrcconf: Don't disable wallet by default (luke-jr) +- #10323 `00d3692` Update to latest libsecp256k1 master (sipa) +- #10422 `cec9e1e` Fix timestamp in fee estimate debug message (morcos) +- #10566 `5d034ee` [docs] Use the "domain name setup" image (previously unused) in the gitian docs (practicalswift) +- #10534 `a514ac3` Clarify prevector::erase and avoid swap-to-clear (sipa) +- #10575 `22ec768` Header include guideline (sipa) +- #10480 `fbf5d3b` Improve commit-check-script.sh (sipa) +- #10502 `1ad3d4e` scripted-diff: Remove BOOST_FOREACH, Q_FOREACH and PAIRTYPE (jtimon) +- #10377 `b63be2c` Use rdrand as entropy source on supported platforms (sipa) +- #9895 `228c319` Turn TryCreateDirectory() into TryCreateDirectories() (benma) +- #10602 `d76e84a` Make clang-format use C++11 features (e.g. A> instead of A >) (practicalswift) +- #10623 `c38f540` doc: Add 0.14.2 release notes (MarcoFalke) +- #10276 `b750b33` contrib/verifybinaries: allow filtering by platform (knocte) +- #10248 `01c4b14` Rewrite addrdb with less duplication using CHashVerifier (sipa) +- #10577 `232508f` Add an explanation of quickly hashing onto a non-power of two range (gmaxwell) +- #10608 `eee398f` Add a comment explaining the use of MAX_BLOCK_BASE_SIZE (gmaxwell) +- #10728 `7397af9` fix typo in help text for removeprunedfunds (AkioNak) +- #10193 `6dbcc74` scripted-diff: Remove #include (jtimon) +- #10676 `379aed0` document script-based return fields for validateaddress (instagibbs) +- #10651 `cef4b5c` Verify binaries from bitcoincore.org and bitcoin.org (TheBlueMatt) +- #10786 `ca4c545` Add PR description to merge commit in github-merge.py (sipa) +- #10812 `c5904e8` [utils] Allow bitcoin-cli's -rpcconnect option to be used with square brackets (jnewbery) +- #10842 `3895e25` Fix incorrect Doxygen tag (@ince → @since). Doxygen parameter name matching (practicalswift) +- #10681 `df0793f` add gdb attach process to test README (instagibbs) +- #10789 `1124328` Punctuation/grammer fixes in rpcwallet.cpp (stevendlander) +- #10655 `78f307b` Properly document target_confirmations in listsinceblock (RHavar) +- #10917 `5c003cb` developer-notes: add reference to snake_case and PascalCase (benma) +- #11003 `4b5a7ce` Docs: Capitalize bullet points in CONTRIBUTING guide (eklitzke) +- #10968 `98aa3f6` Add instructions for parallel gitian builds (coblee) +- #11076 `1c4b9b3` 0.15 release-notes nits: fix redundancy, remove accidental parenthesis & fix range style (practicalswift) +- #11090 `8f0121c` Update contributor names in release-notes.md (Derek701) +- #11056 `cbdd338` disable jni in builds (instagibbs) +- #11080 `2b59cfb` doc: Update build-openbsd for 6.1 (laanwj) +- #11119 `0a6af47` [doc] build-windows: Mention that only trusty works (MarcoFalke) +- #11108 `e8ad101` Changing -txindex requires -reindex, not -reindex-chainstate (TheBlueMatt) +- #9792 `342b9bc` FastRandomContext improvements and switch to ChaCha20 (sipa) +- #9505 `67ed40e` Prevector Quick Destruct (JeremyRubin) +- #10820 `ef37f20` Use cpuid intrinsics instead of asm code (sipa) +- #9999 `a328904` [LevelDB] Plug leveldb logs to bitcoin logs (NicolasDorier) +- #9693 `c5e9e42` Prevent integer overflow in ReadVarInt (gmaxwell) +- #10129 `351d0ad` scheduler: fix sub-second precision with boost < 1.50 (theuni) +- #10153 `fade788` logging: Fix off-by-one for shrinkdebugfile default (MarcoFalke) +- #10305 `c45da32` Fix potential NPD introduced in b297426c (TheBlueMatt) +- #10338 `daf3e7d` Maintain state across GetStrongRandBytes calls (sipa) +- #10544 `a4fe077` Update to LevelDB 1.20 (sipa) +- #10614 `cafe24f` random: fix crash on some 64bit platforms (theuni) +- #10714 `2a09a38` Avoid printing incorrect block indexing time due to uninitialized variable (practicalswift) +- #10837 `8bc6d1f` Fix resource leak on error in GetDevURandom (corebob) +- #10832 `89bb036` init: Factor out AppInitLockDataDirectory and fix startup core dump issue (laanwj) +- #10914 `b995a37` Add missing lock in CScheduler::AreThreadsServicingQueue() (TheBlueMatt) +- #10958 `659c096` Update to latest Bitcoin patches for LevelDB (sipa) +- #10919 `c1c671f` Fix more init bugs (TheBlueMatt) + +Credits +======= + +Thanks to everyone who directly contributed to this release: + +- ロハン ダル +- Ahmad Kazi +- aideca +- Akio Nakamura +- Alex Morcos +- Allan Doensen +- Andres G. Aragoneses +- Andrew Chow +- Angel Leon +- Awemany +- Bob McElrath +- Brian McMichael +- BtcDrak +- Charlie Lee +- Chris Gavin +- Chris Stewart +- Cory Fields +- CryptAxe +- Dag Robole +- Daniel Aleksandersen +- Daniel Cousens +- darksh1ne +- Dimitris Tsapakidis +- Eric Shaw +- Evan Klitzke +- fanquake +- Felix Weis +- flack +- Guido Vranken +- Greg Griffith +- Gregory Maxwell +- Gregory Sanders +- Ian Kelling +- Jack Grigg +- James Evans +- James Hilliard +- Jameson Lopp +- Jeremy Rubin +- Jimmy Song +- João Barbosa +- Johnathan Corgan +- John Newbery +- Jonas Schnelli +- Jorge Timón +- Karl-Johan Alm +- kewde +- KibbledJiveElkZoo +- Kirit Thadaka +- kobake +- Kyle Honeycutt +- Lawrence Nahum +- Luke Dashjr +- Marco Falke +- Marcos Mayorga +- Marijn Stollenga +- Mario Dian +- Mark Friedenbach +- Marko Bencun +- Masahiko Hyuga +- Matt Corallo +- Matthew Zipkin +- Matthias Grundmann +- Michael Goldstein +- Michael Rotarius +- Mikerah +- Mike van Rossum +- Mitchell Cash +- Nicolas Dorier +- Patrick Strateman +- Pavel Janík +- Pavlos Antoniou +- Pavol Rusnak +- Pedro Branco +- Peter Todd +- Pieter Wuille +- practicalswift +- René Nyffenegger +- Ricardo Velhote +- romanornr +- Russell Yanofsky +- Rusty Russell +- Ryan Havar +- shaolinfry +- Shigeya Suzuki +- Simone Madeo +- Spencer Lievens +- Steven D. Lander +- Suhas Daftuar +- Takashi Mitsuta +- Thomas Snider +- Timothy Redaelli +- tintinweb +- tnaka +- Warren Togami +- Wladimir J. van der Laan + +As well as everyone that helped translating on [Transifex](https://www.transifex.com/projects/p/bitcoin/). From 96d91b79bdfe1c0a58bd645f0f1d657caba16deb Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 14 Sep 2017 16:20:04 +0200 Subject: [PATCH 241/382] contrib: Ignore historical release notes for whitespace check Lint checks should not test these, they are historical documents, and we don't want to encourage silly changes to them to satisfy a checker. Hopefully makes travis pass again on master. Tree-SHA512: 37e6716c4fd5e8a4e579f9b84042e6b0ac224836b6c851cd1ca3f7d46611ffd3003bed0ae08dd0457f69d6eaa485a0d21c631e7ef16b14bdb0f2f78ea700332d --- contrib/devtools/lint-whitespace.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/devtools/lint-whitespace.sh b/contrib/devtools/lint-whitespace.sh index 6150dd3f6..989923f31 100755 --- a/contrib/devtools/lint-whitespace.sh +++ b/contrib/devtools/lint-whitespace.sh @@ -16,14 +16,14 @@ if [ -z "${TRAVIS_COMMIT_RANGE}" ]; then fi showdiff() { - if ! git diff -U0 "${TRAVIS_COMMIT_RANGE}" -- "." ":(exclude)src/leveldb/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/"; then + if ! git diff -U0 "${TRAVIS_COMMIT_RANGE}" -- "." ":(exclude)src/leveldb/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/" ":(exclude)doc/release-notes/"; then echo "Failed to get a diff" exit 1 fi } showcodediff() { - if ! git diff -U0 "${TRAVIS_COMMIT_RANGE}" -- *.cpp *.h *.md *.py *.sh ":(exclude)src/leveldb/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/"; then + if ! git diff -U0 "${TRAVIS_COMMIT_RANGE}" -- *.cpp *.h *.md *.py *.sh ":(exclude)src/leveldb/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/" ":(exclude)doc/release-notes/"; then echo "Failed to get a diff" exit 1 fi From dc2f737ae323eb2dd017d7d3f6e6c887f73cafd5 Mon Sep 17 00:00:00 2001 From: danra Date: Thu, 14 Sep 2017 20:23:26 +0300 Subject: [PATCH 242/382] Trivial: Fix comments for DEFAULT_WHITELIST[FORCE]RELAY --- src/validation.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/validation.h b/src/validation.h index aa4d7abb4..db8e8f9fe 100644 --- a/src/validation.h +++ b/src/validation.h @@ -45,9 +45,9 @@ struct ChainTxData; struct PrecomputedTransactionData; struct LockPoints; -/** Default for DEFAULT_WHITELISTRELAY. */ +/** Default for -whitelistrelay. */ static const bool DEFAULT_WHITELISTRELAY = true; -/** Default for DEFAULT_WHITELISTFORCERELAY. */ +/** Default for -whitelistforcerelay. */ static const bool DEFAULT_WHITELISTFORCERELAY = true; /** Default for -minrelaytxfee, minimum relay fee for transactions */ static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 1000; From cdaf3a1f9e93be273ebf3e470dc709828c55476c Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Thu, 14 Sep 2017 15:32:24 -0400 Subject: [PATCH 243/382] Fix Qt 0.14.2->0.15.0 segfault if "total at least" is selected A button was removed, so now button(1) is nullptr --- src/qt/sendcoinsdialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 05c5ccbfe..cc7786a07 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -128,7 +128,7 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *p ui->groupFee->setId(ui->radioCustomFee, 1); ui->groupFee->button((int)std::max(0, std::min(1, settings.value("nFeeRadio").toInt())))->setChecked(true); ui->groupCustomFee->setId(ui->radioCustomPerKilobyte, 0); - ui->groupCustomFee->button((int)std::max(0, std::min(1, settings.value("nCustomFeeRadio").toInt())))->setChecked(true); + ui->groupCustomFee->button(0)->setChecked(true); ui->customFee->setValue(settings.value("nTransactionFee").toLongLong()); ui->checkBoxMinimumFee->setChecked(settings.value("fPayOnlyMinFee").toBool()); minimizeFeeSection(settings.value("fFeeSectionMinimized").toBool()); From a0b4c2461724e9ec70e6cd3e36929c9270ffcebd Mon Sep 17 00:00:00 2001 From: Dan Raviv Date: Thu, 14 Sep 2017 17:59:09 +0300 Subject: [PATCH 244/382] Trivial: Fix validation comments - Move comment about transaction/block weight calculation so it applies not only to the GetBlockWeight function but also to GetTransactionWeight - Fix comment in validation.cpp referencing future deployment of BIP113. It has already been deployed. - The doc comment for BLOCK_DOWNLOAD_WINDOW wasn't updated since pruning was introduced, so it still refers to pruning as something that might happen in the future. A larger BLOCK_DOWNLOAD_WINDOW window would now, indeed, make pruning harder. --- src/consensus/validation.h | 11 +++++------ src/validation.cpp | 2 +- src/validation.h | 4 ++-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/consensus/validation.h b/src/consensus/validation.h index 5494ce40e..b6740c9d9 100644 --- a/src/consensus/validation.h +++ b/src/consensus/validation.h @@ -89,17 +89,16 @@ class CValidationState { std::string GetDebugMessage() const { return strDebugMessage; } }; +// These implement the weight = (stripped_size * 4) + witness_size formula, +// using only serialization with and without witness data. As witness_size +// is equal to total_size - stripped_size, this formula is identical to: +// weight = (stripped_size * 3) + total_size. static inline int64_t GetTransactionWeight(const CTransaction& tx) { - return ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR -1) + ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); + return ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); } - static inline int64_t GetBlockWeight(const CBlock& block) { - // This implements the weight = (stripped_size * 4) + witness_size formula, - // using only serialization with and without witness data. As witness_size - // is equal to total_size - stripped_size, this formula is identical to: - // weight = (stripped_size * 3) + total_size. return ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION); } diff --git a/src/validation.cpp b/src/validation.cpp index 0bd1ec672..12ffe085d 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -219,7 +219,7 @@ bool CheckFinalTx(const CTransaction &tx, int flags) // IsFinalTx() with one more than chainActive.Height(). const int nBlockHeight = chainActive.Height() + 1; - // BIP113 will require that time-locked transactions have nLockTime set to + // BIP113 requires that time-locked transactions have nLockTime set to // less than the median time of the previous block they're contained in. // When the next block is created its previous block will be the current // chain tip, so we use that to calculate the median time passed to diff --git a/src/validation.h b/src/validation.h index db8e8f9fe..6a77fe56b 100644 --- a/src/validation.h +++ b/src/validation.h @@ -94,8 +94,8 @@ static const int MAX_CMPCTBLOCK_DEPTH = 5; static const int MAX_BLOCKTXN_DEPTH = 10; /** Size of the "block download window": how far ahead of our current height do we fetch? * Larger windows tolerate larger download speed differences between peer, but increase the potential - * degree of disordering of blocks on disk (which make reindexing and in the future perhaps pruning - * harder). We'll probably want to make this a per-peer adaptive value at some point. */ + * degree of disordering of blocks on disk (which make reindexing and pruning harder). We'll probably + * want to make this a per-peer adaptive value at some point. */ static const unsigned int BLOCK_DOWNLOAD_WINDOW = 1024; /** Time to wait (in seconds) between writing blocks/block index to disk. */ static const unsigned int DATABASE_WRITE_INTERVAL = 60 * 60; From e53fa4a1ca58ef46416c4a96542722601fb75c5a Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Fri, 15 Sep 2017 00:07:45 -0400 Subject: [PATCH 245/382] Remove custom fee radio group Removes the extraneous custom fee radio group and its single radio button. The radio button is replaced with a label that has the radio button's text. --- src/qt/forms/sendcoinsdialog.ui | 9 +-------- src/qt/sendcoinsdialog.cpp | 14 ++------------ 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 1e2f2302b..a0e48334c 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -846,19 +846,13 @@ - + 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. per kilobyte - - true - - - groupCustomFee - @@ -1285,6 +1279,5 @@ - diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index cc7786a07..6309070fe 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -114,10 +114,6 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *p settings.setValue("nFeeRadio", 1); // custom if (!settings.contains("nFeeRadio")) settings.setValue("nFeeRadio", 0); // recommended - if (!settings.contains("nCustomFeeRadio") && settings.contains("nTransactionFee") && settings.value("nTransactionFee").toLongLong() > 0) // compatibility - settings.setValue("nCustomFeeRadio", 1); // total at least - if (!settings.contains("nCustomFeeRadio")) - settings.setValue("nCustomFeeRadio", 0); // per kilobyte if (!settings.contains("nSmartFeeSliderPosition")) settings.setValue("nSmartFeeSliderPosition", 0); if (!settings.contains("nTransactionFee")) @@ -127,8 +123,6 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *p ui->groupFee->setId(ui->radioSmartFee, 0); ui->groupFee->setId(ui->radioCustomFee, 1); ui->groupFee->button((int)std::max(0, std::min(1, settings.value("nFeeRadio").toInt())))->setChecked(true); - ui->groupCustomFee->setId(ui->radioCustomPerKilobyte, 0); - ui->groupCustomFee->button(0)->setChecked(true); ui->customFee->setValue(settings.value("nTransactionFee").toLongLong()); ui->checkBoxMinimumFee->setChecked(settings.value("fPayOnlyMinFee").toBool()); minimizeFeeSection(settings.value("fFeeSectionMinimized").toBool()); @@ -178,7 +172,6 @@ void SendCoinsDialog::setModel(WalletModel *_model) connect(ui->confTargetSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(coinControlUpdateLabels())); connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(updateFeeSectionControls())); connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels())); - connect(ui->groupCustomFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels())); connect(ui->customFee, SIGNAL(valueChanged()), this, SLOT(coinControlUpdateLabels())); connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(setMinimumFee())); connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(updateFeeSectionControls())); @@ -214,7 +207,6 @@ SendCoinsDialog::~SendCoinsDialog() QSettings settings; settings.setValue("fFeeSectionMinimized", fFeeMinimized); settings.setValue("nFeeRadio", ui->groupFee->checkedId()); - settings.setValue("nCustomFeeRadio", ui->groupCustomFee->checkedId()); settings.setValue("nConfTarget", getConfTargetForIndex(ui->confTargetSelector->currentIndex())); settings.setValue("nTransactionFee", (qint64)ui->customFee->value()); settings.setValue("fPayOnlyMinFee", ui->checkBoxMinimumFee->isChecked()); @@ -609,7 +601,6 @@ void SendCoinsDialog::on_buttonMinimizeFee_clicked() void SendCoinsDialog::setMinimumFee() { - ui->radioCustomPerKilobyte->setChecked(true); ui->customFee->setValue(GetRequiredFee(1000)); } @@ -622,7 +613,7 @@ void SendCoinsDialog::updateFeeSectionControls() ui->labelFeeEstimation ->setEnabled(ui->radioSmartFee->isChecked()); ui->checkBoxMinimumFee ->setEnabled(ui->radioCustomFee->isChecked()); ui->labelMinFeeWarning ->setEnabled(ui->radioCustomFee->isChecked()); - ui->radioCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked()); + ui->labelCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked()); ui->customFee ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked()); } @@ -634,8 +625,7 @@ void SendCoinsDialog::updateFeeMinimizedLabel() if (ui->radioSmartFee->isChecked()) ui->labelFeeMinimized->setText(ui->labelSmartFee->text()); else { - ui->labelFeeMinimized->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), ui->customFee->value()) + - ((ui->radioCustomPerKilobyte->isChecked()) ? "/kB" : "")); + ui->labelFeeMinimized->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), ui->customFee->value()) + "/kB"); } } From e9e9391083721ca9733cc00a1907384f83b6435e Mon Sep 17 00:00:00 2001 From: John Newbery Date: Fri, 15 Sep 2017 14:36:12 -0400 Subject: [PATCH 246/382] [tests] Check connectivity before sending in assumevalid.py assumevalid.py would try to send over a closed P2P connection in a loop, hitting the following failure many times: TestFramework.mininode (ERROR): Cannot send message. No connection to node! The test still passes, but this is a lot of noise in the test log. Just check that the connection is open before trying to send. --- test/functional/assumevalid.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/functional/assumevalid.py b/test/functional/assumevalid.py index beaf8c705..65685c48b 100755 --- a/test/functional/assumevalid.py +++ b/test/functional/assumevalid.py @@ -68,6 +68,8 @@ def setup_network(self): def send_blocks_until_disconnected(self, node): """Keep sending blocks to the node until we're disconnected.""" for i in range(len(self.blocks)): + if not node.connection: + break try: node.send_message(msg_block(self.blocks[i])) except IOError as e: From 2a07f878a80003f2142d5052d015a9ac81a3a6bc Mon Sep 17 00:00:00 2001 From: Dan Raviv Date: Sat, 16 Sep 2017 13:06:05 +0300 Subject: [PATCH 247/382] Refactor: Modernize disallowed copy constructors/assignment Use C++11's better capability of expressing an interface of a non-copyable class by publicly deleting its copy ctor and assignment operator instead of just declaring them private. --- src/coins.h | 10 +++++----- src/net.h | 6 ++---- src/streams.h | 16 ++++++++-------- src/support/lockedpool.h | 12 ++++++------ src/txdb.h | 8 ++++---- src/wallet/db.h | 7 +++---- src/wallet/walletdb.h | 5 ++--- 7 files changed, 30 insertions(+), 34 deletions(-) diff --git a/src/coins.h b/src/coins.h index efb5ce869..181b2fd4b 100644 --- a/src/coins.h +++ b/src/coins.h @@ -214,6 +214,11 @@ class CCoinsViewCache : public CCoinsViewBacked public: CCoinsViewCache(CCoinsView *baseIn); + /** + * By deleting the copy constructor, we prevent accidentally using it when one intends to create a cache on top of a base cache. + */ + CCoinsViewCache(const CCoinsViewCache &) = delete; + // Standard CCoinsView methods bool GetCoin(const COutPoint &outpoint, Coin &coin) const override; bool HaveCoin(const COutPoint &outpoint) const override; @@ -290,11 +295,6 @@ class CCoinsViewCache : public CCoinsViewBacked private: CCoinsMap::iterator FetchCoin(const COutPoint &outpoint) const; - - /** - * By making the copy constructor private, we prevent accidentally using it when one intends to create a cache on top of a base cache. - */ - CCoinsViewCache(const CCoinsViewCache &); }; //! Utility function to add all of a transaction's outputs to a cache. diff --git a/src/net.h b/src/net.h index ca2433aa5..cfc383ced 100644 --- a/src/net.h +++ b/src/net.h @@ -702,13 +702,11 @@ class CNode CNode(NodeId id, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress &addrBindIn, const std::string &addrNameIn = "", bool fInboundIn = false); ~CNode(); + CNode(const CNode&) = delete; + CNode& operator=(const CNode&) = delete; private: - CNode(const CNode&); - void operator=(const CNode&); const NodeId id; - - const uint64_t nLocalHostNonce; // Services offered to this peer const ServiceFlags nLocalServices; diff --git a/src/streams.h b/src/streams.h index 159847279..9a3badea5 100644 --- a/src/streams.h +++ b/src/streams.h @@ -455,10 +455,6 @@ class CDataStream class CAutoFile { private: - // Disallow copies - CAutoFile(const CAutoFile&); - CAutoFile& operator=(const CAutoFile&); - const int nType; const int nVersion; @@ -475,6 +471,10 @@ class CAutoFile fclose(); } + // Disallow copies + CAutoFile(const CAutoFile&) = delete; + CAutoFile& operator=(const CAutoFile&) = delete; + void fclose() { if (file) { @@ -564,10 +564,6 @@ class CAutoFile class CBufferedFile { private: - // Disallow copies - CBufferedFile(const CBufferedFile&); - CBufferedFile& operator=(const CBufferedFile&); - const int nType; const int nVersion; @@ -609,6 +605,10 @@ class CBufferedFile fclose(); } + // Disallow copies + CBufferedFile(const CBufferedFile&) = delete; + CBufferedFile& operator=(const CBufferedFile&) = delete; + int GetVersion() const { return nVersion; } int GetType() const { return nType; } diff --git a/src/support/lockedpool.h b/src/support/lockedpool.h index cecbdec1a..834f0371e 100644 --- a/src/support/lockedpool.h +++ b/src/support/lockedpool.h @@ -50,6 +50,9 @@ class Arena Arena(void *base, size_t size, size_t alignment); virtual ~Arena(); + Arena(const Arena& other) = delete; // non construction-copyable + Arena& operator=(const Arena&) = delete; // non copyable + /** Memory statistics. */ struct Stats { @@ -85,9 +88,6 @@ class Arena */ bool addressInArena(void *ptr) const { return ptr >= base && ptr < end; } private: - Arena(const Arena& other) = delete; // non construction-copyable - Arena& operator=(const Arena&) = delete; // non copyable - /** Map of chunk address to chunk information. This class makes use of the * sorted order to merge previous and next chunks during deallocation. */ @@ -153,6 +153,9 @@ class LockedPool explicit LockedPool(std::unique_ptr allocator, LockingFailed_Callback lf_cb_in = nullptr); ~LockedPool(); + LockedPool(const LockedPool& other) = delete; // non construction-copyable + LockedPool& operator=(const LockedPool&) = delete; // non copyable + /** Allocate size bytes from this arena. * Returns pointer on success, or 0 if memory is full or * the application tried to allocate 0 bytes. @@ -168,9 +171,6 @@ class LockedPool /** Get pool usage statistics */ Stats stats() const; private: - LockedPool(const LockedPool& other) = delete; // non construction-copyable - LockedPool& operator=(const LockedPool&) = delete; // non copyable - std::unique_ptr allocator; /** Create an arena from locked pages */ diff --git a/src/txdb.h b/src/txdb.h index d1cd5a425..c254ba91c 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -110,10 +110,10 @@ class CBlockTreeDB : public CDBWrapper { public: explicit CBlockTreeDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); -private: - CBlockTreeDB(const CBlockTreeDB&); - void operator=(const CBlockTreeDB&); -public: + + CBlockTreeDB(const CBlockTreeDB&) = delete; + CBlockTreeDB& operator=(const CBlockTreeDB&) = delete; + bool WriteBatchSync(const std::vector >& fileInfo, int nLastFile, const std::vector& blockinfo); bool ReadBlockFileInfo(int nFile, CBlockFileInfo &fileinfo); bool ReadLastBlockFile(int &nFile); diff --git a/src/wallet/db.h b/src/wallet/db.h index 6f3cfe955..14283ac8f 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -156,6 +156,9 @@ class CDB explicit CDB(CWalletDBWrapper& dbw, const char* pszMode = "r+", bool fFlushOnCloseIn=true); ~CDB() { Close(); } + CDB(const CDB&) = delete; + CDB& operator=(const CDB&) = delete; + void Flush(); void Close(); static bool Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename); @@ -168,10 +171,6 @@ class CDB /* verifies the database file */ static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc); -private: - CDB(const CDB&); - void operator=(const CDB&); - public: template bool Read(const K& key, T& value) diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 4f8ea185d..3a146179a 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -167,6 +167,8 @@ class CWalletDB m_dbw(dbw) { } + CWalletDB(const CWalletDB&) = delete; + CWalletDB& operator=(const CWalletDB&) = delete; bool WriteName(const std::string& strAddress, const std::string& strName); bool EraseName(const std::string& strAddress); @@ -244,9 +246,6 @@ class CWalletDB private: CDB batch; CWalletDBWrapper& m_dbw; - - CWalletDB(const CWalletDB&); - void operator=(const CWalletDB&); }; //! Compacts BDB state so that wallet.dat is self-contained (if there are changes) From 7b137acedd5e2ff3fe5395a927c546a528b0cac6 Mon Sep 17 00:00:00 2001 From: Lucas Betschart Date: Sun, 6 Aug 2017 22:55:14 +0200 Subject: [PATCH 248/382] [Qt] Add delay before filtering transactions Fixes 3141 --- src/qt/transactionview.cpp | 27 ++++++++++++++++++++------- src/qt/transactionview.h | 4 ++-- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 53c38da9d..39dfdb587 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -112,6 +113,17 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa amountWidget->setValidator(new QDoubleValidator(0, 1e20, 8, this)); hlayout->addWidget(amountWidget); + // Delay before filtering transactions in ms + static const int input_filter_delay = 200; + + QTimer* amount_typing_delay = new QTimer(this); + amount_typing_delay->setSingleShot(true); + amount_typing_delay->setInterval(input_filter_delay); + + QTimer* prefix_typing_delay = new QTimer(this); + prefix_typing_delay->setSingleShot(true); + prefix_typing_delay->setInterval(input_filter_delay); + QVBoxLayout *vlayout = new QVBoxLayout(this); vlayout->setContentsMargins(0,0,0,0); vlayout->setSpacing(0); @@ -173,8 +185,10 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa connect(dateWidget, SIGNAL(activated(int)), this, SLOT(chooseDate(int))); connect(typeWidget, SIGNAL(activated(int)), this, SLOT(chooseType(int))); connect(watchOnlyWidget, SIGNAL(activated(int)), this, SLOT(chooseWatchonly(int))); - connect(addressWidget, SIGNAL(textChanged(QString)), this, SLOT(changedPrefix(QString))); - connect(amountWidget, SIGNAL(textChanged(QString)), this, SLOT(changedAmount(QString))); + connect(amountWidget, SIGNAL(textChanged(QString)), amount_typing_delay, SLOT(start())); + connect(amount_typing_delay, SIGNAL(timeout()), this, SLOT(changedAmount())); + connect(addressWidget, SIGNAL(textChanged(QString)), prefix_typing_delay, SLOT(start())); + connect(prefix_typing_delay, SIGNAL(timeout()), this, SLOT(changedPrefix())); connect(view, SIGNAL(doubleClicked(QModelIndex)), this, SIGNAL(doubleClicked(QModelIndex))); connect(view, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint))); @@ -312,20 +326,19 @@ void TransactionView::chooseWatchonly(int idx) (TransactionFilterProxy::WatchOnlyFilter)watchOnlyWidget->itemData(idx).toInt()); } -void TransactionView::changedPrefix(const QString &prefix) +void TransactionView::changedPrefix() { if(!transactionProxyModel) return; - transactionProxyModel->setAddressPrefix(prefix); + transactionProxyModel->setAddressPrefix(addressWidget->text()); } -void TransactionView::changedAmount(const QString &amount) +void TransactionView::changedAmount() { if(!transactionProxyModel) return; CAmount amount_parsed = 0; - if(BitcoinUnits::parse(model->getOptionsModel()->getDisplayUnit(), amount, &amount_parsed)) - { + if (BitcoinUnits::parse(model->getOptionsModel()->getDisplayUnit(), amountWidget->text(), &amount_parsed)) { transactionProxyModel->setMinAmount(amount_parsed); } else diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index 52e57cae4..5b4cfd4a8 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -112,8 +112,8 @@ public Q_SLOTS: void chooseDate(int idx); void chooseType(int idx); void chooseWatchonly(int idx); - void changedPrefix(const QString &prefix); - void changedAmount(const QString &amount); + void changedAmount(); + void changedPrefix(); void exportClicked(); void focusTransaction(const QModelIndex&); From 2416dd7cc94e765efbbf9069dbf0f6fb71404ebb Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Thu, 22 Jun 2017 14:01:04 -0400 Subject: [PATCH 249/382] net: separate resolving and conecting ConnectSocketByName handled resolves as necessary, obscuring the connection process. With them separated, each can be handled asynchronously. Also, since proxies must be considered now anyway, go ahead and eliminate the ConnectSocket wrapper and use ConnectSocketDirectly... directly. --- src/net.cpp | 58 +++++++++++++++++++++++++++++++++---------------- src/netbase.cpp | 4 ++-- src/netbase.h | 3 +++ 3 files changed, 44 insertions(+), 21 deletions(-) diff --git a/src/net.cpp b/src/net.cpp index 587c9e511..a9f26fb56 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -380,19 +380,16 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo pszDest ? pszDest : addrConnect.ToString(), pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0); - // Connect - SOCKET hSocket; - bool proxyConnectionFailed = false; - if (pszDest ? ConnectSocketByName(addrConnect, hSocket, pszDest, Params().GetDefaultPort(), nConnectTimeout, &proxyConnectionFailed) : - ConnectSocket(addrConnect, hSocket, nConnectTimeout, &proxyConnectionFailed)) - { - if (!IsSelectableSocket(hSocket)) { - LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\n"); - CloseSocket(hSocket); - return nullptr; - } - - if (pszDest && addrConnect.IsValid()) { + // Resolve + const int default_port = Params().GetDefaultPort(); + if (pszDest) { + std::vector resolved; + if (Lookup(pszDest, resolved, default_port, fNameLookup && !HaveNameProxy(), 256) && !resolved.empty()) { + addrConnect = CAddress(resolved[GetRand(resolved.size())], NODE_NONE); + if (!addrConnect.IsValid()) { + LogPrint(BCLog::NET, "Resolver returned invalid address %s for %s", addrConnect.ToString(), pszDest); + return nullptr; + } // It is possible that we already have a connection to the IP/port pszDest resolved to. // 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 @@ -402,13 +399,40 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo if (pnode) { pnode->MaybeSetAddrName(std::string(pszDest)); - CloseSocket(hSocket); LogPrintf("Failed to open new connection, already connected\n"); return nullptr; } } + } - addrman.Attempt(addrConnect, fCountFailure); + // Connect + bool connected = false; + SOCKET hSocket; + proxyType proxy; + if (addrConnect.IsValid()) { + bool proxyConnectionFailed = false; + + if (GetProxy(addrConnect.GetNetwork(), proxy)) + connected = ConnectThroughProxy(proxy, addrConnect.ToStringIP(), addrConnect.GetPort(), hSocket, nConnectTimeout, &proxyConnectionFailed); + else // no proxy needed (none set for target network) + connected = ConnectSocketDirectly(addrConnect, hSocket, nConnectTimeout); + if (!proxyConnectionFailed) { + // If a connection to the node was attempted, and failure (if any) is not caused by a problem connecting to + // the proxy, mark this as an attempt. + addrman.Attempt(addrConnect, fCountFailure); + } + } else if (pszDest && GetNameProxy(proxy)) { + std::string host; + int port = default_port; + SplitHostPort(std::string(pszDest), port, host); + connected = ConnectThroughProxy(proxy, host, port, hSocket, nConnectTimeout, nullptr); + } + if (connected) { + if (!IsSelectableSocket(hSocket)) { + LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\n"); + CloseSocket(hSocket); + return nullptr; + } // Add node NodeId id = GetNewNodeId(); @@ -419,10 +443,6 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo pnode->AddRef(); return pnode; - } else if (!proxyConnectionFailed) { - // If connecting to the node failed, and failure is not caused by a problem connecting to - // the proxy, mark this as an attempt. - addrman.Attempt(addrConnect, fCountFailure); } return nullptr; diff --git a/src/netbase.cpp b/src/netbase.cpp index 05f9f6961..d4a91da1f 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -399,7 +399,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials return true; } -bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRet, int nTimeout) +bool ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRet, int nTimeout) { hSocketRet = INVALID_SOCKET; @@ -534,7 +534,7 @@ bool IsProxy(const CNetAddr &addr) { return false; } -static bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed) +bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed) { SOCKET hSocket = INVALID_SOCKET; // first connect to proxy server diff --git a/src/netbase.h b/src/netbase.h index 6572f0a12..50425f0f8 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -44,6 +44,7 @@ bool GetProxy(enum Network net, proxyType &proxyInfoOut); bool IsProxy(const CNetAddr &addr); bool SetNameProxy(const proxyType &addrProxy); bool HaveNameProxy(); +bool GetNameProxy(proxyType &nameProxyOut); bool LookupHost(const char *pszName, std::vector& vIP, unsigned int nMaxSolutions, bool fAllowLookup); bool LookupHost(const char *pszName, CNetAddr& addr, bool fAllowLookup); bool Lookup(const char *pszName, CService& addr, int portDefault, bool fAllowLookup); @@ -52,6 +53,8 @@ CService LookupNumeric(const char *pszName, int portDefault = 0); bool LookupSubNet(const char *pszName, CSubNet& subnet); bool ConnectSocket(const CService &addr, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed = nullptr); bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, int portDefault, int nTimeout, bool *outProxyConnectionFailed = nullptr); +bool ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRet, int nTimeout); +bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed); /** Return readable error string for a network error code */ std::string NetworkErrorString(int err); /** Close socket and set hSocket to INVALID_SOCKET */ From 45fd75453e59868471572907db237630ca2b7afd Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Fri, 23 Jun 2017 12:29:50 -0400 Subject: [PATCH 250/382] net: remove now-superfluous numeric resolve This was added in order to help OpenNetworkConnection avoid creating a connection that it would end up aborting. It was necessary because resolving was done as part of the connection process. Now that resolving is separated from connecting, this case is detected before the connection is attempted. --- src/net.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/net.cpp b/src/net.cpp index a9f26fb56..64b8fe726 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1934,11 +1934,9 @@ void CConnman::ThreadOpenAddedConnections() // the addednodeinfo state might change. break; } - // If strAddedNode is an IP/port, decode it immediately, so - // OpenNetworkConnection can detect existing connections to that IP/port. tried = true; - CService service(LookupNumeric(info.strAddedNode.c_str(), Params().GetDefaultPort())); - OpenNetworkConnection(CAddress(service, NODE_NONE), false, &grant, info.strAddedNode.c_str(), false, false, true); + CAddress addr(CService(), NODE_NONE); + OpenNetworkConnection(addr, false, &grant, info.strAddedNode.c_str(), false, false, true); if (!interruptNet.sleep_for(std::chrono::milliseconds(500))) return; } From b887676e1b86ce03181b5876cf6203d617750d0a Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Fri, 23 Jun 2017 11:53:45 -0400 Subject: [PATCH 251/382] net: remove now-unused functions --- src/netbase.cpp | 41 ----------------------------------------- src/netbase.h | 2 -- 2 files changed, 43 deletions(-) diff --git a/src/netbase.cpp b/src/netbase.cpp index d4a91da1f..9212be981 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -558,47 +558,6 @@ bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int hSocketRet = hSocket; return true; } - -bool ConnectSocket(const CService &addrDest, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed) -{ - proxyType proxy; - if (outProxyConnectionFailed) - *outProxyConnectionFailed = false; - - if (GetProxy(addrDest.GetNetwork(), proxy)) - return ConnectThroughProxy(proxy, addrDest.ToStringIP(), addrDest.GetPort(), hSocketRet, nTimeout, outProxyConnectionFailed); - else // no proxy needed (none set for target network) - return ConnectSocketDirectly(addrDest, hSocketRet, nTimeout); -} - -bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, int portDefault, int nTimeout, bool *outProxyConnectionFailed) -{ - std::string strDest; - int port = portDefault; - - if (outProxyConnectionFailed) - *outProxyConnectionFailed = false; - - SplitHostPort(std::string(pszDest), port, strDest); - - proxyType proxy; - GetNameProxy(proxy); - - std::vector addrResolved; - if (Lookup(strDest.c_str(), addrResolved, port, fNameLookup && !HaveNameProxy(), 256)) { - if (addrResolved.size() > 0) { - addr = addrResolved[GetRand(addrResolved.size())]; - return ConnectSocket(addr, hSocketRet, nTimeout); - } - } - - addr = CService(); - - if (!HaveNameProxy()) - return false; - return ConnectThroughProxy(proxy, strDest, port, hSocketRet, nTimeout, outProxyConnectionFailed); -} - bool LookupSubNet(const char* pszName, CSubNet& ret) { std::string strSubnet(pszName); diff --git a/src/netbase.h b/src/netbase.h index 50425f0f8..e7d7bcb37 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -51,8 +51,6 @@ bool Lookup(const char *pszName, CService& addr, int portDefault, bool fAllowLoo bool Lookup(const char *pszName, std::vector& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions); CService LookupNumeric(const char *pszName, int portDefault = 0); bool LookupSubNet(const char *pszName, CSubNet& subnet); -bool ConnectSocket(const CService &addr, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed = nullptr); -bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, int portDefault, int nTimeout, bool *outProxyConnectionFailed = nullptr); bool ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRet, int nTimeout); bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed); /** Return readable error string for a network error code */ From 22fd04beb9e7ee4c8576a2f43796200faa780938 Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Mon, 18 Sep 2017 23:40:38 +0000 Subject: [PATCH 252/382] Remove nBlockMaxSize from miner opt struct as it is no longer used. --- src/miner.h | 1 - src/test/miner_tests.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/src/miner.h b/src/miner.h index 683f4fe08..db165e71c 100644 --- a/src/miner.h +++ b/src/miner.h @@ -158,7 +158,6 @@ class BlockAssembler struct Options { Options(); size_t nBlockMaxWeight; - size_t nBlockMaxSize; CFeeRate blockMinFeeRate; }; diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 9fa9a8509..41e0626eb 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -32,7 +32,6 @@ static BlockAssembler AssemblerForTest(const CChainParams& params) { BlockAssembler::Options options; options.nBlockMaxWeight = MAX_BLOCK_WEIGHT; - options.nBlockMaxSize = MAX_BLOCK_SERIALIZED_SIZE; options.blockMinFeeRate = blockMinFeeRate; return BlockAssembler(params, options); } From d3677ab757ea57b21d8aed24d5bc3c4bfe9bb68c Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Wed, 7 Jun 2017 17:08:32 -0700 Subject: [PATCH 253/382] Tests for zmqpubrawtx and zmqpubrawblock --- test/functional/test_framework/util.py | 8 ++++ test/functional/zmq_test.py | 52 +++++++++++++++++++++++--- 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index a14cda07d..f819edcc3 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -7,6 +7,7 @@ from base64 import b64encode from binascii import hexlify, unhexlify from decimal import Decimal, ROUND_DOWN +import hashlib import json import logging import os @@ -148,6 +149,13 @@ def count_bytes(hex_string): def bytes_to_hex_str(byte_str): return hexlify(byte_str).decode('ascii') +def hash256(byte_str): + sha256 = hashlib.sha256() + sha256.update(byte_str) + sha256d = hashlib.sha256() + sha256d.update(sha256.digest()) + return sha256d.digest()[::-1] + def hex_str_to_bytes(hex_str): return unhexlify(hex_str.encode('ascii')) diff --git a/test/functional/zmq_test.py b/test/functional/zmq_test.py index 3f2668ee8..382ef5bae 100755 --- a/test/functional/zmq_test.py +++ b/test/functional/zmq_test.py @@ -10,7 +10,8 @@ from test_framework.test_framework import BitcoinTestFramework, SkipTest from test_framework.util import (assert_equal, bytes_to_hex_str, - ) + hash256, + ) class ZMQTest (BitcoinTestFramework): def set_test_params(self): @@ -37,9 +38,12 @@ def setup_nodes(self): self.zmqSubSocket.set(zmq.RCVTIMEO, 60000) self.zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashblock") self.zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashtx") + self.zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"rawblock") + self.zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"rawtx") ip_address = "tcp://127.0.0.1:28332" self.zmqSubSocket.connect(ip_address) - self.extra_args = [['-zmqpubhashtx=%s' % ip_address, '-zmqpubhashblock=%s' % ip_address], []] + self.extra_args = [['-zmqpubhashblock=%s' % ip_address, '-zmqpubhashtx=%s' % ip_address, + '-zmqpubrawblock=%s' % ip_address, '-zmqpubrawtx=%s' % ip_address], []] self.add_nodes(self.num_nodes, self.extra_args) self.start_nodes() @@ -59,28 +63,51 @@ def _zmq_test(self): msg = self.zmqSubSocket.recv_multipart() topic = msg[0] assert_equal(topic, b"hashtx") - body = msg[1] + txhash = msg[1] msgSequence = struct.unpack(' Date: Wed, 31 May 2017 14:22:56 +0900 Subject: [PATCH 254/382] [Tests] Add Qt GUI tests to Overview and ReceiveCoin Page --- src/qt/test/wallettests.cpp | 73 ++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 2 deletions(-) diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index 5031d7651..eeae58bd0 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -13,6 +13,10 @@ #include "test/test_bitcoin.h" #include "validation.h" #include "wallet/wallet.h" +#include "qt/overviewpage.h" +#include "qt/receivecoinsdialog.h" +#include "qt/recentrequeststablemodel.h" +#include "qt/receiverequestdialog.h" #include #include @@ -21,6 +25,9 @@ #include #include #include +#include +#include +#include namespace { @@ -140,7 +147,7 @@ void BumpFee(TransactionView& view, const uint256& txid, bool expectDisabled, st // src/qt/test/test_bitcoin-qt -platform xcb # Linux // src/qt/test/test_bitcoin-qt -platform windows # Windows // src/qt/test/test_bitcoin-qt -platform cocoa # macOS -void TestSendCoins() +void TestGUI() { // Set up wallet and chain with 105 blocks (5 mature blocks for spending). TestChain100Setup test; @@ -184,6 +191,68 @@ void TestSendCoins() BumpFee(transactionView, txid2, false /* expect disabled */, {} /* expected error */, false /* cancel */); BumpFee(transactionView, txid2, true /* expect disabled */, "already bumped" /* expected error */, false /* cancel */); + // Check current balance on OverviewPage + OverviewPage overviewPage(platformStyle.get()); + overviewPage.setWalletModel(&walletModel); + QLabel* balanceLabel = overviewPage.findChild("labelBalance"); + QString balanceText = balanceLabel->text(); + int unit = walletModel.getOptionsModel()->getDisplayUnit(); + CAmount balance = walletModel.getBalance(); + QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::separatorAlways); + QCOMPARE(balanceText, balanceComparison); + + // Check Request Payment button + ReceiveCoinsDialog receiveCoinsDialog(platformStyle.get()); + receiveCoinsDialog.setModel(&walletModel); + RecentRequestsTableModel* requestTableModel = walletModel.getRecentRequestsTableModel(); + + // Label input + QLineEdit* labelInput = receiveCoinsDialog.findChild("reqLabel"); + labelInput->setText("TEST_LABEL_1"); + + // Amount input + BitcoinAmountField* amountInput = receiveCoinsDialog.findChild("reqAmount"); + amountInput->setValue(1); + + // Message input + QLineEdit* messageInput = receiveCoinsDialog.findChild("reqMessage"); + messageInput->setText("TEST_MESSAGE_1"); + int initialRowCount = requestTableModel->rowCount({}); + QPushButton* requestPaymentButton = receiveCoinsDialog.findChild("receiveButton"); + requestPaymentButton->click(); + for (QWidget* widget : QApplication::topLevelWidgets()) { + if (widget->inherits("ReceiveRequestDialog")) { + ReceiveRequestDialog* receiveRequestDialog = qobject_cast(widget); + QTextEdit* rlist = receiveRequestDialog->QObject::findChild("outUri"); + QString paymentText = rlist->toPlainText(); + QStringList paymentTextList = paymentText.split('\n'); + QCOMPARE(paymentTextList.at(0), QString("Payment information")); + QVERIFY(paymentTextList.at(1).indexOf(QString("URI: bitcoin:")) != -1); + QVERIFY(paymentTextList.at(2).indexOf(QString("Address:")) != -1); + QCOMPARE(paymentTextList.at(3), QString("Amount: 0.00000001 ") + QString::fromStdString(CURRENCY_UNIT)); + QCOMPARE(paymentTextList.at(4), QString("Label: TEST_LABEL_1")); + QCOMPARE(paymentTextList.at(5), QString("Message: TEST_MESSAGE_1")); + } + } + + // Clear button + QPushButton* clearButton = receiveCoinsDialog.findChild("clearButton"); + clearButton->click(); + QCOMPARE(labelInput->text(), QString("")); + QCOMPARE(amountInput->value(), CAmount(0)); + QCOMPARE(messageInput->text(), QString("")); + + // Check addition to history + int currentRowCount = requestTableModel->rowCount({}); + QCOMPARE(currentRowCount, initialRowCount+1); + + // Check Remove button + QTableView* table = receiveCoinsDialog.findChild("recentRequestsView"); + table->selectRow(currentRowCount-1); + QPushButton* removeRequestButton = receiveCoinsDialog.findChild("removeRequestButton"); + removeRequestButton->click(); + QCOMPARE(requestTableModel->rowCount({}), currentRowCount-1); + bitdb.Flush(true); bitdb.Reset(); } @@ -192,5 +261,5 @@ void TestSendCoins() void WalletTests::walletTests() { - TestSendCoins(); + TestGUI(); } From 1ab1b687cb5870258add2b08169cd974f08ed032 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 19 Sep 2017 15:03:31 +0200 Subject: [PATCH 255/382] doc: Add release notes for 0.15.0.1 Tree-SHA512: 488ee6fda9f9d2eb1c4d78f37d7b21c019ca70dc1899ef7dd275b5ef615fb5afebd6805147649753dd3497e2e98bfb2c601d8c3cffed04781652f84c9c0d32e5 --- doc/release-notes/release-notes-0.15.0.1.md | 87 +++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 doc/release-notes/release-notes-0.15.0.1.md diff --git a/doc/release-notes/release-notes-0.15.0.1.md b/doc/release-notes/release-notes-0.15.0.1.md new file mode 100644 index 000000000..53ab02cbb --- /dev/null +++ b/doc/release-notes/release-notes-0.15.0.1.md @@ -0,0 +1,87 @@ +Bitcoin Core version *0.15.0.1* is now available from: + + + +and + + + +This is a minor bug fix for 0.15.0. + +Please report bugs using the issue tracker at GitHub: + + + +To receive security and update notifications, please subscribe to: + + + +How to Upgrade +============== + +If you are running an older version, shut it down. Wait until it has completely +shut down (which might take a few minutes for older versions), then run the +installer (on Windows) or just copy over `/Applications/Bitcoin-Qt` (on Mac) +or `bitcoind`/`bitcoin-qt` (on Linux). + +The first time you run version 0.15.0 or higher, your chainstate database will +be converted to a new format, which will take anywhere from a few minutes to +half an hour, depending on the speed of your machine. + +The file format of `fee_estimates.dat` changed in version 0.15.0. Hence, a +downgrade from version 0.15.0 or upgrade to version 0.15.0 will cause all fee +estimates to be discarded. + +Note that the block database format also changed in version 0.8.0 and there is no +automatic upgrade code from before version 0.8 to version 0.15.0. Upgrading +directly from 0.7.x and earlier without redownloading the blockchain is not supported. +However, as usual, old wallet versions are still supported. + +Downgrading warning +------------------- + +The chainstate database for this release is not compatible with previous +releases, so if you run 0.15 and then decide to switch back to any +older version, you will need to run the old release with the `-reindex-chainstate` +option to rebuild the chainstate data structures in the old format. + +If your node has pruning enabled, this will entail re-downloading and +processing the entire blockchain. + +Compatibility +============== + +Bitcoin Core is extensively tested on multiple operating systems using +the Linux kernel, macOS 10.8+, and Windows Vista and later. Windows XP is not supported. + +Bitcoin Core should also work on most other Unix-like systems but is not +frequently tested on them. + +Notable changes +=============== + +GUI startup crash issue +------------------------- + +After upgrade to 0.15.0, some clients would crash at startup because a custom +fee setting was configured that no longer exists in the GUI. This is a minimal +patch to avoid this issue from occuring. + +0.15.0.1 Change log +==================== + +- #11332 `46c8d23` Fix possible crash with invalid nCustomFeeRadio in QSettings (achow101, TheBlueMatt) + +Also the manpages were updated, as this was forgotten for 0.15.0. + +Credits +======= + +Thanks to everyone who directly contributed to this release: + +- Andrew Chow +- Matt Corallo +- Jonas Schnelli +- Wladimir J. van der Laan + +As well as everyone that helped translating on [Transifex](https://www.transifex.com/projects/p/bitcoin/). From fd8f45fe88c7f34785ee0724b875cf14e8a29e07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= Date: Wed, 20 Sep 2017 15:00:28 +0100 Subject: [PATCH 256/382] [test] Add restart_node to BitcoinTestFramework --- test/functional/test_framework/test_framework.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index a53eb5179..381513ab9 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -273,6 +273,11 @@ def stop_nodes(self): # Wait for nodes to stop node.wait_until_stopped() + def restart_node(self, i, extra_args=None): + """Stop and start a test node""" + self.stop_node(i) + self.start_node(i, extra_args) + def assert_start_raises_init_error(self, i, extra_args=None, expected_msg=None): with tempfile.SpooledTemporaryFile(max_size=2**16) as log_stderr: try: From f6ffb143679aa7e89ad3c3342ceba5e5aaf71a0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= Date: Wed, 20 Sep 2017 00:45:39 +0100 Subject: [PATCH 257/382] [test] Add getblockchaininfo functional test --- test/functional/blockchain.py | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/test/functional/blockchain.py b/test/functional/blockchain.py index 50be9262e..63c56d0e9 100755 --- a/test/functional/blockchain.py +++ b/test/functional/blockchain.py @@ -33,9 +33,10 @@ class BlockchainTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 - self.extra_args = [['-stopatheight=207']] + self.extra_args = [['-stopatheight=207', '-prune=1']] def run_test(self): + self._test_getblockchaininfo() self._test_getchaintxstats() self._test_gettxoutsetinfo() self._test_getblockheader() @@ -44,6 +45,33 @@ def run_test(self): self._test_stopatheight() assert self.nodes[0].verifychain(4, 0) + def _test_getblockchaininfo(self): + self.log.info("Test getblockchaininfo") + + keys = [ + 'bestblockhash', + 'bip9_softforks', + 'blocks', + 'chain', + 'chainwork', + 'difficulty', + 'headers', + 'mediantime', + 'pruned', + 'softforks', + 'verificationprogress', + ] + res = self.nodes[0].getblockchaininfo() + # result should have pruneheight and default keys if pruning is enabled + assert_equal(sorted(res.keys()), sorted(['pruneheight'] + keys)) + # pruneheight should be greater or equal to 0 + assert res['pruneheight'] >= 0 + + self.restart_node(0, ['-stopatheight=207']) + res = self.nodes[0].getblockchaininfo() + # should have exact keys + assert_equal(sorted(res.keys()), keys) + def _test_getchaintxstats(self): chaintxstats = self.nodes[0].getchaintxstats(1) # 200 txs plus genesis tx From 832e0744cb8b1e1625cdb19b257f97316ac16a90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Thu, 11 Aug 2016 04:42:36 +0200 Subject: [PATCH 258/382] Optimization: Minimize the number of times it is checked that no money is created by individual transactions to 2 places (but call only once in each): - ConnectBlock ( before calculated fees per txs twice ) - AcceptToMemoryPoolWorker ( before called CheckTxInputs 4 times and calculated fees per tx one extra time ) Also call tx.GetValueOut() only once per call of CheckTxInputs (instead of 2) --- src/consensus/tx_verify.cpp | 24 +++++++++++------------- src/consensus/tx_verify.h | 5 ++++- src/txmempool.cpp | 8 +++++--- src/validation.cpp | 24 +++++++++++------------- 4 files changed, 31 insertions(+), 30 deletions(-) diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp index 25647ba6a..f2f85edad 100644 --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -205,16 +205,15 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe return true; } -bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight) +bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee) { - // This doesn't trigger the DoS code on purpose; if it did, it would make it easier - // for an attacker to attempt to split the network. + // are the actual inputs available? if (!inputs.HaveInputs(tx)) { - return state.Invalid(false, 0, "", "Inputs unavailable"); + return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-missingorspent", false, + strprintf("%s: inputs missing/spent", __func__)); } CAmount nValueIn = 0; - CAmount nFees = 0; for (unsigned int i = 0; i < tx.vin.size(); ++i) { const COutPoint &prevout = tx.vin[i].prevout; const Coin& coin = inputs.AccessCoin(prevout); @@ -234,19 +233,18 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, c } } - if (nValueIn < tx.GetValueOut()) { + const CAmount value_out = tx.GetValueOut(); + if (nValueIn < value_out) { return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false, - strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(tx.GetValueOut()))); + strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(value_out))); } // Tally transaction fees - CAmount nTxFee = nValueIn - tx.GetValueOut(); - if (nTxFee < 0) { - return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-negative"); - } - nFees += nTxFee; - if (!MoneyRange(nFees)) { + const CAmount txfee_aux = nValueIn - value_out; + if (!MoneyRange(txfee_aux)) { return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange"); } + + txfee = txfee_aux; return true; } diff --git a/src/consensus/tx_verify.h b/src/consensus/tx_verify.h index d46d3294c..288892462 100644 --- a/src/consensus/tx_verify.h +++ b/src/consensus/tx_verify.h @@ -5,6 +5,8 @@ #ifndef BITCOIN_CONSENSUS_TX_VERIFY_H #define BITCOIN_CONSENSUS_TX_VERIFY_H +#include "amount.h" + #include #include @@ -22,9 +24,10 @@ namespace Consensus { /** * Check whether all inputs of this transaction are valid (no double spends and amounts) * This does not modify the UTXO set. This does not check scripts and sigs. + * @param[out] txfee Set to the transaction fee if successful. * Preconditions: tx.IsCoinBase() is false. */ -bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight); +bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee); } // namespace Consensus /** Auxiliary functions for transaction validation (ideally should not be exposed) */ diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 8deb703d2..ef955a17e 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -625,7 +625,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const uint64_t innerUsage = 0; CCoinsViewCache mempoolDuplicate(const_cast(pcoins)); - const int64_t nSpendHeight = GetSpendHeight(mempoolDuplicate); + const int64_t spendheight = GetSpendHeight(mempoolDuplicate); LOCK(cs); std::list waitingOnDependants; @@ -705,8 +705,9 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const waitingOnDependants.push_back(&(*it)); else { CValidationState state; + CAmount txfee = 0; bool fCheckResult = tx.IsCoinBase() || - Consensus::CheckTxInputs(tx, state, mempoolDuplicate, nSpendHeight); + Consensus::CheckTxInputs(tx, state, mempoolDuplicate, spendheight, txfee); assert(fCheckResult); UpdateCoins(tx, mempoolDuplicate, 1000000); } @@ -721,8 +722,9 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const stepsSinceLastRemove++; assert(stepsSinceLastRemove < waitingOnDependants.size()); } else { + CAmount txfee = 0; bool fCheckResult = entry->GetTx().IsCoinBase() || - Consensus::CheckTxInputs(entry->GetTx(), state, mempoolDuplicate, nSpendHeight); + Consensus::CheckTxInputs(entry->GetTx(), state, mempoolDuplicate, spendheight, txfee); assert(fCheckResult); UpdateCoins(entry->GetTx(), mempoolDuplicate, 1000000); stepsSinceLastRemove = 0; diff --git a/src/validation.cpp b/src/validation.cpp index eb6ea42b6..8899bd2cd 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -484,7 +484,6 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool CCoinsView dummy; CCoinsViewCache view(&dummy); - CAmount nValueIn = 0; LockPoints lp; { LOCK(pool.cs); @@ -519,8 +518,6 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool // Bring the best block into scope view.GetBestBlock(); - nValueIn = view.GetValueIn(tx); - // we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool view.SetBackend(dummy); @@ -531,6 +528,12 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool // CoinsViewCache instead of create its own if (!CheckSequenceLocks(tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp)) return state.DoS(0, false, REJECT_NONSTANDARD, "non-BIP68-final"); + + } // end LOCK(pool.cs) + + CAmount nFees = 0; + if (!Consensus::CheckTxInputs(tx, state, view, GetSpendHeight(view), nFees)) { + return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state)); } // Check for non-standard pay-to-script-hash in inputs @@ -543,8 +546,6 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool int64_t nSigOpsCost = GetTransactionSigOpCost(tx, view, STANDARD_SCRIPT_VERIFY_FLAGS); - CAmount nValueOut = tx.GetValueOut(); - CAmount nFees = nValueIn-nValueOut; // nModifiedFees includes any fee deltas from PrioritiseTransaction CAmount nModifiedFees = nFees; pool.ApplyDelta(hash, nModifiedFees); @@ -1161,9 +1162,6 @@ static bool CheckInputs(const CTransaction& tx, CValidationState &state, const C { if (!tx.IsCoinBase()) { - if (!Consensus::CheckTxInputs(tx, state, inputs, GetSpendHeight(inputs))) - return false; - if (pvChecks) pvChecks->reserve(tx.vin.size()); @@ -1635,9 +1633,11 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd if (!tx.IsCoinBase()) { - if (!view.HaveInputs(tx)) - return state.DoS(100, error("ConnectBlock(): inputs missing/spent"), - REJECT_INVALID, "bad-txns-inputs-missingorspent"); + CAmount txfee = 0; + if (!Consensus::CheckTxInputs(tx, state, view, pindex->nHeight, txfee)) { + return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state)); + } + nFees += txfee; // Check that transaction is BIP68 final // BIP68 lock checks (as opposed to nLockTime checks) must @@ -1665,8 +1665,6 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd txdata.emplace_back(tx); if (!tx.IsCoinBase()) { - nFees += view.GetValueIn(tx)-tx.GetValueOut(); - std::vector vChecks; bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */ if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, txdata[i], nScriptCheckThreads ? &vChecks : NULL)) From 3e8c91629e646f1ace700fe394f5bbef88b9cd31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Tue, 18 Apr 2017 22:49:47 +0200 Subject: [PATCH 259/382] Introduce CheckInputsAndUpdateCoins static wrapper in txmempool.cpp --- src/txmempool.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/txmempool.cpp b/src/txmempool.cpp index ef955a17e..508658e5d 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -611,6 +611,15 @@ void CTxMemPool::clear() _clear(); } +static void CheckInputsAndUpdateCoins(const CTransaction& tx, CCoinsViewCache& mempoolDuplicate, const int64_t spendheight) +{ + CValidationState state; + CAmount txfee = 0; + bool fCheckResult = tx.IsCoinBase() || Consensus::CheckTxInputs(tx, state, mempoolDuplicate, spendheight, txfee); + assert(fCheckResult); + UpdateCoins(tx, mempoolDuplicate, 1000000); +} + void CTxMemPool::check(const CCoinsViewCache *pcoins) const { if (nCheckFrequency == 0) @@ -704,12 +713,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const if (fDependsWait) waitingOnDependants.push_back(&(*it)); else { - CValidationState state; - CAmount txfee = 0; - bool fCheckResult = tx.IsCoinBase() || - Consensus::CheckTxInputs(tx, state, mempoolDuplicate, spendheight, txfee); - assert(fCheckResult); - UpdateCoins(tx, mempoolDuplicate, 1000000); + CheckInputsAndUpdateCoins(tx, mempoolDuplicate, spendheight); } } unsigned int stepsSinceLastRemove = 0; @@ -722,11 +726,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const stepsSinceLastRemove++; assert(stepsSinceLastRemove < waitingOnDependants.size()); } else { - CAmount txfee = 0; - bool fCheckResult = entry->GetTx().IsCoinBase() || - Consensus::CheckTxInputs(entry->GetTx(), state, mempoolDuplicate, spendheight, txfee); - assert(fCheckResult); - UpdateCoins(entry->GetTx(), mempoolDuplicate, 1000000); + CheckInputsAndUpdateCoins(entry->GetTx(), mempoolDuplicate, spendheight); stepsSinceLastRemove = 0; } } From 4e955c58e13cfe089208f6b23b195d395ad99baa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Tue, 4 Jul 2017 17:45:46 +0200 Subject: [PATCH 260/382] Near-Bugfix: Reestablish consensus check removed in 8d7849b in 8d7849b6db5f54dc32fe4f8c6c7283068473cd21 This can potentially prevent an overflow that could at least in theory allow the creation of money. --- src/validation.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/validation.cpp b/src/validation.cpp index 8899bd2cd..2e2a89bcf 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1638,6 +1638,10 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state)); } nFees += txfee; + if (!MoneyRange(nFees)) { + return state.DoS(100, error("%s: accumulated fee in the block out of range.", __func__), + REJECT_INVALID, "bad-txns-accumulated-fee-outofrange"); + } // Check that transaction is BIP68 final // BIP68 lock checks (as opposed to nLockTime checks) must From 28d4542a0ac77a30a242d0568e580a5b437f53fa Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Wed, 20 Sep 2017 20:19:14 -0400 Subject: [PATCH 261/382] Disallow uncompressed pubkeys in bitcoin-tx [multisig] output adds --- Makefile.am | 1 + src/bitcoin-tx.cpp | 8 ++++++++ test/util/data/bitcoin-util-test.json | 18 ++++++++++++++++++ test/util/data/txcreatemultisig5.json | 26 ++++++++++++++++++++++++++ 4 files changed, 53 insertions(+) create mode 100644 test/util/data/txcreatemultisig5.json diff --git a/Makefile.am b/Makefile.am index 8216b7d60..3b62a1060 100644 --- a/Makefile.am +++ b/Makefile.am @@ -249,6 +249,7 @@ EXTRA_DIST += \ test/util/data/txcreatemultisig3.json \ test/util/data/txcreatemultisig4.hex \ test/util/data/txcreatemultisig4.json \ + test/util/data/txcreatemultisig5.json \ test/util/data/txcreateoutpubkey1.hex \ test/util/data/txcreateoutpubkey1.json \ test/util/data/txcreateoutpubkey2.hex \ diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index d8d7934bf..e4f44435b 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -310,6 +310,9 @@ static void MutateTxAddOutPubKey(CMutableTransaction& tx, const std::string& str } if (bSegWit) { + if (!pubkey.IsCompressed()) { + throw std::runtime_error("Uncompressed pubkeys are not useable for SegWit outputs"); + } // Call GetScriptForWitness() to build a P2WSH scriptPubKey scriptPubKey = GetScriptForWitness(scriptPubKey); } @@ -375,6 +378,11 @@ static void MutateTxAddOutMultiSig(CMutableTransaction& tx, const std::string& s CScript scriptPubKey = GetScriptForMultisig(required, pubkeys); if (bSegWit) { + for (CPubKey& pubkey : pubkeys) { + if (!pubkey.IsCompressed()) { + throw std::runtime_error("Uncompressed pubkeys are not useable for SegWit outputs"); + } + } // Call GetScriptForWitness() to build a P2WSH scriptPubKey scriptPubKey = GetScriptForWitness(scriptPubKey); } diff --git a/test/util/data/bitcoin-util-test.json b/test/util/data/bitcoin-util-test.json index b61a4f7f8..89b28bba6 100644 --- a/test/util/data/bitcoin-util-test.json +++ b/test/util/data/bitcoin-util-test.json @@ -261,6 +261,13 @@ "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": + ["-json", "-create", "outpubkey=0:047d1368ba7ae01c94bc32293efd70bd7e3be7aa7912d07d0b1c659c1008d179b8642f5fb90f47580feb29f045e216ff5a4716d3a0fed36da414d332046303c44a:WS", "nversion=1"], + "return_code": 1, + "error_txt": "error: Uncompressed pubkeys are not useable for SegWit outputs", + "description": "Creates a new transaction with a single pay-to-pub-key output, wrapped in P2SH (output as json)" + }, { "exec": "./bitcoin-tx", "args": ["-create", @@ -388,5 +395,16 @@ "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)" + }, + { "exec": "./bitcoin-tx", + "args": ["-json", "-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:047d1368ba7ae01c94bc32293efd70bd7e3be7aa7912d07d0b1c659c1008d179b8642f5fb90f47580feb29f045e216ff5a4716d3a0fed36da414d332046303c44a:S"], + "output_cmp": "txcreatemultisig5.json", + "description": "Uncompressed pubkeys should work just fine for non-witness outputs" + }, + { "exec": "./bitcoin-tx", + "args": ["-json", "-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:047d1368ba7ae01c94bc32293efd70bd7e3be7aa7912d07d0b1c659c1008d179b8642f5fb90f47580feb29f045e216ff5a4716d3a0fed36da414d332046303c44a:WS"], + "return_code": 1, + "error_txt": "error: Uncompressed pubkeys are not useable for SegWit outputs", + "description": "Ensure adding witness outputs with uncompressed pubkeys fails" } ] diff --git a/test/util/data/txcreatemultisig5.json b/test/util/data/txcreatemultisig5.json new file mode 100644 index 000000000..20e9bb077 --- /dev/null +++ b/test/util/data/txcreatemultisig5.json @@ -0,0 +1,26 @@ +{ + "txid": "813cf75e1f08debd242ef7c8192b7d478fb651355209369499a0de779ba7eb2f", + "hash": "813cf75e1f08debd242ef7c8192b7d478fb651355209369499a0de779ba7eb2f", + "version": 2, + "size": 42, + "vsize": 42, + "locktime": 0, + "vin": [ + ], + "vout": [ + { + "value": 1.00000000, + "n": 0, + "scriptPubKey": { + "asm": "OP_HASH160 a4051c02398868af83f28f083208fae99a769263 OP_EQUAL", + "hex": "a914a4051c02398868af83f28f083208fae99a76926387", + "reqSigs": 1, + "type": "scripthash", + "addresses": [ + "3GeGs1eHUxPz5YyuFe9WPpXid2UsUb5Jos" + ] + } + } + ], + "hex": "02000000000100e1f5050000000017a914a4051c02398868af83f28f083208fae99a7692638700000000" +} From 5ab586f90b74d84e29156ebf6692b9e9055aa047 Mon Sep 17 00:00:00 2001 From: James O'Beirne Date: Mon, 11 Sep 2017 17:42:37 -0700 Subject: [PATCH 262/382] Consolidate CMerkleBlock constructor into a single method Incorporates feedback suggested by @sipa, @promag, @TheBlueMatt. --- src/merkleblock.cpp | 35 +++++++---------------------------- src/merkleblock.h | 16 ++++++++++++---- 2 files changed, 19 insertions(+), 32 deletions(-) diff --git a/src/merkleblock.cpp b/src/merkleblock.cpp index f0abea061..3f07b4dac 100644 --- a/src/merkleblock.cpp +++ b/src/merkleblock.cpp @@ -9,33 +9,8 @@ #include "consensus/consensus.h" #include "utilstrencodings.h" -CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter& filter) -{ - header = block.GetBlockHeader(); - std::vector vMatch; - std::vector vHashes; - - vMatch.reserve(block.vtx.size()); - vHashes.reserve(block.vtx.size()); - - for (unsigned int i = 0; i < block.vtx.size(); i++) - { - const uint256& hash = block.vtx[i]->GetHash(); - if (filter.IsRelevantAndUpdate(*block.vtx[i])) - { - vMatch.push_back(true); - vMatchedTxn.push_back(std::make_pair(i, hash)); - } - else - vMatch.push_back(false); - vHashes.push_back(hash); - } - - txn = CPartialMerkleTree(vHashes, vMatch); -} - -CMerkleBlock::CMerkleBlock(const CBlock& block, const std::set& txids) +CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter* filter, const std::set* txids) { header = block.GetBlockHeader(); @@ -48,10 +23,14 @@ CMerkleBlock::CMerkleBlock(const CBlock& block, const std::set& txids) for (unsigned int i = 0; i < block.vtx.size(); i++) { const uint256& hash = block.vtx[i]->GetHash(); - if (txids.count(hash)) + if (txids && txids->count(hash)) { vMatch.push_back(true); - else + } else if (filter && filter->IsRelevantAndUpdate(*block.vtx[i])) { + vMatch.push_back(true); + vMatchedTxn.emplace_back(i, hash); + } else { vMatch.push_back(false); + } vHashes.push_back(hash); } diff --git a/src/merkleblock.h b/src/merkleblock.h index 20f2b3688..6c05f2c1f 100644 --- a/src/merkleblock.h +++ b/src/merkleblock.h @@ -131,8 +131,12 @@ class CMerkleBlock CBlockHeader header; CPartialMerkleTree txn; -public: - /** Public only for unit testing and relay testing (not relayed) */ + /** + * Public only for unit testing and relay testing (not relayed). + * + * Used only when a bloom filter is specified to allow + * testing the transactions which matched the bloom filter. + */ std::vector > vMatchedTxn; /** @@ -140,10 +144,10 @@ class CMerkleBlock * Note that this will call IsRelevantAndUpdate on the filter for each transaction, * thus the filter will likely be modified. */ - CMerkleBlock(const CBlock& block, CBloomFilter& filter); + CMerkleBlock(const CBlock& block, CBloomFilter& filter) : CMerkleBlock(block, &filter, nullptr) { } // Create from a CBlock, matching the txids in the set - CMerkleBlock(const CBlock& block, const std::set& txids); + CMerkleBlock(const CBlock& block, const std::set& txids) : CMerkleBlock(block, nullptr, &txids) { } CMerkleBlock() {} @@ -154,6 +158,10 @@ class CMerkleBlock READWRITE(header); READWRITE(txn); } + +private: + // Combined constructor to consolidate code + CMerkleBlock(const CBlock& block, CBloomFilter* filter, const std::set* txids); }; #endif // BITCOIN_MERKLEBLOCK_H From 46ce223d15d4111d096f6342eb6f526d2507d7d7 Mon Sep 17 00:00:00 2001 From: James O'Beirne Date: Mon, 11 Sep 2017 17:43:48 -0700 Subject: [PATCH 263/382] Add tests for CMerkleBlock usage with txids specified --- src/Makefile.test.include | 1 + src/test/bloom_tests.cpp | 12 ++---- src/test/merkleblock_tests.cpp | 78 ++++++++++++++++++++++++++++++++++ src/test/test_bitcoin.cpp | 13 ++++++ src/test/test_bitcoin.h | 5 ++- 5 files changed, 100 insertions(+), 9 deletions(-) create mode 100644 src/test/merkleblock_tests.cpp diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 01ab0134f..7a921a1ba 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -49,6 +49,7 @@ BITCOIN_TESTS =\ test/main_tests.cpp \ test/mempool_tests.cpp \ test/merkle_tests.cpp \ + test/merkleblock_tests.cpp \ test/miner_tests.cpp \ test/multisig_tests.cpp \ test/net_tests.cpp \ diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp index 9274ceefc..eac2c102a 100644 --- a/src/test/bloom_tests.cpp +++ b/src/test/bloom_tests.cpp @@ -8,6 +8,7 @@ #include "clientversion.h" #include "key.h" #include "merkleblock.h" +#include "primitives/block.h" #include "random.h" #include "serialize.h" #include "streams.h" @@ -179,20 +180,15 @@ BOOST_AUTO_TEST_CASE(bloom_match) BOOST_AUTO_TEST_CASE(merkle_block_1) { - // Random real block (0000000000013b8ab2cd513b0261a14096412195a72a0c4827d229dcc7e0f7af) - // With 9 txes - CBlock block; - CDataStream stream(ParseHex("0100000090f0a9f110702f808219ebea1173056042a714bad51b916cb6800000000000005275289558f51c9966699404ae2294730c3c9f9bda53523ce50e9b95e558da2fdb261b4d4c86041b1ab1bf930901000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0146ffffffff0100f2052a01000000434104e18f7afbe4721580e81e8414fc8c24d7cfacf254bb5c7b949450c3e997c2dc1242487a8169507b631eb3771f2b425483fb13102c4eb5d858eef260fe70fbfae0ac00000000010000000196608ccbafa16abada902780da4dc35dafd7af05fa0da08cf833575f8cf9e836000000004a493046022100dab24889213caf43ae6adc41cf1c9396c08240c199f5225acf45416330fd7dbd022100fe37900e0644bf574493a07fc5edba06dbc07c311b947520c2d514bc5725dcb401ffffffff0100f2052a010000001976a914f15d1921f52e4007b146dfa60f369ed2fc393ce288ac000000000100000001fb766c1288458c2bafcfec81e48b24d98ec706de6b8af7c4e3c29419bfacb56d000000008c493046022100f268ba165ce0ad2e6d93f089cfcd3785de5c963bb5ea6b8c1b23f1ce3e517b9f022100da7c0f21adc6c401887f2bfd1922f11d76159cbc597fbd756a23dcbb00f4d7290141042b4e8625a96127826915a5b109852636ad0da753c9e1d5606a50480cd0c40f1f8b8d898235e571fe9357d9ec842bc4bba1827daaf4de06d71844d0057707966affffffff0280969800000000001976a9146963907531db72d0ed1a0cfb471ccb63923446f388ac80d6e34c000000001976a914f0688ba1c0d1ce182c7af6741e02658c7d4dfcd388ac000000000100000002c40297f730dd7b5a99567eb8d27b78758f607507c52292d02d4031895b52f2ff010000008b483045022100f7edfd4b0aac404e5bab4fd3889e0c6c41aa8d0e6fa122316f68eddd0a65013902205b09cc8b2d56e1cd1f7f2fafd60a129ed94504c4ac7bdc67b56fe67512658b3e014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffffca5065ff9617cbcba45eb23726df6498a9b9cafed4f54cbab9d227b0035ddefb000000008a473044022068010362a13c7f9919fa832b2dee4e788f61f6f5d344a7c2a0da6ae740605658022006d1af525b9a14a35c003b78b72bd59738cd676f845d1ff3fc25049e01003614014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffff01001ec4110200000043410469ab4181eceb28985b9b4e895c13fa5e68d85761b7eee311db5addef76fa8621865134a221bd01f28ec9999ee3e021e60766e9d1f3458c115fb28650605f11c9ac000000000100000001cdaf2f758e91c514655e2dc50633d1e4c84989f8aa90a0dbc883f0d23ed5c2fa010000008b48304502207ab51be6f12a1962ba0aaaf24a20e0b69b27a94fac5adf45aa7d2d18ffd9236102210086ae728b370e5329eead9accd880d0cb070aea0c96255fae6c4f1ddcce1fd56e014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff02404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac002d3101000000001976a9141befba0cdc1ad56529371864d9f6cb042faa06b588ac000000000100000001b4a47603e71b61bc3326efd90111bf02d2f549b067f4c4a8fa183b57a0f800cb010000008a4730440220177c37f9a505c3f1a1f0ce2da777c339bd8339ffa02c7cb41f0a5804f473c9230220585b25a2ee80eb59292e52b987dad92acb0c64eced92ed9ee105ad153cdb12d001410443bd44f683467e549dae7d20d1d79cbdb6df985c6e9c029c8d0c6cb46cc1a4d3cf7923c5021b27f7a0b562ada113bc85d5fda5a1b41e87fe6e8802817cf69996ffffffff0280651406000000001976a9145505614859643ab7b547cd7f1f5e7e2a12322d3788ac00aa0271000000001976a914ea4720a7a52fc166c55ff2298e07baf70ae67e1b88ac00000000010000000586c62cd602d219bb60edb14a3e204de0705176f9022fe49a538054fb14abb49e010000008c493046022100f2bc2aba2534becbdf062eb993853a42bbbc282083d0daf9b4b585bd401aa8c9022100b1d7fd7ee0b95600db8535bbf331b19eed8d961f7a8e54159c53675d5f69df8c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff03ad0e58ccdac3df9dc28a218bcf6f1997b0a93306faaa4b3a28ae83447b2179010000008b483045022100be12b2937179da88599e27bb31c3525097a07cdb52422d165b3ca2f2020ffcf702200971b51f853a53d644ebae9ec8f3512e442b1bcb6c315a5b491d119d10624c83014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff2acfcab629bbc8685792603762c921580030ba144af553d271716a95089e107b010000008b483045022100fa579a840ac258871365dd48cd7552f96c8eea69bd00d84f05b283a0dab311e102207e3c0ee9234814cfbb1b659b83671618f45abc1326b9edcc77d552a4f2a805c0014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffdcdc6023bbc9944a658ddc588e61eacb737ddf0a3cd24f113b5a8634c517fcd2000000008b4830450221008d6df731df5d32267954bd7d2dda2302b74c6c2a6aa5c0ca64ecbabc1af03c75022010e55c571d65da7701ae2da1956c442df81bbf076cdbac25133f99d98a9ed34c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffe15557cd5ce258f479dfd6dc6514edf6d7ed5b21fcfa4a038fd69f06b83ac76e010000008b483045022023b3e0ab071eb11de2eb1cc3a67261b866f86bf6867d4558165f7c8c8aca2d86022100dc6e1f53a91de3efe8f63512850811f26284b62f850c70ca73ed5de8771fb451014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff01404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000010000000166d7577163c932b4f9690ca6a80b6e4eb001f0a2fa9023df5595602aae96ed8d000000008a4730440220262b42546302dfb654a229cefc86432b89628ff259dc87edd1154535b16a67e102207b4634c020a97c3e7bbd0d4d19da6aa2269ad9dded4026e896b213d73ca4b63f014104979b82d02226b3a4597523845754d44f13639e3bf2df5e82c6aab2bdc79687368b01b1ab8b19875ae3c90d661a3d0a33161dab29934edeb36aa01976be3baf8affffffff02404b4c00000000001976a9144854e695a02af0aeacb823ccbc272134561e0a1688ac40420f00000000001976a914abee93376d6b37b5c2940655a6fcaf1c8e74237988ac0000000001000000014e3f8ef2e91349a9059cb4f01e54ab2597c1387161d3da89919f7ea6acdbb371010000008c49304602210081f3183471a5ca22307c0800226f3ef9c353069e0773ac76bb580654d56aa523022100d4c56465bdc069060846f4fbf2f6b20520b2a80b08b168b31e66ddb9c694e240014104976c79848e18251612f8940875b2b08d06e6dc73b9840e8860c066b7e87432c477e9a59a453e71e6d76d5fe34058b800a098fc1740ce3012e8fc8a00c96af966ffffffff02c0e1e400000000001976a9144134e75a6fcb6042034aab5e18570cf1f844f54788ac404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000"), SER_NETWORK, PROTOCOL_VERSION); - stream >> block; - + CBlock block = getBlock13b8a(); CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL); // Match the last transaction filter.insert(uint256S("0x74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20")); CMerkleBlock merkleBlock(block, filter); - BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash()); + BOOST_CHECK_EQUAL(merkleBlock.header.GetHash().GetHex(), block.GetHash().GetHex()); - BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 1); + BOOST_CHECK_EQUAL(merkleBlock.vMatchedTxn.size(), 1); std::pair pair = merkleBlock.vMatchedTxn[0]; BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256S("0x74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20")); diff --git a/src/test/merkleblock_tests.cpp b/src/test/merkleblock_tests.cpp new file mode 100644 index 000000000..3e66c6f2c --- /dev/null +++ b/src/test/merkleblock_tests.cpp @@ -0,0 +1,78 @@ +// Copyright (c) 2012-2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "merkleblock.h" +#include "uint256.h" +#include "test/test_bitcoin.h" + +#include + + +BOOST_FIXTURE_TEST_SUITE(merkleblock_tests, BasicTestingSetup) + +/** + * Create a CMerkleBlock using a list of txids which will be found in the + * given block. + */ +BOOST_AUTO_TEST_CASE(merkleblock_construct_from_txids_found) +{ + CBlock block = getBlock13b8a(); + + std::set txids; + + // Last txn in block. + uint256 txhash1 = uint256S("0x74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20"); + + // Second txn in block. + uint256 txhash2 = uint256S("0xf9fc751cb7dc372406a9f8d738d5e6f8f63bab71986a39cf36ee70ee17036d07"); + + txids.insert(txhash1); + txids.insert(txhash2); + + CMerkleBlock merkleBlock(block, txids); + + BOOST_CHECK_EQUAL(merkleBlock.header.GetHash().GetHex(), block.GetHash().GetHex()); + + // vMatchedTxn is only used when bloom filter is specified. + BOOST_CHECK_EQUAL(merkleBlock.vMatchedTxn.size(), 0); + + std::vector vMatched; + std::vector vIndex; + + BOOST_CHECK_EQUAL(merkleBlock.txn.ExtractMatches(vMatched, vIndex).GetHex(), block.hashMerkleRoot.GetHex()); + BOOST_CHECK_EQUAL(vMatched.size(), 2); + + // Ordered by occurrence in depth-first tree traversal. + BOOST_CHECK_EQUAL(vMatched[0].ToString(), txhash2.ToString()); + BOOST_CHECK_EQUAL(vIndex[0], 1); + + BOOST_CHECK_EQUAL(vMatched[1].ToString(), txhash1.ToString()); + BOOST_CHECK_EQUAL(vIndex[1], 8); +} + + +/** + * Create a CMerkleBlock using a list of txids which will not be found in the + * given block. + */ +BOOST_AUTO_TEST_CASE(merkleblock_construct_from_txids_not_found) +{ + CBlock block = getBlock13b8a(); + + std::set txids2; + txids2.insert(uint256S("0xc0ffee00003bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20")); + CMerkleBlock merkleBlock(block, txids2); + + BOOST_CHECK_EQUAL(merkleBlock.header.GetHash().GetHex(), block.GetHash().GetHex()); + BOOST_CHECK_EQUAL(merkleBlock.vMatchedTxn.size(), 0); + + std::vector vMatched; + std::vector vIndex; + + BOOST_CHECK_EQUAL(merkleBlock.txn.ExtractMatches(vMatched, vIndex).GetHex(), block.hashMerkleRoot.GetHex()); + BOOST_CHECK_EQUAL(vMatched.size(), 0); + BOOST_CHECK_EQUAL(vIndex.size(), 0); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 045655983..79bc48a11 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -18,6 +18,7 @@ #include "txdb.h" #include "txmempool.h" #include "ui_interface.h" +#include "streams.h" #include "rpc/server.h" #include "rpc/register.h" #include "script/sigcache.h" @@ -158,3 +159,15 @@ CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CTransaction &txn) { return CTxMemPoolEntry(MakeTransactionRef(txn), nFee, nTime, nHeight, spendsCoinbase, sigOpCost, lp); } + +/** + * @returns a real block (0000000000013b8ab2cd513b0261a14096412195a72a0c4827d229dcc7e0f7af) + * with 9 txs. + */ +CBlock getBlock13b8a() +{ + CBlock block; + CDataStream stream(ParseHex("0100000090f0a9f110702f808219ebea1173056042a714bad51b916cb6800000000000005275289558f51c9966699404ae2294730c3c9f9bda53523ce50e9b95e558da2fdb261b4d4c86041b1ab1bf930901000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0146ffffffff0100f2052a01000000434104e18f7afbe4721580e81e8414fc8c24d7cfacf254bb5c7b949450c3e997c2dc1242487a8169507b631eb3771f2b425483fb13102c4eb5d858eef260fe70fbfae0ac00000000010000000196608ccbafa16abada902780da4dc35dafd7af05fa0da08cf833575f8cf9e836000000004a493046022100dab24889213caf43ae6adc41cf1c9396c08240c199f5225acf45416330fd7dbd022100fe37900e0644bf574493a07fc5edba06dbc07c311b947520c2d514bc5725dcb401ffffffff0100f2052a010000001976a914f15d1921f52e4007b146dfa60f369ed2fc393ce288ac000000000100000001fb766c1288458c2bafcfec81e48b24d98ec706de6b8af7c4e3c29419bfacb56d000000008c493046022100f268ba165ce0ad2e6d93f089cfcd3785de5c963bb5ea6b8c1b23f1ce3e517b9f022100da7c0f21adc6c401887f2bfd1922f11d76159cbc597fbd756a23dcbb00f4d7290141042b4e8625a96127826915a5b109852636ad0da753c9e1d5606a50480cd0c40f1f8b8d898235e571fe9357d9ec842bc4bba1827daaf4de06d71844d0057707966affffffff0280969800000000001976a9146963907531db72d0ed1a0cfb471ccb63923446f388ac80d6e34c000000001976a914f0688ba1c0d1ce182c7af6741e02658c7d4dfcd388ac000000000100000002c40297f730dd7b5a99567eb8d27b78758f607507c52292d02d4031895b52f2ff010000008b483045022100f7edfd4b0aac404e5bab4fd3889e0c6c41aa8d0e6fa122316f68eddd0a65013902205b09cc8b2d56e1cd1f7f2fafd60a129ed94504c4ac7bdc67b56fe67512658b3e014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffffca5065ff9617cbcba45eb23726df6498a9b9cafed4f54cbab9d227b0035ddefb000000008a473044022068010362a13c7f9919fa832b2dee4e788f61f6f5d344a7c2a0da6ae740605658022006d1af525b9a14a35c003b78b72bd59738cd676f845d1ff3fc25049e01003614014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffff01001ec4110200000043410469ab4181eceb28985b9b4e895c13fa5e68d85761b7eee311db5addef76fa8621865134a221bd01f28ec9999ee3e021e60766e9d1f3458c115fb28650605f11c9ac000000000100000001cdaf2f758e91c514655e2dc50633d1e4c84989f8aa90a0dbc883f0d23ed5c2fa010000008b48304502207ab51be6f12a1962ba0aaaf24a20e0b69b27a94fac5adf45aa7d2d18ffd9236102210086ae728b370e5329eead9accd880d0cb070aea0c96255fae6c4f1ddcce1fd56e014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff02404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac002d3101000000001976a9141befba0cdc1ad56529371864d9f6cb042faa06b588ac000000000100000001b4a47603e71b61bc3326efd90111bf02d2f549b067f4c4a8fa183b57a0f800cb010000008a4730440220177c37f9a505c3f1a1f0ce2da777c339bd8339ffa02c7cb41f0a5804f473c9230220585b25a2ee80eb59292e52b987dad92acb0c64eced92ed9ee105ad153cdb12d001410443bd44f683467e549dae7d20d1d79cbdb6df985c6e9c029c8d0c6cb46cc1a4d3cf7923c5021b27f7a0b562ada113bc85d5fda5a1b41e87fe6e8802817cf69996ffffffff0280651406000000001976a9145505614859643ab7b547cd7f1f5e7e2a12322d3788ac00aa0271000000001976a914ea4720a7a52fc166c55ff2298e07baf70ae67e1b88ac00000000010000000586c62cd602d219bb60edb14a3e204de0705176f9022fe49a538054fb14abb49e010000008c493046022100f2bc2aba2534becbdf062eb993853a42bbbc282083d0daf9b4b585bd401aa8c9022100b1d7fd7ee0b95600db8535bbf331b19eed8d961f7a8e54159c53675d5f69df8c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff03ad0e58ccdac3df9dc28a218bcf6f1997b0a93306faaa4b3a28ae83447b2179010000008b483045022100be12b2937179da88599e27bb31c3525097a07cdb52422d165b3ca2f2020ffcf702200971b51f853a53d644ebae9ec8f3512e442b1bcb6c315a5b491d119d10624c83014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff2acfcab629bbc8685792603762c921580030ba144af553d271716a95089e107b010000008b483045022100fa579a840ac258871365dd48cd7552f96c8eea69bd00d84f05b283a0dab311e102207e3c0ee9234814cfbb1b659b83671618f45abc1326b9edcc77d552a4f2a805c0014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffdcdc6023bbc9944a658ddc588e61eacb737ddf0a3cd24f113b5a8634c517fcd2000000008b4830450221008d6df731df5d32267954bd7d2dda2302b74c6c2a6aa5c0ca64ecbabc1af03c75022010e55c571d65da7701ae2da1956c442df81bbf076cdbac25133f99d98a9ed34c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffe15557cd5ce258f479dfd6dc6514edf6d7ed5b21fcfa4a038fd69f06b83ac76e010000008b483045022023b3e0ab071eb11de2eb1cc3a67261b866f86bf6867d4558165f7c8c8aca2d86022100dc6e1f53a91de3efe8f63512850811f26284b62f850c70ca73ed5de8771fb451014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff01404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000010000000166d7577163c932b4f9690ca6a80b6e4eb001f0a2fa9023df5595602aae96ed8d000000008a4730440220262b42546302dfb654a229cefc86432b89628ff259dc87edd1154535b16a67e102207b4634c020a97c3e7bbd0d4d19da6aa2269ad9dded4026e896b213d73ca4b63f014104979b82d02226b3a4597523845754d44f13639e3bf2df5e82c6aab2bdc79687368b01b1ab8b19875ae3c90d661a3d0a33161dab29934edeb36aa01976be3baf8affffffff02404b4c00000000001976a9144854e695a02af0aeacb823ccbc272134561e0a1688ac40420f00000000001976a914abee93376d6b37b5c2940655a6fcaf1c8e74237988ac0000000001000000014e3f8ef2e91349a9059cb4f01e54ab2597c1387161d3da89919f7ea6acdbb371010000008c49304602210081f3183471a5ca22307c0800226f3ef9c353069e0773ac76bb580654d56aa523022100d4c56465bdc069060846f4fbf2f6b20520b2a80b08b168b31e66ddb9c694e240014104976c79848e18251612f8940875b2b08d06e6dc73b9840e8860c066b7e87432c477e9a59a453e71e6d76d5fe34058b800a098fc1740ce3012e8fc8a00c96af966ffffffff02c0e1e400000000001976a9144134e75a6fcb6042034aab5e18570cf1f844f54788ac404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000"), SER_NETWORK, PROTOCOL_VERSION); + stream >> block; + return block; +} diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h index 6ada96f88..2390aca34 100644 --- a/src/test/test_bitcoin.h +++ b/src/test/test_bitcoin.h @@ -99,7 +99,7 @@ struct TestMemPoolEntryHelper TestMemPoolEntryHelper() : nFee(0), nTime(0), nHeight(1), spendsCoinbase(false), sigOpCost(4) { } - + CTxMemPoolEntry FromTx(const CMutableTransaction &tx); CTxMemPoolEntry FromTx(const CTransaction &tx); @@ -110,4 +110,7 @@ struct TestMemPoolEntryHelper TestMemPoolEntryHelper &SpendsCoinbase(bool _flag) { spendsCoinbase = _flag; return *this; } TestMemPoolEntryHelper &SigOpsCost(unsigned int _sigopsCost) { sigOpCost = _sigopsCost; return *this; } }; + +CBlock getBlock13b8a(); + #endif From 6951a1c6758ab642ef99f93c8d6a43f1b72214cb Mon Sep 17 00:00:00 2001 From: MeshCollider Date: Thu, 21 Sep 2017 15:42:40 +1200 Subject: [PATCH 264/382] Remove extremely outdated share/certs dir --- share/certs/BitcoinFoundation_Apple_Cert.pem | 37 --------------- share/certs/BitcoinFoundation_Comodo_Cert.pem | 37 --------------- share/certs/PrivateKeyNotes.md | 46 ------------------- 3 files changed, 120 deletions(-) delete mode 100644 share/certs/BitcoinFoundation_Apple_Cert.pem delete mode 100644 share/certs/BitcoinFoundation_Comodo_Cert.pem delete mode 100644 share/certs/PrivateKeyNotes.md diff --git a/share/certs/BitcoinFoundation_Apple_Cert.pem b/share/certs/BitcoinFoundation_Apple_Cert.pem deleted file mode 100644 index beb0d7073..000000000 --- a/share/certs/BitcoinFoundation_Apple_Cert.pem +++ /dev/null @@ -1,37 +0,0 @@ -Bag Attributes - friendlyName: Developer ID Application: BITCOIN FOUNDATION, INC., THE - localKeyID: 6B 9C 6C A8 A5 73 70 70 E2 57 A3 49 D8 62 FB 97 C7 A5 5D 5E -subject=/UID=PBV4GLS9J4/CN=Developer ID Application: BITCOIN FOUNDATION, INC., THE/OU=PBV4GLS9J4/O=BITCOIN FOUNDATION, INC., THE/C=US -issuer=/CN=Developer ID Certification Authority/OU=Apple Certification Authority/O=Apple Inc./C=US ------BEGIN CERTIFICATE----- -MIIFhzCCBG+gAwIBAgIIJ0r1rumyfZAwDQYJKoZIhvcNAQELBQAweTEtMCsGA1UE -AwwkRGV2ZWxvcGVyIElEIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSYwJAYDVQQL -DB1BcHBsZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUg -SW5jLjELMAkGA1UEBhMCVVMwHhcNMTMwMTEwMjIzOTAxWhcNMTgwMTExMjIzOTAx -WjCBqDEaMBgGCgmSJomT8ixkAQEMClBCVjRHTFM5SjQxQDA+BgNVBAMMN0RldmVs -b3BlciBJRCBBcHBsaWNhdGlvbjogQklUQ09JTiBGT1VOREFUSU9OLCBJTkMuLCBU -SEUxEzARBgNVBAsMClBCVjRHTFM5SjQxJjAkBgNVBAoMHUJJVENPSU4gRk9VTkRB -VElPTiwgSU5DLiwgVEhFMQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBALTd5zURuZVoJviusr119aktXksenb9IN9vq6kBbq38vxEk7 -9wkKMES2XfBRh0HxcEizGzhMNy5OCXuTLMaNMihYdfwYSoBoR2foEU+6kjPUnyJ4 -dQBFLJZJr5/QeQmALmYHEgZ6lwXFD2lU8t92340zeJ4y5LZw5pcEHtH9IummYDut -OGCkCGXDcjL+5nHhNScJiXHhswM+62o6XXsQiP6EWbM1CsgrGTNLtaa0U/UvVDwE -79YKklSC5Bog2LD0jBcTuveI66mFzqu++L9X9u+ZArtebwCl7BPNQ+uboYy5uV2d -zf8lpNNZLfXCFjoLe9bLICKfZ7ub9V5aC8+GhckCAwEAAaOCAeEwggHdMD4GCCsG -AQUFBwEBBDIwMDAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuYXBwbGUuY29tL29j -c3AtZGV2aWQwMTAdBgNVHQ4EFgQUa5xsqKVzcHDiV6NJ2GL7l8elXV4wDAYDVR0T -AQH/BAIwADAfBgNVHSMEGDAWgBRXF+2iz9x8mKEQ4Py+hy0s8uMXVDCCAQ4GA1Ud -IASCAQUwggEBMIH+BgkqhkiG92NkBQEwgfAwKAYIKwYBBQUHAgEWHGh0dHA6Ly93 -d3cuYXBwbGUuY29tL2FwcGxlY2EwgcMGCCsGAQUFBwICMIG2DIGzUmVsaWFuY2Ug -b24gdGhpcyBjZXJ0aWZpY2F0ZSBieSBhbnkgcGFydHkgYXNzdW1lcyBhY2NlcHRh -bmNlIG9mIHRoZSB0aGVuIGFwcGxpY2FibGUgc3RhbmRhcmQgdGVybXMgYW5kIGNv -bmRpdGlvbnMgb2YgdXNlLCBjZXJ0aWZpY2F0ZSBwb2xpY3kgYW5kIGNlcnRpZmlj -YXRpb24gcHJhY3RpY2Ugc3RhdGVtZW50cy4wDgYDVR0PAQH/BAQDAgeAMBYGA1Ud -JQEB/wQMMAoGCCsGAQUFBwMDMBMGCiqGSIb3Y2QGAQ0BAf8EAgUAMA0GCSqGSIb3 -DQEBCwUAA4IBAQAfJ0BjID/1dS2aEeVyhAzPzCBjG8vm0gDf+/qfwRn3+yWeL9vS -nMdbilwM48IyQWTagjGGcojbsAd/vE4N7NhQyHInoCllNoeor1I5xx+blTaGRBK+ -dDhJbbdlGCjsLnH/BczGZi5fyEJds9lUIrp1hJidRcUKO76qb/9gc6qNZpl1vH5k -lDUuJYt7YhAs+L6rTXDyqcK9maeQr0gaOPsRRAQLLwiQCorPeMTUNsbVMdMwZYJs -R+PxiAnk+nyi7rfiFvPoASAYUuI6OzYL/Fa6QU4/gYyPgic944QYVkaQBnc0vEP1 -nXq6LGKwgVGcqJnkr/E2kui5gJoV5C3qll3e ------END CERTIFICATE----- diff --git a/share/certs/BitcoinFoundation_Comodo_Cert.pem b/share/certs/BitcoinFoundation_Comodo_Cert.pem deleted file mode 100644 index dc752d455..000000000 --- a/share/certs/BitcoinFoundation_Comodo_Cert.pem +++ /dev/null @@ -1,37 +0,0 @@ -Bag Attributes - friendlyName: The Bitcoin Foundation, Inc.'s COMODO CA Limited ID - localKeyID: 8C 94 64 E3 B5 B0 41 89 5B 89 B0 57 CC 74 B9 44 E5 B2 92 66 -subject=/C=US/postalCode=98104-1444/ST=WA/L=Seattle/street=Suite 300/street=71 Columbia St/O=The Bitcoin Foundation, Inc./CN=The Bitcoin Foundation, Inc. -issuer=/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO Code Signing CA 2 ------BEGIN CERTIFICATE----- -MIIFeDCCBGCgAwIBAgIRAJVYMd+waOER7lUqtiz3M2IwDQYJKoZIhvcNAQEFBQAw -ezELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G -A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxITAfBgNV -BAMTGENPTU9ETyBDb2RlIFNpZ25pbmcgQ0EgMjAeFw0xMzAxMTYwMDAwMDBaFw0x -NDAxMTYyMzU5NTlaMIG8MQswCQYDVQQGEwJVUzETMBEGA1UEEQwKOTgxMDQtMTQ0 -NDELMAkGA1UECAwCV0ExEDAOBgNVBAcMB1NlYXR0bGUxEjAQBgNVBAkMCVN1aXRl -IDMwMDEXMBUGA1UECQwONzEgQ29sdW1iaWEgU3QxJTAjBgNVBAoMHFRoZSBCaXRj -b2luIEZvdW5kYXRpb24sIEluYy4xJTAjBgNVBAMMHFRoZSBCaXRjb2luIEZvdW5k -YXRpb24sIEluYy4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQChUwLD -u/hu5aFZ/n11B27awONaaDrmHm0pamiWHb01yL4JmTBtaLCrSftF8RhCscQ8jpI0 -UG1Cchmay0e3zH5o5XRs0H9C3x+SM5ozms0TWDmAYiB8aQEghsGovDk0D2nyTQeK -Q0xqyCh0m8ZPOnMnYrakHEmF6WvhLdJvI6Od4KIwbKxgN17cPFIfLVsZ7GrzmmbU -Gdi4wSQCHy5rxzvBxho8Qq/SfBl93uOMUrqOHjOUAPhNuTJG3t/MdhU8Zp24s29M -abHtYkT9W86hMjIiI8RTAR+WHKVglx9SB0cjDabXN8SZ3gME0+H++LyzlySHT8sI -ykepojZ7UBRgp9w3AgMBAAGjggGzMIIBrzAfBgNVHSMEGDAWgBQexbEsfYfaAmh8 -JbwMB4Q/ts/e8TAdBgNVHQ4EFgQUfPf+ZyDWl/4LH0Y5BuJTelkRd/EwDgYDVR0P -AQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEQYJ -YIZIAYb4QgEBBAQDAgQQMEYGA1UdIAQ/MD0wOwYMKwYBBAGyMQECAQMCMCswKQYI -KwYBBQUHAgEWHWh0dHBzOi8vc2VjdXJlLmNvbW9kby5uZXQvQ1BTMEEGA1UdHwQ6 -MDgwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0NPTU9ET0NvZGVTaWdu -aW5nQ0EyLmNybDByBggrBgEFBQcBAQRmMGQwPAYIKwYBBQUHMAKGMGh0dHA6Ly9j -cnQuY29tb2RvY2EuY29tL0NPTU9ET0NvZGVTaWduaW5nQ0EyLmNydDAkBggrBgEF -BQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2EuY29tMCgGA1UdEQQhMB+BHWxpbmRz -YXlAYml0Y29pbmZvdW5kYXRpb24ub3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAqibjo -D4HG5XSIIMCmYE5RgQBSEAJfI+EZERk1G9F83ZUWr0yNRZCw4O+RaM7xQhvJhEoD -G2kpk/q2bNOc71/VyZ6SrE1JRVUON41/Flhz4M6cP0BclTicXvh+efVwqZhIz+ws -UxF2hvC/1Xx6rqI7NYAlOYXk2MSUq3HREo+gWUPKM8em4MZZV/7XCH4QbsfxOl1J -xS6EOQmV8hfUN4KRXI5WfGUmedBxq7dM0RSJOSQl8fq2f+JjRLfjQwQucy7LDY+y -pRTsL2TdQV/DuDuI3s0NHRGznQNddoX5jqpXhSQFAAdgrhN1gGkWaaTPzr9IF2TG -qgr6PEp9tIYC+MbM ------END CERTIFICATE----- diff --git a/share/certs/PrivateKeyNotes.md b/share/certs/PrivateKeyNotes.md deleted file mode 100644 index 8d50144c2..000000000 --- a/share/certs/PrivateKeyNotes.md +++ /dev/null @@ -1,46 +0,0 @@ -Code-signing private key notes -== - -The private keys for these certificates were generated on Gavin's main work machine, -following the certificate authority's recommendations for generating certificate -signing requests. - -For OSX, the private key was generated by Keychain.app on Gavin's main work machine. -The key and certificate is in a separate, passphrase-protected keychain file that is -unlocked to sign the Bitcoin-Qt.app bundle. - -For Windows, the private key was generated by Firefox running on Gavin's main work machine. -The key and certificate were exported into a separate, passphrase-protected PKCS#12 file, and -then deleted from Firefox's keystore. The exported file is used to sign the Windows setup.exe. - -Threat analysis --- - -Gavin is a single point of failure. He could be coerced to divulge the secret signing keys, -allowing somebody to distribute a Bitcoin-Qt.app or bitcoin-qt-setup.exe with a valid -signature but containing a malicious binary. - -Or the machine Gavin uses to sign the binaries could be compromised, either remotely or -by breaking in to his office, allowing the attacker to get the private key files and then -install a keylogger to get the passphrase that protects them. - -Threat Mitigation --- - -"Air gapping" the machine used to do the signing will not work, because the signing -process needs to access a timestamp server over the network. And it would not -prevent the "rubber hose cryptography" threat (coercing Gavin to sign a bad binary -or divulge the private keys). - -Windows binaries are reproducibly 'gitian-built', and the setup.exe file created -by the NSIS installer system is a 7zip archive, so you could check to make sure -that the bitcoin-qt.exe file inside the installer had not been tampered with. -However, an attacker could modify the installer's code, so when the setup.exe -was run it compromised users' systems. A volunteer to write an auditing tool -that checks the setup.exe for tampering, and checks the files in it against -the list of gitian signatures, is needed. - -The long-term solution is something like the 'gitian downloader' system, which -uses signatures from multiple developers to determine whether or not a binary -should be trusted. However, that just pushes the problem to "how will -non-technical users securely get the gitian downloader code to start?" From 13baf7217bf8394ae02efc376208ae86eac4d0f6 Mon Sep 17 00:00:00 2001 From: MeshCollider Date: Fri, 15 Sep 2017 16:38:42 +1200 Subject: [PATCH 265/382] Replace save|restoreWindowGeometry with Qt functions --- src/qt/bitcoingui.cpp | 9 +++++++-- src/qt/guiutil.cpp | 26 -------------------------- src/qt/guiutil.h | 5 ----- src/qt/rpcconsole.cpp | 11 ++++++++--- 4 files changed, 15 insertions(+), 36 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index be2d21dae..dc5514190 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -123,7 +123,11 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle * spinnerFrame(0), platformStyle(_platformStyle) { - GUIUtil::restoreWindowGeometry("nWindow", QSize(850, 550), this); + QSettings settings; + if (!restoreGeometry(settings.value("MainWindowGeometry").toByteArray())) { + // Restore failed (perhaps missing setting), center the window + move(QApplication::desktop()->availableGeometry().center() - frameGeometry().center()); + } QString windowTitle = tr(PACKAGE_NAME) + " - "; #ifdef ENABLE_WALLET @@ -261,7 +265,8 @@ BitcoinGUI::~BitcoinGUI() // Unsubscribe from notifications from core unsubscribeFromCoreSignals(); - GUIUtil::saveWindowGeometry("nWindow", this); + QSettings settings; + settings.setValue("MainWindowGeometry", saveGeometry()); if(trayIcon) // Hide tray icon, as deleting will let it linger until quit (on Ubuntu) trayIcon->hide(); #ifdef Q_OS_MAC diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index c3f98f764..b916df69a 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -862,32 +862,6 @@ bool SetStartOnSystemStartup(bool fAutoStart) { return false; } #endif -void saveWindowGeometry(const QString& strSetting, QWidget *parent) -{ - QSettings settings; - settings.setValue(strSetting + "Pos", parent->pos()); - settings.setValue(strSetting + "Size", parent->size()); -} - -void restoreWindowGeometry(const QString& strSetting, const QSize& defaultSize, QWidget *parent) -{ - QSettings settings; - QPoint pos = settings.value(strSetting + "Pos").toPoint(); - QSize size = settings.value(strSetting + "Size", defaultSize).toSize(); - - parent->resize(size); - parent->move(pos); - - if ((!pos.x() && !pos.y()) || (QApplication::desktop()->screenNumber(parent) == -1)) - { - QRect screen = QApplication::desktop()->screenGeometry(); - QPoint defaultPos((screen.width() - defaultSize.width()) / 2, - (screen.height() - defaultSize.height()) / 2); - parent->resize(defaultSize); - parent->move(defaultPos); - } -} - void setClipboard(const QString& str) { QApplication::clipboard()->setText(str, QClipboard::Clipboard); diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index d6aa8c4ea..d10818d0c 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -179,11 +179,6 @@ namespace GUIUtil bool GetStartOnSystemStartup(); bool SetStartOnSystemStartup(bool fAutoStart); - /** Save window size and position */ - void saveWindowGeometry(const QString& strSetting, QWidget *parent); - /** Restore window size and position */ - void restoreWindowGeometry(const QString& strSetting, const QSize &defaultSizeIn, QWidget *parent); - /* Convert QString to OS specific boost path through UTF-8 */ fs::path qstringToBoostPath(const QString &path); diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 3590a98ef..d895fc166 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -28,6 +28,7 @@ #include #endif +#include #include #include #include @@ -428,7 +429,11 @@ RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) : consoleFontSize(0) { ui->setupUi(this); - GUIUtil::restoreWindowGeometry("nRPCConsoleWindow", this->size(), this); + QSettings settings; + if (!restoreGeometry(settings.value("RPCConsoleWindowGeometry").toByteArray())) { + // Restore failed (perhaps missing setting), center the window + move(QApplication::desktop()->availableGeometry().center() - frameGeometry().center()); + } ui->openDebugLogfileButton->setToolTip(ui->openDebugLogfileButton->toolTip().arg(tr(PACKAGE_NAME))); @@ -466,14 +471,14 @@ RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) : ui->detailWidget->hide(); ui->peerHeading->setText(tr("Select a peer to view detailed information.")); - QSettings settings; consoleFontSize = settings.value(fontSizeSettingsKey, QFontInfo(QFont()).pointSize()).toInt(); clear(); } RPCConsole::~RPCConsole() { - GUIUtil::saveWindowGeometry("nRPCConsoleWindow", this); + QSettings settings; + settings.setValue("RPCConsoleWindowGeometry", saveGeometry()); RPCUnsetTimerInterface(rpcTimerInterface); delete rpcTimerInterface; delete ui; From d7afe2d1577eb3f6775301c81b94a191e0db99d9 Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Fri, 18 Aug 2017 14:40:29 -0700 Subject: [PATCH 266/382] [script] Unit tests for script/standard functions --- src/Makefile.test.include | 1 + src/test/multisig_tests.cpp | 91 ------- src/test/script_standard_tests.cpp | 385 +++++++++++++++++++++++++++++ 3 files changed, 386 insertions(+), 91 deletions(-) create mode 100644 src/test/script_standard_tests.cpp diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 01ab0134f..3a932f460 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -65,6 +65,7 @@ BITCOIN_TESTS =\ test/scheduler_tests.cpp \ test/script_P2SH_tests.cpp \ test/script_tests.cpp \ + test/script_standard_tests.cpp \ test/scriptnum_tests.cpp \ test/serialize_tests.cpp \ test/sighash_tests.cpp \ diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index 5e89ef60d..de7f3b48f 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -16,8 +16,6 @@ #include -typedef std::vector valtype; - BOOST_FIXTURE_TEST_SUITE(multisig_tests, BasicTestingSetup) CScript @@ -173,95 +171,6 @@ BOOST_AUTO_TEST_CASE(multisig_IsStandard) BOOST_CHECK(!::IsStandard(malformed[i], whichType)); } -BOOST_AUTO_TEST_CASE(multisig_Solver1) -{ - // Tests Solver() that returns lists of keys that are - // required to satisfy a ScriptPubKey - // - // Also tests IsMine() and ExtractDestination() - // - // Note: ExtractDestination for the multisignature transactions - // always returns false for this release, even if you have - // one key that would satisfy an (a|b) or 2-of-3 keys needed - // to spend an escrow transaction. - // - CBasicKeyStore keystore, emptykeystore, partialkeystore; - CKey key[3]; - CTxDestination keyaddr[3]; - for (int i = 0; i < 3; i++) - { - key[i].MakeNewKey(true); - keystore.AddKey(key[i]); - keyaddr[i] = key[i].GetPubKey().GetID(); - } - partialkeystore.AddKey(key[0]); - - { - std::vector solutions; - txnouttype whichType; - CScript s; - s << ToByteVector(key[0].GetPubKey()) << OP_CHECKSIG; - BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK(solutions.size() == 1); - CTxDestination addr; - BOOST_CHECK(ExtractDestination(s, addr)); - BOOST_CHECK(addr == keyaddr[0]); - BOOST_CHECK(IsMine(keystore, s)); - BOOST_CHECK(!IsMine(emptykeystore, s)); - } - { - std::vector solutions; - txnouttype whichType; - CScript s; - s << OP_DUP << OP_HASH160 << ToByteVector(key[0].GetPubKey().GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; - BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK(solutions.size() == 1); - CTxDestination addr; - BOOST_CHECK(ExtractDestination(s, addr)); - BOOST_CHECK(addr == keyaddr[0]); - BOOST_CHECK(IsMine(keystore, s)); - BOOST_CHECK(!IsMine(emptykeystore, s)); - } - { - std::vector solutions; - txnouttype whichType; - CScript s; - s << OP_2 << ToByteVector(key[0].GetPubKey()) << ToByteVector(key[1].GetPubKey()) << OP_2 << OP_CHECKMULTISIG; - BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(solutions.size(), 4U); - CTxDestination addr; - BOOST_CHECK(!ExtractDestination(s, addr)); - BOOST_CHECK(IsMine(keystore, s)); - BOOST_CHECK(!IsMine(emptykeystore, s)); - BOOST_CHECK(!IsMine(partialkeystore, s)); - } - { - std::vector solutions; - txnouttype whichType; - CScript s; - s << OP_1 << ToByteVector(key[0].GetPubKey()) << ToByteVector(key[1].GetPubKey()) << OP_2 << OP_CHECKMULTISIG; - BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(solutions.size(), 4U); - std::vector addrs; - int nRequired; - BOOST_CHECK(ExtractDestinations(s, whichType, addrs, nRequired)); - BOOST_CHECK(addrs[0] == keyaddr[0]); - BOOST_CHECK(addrs[1] == keyaddr[1]); - BOOST_CHECK(nRequired == 1); - BOOST_CHECK(IsMine(keystore, s)); - BOOST_CHECK(!IsMine(emptykeystore, s)); - BOOST_CHECK(!IsMine(partialkeystore, s)); - } - { - std::vector solutions; - txnouttype whichType; - CScript s; - s << OP_2 << ToByteVector(key[0].GetPubKey()) << ToByteVector(key[1].GetPubKey()) << ToByteVector(key[2].GetPubKey()) << OP_3 << OP_CHECKMULTISIG; - BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK(solutions.size() == 5); - } -} - BOOST_AUTO_TEST_CASE(multisig_Sign) { // Test SignSignature() (and therefore the version of Solver() that signs transactions) diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp new file mode 100644 index 000000000..e6a63ad54 --- /dev/null +++ b/src/test/script_standard_tests.cpp @@ -0,0 +1,385 @@ +// Copyright (c) 2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "key.h" +#include "script/script.h" +#include "script/script_error.h" +#include "script/standard.h" +#include "test/test_bitcoin.h" + +#include + + +BOOST_FIXTURE_TEST_SUITE(script_standard_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(script_standard_Solver_success) +{ + CKey keys[3]; + CPubKey pubkeys[3]; + for (int i = 0; i < 3; i++) { + keys[i].MakeNewKey(true); + pubkeys[i] = keys[i].GetPubKey(); + } + + CScript s; + txnouttype whichType; + std::vector > solutions; + + // TX_PUBKEY + s.clear(); + s << ToByteVector(pubkeys[0]) << OP_CHECKSIG; + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_PUBKEY); + BOOST_CHECK_EQUAL(solutions.size(), 1); + BOOST_CHECK(solutions[0] == ToByteVector(pubkeys[0])); + + // TX_PUBKEYHASH + s.clear(); + s << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_PUBKEYHASH); + BOOST_CHECK_EQUAL(solutions.size(), 1); + BOOST_CHECK(solutions[0] == ToByteVector(pubkeys[0].GetID())); + + // TX_SCRIPTHASH + CScript redeemScript(s); // initialize with leftover P2PKH script + s.clear(); + s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_SCRIPTHASH); + BOOST_CHECK_EQUAL(solutions.size(), 1); + BOOST_CHECK(solutions[0] == ToByteVector(CScriptID(redeemScript))); + + // TX_MULTISIG + s.clear(); + s << OP_1 << + ToByteVector(pubkeys[0]) << + ToByteVector(pubkeys[1]) << + OP_2 << OP_CHECKMULTISIG; + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_MULTISIG); + BOOST_CHECK_EQUAL(solutions.size(), 4); + BOOST_CHECK(solutions[0] == std::vector({1})); + BOOST_CHECK(solutions[1] == ToByteVector(pubkeys[0])); + BOOST_CHECK(solutions[2] == ToByteVector(pubkeys[1])); + BOOST_CHECK(solutions[3] == std::vector({2})); + + s.clear(); + s << OP_2 << + ToByteVector(pubkeys[0]) << + ToByteVector(pubkeys[1]) << + ToByteVector(pubkeys[2]) << + OP_3 << OP_CHECKMULTISIG; + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_MULTISIG); + BOOST_CHECK_EQUAL(solutions.size(), 5); + BOOST_CHECK(solutions[0] == std::vector({2})); + BOOST_CHECK(solutions[1] == ToByteVector(pubkeys[0])); + BOOST_CHECK(solutions[2] == ToByteVector(pubkeys[1])); + BOOST_CHECK(solutions[3] == ToByteVector(pubkeys[2])); + BOOST_CHECK(solutions[4] == std::vector({3})); + + // TX_NULL_DATA + solutions.clear(); + s.clear(); + s << OP_RETURN << + std::vector({0}) << + std::vector({75}) << + std::vector({255}); + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_NULL_DATA); + BOOST_CHECK_EQUAL(solutions.size(), 0); + + // TX_WITNESS_V0_KEYHASH + solutions.clear(); + s.clear(); + s << OP_0 << ToByteVector(pubkeys[0].GetID()); + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_WITNESS_V0_KEYHASH); + BOOST_CHECK_EQUAL(solutions.size(), 1); + BOOST_CHECK(solutions[0] == ToByteVector(pubkeys[0].GetID())); + + // TX_WITNESS_V0_SCRIPTHASH + uint256 scriptHash; + CSHA256().Write(&redeemScript[0], redeemScript.size()).Finalize(scriptHash.begin()); + + solutions.clear(); + s.clear(); + s << OP_0 << ToByteVector(scriptHash); + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_WITNESS_V0_SCRIPTHASH); + BOOST_CHECK_EQUAL(solutions.size(), 1); + BOOST_CHECK(solutions[0] == ToByteVector(scriptHash)); + + // TX_NONSTANDARD + solutions.clear(); + s.clear(); + s << OP_9 << OP_ADD << OP_11 << OP_EQUAL; + BOOST_CHECK(!Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_NONSTANDARD); +} + +BOOST_AUTO_TEST_CASE(script_standard_Solver_failure) +{ + CKey key; + CPubKey pubkey; + key.MakeNewKey(true); + pubkey = key.GetPubKey(); + + CScript s; + txnouttype whichType; + std::vector > solutions; + + // TX_PUBKEY with incorrectly sized pubkey + s.clear(); + s << std::vector(30, 0x01) << OP_CHECKSIG; + BOOST_CHECK(!Solver(s, whichType, solutions)); + + // TX_PUBKEYHASH with incorrectly sized key hash + s.clear(); + s << OP_DUP << OP_HASH160 << ToByteVector(pubkey) << OP_EQUALVERIFY << OP_CHECKSIG; + BOOST_CHECK(!Solver(s, whichType, solutions)); + + // TX_SCRIPTHASH with incorrectly sized script hash + s.clear(); + s << OP_HASH160 << std::vector(21, 0x01) << OP_EQUAL; + BOOST_CHECK(!Solver(s, whichType, solutions)); + + // TX_MULTISIG 0/2 + s.clear(); + s << OP_0 << ToByteVector(pubkey) << OP_1 << OP_CHECKMULTISIG; + BOOST_CHECK(!Solver(s, whichType, solutions)); + + // TX_MULTISIG 2/1 + s.clear(); + s << OP_2 << ToByteVector(pubkey) << OP_1 << OP_CHECKMULTISIG; + BOOST_CHECK(!Solver(s, whichType, solutions)); + + // TX_MULTISIG n = 2 with 1 pubkey + s.clear(); + s << OP_1 << ToByteVector(pubkey) << OP_2 << OP_CHECKMULTISIG; + BOOST_CHECK(!Solver(s, whichType, solutions)); + + // TX_MULTISIG n = 1 with 0 pubkeys + s.clear(); + s << OP_1 << OP_1 << OP_CHECKMULTISIG; + BOOST_CHECK(!Solver(s, whichType, solutions)); + + // TX_NULL_DATA with other opcodes + s.clear(); + s << OP_RETURN << std::vector({75}) << OP_ADD; + BOOST_CHECK(!Solver(s, whichType, solutions)); + + // TX_WITNESS with unknown version + s.clear(); + s << OP_1 << ToByteVector(pubkey); + BOOST_CHECK(!Solver(s, whichType, solutions)); + + // TX_WITNESS with incorrect program size + s.clear(); + s << OP_0 << std::vector(19, 0x01); + BOOST_CHECK(!Solver(s, whichType, solutions)); +} + +BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination) +{ + CKey key; + CPubKey pubkey; + key.MakeNewKey(true); + pubkey = key.GetPubKey(); + + CScript s; + CTxDestination address; + + // TX_PUBKEY + s.clear(); + s << ToByteVector(pubkey) << OP_CHECKSIG; + BOOST_CHECK(ExtractDestination(s, address)); + BOOST_CHECK(boost::get(&address) && + *boost::get(&address) == pubkey.GetID()); + + // TX_PUBKEYHASH + s.clear(); + s << OP_DUP << OP_HASH160 << ToByteVector(pubkey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; + BOOST_CHECK(ExtractDestination(s, address)); + BOOST_CHECK(boost::get(&address) && + *boost::get(&address) == pubkey.GetID()); + + // TX_SCRIPTHASH + CScript redeemScript(s); // initialize with leftover P2PKH script + s.clear(); + s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; + BOOST_CHECK(ExtractDestination(s, address)); + BOOST_CHECK(boost::get(&address) && + *boost::get(&address) == CScriptID(redeemScript)); + + // TX_MULTISIG + s.clear(); + s << OP_1 << ToByteVector(pubkey) << OP_1 << OP_CHECKMULTISIG; + BOOST_CHECK(!ExtractDestination(s, address)); + + // TX_NULL_DATA + s.clear(); + s << OP_RETURN << std::vector({75}); + BOOST_CHECK(!ExtractDestination(s, address)); + + // TX_WITNESS_V0_KEYHASH + s.clear(); + s << OP_0 << ToByteVector(pubkey); + BOOST_CHECK(!ExtractDestination(s, address)); + + // TX_WITNESS_V0_SCRIPTHASH + s.clear(); + s << OP_0 << ToByteVector(CScriptID(redeemScript)); + BOOST_CHECK(!ExtractDestination(s, address)); +} + +BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations) +{ + CKey keys[3]; + CPubKey pubkeys[3]; + for (int i = 0; i < 3; i++) { + keys[i].MakeNewKey(true); + pubkeys[i] = keys[i].GetPubKey(); + } + + CScript s; + txnouttype whichType; + std::vector addresses; + int nRequired; + + // TX_PUBKEY + s.clear(); + s << ToByteVector(pubkeys[0]) << OP_CHECKSIG; + BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired)); + BOOST_CHECK_EQUAL(whichType, TX_PUBKEY); + BOOST_CHECK_EQUAL(addresses.size(), 1); + BOOST_CHECK_EQUAL(nRequired, 1); + BOOST_CHECK(boost::get(&addresses[0]) && + *boost::get(&addresses[0]) == pubkeys[0].GetID()); + + // TX_PUBKEYHASH + s.clear(); + s << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; + BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired)); + BOOST_CHECK_EQUAL(whichType, TX_PUBKEYHASH); + BOOST_CHECK_EQUAL(addresses.size(), 1); + BOOST_CHECK_EQUAL(nRequired, 1); + BOOST_CHECK(boost::get(&addresses[0]) && + *boost::get(&addresses[0]) == pubkeys[0].GetID()); + + // TX_SCRIPTHASH + CScript redeemScript(s); // initialize with leftover P2PKH script + s.clear(); + s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; + BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired)); + BOOST_CHECK_EQUAL(whichType, TX_SCRIPTHASH); + BOOST_CHECK_EQUAL(addresses.size(), 1); + BOOST_CHECK_EQUAL(nRequired, 1); + BOOST_CHECK(boost::get(&addresses[0]) && + *boost::get(&addresses[0]) == CScriptID(redeemScript)); + + // TX_MULTISIG + s.clear(); + s << OP_2 << + ToByteVector(pubkeys[0]) << + ToByteVector(pubkeys[1]) << + OP_2 << OP_CHECKMULTISIG; + BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired)); + BOOST_CHECK_EQUAL(whichType, TX_MULTISIG); + BOOST_CHECK_EQUAL(addresses.size(), 2); + BOOST_CHECK_EQUAL(nRequired, 2); + BOOST_CHECK(boost::get(&addresses[0]) && + *boost::get(&addresses[0]) == pubkeys[0].GetID()); + BOOST_CHECK(boost::get(&addresses[1]) && + *boost::get(&addresses[1]) == pubkeys[1].GetID()); + + // TX_NULL_DATA + s.clear(); + s << OP_RETURN << std::vector({75}); + BOOST_CHECK(!ExtractDestinations(s, whichType, addresses, nRequired)); + + // TX_WITNESS_V0_KEYHASH + s.clear(); + s << OP_0 << ToByteVector(pubkeys[0].GetID()); + BOOST_CHECK(!ExtractDestinations(s, whichType, addresses, nRequired)); + + // TX_WITNESS_V0_SCRIPTHASH + s.clear(); + s << OP_0 << ToByteVector(CScriptID(redeemScript)); + BOOST_CHECK(!ExtractDestinations(s, whichType, addresses, nRequired)); +} + +BOOST_AUTO_TEST_CASE(script_standard_GetScriptFor_) +{ + CKey keys[3]; + CPubKey pubkeys[3]; + for (int i = 0; i < 3; i++) { + keys[i].MakeNewKey(true); + pubkeys[i] = keys[i].GetPubKey(); + } + + CScript expected, result; + + // CKeyID + expected.clear(); + expected << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; + result = GetScriptForDestination(pubkeys[0].GetID()); + BOOST_CHECK(result == expected); + + // CScriptID + CScript redeemScript(result); + expected.clear(); + expected << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; + result = GetScriptForDestination(CScriptID(redeemScript)); + BOOST_CHECK(result == expected); + + // CNoDestination + expected.clear(); + result = GetScriptForDestination(CNoDestination()); + BOOST_CHECK(result == expected); + + // GetScriptForRawPubKey + expected.clear(); + expected << ToByteVector(pubkeys[0]) << OP_CHECKSIG; + result = GetScriptForRawPubKey(pubkeys[0]); + BOOST_CHECK(result == expected); + + // GetScriptForMultisig + expected.clear(); + expected << OP_2 << + ToByteVector(pubkeys[0]) << + ToByteVector(pubkeys[1]) << + ToByteVector(pubkeys[2]) << + OP_3 << OP_CHECKMULTISIG; + result = GetScriptForMultisig(2, std::vector(pubkeys, pubkeys + 3)); + BOOST_CHECK(result == expected); + + // GetScriptForWitness + CScript witnessScript; + + witnessScript << ToByteVector(pubkeys[0]) << OP_CHECKSIG; + expected.clear(); + expected << OP_0 << ToByteVector(pubkeys[0].GetID()); + result = GetScriptForWitness(witnessScript); + BOOST_CHECK(result == expected); + + witnessScript.clear(); + witnessScript << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; + result = GetScriptForWitness(witnessScript); + BOOST_CHECK(result == expected); + + witnessScript.clear(); + witnessScript << OP_1 << ToByteVector(pubkeys[0]) << OP_1 << OP_CHECKMULTISIG; + + uint256 scriptHash; + CSHA256().Write(&witnessScript[0], witnessScript.size()).Finalize(scriptHash.begin()); + + expected.clear(); + expected << OP_0 << ToByteVector(scriptHash); + result = GetScriptForWitness(witnessScript); + BOOST_CHECK(result == expected); +} + +BOOST_AUTO_TEST_SUITE_END() From 7a1e873b27b790c965d9927ecd465710dc103136 Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Tue, 22 Aug 2017 17:47:59 -0700 Subject: [PATCH 267/382] [script] Unit tests for IsMine Does not test watch-only addresses. --- src/script/ismine.cpp | 2 + src/test/script_standard_tests.cpp | 367 ++++++++++++++++++++++++++++- 2 files changed, 363 insertions(+), 6 deletions(-) diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp index 0a3961973..c3aade177 100644 --- a/src/script/ismine.cpp +++ b/src/script/ismine.cpp @@ -46,6 +46,8 @@ isminetype IsMine(const CKeyStore &keystore, const CTxDestination& dest, bool& i isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& isInvalid, SigVersion sigversion) { + isInvalid = false; + std::vector vSolutions; txnouttype whichType; if (!Solver(scriptPubKey, whichType, vSolutions)) { diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp index e6a63ad54..3d17a0dbb 100644 --- a/src/test/script_standard_tests.cpp +++ b/src/test/script_standard_tests.cpp @@ -3,6 +3,8 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "key.h" +#include "keystore.h" +#include "script/ismine.h" #include "script/script.h" #include "script/script_error.h" #include "script/standard.h" @@ -81,7 +83,6 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success) BOOST_CHECK(solutions[4] == std::vector({3})); // TX_NULL_DATA - solutions.clear(); s.clear(); s << OP_RETURN << std::vector({0}) << @@ -92,7 +93,6 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success) BOOST_CHECK_EQUAL(solutions.size(), 0); // TX_WITNESS_V0_KEYHASH - solutions.clear(); s.clear(); s << OP_0 << ToByteVector(pubkeys[0].GetID()); BOOST_CHECK(Solver(s, whichType, solutions)); @@ -102,9 +102,9 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success) // TX_WITNESS_V0_SCRIPTHASH uint256 scriptHash; - CSHA256().Write(&redeemScript[0], redeemScript.size()).Finalize(scriptHash.begin()); + CSHA256().Write(&redeemScript[0], redeemScript.size()) + .Finalize(scriptHash.begin()); - solutions.clear(); s.clear(); s << OP_0 << ToByteVector(scriptHash); BOOST_CHECK(Solver(s, whichType, solutions)); @@ -113,7 +113,6 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success) BOOST_CHECK(solutions[0] == ToByteVector(scriptHash)); // TX_NONSTANDARD - solutions.clear(); s.clear(); s << OP_9 << OP_ADD << OP_11 << OP_EQUAL; BOOST_CHECK(!Solver(s, whichType, solutions)); @@ -374,7 +373,8 @@ BOOST_AUTO_TEST_CASE(script_standard_GetScriptFor_) witnessScript << OP_1 << ToByteVector(pubkeys[0]) << OP_1 << OP_CHECKMULTISIG; uint256 scriptHash; - CSHA256().Write(&witnessScript[0], witnessScript.size()).Finalize(scriptHash.begin()); + CSHA256().Write(&witnessScript[0], witnessScript.size()) + .Finalize(scriptHash.begin()); expected.clear(); expected << OP_0 << ToByteVector(scriptHash); @@ -382,4 +382,359 @@ BOOST_AUTO_TEST_CASE(script_standard_GetScriptFor_) BOOST_CHECK(result == expected); } +BOOST_AUTO_TEST_CASE(script_standard_IsMine) +{ + CKey keys[2]; + CPubKey pubkeys[2]; + for (int i = 0; i < 2; i++) { + keys[i].MakeNewKey(true); + pubkeys[i] = keys[i].GetPubKey(); + } + + CKey uncompressedKey; + uncompressedKey.MakeNewKey(false); + CPubKey uncompressedPubkey = uncompressedKey.GetPubKey(); + + CScript scriptPubKey; + isminetype result; + bool isInvalid; + + // P2PK compressed + { + CBasicKeyStore keystore; + scriptPubKey.clear(); + scriptPubKey << ToByteVector(pubkeys[0]) << OP_CHECKSIG; + + // Keystore does not have key + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has key + keystore.AddKey(keys[0]); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); + BOOST_CHECK(!isInvalid); + } + + // P2PK uncompressed + { + CBasicKeyStore keystore; + scriptPubKey.clear(); + scriptPubKey << ToByteVector(uncompressedPubkey) << OP_CHECKSIG; + + // Keystore does not have key + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has key + keystore.AddKey(uncompressedKey); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); + BOOST_CHECK(!isInvalid); + } + + // P2PKH compressed + { + CBasicKeyStore keystore; + scriptPubKey.clear(); + scriptPubKey << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; + + // Keystore does not have key + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has key + keystore.AddKey(keys[0]); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); + BOOST_CHECK(!isInvalid); + } + + // P2PKH uncompressed + { + CBasicKeyStore keystore; + scriptPubKey.clear(); + scriptPubKey << OP_DUP << OP_HASH160 << ToByteVector(uncompressedPubkey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; + + // Keystore does not have key + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has key + keystore.AddKey(uncompressedKey); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); + BOOST_CHECK(!isInvalid); + } + + // P2SH + { + CBasicKeyStore keystore; + + CScript redeemScript; + redeemScript << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; + + scriptPubKey.clear(); + scriptPubKey << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; + + // Keystore does not have redeemScript or key + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has redeemScript but no key + keystore.AddCScript(redeemScript); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has redeemScript and key + keystore.AddKey(keys[0]); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); + BOOST_CHECK(!isInvalid); + } + + // P2WPKH compressed + { + CBasicKeyStore keystore; + keystore.AddKey(keys[0]); + + scriptPubKey.clear(); + scriptPubKey << OP_0 << ToByteVector(pubkeys[0].GetID()); + + // Keystore has key, but no P2SH redeemScript + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has key and P2SH redeemScript + keystore.AddCScript(scriptPubKey); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); + BOOST_CHECK(!isInvalid); + } + + // P2WPKH uncompressed + { + CBasicKeyStore keystore; + keystore.AddKey(uncompressedKey); + + scriptPubKey.clear(); + scriptPubKey << OP_0 << ToByteVector(uncompressedPubkey.GetID()); + + // Keystore has key, but no P2SH redeemScript + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has key and P2SH redeemScript + keystore.AddCScript(scriptPubKey); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(isInvalid); + } + + // scriptPubKey multisig + { + CBasicKeyStore keystore; + + scriptPubKey.clear(); + scriptPubKey << OP_2 << + ToByteVector(uncompressedPubkey) << + ToByteVector(pubkeys[1]) << + OP_2 << OP_CHECKMULTISIG; + + // Keystore does not have any keys + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has 1/2 keys + keystore.AddKey(uncompressedKey); + + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has 2/2 keys + keystore.AddKey(keys[1]); + + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); + BOOST_CHECK(!isInvalid); + } + + // P2SH multisig + { + CBasicKeyStore keystore; + keystore.AddKey(uncompressedKey); + keystore.AddKey(keys[1]); + + CScript redeemScript; + redeemScript << OP_2 << + ToByteVector(uncompressedPubkey) << + ToByteVector(pubkeys[1]) << + OP_2 << OP_CHECKMULTISIG; + + scriptPubKey.clear(); + scriptPubKey << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; + + // Keystore has no redeemScript + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has redeemScript + keystore.AddCScript(redeemScript); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); + BOOST_CHECK(!isInvalid); + } + + // P2WSH multisig with compressed keys + { + CBasicKeyStore keystore; + keystore.AddKey(keys[0]); + keystore.AddKey(keys[1]); + + CScript witnessScript; + witnessScript << OP_2 << + ToByteVector(pubkeys[0]) << + ToByteVector(pubkeys[1]) << + OP_2 << OP_CHECKMULTISIG; + + uint256 scriptHash; + CSHA256().Write(&witnessScript[0], witnessScript.size()) + .Finalize(scriptHash.begin()); + + scriptPubKey.clear(); + scriptPubKey << OP_0 << ToByteVector(scriptHash); + + // Keystore has keys, but no witnessScript or P2SH redeemScript + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has keys and witnessScript, but no P2SH redeemScript + keystore.AddCScript(witnessScript); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has keys, witnessScript, P2SH redeemScript + keystore.AddCScript(scriptPubKey); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); + BOOST_CHECK(!isInvalid); + } + + // P2WSH multisig with uncompressed key + { + CBasicKeyStore keystore; + keystore.AddKey(uncompressedKey); + keystore.AddKey(keys[1]); + + CScript witnessScript; + witnessScript << OP_2 << + ToByteVector(uncompressedPubkey) << + ToByteVector(pubkeys[1]) << + OP_2 << OP_CHECKMULTISIG; + + uint256 scriptHash; + CSHA256().Write(&witnessScript[0], witnessScript.size()) + .Finalize(scriptHash.begin()); + + scriptPubKey.clear(); + scriptPubKey << OP_0 << ToByteVector(scriptHash); + + // Keystore has keys, but no witnessScript or P2SH redeemScript + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has keys and witnessScript, but no P2SH redeemScript + keystore.AddCScript(witnessScript); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has keys, witnessScript, P2SH redeemScript + keystore.AddCScript(scriptPubKey); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(isInvalid); + } + + // P2WSH multisig wrapped in P2SH + { + CBasicKeyStore keystore; + + CScript witnessScript; + witnessScript << OP_2 << + ToByteVector(pubkeys[0]) << + ToByteVector(pubkeys[1]) << + OP_2 << OP_CHECKMULTISIG; + + uint256 scriptHash; + CSHA256().Write(&witnessScript[0], witnessScript.size()) + .Finalize(scriptHash.begin()); + + CScript redeemScript; + redeemScript << OP_0 << ToByteVector(scriptHash); + + scriptPubKey.clear(); + scriptPubKey << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; + + // Keystore has no witnessScript, P2SH redeemScript, or keys + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has witnessScript and P2SH redeemScript, but no keys + keystore.AddCScript(redeemScript); + keystore.AddCScript(witnessScript); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has keys, witnessScript, P2SH redeemScript + keystore.AddKey(keys[0]); + keystore.AddKey(keys[1]); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); + BOOST_CHECK(!isInvalid); + } + + // OP_RETURN + { + CBasicKeyStore keystore; + keystore.AddKey(keys[0]); + + scriptPubKey.clear(); + scriptPubKey << OP_RETURN << ToByteVector(pubkeys[0]); + + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + } + + // Nonstandard + { + CBasicKeyStore keystore; + keystore.AddKey(keys[0]); + + scriptPubKey.clear(); + scriptPubKey << OP_9 << OP_ADD << OP_11 << OP_EQUAL; + + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + } +} + BOOST_AUTO_TEST_SUITE_END() From 3a131b72446724b2d6322930d92348065f1c1891 Mon Sep 17 00:00:00 2001 From: Johnson Lau Date: Fri, 22 Sep 2017 14:27:03 +0800 Subject: [PATCH 268/382] Rename out to m_tx_out in CScriptCheck --- src/validation.cpp | 2 +- src/validation.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/validation.cpp b/src/validation.cpp index d1b080cc9..1d6454e1a 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1202,7 +1202,7 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight) bool CScriptCheck::operator()() { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; const CScriptWitness *witness = &ptxTo->vin[nIn].scriptWitness; - return VerifyScript(scriptSig, out.scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, out.nValue, cacheStore, *txdata), &error); + return VerifyScript(scriptSig, m_tx_out.scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, m_tx_out.nValue, cacheStore, *txdata), &error); } int GetSpendHeight(const CCoinsViewCache& inputs) diff --git a/src/validation.h b/src/validation.h index 69751b8c6..9cd9fe536 100644 --- a/src/validation.h +++ b/src/validation.h @@ -355,7 +355,7 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp = null class CScriptCheck { private: - CTxOut out; + CTxOut m_tx_out; const CTransaction *ptxTo; unsigned int nIn; unsigned int nFlags; @@ -366,13 +366,13 @@ class CScriptCheck public: CScriptCheck(): ptxTo(nullptr), nIn(0), nFlags(0), cacheStore(false), error(SCRIPT_ERR_UNKNOWN_ERROR) {} CScriptCheck(const CTxOut& outIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn, PrecomputedTransactionData* txdataIn) : - out(outIn), ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), error(SCRIPT_ERR_UNKNOWN_ERROR), txdata(txdataIn) { } + m_tx_out(outIn), ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), error(SCRIPT_ERR_UNKNOWN_ERROR), txdata(txdataIn) { } bool operator()(); void swap(CScriptCheck &check) { std::swap(ptxTo, check.ptxTo); - std::swap(out, check.out); + std::swap(m_tx_out, check.m_tx_out); std::swap(nIn, check.nIn); std::swap(nFlags, check.nFlags); std::swap(cacheStore, check.cacheStore); From 46c90437f9efbabcb5c810cd82b00a9e5d1c5cd7 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 21 Sep 2017 23:40:33 -0700 Subject: [PATCH 269/382] Remove some unused functions and methods In the case of CKey's destructor, it seems to have been an oversight in f4d1fc259 not to delete it. At this point, it results in the move constructors/assignment operators for CKey being deleted, which may have a performance impact. --- src/hash.h | 14 -------------- src/key.h | 5 ----- src/uint256.h | 2 -- 3 files changed, 21 deletions(-) diff --git a/src/hash.h b/src/hash.h index ad59bb181..474b13d65 100644 --- a/src/hash.h +++ b/src/hash.h @@ -88,20 +88,6 @@ inline uint256 Hash(const T1 p1begin, const T1 p1end, return result; } -/** Compute the 256-bit hash of the concatenation of three objects. */ -template -inline uint256 Hash(const T1 p1begin, const T1 p1end, - const T2 p2begin, const T2 p2end, - const T3 p3begin, const T3 p3end) { - static const unsigned char pblank[1] = {}; - uint256 result; - CHash256().Write(p1begin == p1end ? pblank : (const unsigned char*)&p1begin[0], (p1end - p1begin) * sizeof(p1begin[0])) - .Write(p2begin == p2end ? pblank : (const unsigned char*)&p2begin[0], (p2end - p2begin) * sizeof(p2begin[0])) - .Write(p3begin == p3end ? pblank : (const unsigned char*)&p3begin[0], (p3end - p3begin) * sizeof(p3begin[0])) - .Finalize((unsigned char*)&result); - return result; -} - /** Compute the 160-bit hash an object. */ template inline uint160 Hash160(const T1 pbegin, const T1 pend) diff --git a/src/key.h b/src/key.h index 151e63531..54b5be227 100644 --- a/src/key.h +++ b/src/key.h @@ -56,11 +56,6 @@ class CKey keydata.resize(32); } - //! Destructor (again necessary because of memlocking). - ~CKey() - { - } - friend bool operator==(const CKey& a, const CKey& b) { return a.fCompressed == b.fCompressed && diff --git a/src/uint256.h b/src/uint256.h index 3ed694d72..94a4f7fc3 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -111,7 +111,6 @@ class base_blob class uint160 : public base_blob<160> { public: uint160() {} - explicit uint160(const base_blob<160>& b) : base_blob<160>(b) {} explicit uint160(const std::vector& vch) : base_blob<160>(vch) {} }; @@ -123,7 +122,6 @@ class uint160 : public base_blob<160> { class uint256 : public base_blob<256> { public: uint256() {} - explicit uint256(const base_blob<256>& b) : base_blob<256>(b) {} explicit uint256(const std::vector& vch) : base_blob<256>(vch) {} /** A cheap hash function that just returns 64 bits from the result, it can be From 723aa1b8752c1d6c6c0a76059c532ebe2f406fc1 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 15 Sep 2017 11:50:45 +0200 Subject: [PATCH 270/382] qt: Backup former GUI settings on `-resetguisettings` Writes the GUI settings to `guisettings.bak` in the data directory before wiping them. This can be used to retroactively troubleshoot issues (e.g. #11262) where `-resetguisettings` solves the problem. --- doc/files.md | 1 + src/qt/optionsmodel.cpp | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/doc/files.md b/doc/files.md index 928977143..3d603445f 100644 --- a/doc/files.md +++ b/doc/files.md @@ -15,6 +15,7 @@ * wallet.dat: personal wallet (BDB) with keys and transactions * .cookie: session RPC authentication cookie (written at start when cookie authentication is used, deleted on shutdown): since 0.12.0 * onion_private_key: cached Tor hidden service private key for `-listenonion`: since 0.12.0 +* guisettings.ini.bak: backup of former GUI settings after `-resetguisettings` is used Only used in pre-0.8.0 --------------------- diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index e9960a01b..feb00a33b 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -151,10 +151,32 @@ void OptionsModel::Init(bool resetSettings) language = settings.value("language").toString(); } +/** Helper function to copy contents from one QSettings to another. + * By using allKeys this also covers nested settings in a hierarchy. + */ +static void CopySettings(QSettings& dst, const QSettings& src) +{ + for (const QString& key : src.allKeys()) { + dst.setValue(key, src.value(key)); + } +} + +/** Back up a QSettings to an ini-formatted file. */ +static void BackupSettings(const fs::path& filename, const QSettings& src) +{ + qWarning() << "Backing up GUI settings to" << GUIUtil::boostPathToQString(filename); + QSettings dst(GUIUtil::boostPathToQString(filename), QSettings::IniFormat); + dst.clear(); + CopySettings(dst, src); +} + void OptionsModel::Reset() { QSettings settings; + // Backup old settings to chain-specific datadir for troubleshooting + BackupSettings(GetDataDir(true) / "guisettings.ini.bak", settings); + // Save the strDataDir setting QString dataDir = Intro::getDefaultDataDirectory(); dataDir = settings.value("strDataDir", dataDir).toString(); From 1e46ebdf8618e585568ffc1b093c79cc9be07b57 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 25 Aug 2017 17:43:11 -0700 Subject: [PATCH 271/382] Implement {Encode,Decode}Destination without CBitcoinAddress --- src/base58.cpp | 130 +++++++++++++++++++------------------------------ src/base58.h | 1 - 2 files changed, 49 insertions(+), 82 deletions(-) diff --git a/src/base58.cpp b/src/base58.cpp index 3b907c20d..027271157 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -5,16 +5,17 @@ #include "base58.h" #include "hash.h" +#include "script/script.h" #include "uint256.h" -#include -#include -#include -#include -#include #include #include +#include +#include +#include + + /** All alphanumeric characters except for "0", "I", "O", and "l" */ static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; @@ -212,86 +213,55 @@ int CBase58Data::CompareTo(const CBase58Data& b58) const namespace { -/** base58-encoded Bitcoin addresses. - * Public-key-hash-addresses have version 0 (or 111 testnet). - * The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key. - * Script-hash-addresses have version 5 (or 196 testnet). - * The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script. - */ -class CBitcoinAddress : public CBase58Data { -public: - bool Set(const CKeyID &id); - bool Set(const CScriptID &id); - bool Set(const CTxDestination &dest); - bool IsValid() const; - bool IsValid(const CChainParams ¶ms) const; - - CBitcoinAddress() {} - CBitcoinAddress(const CTxDestination &dest) { Set(dest); } - CBitcoinAddress(const std::string& strAddress) { SetString(strAddress); } - CBitcoinAddress(const char* pszAddress) { SetString(pszAddress); } - - CTxDestination Get() const; -}; - -class CBitcoinAddressVisitor : public boost::static_visitor +class DestinationEncoder : public boost::static_visitor { private: - CBitcoinAddress* addr; + const CChainParams& m_params; public: - explicit CBitcoinAddressVisitor(CBitcoinAddress* addrIn) : addr(addrIn) {} - - bool operator()(const CKeyID& id) const { return addr->Set(id); } - bool operator()(const CScriptID& id) const { return addr->Set(id); } - bool operator()(const CNoDestination& no) const { return false; } -}; - -} // namespace - -bool CBitcoinAddress::Set(const CKeyID& id) -{ - SetData(Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS), &id, 20); - return true; -} + DestinationEncoder(const CChainParams& params) : m_params(params) {} -bool CBitcoinAddress::Set(const CScriptID& id) -{ - SetData(Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS), &id, 20); - return true; -} - -bool CBitcoinAddress::Set(const CTxDestination& dest) -{ - return boost::apply_visitor(CBitcoinAddressVisitor(this), dest); -} + std::string operator()(const CKeyID& id) const + { + std::vector data = m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS); + data.insert(data.end(), id.begin(), id.end()); + return EncodeBase58Check(data); + } -bool CBitcoinAddress::IsValid() const -{ - return IsValid(Params()); -} + std::string operator()(const CScriptID& id) const + { + std::vector data = m_params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); + data.insert(data.end(), id.begin(), id.end()); + return EncodeBase58Check(data); + } -bool CBitcoinAddress::IsValid(const CChainParams& params) const -{ - bool fCorrectSize = vchData.size() == 20; - bool fKnownVersion = vchVersion == params.Base58Prefix(CChainParams::PUBKEY_ADDRESS) || - vchVersion == params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); - return fCorrectSize && fKnownVersion; -} + std::string operator()(const CNoDestination& no) const { return ""; } +}; -CTxDestination CBitcoinAddress::Get() const +CTxDestination DecodeDestination(const std::string& str, const CChainParams& params) { - if (!IsValid()) - return CNoDestination(); - uint160 id; - memcpy(&id, vchData.data(), 20); - if (vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS)) - return CKeyID(id); - else if (vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS)) - return CScriptID(id); - else - return CNoDestination(); + std::vector data; + uint160 hash; + if (DecodeBase58Check(str, data)) { + // base58-encoded Bitcoin addresses. + // Public-key-hash-addresses have version 0 (or 111 testnet). + // The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key. + const std::vector& pubkey_prefix = params.Base58Prefix(CChainParams::PUBKEY_ADDRESS); + if (data.size() == hash.size() + pubkey_prefix.size() && std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin())) { + std::copy(data.begin() + pubkey_prefix.size(), data.end(), hash.begin()); + return CKeyID(hash); + } + // Script-hash-addresses have version 5 (or 196 testnet). + // The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script. + const std::vector& script_prefix = params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); + if (data.size() == hash.size() + script_prefix.size() && std::equal(script_prefix.begin(), script_prefix.end(), data.begin())) { + std::copy(data.begin() + script_prefix.size(), data.end(), hash.begin()); + return CScriptID(hash); + } + } + return CNoDestination(); } +} // namespace void CBitcoinSecret::SetKey(const CKey& vchSecret) { @@ -328,22 +298,20 @@ bool CBitcoinSecret::SetString(const std::string& strSecret) std::string EncodeDestination(const CTxDestination& dest) { - CBitcoinAddress addr(dest); - if (!addr.IsValid()) return ""; - return addr.ToString(); + return boost::apply_visitor(DestinationEncoder(Params()), dest); } CTxDestination DecodeDestination(const std::string& str) { - return CBitcoinAddress(str).Get(); + return DecodeDestination(str, Params()); } bool IsValidDestinationString(const std::string& str, const CChainParams& params) { - return CBitcoinAddress(str).IsValid(params); + return IsValidDestination(DecodeDestination(str, params)); } bool IsValidDestinationString(const std::string& str) { - return CBitcoinAddress(str).IsValid(); + return IsValidDestinationString(str, Params()); } diff --git a/src/base58.h b/src/base58.h index 4b895ca02..9dc423424 100644 --- a/src/base58.h +++ b/src/base58.h @@ -17,7 +17,6 @@ #include "chainparams.h" #include "key.h" #include "pubkey.h" -#include "script/script.h" #include "script/standard.h" #include "support/allocators/zeroafterfree.h" From 8849130415f69bb506950b0eb247e3e97c3cd1a3 Mon Sep 17 00:00:00 2001 From: MeshCollider Date: Sun, 24 Sep 2017 14:00:26 +1300 Subject: [PATCH 272/382] Remove lxcbr0 lines from gitian-build.sh --- contrib/gitian-build.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/contrib/gitian-build.sh b/contrib/gitian-build.sh index d94c7f4f8..8fdec21b0 100755 --- a/contrib/gitian-build.sh +++ b/contrib/gitian-build.sh @@ -179,8 +179,6 @@ done if [[ $lxc = true ]] then export USE_LXC=1 - export LXC_BRIDGE=lxcbr0 - sudo ifconfig lxcbr0 up 10.0.2.2 fi # Check for OSX SDK From 204cc98261f68233aec6ce5e4ce75182cfd82677 Mon Sep 17 00:00:00 2001 From: Shooter Date: Sun, 24 Sep 2017 17:39:41 +0800 Subject: [PATCH 273/382] fix link error The `perform-gitian-builds` is not exists, repalce `perform-gitian-builds` with `setup-and-perform-gitian-builds`. --- doc/gitian-building.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/gitian-building.md b/doc/gitian-building.md index 636686b39..f88d14eb9 100644 --- a/doc/gitian-building.md +++ b/doc/gitian-building.md @@ -353,7 +353,7 @@ Building Bitcoin Core ---------------- To build Bitcoin Core (for Linux, OS X and Windows) just follow the steps under 'perform -Gitian builds' in [doc/release-process.md](release-process.md#perform-gitian-builds) in the bitcoin repository. +Gitian builds' in [doc/release-process.md](release-process.md#setup-and-perform-gitian-builds) in the bitcoin repository. This may take some time as it will build all the dependencies needed for each descriptor. These dependencies will be cached after a successful build to avoid rebuilding them when possible. From 90ab62c4512942f1d252259a892e1cbeeb92789e Mon Sep 17 00:00:00 2001 From: John Newbery Date: Fri, 22 Sep 2017 16:10:25 -0400 Subject: [PATCH 274/382] [docs] document scripted-diff --- doc/developer-notes.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/doc/developer-notes.md b/doc/developer-notes.md index 8fdae2534..33c6ab9cb 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -549,6 +549,26 @@ Git and GitHub tips or `git fetch upstream-pull`. Afterwards, you can use `upstream-pull/NUMBER/head` in arguments to `git show`, `git checkout` and anywhere a commit id would be acceptable to see the changes from pull request NUMBER. +Scripted diffs +-------------- + +For reformatting and refactoring commits where the changes can be easily automated using a bash script, we use +scripted-diff commits. The bash script is included in the commit message and our Travis CI job checks that +the result of the script is identical to the commit. This aids reviewers since they can verify that the script +does exactly what it's supposed to do. It is also helpful for rebasing (since the same script can just be re-run +on the new master commit). + +To create a scripted-diff: + +- start the commit message with `scripted-diff:` (and then a description of the diff on the same line) +- in the commit message include the bash script between lines containing just the following text: + - `-BEGIN VERIFY SCRIPT-` + - `-END VERIFY SCRIPT-` + +The scripted-diff is verified by the tool `contrib/devtools/commit-script-check.sh` + +Commit `bb81e173` is an example of a scripted-diff. + RPC interface guidelines -------------------------- From fa082b4ee05e83ffa0c7c3e6e433595343707c59 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Fri, 25 Aug 2017 19:27:50 +0200 Subject: [PATCH 275/382] doc: move gitian building to external repo This reduces the overhead in the git repo due to binary blobs of the png files. Also, the documentation can be updated independent of any tags and release cycles. --- doc/gitian-building.md | 482 +----------------- .../all_files_in_one_partition.png | Bin 3350 -> 0 bytes doc/gitian-building/create_new_vm.png | Bin 119839 -> 0 bytes .../create_vm_file_location_size.png | Bin 111942 -> 0 bytes doc/gitian-building/create_vm_hard_disk.png | Bin 123400 -> 0 bytes .../create_vm_hard_disk_file_type.png | Bin 170503 -> 0 bytes doc/gitian-building/create_vm_memsize.png | Bin 22158 -> 0 bytes .../create_vm_storage_physical_hard_disk.png | Bin 181681 -> 0 bytes .../debian_install_10_configure_clock.png | Bin 7892 -> 0 bytes .../debian_install_11_partition_disks.png | Bin 9511 -> 0 bytes .../debian_install_12_choose_disk.png | Bin 6613 -> 0 bytes .../debian_install_14_finish.png | Bin 10794 -> 0 bytes .../debian_install_15_write_changes.png | Bin 8790 -> 0 bytes .../debian_install_16_choose_a_mirror.png | Bin 11134 -> 0 bytes .../debian_install_18_proxy_settings.png | Bin 7582 -> 0 bytes .../debian_install_19_software_selection.png | Bin 8767 -> 0 bytes .../debian_install_1_boot_menu.png | Bin 110818 -> 0 bytes .../debian_install_20_install_grub.png | Bin 9784 -> 0 bytes ...ian_install_21_install_grub_bootloader.png | Bin 8878 -> 0 bytes .../debian_install_22_finish_installation.png | Bin 6964 -> 0 bytes .../debian_install_2_select_a_language.png | Bin 13131 -> 0 bytes .../debian_install_3_select_location.png | Bin 10388 -> 0 bytes .../debian_install_4_configure_keyboard.png | Bin 10224 -> 0 bytes ...debian_install_5_configure_the_network.png | Bin 7612 -> 0 bytes .../debian_install_6_domain_name.png | Bin 6526 -> 0 bytes ...debian_install_6a_set_up_root_password.png | Bin 11876 -> 0 bytes .../debian_install_7_set_up_user_fullname.png | Bin 8407 -> 0 bytes .../debian_install_8_set_up_username.png | Bin 7058 -> 0 bytes .../debian_install_9_user_password.png | Bin 6322 -> 0 bytes doc/gitian-building/debian_root_login.png | Bin 7028 -> 0 bytes doc/gitian-building/network_settings.png | Bin 72185 -> 0 bytes doc/gitian-building/port_forwarding_rules.png | Bin 44052 -> 0 bytes doc/gitian-building/select_startup_disk.png | Bin 72785 -> 0 bytes doc/gitian-building/system_settings.png | Bin 76448 -> 0 bytes 34 files changed, 1 insertion(+), 481 deletions(-) delete mode 100644 doc/gitian-building/all_files_in_one_partition.png delete mode 100644 doc/gitian-building/create_new_vm.png delete mode 100644 doc/gitian-building/create_vm_file_location_size.png delete mode 100644 doc/gitian-building/create_vm_hard_disk.png delete mode 100644 doc/gitian-building/create_vm_hard_disk_file_type.png delete mode 100644 doc/gitian-building/create_vm_memsize.png delete mode 100644 doc/gitian-building/create_vm_storage_physical_hard_disk.png delete mode 100644 doc/gitian-building/debian_install_10_configure_clock.png delete mode 100644 doc/gitian-building/debian_install_11_partition_disks.png delete mode 100644 doc/gitian-building/debian_install_12_choose_disk.png delete mode 100644 doc/gitian-building/debian_install_14_finish.png delete mode 100644 doc/gitian-building/debian_install_15_write_changes.png delete mode 100644 doc/gitian-building/debian_install_16_choose_a_mirror.png delete mode 100644 doc/gitian-building/debian_install_18_proxy_settings.png delete mode 100644 doc/gitian-building/debian_install_19_software_selection.png delete mode 100644 doc/gitian-building/debian_install_1_boot_menu.png delete mode 100644 doc/gitian-building/debian_install_20_install_grub.png delete mode 100644 doc/gitian-building/debian_install_21_install_grub_bootloader.png delete mode 100644 doc/gitian-building/debian_install_22_finish_installation.png delete mode 100644 doc/gitian-building/debian_install_2_select_a_language.png delete mode 100644 doc/gitian-building/debian_install_3_select_location.png delete mode 100644 doc/gitian-building/debian_install_4_configure_keyboard.png delete mode 100644 doc/gitian-building/debian_install_5_configure_the_network.png delete mode 100644 doc/gitian-building/debian_install_6_domain_name.png delete mode 100644 doc/gitian-building/debian_install_6a_set_up_root_password.png delete mode 100644 doc/gitian-building/debian_install_7_set_up_user_fullname.png delete mode 100644 doc/gitian-building/debian_install_8_set_up_username.png delete mode 100644 doc/gitian-building/debian_install_9_user_password.png delete mode 100644 doc/gitian-building/debian_root_login.png delete mode 100644 doc/gitian-building/network_settings.png delete mode 100644 doc/gitian-building/port_forwarding_rules.png delete mode 100644 doc/gitian-building/select_startup_disk.png delete mode 100644 doc/gitian-building/system_settings.png diff --git a/doc/gitian-building.md b/doc/gitian-building.md index f88d14eb9..3a48f4a0b 100644 --- a/doc/gitian-building.md +++ b/doc/gitian-building.md @@ -1,484 +1,4 @@ Gitian building ================ -*Setup instructions for a Gitian build of Bitcoin Core using a Debian VM or physical system.* - -Gitian is the deterministic build process that is used to build the Bitcoin -Core executables. It provides a way to be reasonably sure that the -executables are really built from the source on GitHub. It also makes sure that -the same, tested dependencies are used and statically built into the executable. - -Multiple developers build the source code by following a specific descriptor -("recipe"), cryptographically sign the result, and upload the resulting signature. -These results are compared and only if they match, the build is accepted and uploaded -to bitcoin.org. - -More independent Gitian builders are needed, which is why this guide exists. -It is preferred you follow these steps yourself instead of using someone else's -VM image to avoid 'contaminating' the build. - -Table of Contents ------------------- - -- [Create a new VirtualBox VM](#create-a-new-virtualbox-vm) -- [Connecting to the VM](#connecting-to-the-vm) -- [Setting up Debian for Gitian building](#setting-up-debian-for-gitian-building) -- [Installing Gitian](#installing-gitian) -- [Setting up the Gitian image](#setting-up-the-gitian-image) -- [Getting and building the inputs](#getting-and-building-the-inputs) -- [Building Bitcoin Core](#building-bitcoin-core) -- [Building an alternative repository](#building-an-alternative-repository) -- [Signing externally](#signing-externally) -- [Uploading signatures](#uploading-signatures) - -Preparing the Gitian builder host ---------------------------------- - -The first step is to prepare the host environment that will be used to perform the Gitian builds. -This guide explains how to set up the environment, and how to start the builds. - -Debian Linux was chosen as the host distribution because it has a lightweight install (in contrast to Ubuntu) and is readily available. -Any kind of virtualization can be used, for example: -- [VirtualBox](https://www.virtualbox.org/) (covered by this guide) -- [KVM](http://www.linux-kvm.org/page/Main_Page) -- [LXC](https://linuxcontainers.org/), see also [Gitian host docker container](https://github.com/gdm85/tenku/tree/master/docker/gitian-bitcoin-host/README.md). - -You can also install Gitian on actual hardware instead of using virtualization. - -Create a new VirtualBox VM ---------------------------- -In the VirtualBox GUI click "New" and choose the following parameters in the wizard: - -![](gitian-building/create_new_vm.png) - -- Type: Linux, Debian (64-bit) - -![](gitian-building/create_vm_memsize.png) - -- Memory Size: at least 3000MB, anything less and the build might not complete. - -![](gitian-building/create_vm_hard_disk.png) - -- Hard Disk: Create a virtual hard disk now - -![](gitian-building/create_vm_hard_disk_file_type.png) - -- Hard Disk file type: Use the default, VDI (VirtualBox Disk Image) - -![](gitian-building/create_vm_storage_physical_hard_disk.png) - -- Storage on physical hard disk: Dynamically Allocated - -![](gitian-building/create_vm_file_location_size.png) - -- File location and size: at least 40GB; as low as 20GB *may* be possible, but better to err on the safe side -- Click `Create` - -After creating the VM, we need to configure it. - -- Click the `Settings` button, then go to `System` tab and `Processor` sub-tab. Increase the number of processors to the number of cores on your machine if you want builds to be faster. - -![](gitian-building/system_settings.png) - -- Go to the `Network` tab. Adapter 1 should be attached to `NAT`. - -![](gitian-building/network_settings.png) - -- Click `Advanced`, then `Port Forwarding`. We want to set up a port through which we can reach the VM to get files in and out. -- Create a new rule by clicking the plus icon. - -![](gitian-building/port_forwarding_rules.png) - -- Set up the new rule the following way: - - Name: `SSH` - - Protocol: `TCP` - - Leave Host IP empty - - Host Port: `22222` - - Leave Guest IP empty - - Guest Port: `22` - -- Click `Ok` twice to save. - -Get the [Debian 8.x net installer](http://cdimage.debian.org/mirror/cdimage/archive/8.5.0/amd64/iso-cd/debian-8.5.0-amd64-netinst.iso) (a more recent minor version should also work, see also [Debian Network installation](https://www.debian.org/CD/netinst/)). -This DVD image can be [validated](https://www.debian.org/CD/verify) using a SHA256 hashing tool, for example on -Unixy OSes by entering the following in a terminal: - - echo "ad4e8c27c561ad8248d5ebc1d36eb172f884057bfeb2c22ead823f59fa8c3dff debian-8.5.0-amd64-netinst.iso" | sha256sum -c - # (must return OK) - -Then start the VM. On the first launch you will be asked for a CD or DVD image. Choose the downloaded ISO. - -![](gitian-building/select_startup_disk.png) - -Installing Debian ------------------- - -This section will explain how to install Debian on the newly created VM. - -- Choose the non-graphical installer. We do not need the graphical environment; it will only increase installation time and disk usage. - -![](gitian-building/debian_install_1_boot_menu.png) - -**Note**: Navigating in the Debian installer: -To keep a setting at the default and proceed, just press `Enter`. -To select a different button, press `Tab`. - -- Choose locale and keyboard settings (doesn't matter, you can just go with the defaults or select your own information) - -![](gitian-building/debian_install_2_select_a_language.png) -![](gitian-building/debian_install_3_select_location.png) -![](gitian-building/debian_install_4_configure_keyboard.png) - -- The VM will detect network settings using DHCP, this should all proceed automatically -- Configure the network: - - Hostname `debian`. - - Leave domain name empty. - -![](gitian-building/debian_install_5_configure_the_network.png) -![](gitian-building/debian_install_6_domain_name.png) - -- Choose a root password and enter it twice (remember it for later) - -![](gitian-building/debian_install_6a_set_up_root_password.png) - -- Name the new user `debian` (the full name doesn't matter, you can leave it empty) -- Set the account username as `debian` - -![](gitian-building/debian_install_7_set_up_user_fullname.png) -![](gitian-building/debian_install_8_set_up_username.png) - -- Choose a user password and enter it twice (remember it for later) - -![](gitian-building/debian_install_9_user_password.png) - -- The installer will set up the clock using a time server; this process should be automatic -- Set up the clock: choose a time zone (depends on the locale settings that you picked earlier; specifics don't matter) - -![](gitian-building/debian_install_10_configure_clock.png) - -- Disk setup - - Partitioning method: Guided - Use the entire disk - -![](gitian-building/debian_install_11_partition_disks.png) - - - Select disk to partition: SCSI1 (0,0,0) - -![](gitian-building/debian_install_12_choose_disk.png) - - - Partition Disks -> *All files in one partition* - -![](gitian-building/all_files_in_one_partition.png) - - - Finish partitioning and write changes to disk -> *Yes* (`Tab`, `Enter` to select the `Yes` button) - -![](gitian-building/debian_install_14_finish.png) -![](gitian-building/debian_install_15_write_changes.png) - -- The base system will be installed, this will take a minute or so -- Choose a mirror (any will do) - -![](gitian-building/debian_install_16_choose_a_mirror.png) - -- Enter proxy information (unless you are on an intranet, leave this empty) - -![](gitian-building/debian_install_18_proxy_settings.png) - -- Wait a bit while 'Select and install software' runs -- Participate in popularity contest -> *No* -- Choose software to install. We need just the base system. -- Make sure only 'SSH server' and 'Standard System Utilities' are checked -- Uncheck 'Debian Desktop Environment' and 'Print Server' - -![](gitian-building/debian_install_19_software_selection.png) - -- Install the GRUB boot loader to the master boot record? -> Yes - -![](gitian-building/debian_install_20_install_grub.png) - -- Device for boot loader installation -> ata-VBOX_HARDDISK - -![](gitian-building/debian_install_21_install_grub_bootloader.png) - -- Installation Complete -> *Continue* -- After installation, the VM will reboot and you will have a working Debian VM. Congratulations! - -![](gitian-building/debian_install_22_finish_installation.png) - - -After Installation -------------------- -The next step in the guide involves logging in as root via SSH. -SSH login for root users is disabled by default, so we'll enable that now. - -Login to the VM using username `root` and the root password you chose earlier. -You'll be presented with a screen similar to this. - -![](gitian-building/debian_root_login.png) - -Type: - -``` -sed -i 's/^PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config -``` -and press enter. Then, -``` -/etc/init.d/ssh restart -``` -and enter to restart SSH. Logout by typing 'logout' and pressing 'enter'. - -Connecting to the VM ----------------------- - -After the VM has booted you can connect to it using SSH, and files can be copied from and to the VM using a SFTP utility. -Connect to `localhost`, port `22222` (or the port configured when installing the VM). -On Windows you can use [putty](http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html) and [WinSCP](http://winscp.net/eng/index.php). - -For example, to connect as `root` from a Linux command prompt use - - $ ssh root@localhost -p 22222 - The authenticity of host '[localhost]:22222 ([127.0.0.1]:22222)' can't be established. - RSA key fingerprint is ae:f5:c8:9f:17:c6:c7:1b:c2:1b:12:31:1d:bb:d0:c7. - Are you sure you want to continue connecting (yes/no)? yes - Warning: Permanently added '[localhost]:22222' (RSA) to the list of known hosts. - root@localhost's password: (enter root password configured during install) - - The programs included with the Debian GNU/Linux system are free software; - the exact distribution terms for each program are described in the - individual files in /usr/share/doc/*/copyright. - - Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent - permitted by applicable law. - root@debian:~# - -Replace `root` with `debian` to log in as user. - -Setting up Debian for Gitian building --------------------------------------- - -In this section we will be setting up the Debian installation for Gitian building. - -First we need to log in as `root` to set up dependencies and make sure that our -user can use the sudo command. Type/paste the following in the terminal: - -```bash -apt-get install git ruby sudo apt-cacher-ng qemu-utils debootstrap lxc python-cheetah parted kpartx bridge-utils make ubuntu-archive-keyring curl -adduser debian sudo -``` - -Then set up LXC and the rest with the following, which is a complex jumble of settings and workarounds: - -```bash -# the version of lxc-start in Debian needs to run as root, so make sure -# that the build script can execute it without providing a password -echo "%sudo ALL=NOPASSWD: /usr/bin/lxc-start" > /etc/sudoers.d/gitian-lxc -echo "%sudo ALL=NOPASSWD: /usr/bin/lxc-execute" >> /etc/sudoers.d/gitian-lxc -# make /etc/rc.local script that sets up bridge between guest and host -echo '#!/bin/sh -e' > /etc/rc.local -echo 'brctl addbr br0' >> /etc/rc.local -echo 'ifconfig br0 10.0.3.2/24 up' >> /etc/rc.local -echo 'iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE' >> /etc/rc.local -echo 'echo 1 > /proc/sys/net/ipv4/ip_forward' >> /etc/rc.local -echo 'exit 0' >> /etc/rc.local -# make sure that USE_LXC is always set when logging in as debian, -# and configure LXC IP addresses -echo 'export USE_LXC=1' >> /home/debian/.profile -echo 'export GITIAN_HOST_IP=10.0.3.2' >> /home/debian/.profile -echo 'export LXC_GUEST_IP=10.0.3.5' >> /home/debian/.profile -reboot -``` - -At the end the VM is rebooted to make sure that the changes take effect. The steps in this -section only need to be performed once. - -Installing Gitian ------------------- - -Re-login as the user `debian` that was created during installation. -The rest of the steps in this guide will be performed as that user. - -There is no `python-vm-builder` package in Debian, so we need to install it from source ourselves, - -```bash -wget http://archive.ubuntu.com/ubuntu/pool/universe/v/vm-builder/vm-builder_0.12.4+bzr494.orig.tar.gz -echo "76cbf8c52c391160b2641e7120dbade5afded713afaa6032f733a261f13e6a8e vm-builder_0.12.4+bzr494.orig.tar.gz" | sha256sum -c -# (verification -- must return OK) -tar -zxvf vm-builder_0.12.4+bzr494.orig.tar.gz -cd vm-builder-0.12.4+bzr494 -sudo python setup.py install -cd .. -``` - -**Note**: When sudo asks for a password, enter the password for the user *debian* not for *root*. - -Clone the git repositories for bitcoin and Gitian. - -```bash -git clone https://github.com/devrandom/gitian-builder.git -git clone https://github.com/bitcoin/bitcoin -git clone https://github.com/bitcoin-core/gitian.sigs.git -``` - -Setting up the Gitian image -------------------------- - -Gitian needs a virtual image of the operating system to build in. -Currently this is Ubuntu Trusty x86_64. -This image will be copied and used every time that a build is started to -make sure that the build is deterministic. -Creating the image will take a while, but only has to be done once. - -Execute the following as user `debian`: - -```bash -cd gitian-builder -bin/make-base-vm --lxc --arch amd64 --suite trusty -``` - -There will be a lot of warnings printed during the build of the image. These can be ignored. - -**Note**: When sudo asks for a password, enter the password for the user *debian* not for *root*. - -Getting and building the inputs --------------------------------- - -At this point you have two options, you can either use the automated script (found in [contrib/gitian-build.sh](/contrib/gitian-build.sh)) or you could manually do everything by following this guide. If you're using the automated script, then run it with the "--setup" command. Afterwards, run it with the "--build" command (example: "contrib/gitian-build.sh -b signer 0.13.0"). Otherwise ignore this. - -Follow the instructions in [doc/release-process.md](release-process.md#fetch-and-create-inputs-first-time-or-when-dependency-versions-change) -in the bitcoin repository under 'Fetch and create inputs' to install sources which require -manual intervention. Also optionally follow the next step: 'Seed the Gitian sources cache -and offline git repositories' which will fetch the remaining files required for building -offline. - -Building Bitcoin Core ----------------- - -To build Bitcoin Core (for Linux, OS X and Windows) just follow the steps under 'perform -Gitian builds' in [doc/release-process.md](release-process.md#setup-and-perform-gitian-builds) in the bitcoin repository. - -This may take some time as it will build all the dependencies needed for each descriptor. -These dependencies will be cached after a successful build to avoid rebuilding them when possible. - -At any time you can check the package installation and build progress with - -```bash -tail -f var/install.log -tail -f var/build.log -``` - -Output from `gbuild` will look something like - - Initialized empty Git repository in /home/debian/gitian-builder/inputs/bitcoin/.git/ - remote: Counting objects: 57959, done. - remote: Total 57959 (delta 0), reused 0 (delta 0), pack-reused 57958 - Receiving objects: 100% (57959/57959), 53.76 MiB | 484.00 KiB/s, done. - Resolving deltas: 100% (41590/41590), done. - From https://github.com/bitcoin/bitcoin - ... (new tags, new branch etc) - --- Building for trusty amd64 --- - Stopping target if it is up - Making a new image copy - stdin: is not a tty - Starting target - Checking if target is up - Preparing build environment - Updating apt-get repository (log in var/install.log) - Installing additional packages (log in var/install.log) - Grabbing package manifest - stdin: is not a tty - Creating build script (var/build-script) - lxc-start: Connection refused - inotify event with no name (mask 32768) - Running build script (log in var/build.log) - -Building an alternative repository ------------------------------------ - -If you want to do a test build of a pull on GitHub it can be useful to point -the Gitian builder at an alternative repository, using the same descriptors -and inputs. - -For example: -```bash -URL=https://github.com/laanwj/bitcoin.git -COMMIT=2014_03_windows_unicode_path -./bin/gbuild --commit bitcoin=${COMMIT} --url bitcoin=${URL} ../bitcoin/contrib/gitian-descriptors/gitian-linux.yml -./bin/gbuild --commit bitcoin=${COMMIT} --url bitcoin=${URL} ../bitcoin/contrib/gitian-descriptors/gitian-win.yml -./bin/gbuild --commit bitcoin=${COMMIT} --url bitcoin=${URL} ../bitcoin/contrib/gitian-descriptors/gitian-osx.yml -``` - -Building fully offline ------------------------ - -For building fully offline including attaching signatures to unsigned builds, the detached-sigs repository -and the bitcoin git repository with the desired tag must both be available locally, and then gbuild must be -told where to find them. It also requires an apt-cacher-ng which is fully-populated but set to offline mode, or -manually disabling gitian-builder's use of apt-get to update the VM build environment. - -To configure apt-cacher-ng as an offline cacher, you will need to first populate its cache with the relevant -files. You must additionally patch target-bin/bootstrap-fixup to set its apt sources to something other than -plain archive.ubuntu.com: us.archive.ubuntu.com works. - -So, if you use LXC: - -```bash -export PATH="$PATH":/path/to/gitian-builder/libexec -export USE_LXC=1 -cd /path/to/gitian-builder -./libexec/make-clean-vm --suite trusty --arch amd64 - -LXC_ARCH=amd64 LXC_SUITE=trusty on-target -u root apt-get update -LXC_ARCH=amd64 LXC_SUITE=trusty on-target -u root \ - -e DEBIAN_FRONTEND=noninteractive apt-get --no-install-recommends -y install \ - $( sed -ne '/^packages:/,/[^-] .*/ {/^- .*/{s/"//g;s/- //;p}}' ../bitcoin/contrib/gitian-descriptors/*|sort|uniq ) -LXC_ARCH=amd64 LXC_SUITE=trusty on-target -u root apt-get -q -y purge grub -LXC_ARCH=amd64 LXC_SUITE=trusty on-target -u root -e DEBIAN_FRONTEND=noninteractive apt-get -y dist-upgrade -``` - -And then set offline mode for apt-cacher-ng: - -``` -/etc/apt-cacher-ng/acng.conf -[...] -Offlinemode: 1 -[...] - -service apt-cacher-ng restart -``` - -Then when building, override the remote URLs that gbuild would otherwise pull from the Gitian descriptors:: -```bash - -cd /some/root/path/ -git clone https://github.com/bitcoin-core/bitcoin-detached-sigs.git - -BTCPATH=/some/root/path/bitcoin -SIGPATH=/some/root/path/bitcoin-detached-sigs - -./bin/gbuild --url bitcoin=${BTCPATH},signature=${SIGPATH} ../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml -``` - -Signing externally -------------------- - -If you want to do the PGP signing on another device, that's also possible; just define `SIGNER` as mentioned -and follow the steps in the build process as normal. - - gpg: skipped "laanwj": secret key not available - -When you execute `gsign` you will get an error from GPG, which can be ignored. Copy the resulting `.assert` files -in `gitian.sigs` to your signing machine and do - -```bash - gpg --detach-sign ${VERSION}-linux/${SIGNER}/bitcoin-linux-build.assert - gpg --detach-sign ${VERSION}-win/${SIGNER}/bitcoin-win-build.assert - gpg --detach-sign ${VERSION}-osx-unsigned/${SIGNER}/bitcoin-osx-build.assert -``` - -This will create the `.sig` files that can be committed together with the `.assert` files to assert your -Gitian build. - -Uploading signatures ---------------------- - -After building and signing you can push your signatures (both the `.assert` and `.assert.sig` files) to the -[bitcoin-core/gitian.sigs](https://github.com/bitcoin-core/gitian.sigs/) repository, or if that's not possible create a pull -request. You can also mail the files to Wladimir (laanwj@gmail.com) and he will commit them. +This file was moved to [the Bitcoin Core documentation repository](https://github.com/bitcoin-core/docs/blob/master/gitian-building.md) at [https://github.com/bitcoin-core/docs](https://github.com/bitcoin-core/docs). diff --git a/doc/gitian-building/all_files_in_one_partition.png b/doc/gitian-building/all_files_in_one_partition.png deleted file mode 100644 index 8cbb0d8adc1deda1c7c4023611b65521e054ff69..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3350 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

!yfi>8p^lb@l)StJVn>-VRm^@90R3bJH=Iv{nz zfRa3_<3Q{xzsJYccDy=5+i1-@mnIg%7I@9d#Ja1oQOo5w4po$3P7F? z>sJ^&aMJpKtN^V(4n7p;+lagQ@QwPKXMU1k50NGk+u- zujdjBWWgn~$WCtA;5yzoSf)g#={-)k`Rv>!7$iFWRXJZV{{LHVU+hjZGL>8$CJ~i* zWVc!wfVnJ_tB`^;*95%Up9Fu2Rj^pOJGU42J$fq~q~0YHTG+n_=>cBiNgcMh2KD$NUK)DCuDE6Ql63FE}LT zkiVb{e#M9;ktsDa?9q(d8ddya5?)4Etcyoi)x_N1yHIn=S;a?E@J6HF%f5I$01@F( zmrBMau1)ZqUs+Z;Q7NHIGFX_HNQtG1-x1H-5@1TZ@GF)W#f&Kan59Fj4M+t_5Dl8c z{fpI`b6- z7tk~p`ISrLy_|HJp}2qZA-Mlf65B(-g;am zF>7X%ldxntN~X3y3YAf@Ny2iXwgb~cAzK}20( zMYQR?vK2?Q8YPIk-8AoM6gNaWrLj!p1m}p>|NSZVOEJ-JhE(E^NF>cyh zyk~OzkLp^2MN#FLMWKOuV1@a-MlwAqGkvta7b;lZ)R^L;4ubz_7v}%l2_b&G8=LK} zERpmvTM)Eijp6h^+y<8O`nt*RWnz?us}v2;8jprlxzojzYJ4>&CwhO2wRy79ng;Rb zagP<3DUkMP+ELQ7%~eNkXWQ5~+>l%O%}GvB?Cw!JlJb+klkQaxaU(cBN{hm2EbxRB z$)$vqTLmP=d1vtp>$-2XRAW0Xf<>F1oKd)!d3XdJ6K9>lMQ*;9yr3hw+BR=AJ!odL zyXDXsH#J@?dahT3xLpqC)!o8tvYfYO|M#%+At>+p3;$lEi0x~+)0fcN9a(S5+_4Ha zvI_;l37=I(t}Rn_6Xsq-c_N$Ip&R!JQnljIx+yIUf5fq;;3ZEer6AIf)_fJ9E^AJ; zV`#&>QPgxj%?h`n253IX8u-2;6!@{#dx+q-=$M3=9-k;5X!E*oErS>mOSiu@smF^^ zZjF{0-SW4#NlGSN6tE?sNIttsIIQhixwUS!{%=L%j#oMmz*F2M{mVRjpU|eSPq`kQ zm$EAJ94R7>C&E>qBfGrJL7@EI_(KPRa51sF8HJlD9|g;4&#R)#n_E*~h@JH_#G?Pg zGtk`8KlP5g0dxMWFd-v`_|yEoC@IEO_h+Kj^~U&&i-dpIQQt=^HLa|kch!gPe;1Aa zm18y9_mq-aNu3nka@i9(_5kZ4*bFYW>0MtU-3;%K zCW}M9KHqWWRpnejbQG%RmUcGEvxpAbXI3y4l*WcBtF~}&DkL4964uqr8@q}Jo}Yltps=0()_^ay67c0bQS4Y#l$E-q6Qs_W0ZK*y4H zHt)IYf$V#L#jm7hT90Foqn)mjcrlKh=9Js62h@0_L5e>pbG#iJjdeGC5B&XWeQUbE z+T!G;SybhapiqHu+05nnR|S?Vz&jq++gvK*@RnfvbiEnFHp_@KPm>ixn1-60A-$hJ zWT1nY?en-?DM8$CJB2ohCUE=phax($S;t(^nvIhaLh^-{;WrzSlPe%EHAcND|vRITu#0MX3_-IX_`BX zm6F@gTNpzw)#!C=#(P>ku|h5|o-b8Zw5v+>fl+kG3qlSwBMN@hUPa>T_`<=@=NQN) zsIGP@ZUz(fVMo@+qBi=cX7ad*ojn`wL>at77M2)^cm4Umc2ko@Qf^gcpPTpQ1WwJY zFNPYL(~$1}xnoAB67poZ7blZq*XZWZ6&)p|A&Z0q8_4*ZAx~ZwVF@4wq!GA;%2k<; z3b!%Y#{JueiMybmKi=(Xqj;%Xg2`#U}4)IZ8Xpz0;&mhMzL)igqo;2lsjcSADy}1!Oe2 zLI=0t%Tf80HIGt7a(sFk)f^@xDG4Q>%(~c6P^T2Y#=rFY7RZ~(R|2x;tVYC^tJfkto zP((IPVE6FHZ^LJzl!TrBCmg*;d{__=wCsv>bd-XEetmf2`F-W=$|3584tITmTd$N~ zUYs$q=9IwNeeF>3eN9*@$8}|YH$z0kByue@8!GkH58P}{AiMhKV?bfYWK3s zkS)USyv$yttw`RwPcvTItzAr7BC+K;*jvT6!ZCmi< zV~SE_3g(DLUD5!27u_Y?HZ>h6mQvaQ(7lcSy6%*Pe)X(D?%r8_qx4$~kFL~}7|dRH z)xG^SdASISnAMG$ZRMAYX|o5J;$HKRopZODWxPGmqOD{0<%x!WzPRW5Ef|Y-n`$#& zIP+F2623#628xs~s-Lgwxiy&X*9MK{2|JbJii+30Cz1W-LDzmI(ouUN71?dc_tA)< z)(6(MvBNuqO}@hXFQVUmgRxm&bf9W3+ytB4 z-}TW@aE-PD0|f#vBC6jVmg~pGzt1mQrB#XaGu)Ub@aYdzRO{osd5F1-jM&b!rkt~1 zV|_0`+|XljLOU2GSdqBo8O;7uoJq90B*L^Q%k9Vi0e7qLsO-cu`Vs`Q!=4=G>*l@+ z*IVxhGG_%4V3&vMp1Mx4IA1Yjx^3~+*4HPmlUt_`$)(HaTkLr5*Buhf=!ayH(e$FU zrm#ZSF&T$qC~3CIGPkbj=nl76gfCOO+Y`M= z)+l9%!|ACh?{c{)nmYcvki8B>3>^ZcLQTIx?q4{`7II7C@fIw#;I99JfxUX6_I*=2 zZ{As5%$=(+lx$_g6$8TJTM-}Tko599WPM4}S(5X6bNaA7%{Xbgcee2b?;c%zbfVLI zat#2Zw+ZTtUXaL4p&H)HQ}g`&gh8|}mQ4(Q&?b*nqih;!0eUG8D?{q`cJE5=3cn+fx~ z?-P+2MeAp#^e$DUrl``{YfgXucL$-)DHO+s3;R;{yb7~kN*;gU<9+qOo7;Y?>xKL7 zyDRoTzuyJ>hkoOW&+j>(NLtNW4-0d)v{CL9x*FY`b`!QO35%vn<9*bexB0 zPA~phS0_lKrg}iUam#v85hC_d*dl4;+9w*M`7dsrjE)Tr;F zz4jn5&;2#pZa4itq~JaHnW^qOg6pbM%HO=}SUckgT+H1pVE-7X>1^~!|C&jDj)Y-UJqUOEREb9dc3CD88qJeBtw zyD&X^7+eo8_@bS0_isv0&xShEcGrm*ZQOacfPZtfM(|=5aWcA?=d*a_OFzdSIf76l zi*a+$I($q8+Y&+dOxUc zi_|dMNX%%6H$~8sU$QVTWPe!$?pDi#e3#N)ZalCu=YA}?67yre3%%Ky4MDj*;`8W zWJ=yT_ysp73-*^Y44slnSD}RJJ?TtzPUIUuW=1eutO-qcT!8Z=Mj7sI=Y&daV?_bR zYs8+lV3~}zKxDelnZ2L?kX?PA-?rSeml&LH)a-l)`-RpmPxLRWM%Yj1Joeu44jJy5 z=?>&Kr|h>JCJl#wE`ISZqLRsqY+#+5gbla+V_p!WbpJ4(Yw7VFLn-`)3SB6A2%Hr8 zObsDguVHX%EKO`kK!tPSwe|3{0IAzC9Ki3G8%%w)$p?K8qbS=G|2#G1zoc|q!#L3k z0+M1dK!e4KZ&kt#ae=&*8m{6dQwKD5LufFoRnPntjXCRZ#S5lCAc45$Uq^{QB=rvS zVYtMS>DI4Z`8DKbz602uUwBo2Du|$05tbesp>#MR$J}3l@$8)%0Lpw_)aWtu{j2u< z$`UzL@pA0(0pn{y2SL{zK~Cemd#X1@p>far91<~^rR4X7w3Q_xJe z6L+#-3TbrC^d21w4^>}kH;h>#BfgV_=@COSUcuPQ^g6`zMAfis%lC43ukJS$hVqy% zw9>SkvioP;v$8S!cPUC$2lZ*LKyqki-@Xre zel$TAj`tY_jwfIi^c(SwfE90526K~FqkQyA`gO^sN1;Eud5*b4G$j+22z#%ug~8hA z%(Y9d+304pq<&HWuIs?e{oSvcl%I3Aw7}B2f<%`(%nokh?z%Zv#+-~B!iOdj8-X}z z_yY`)fgAZahX+iDmorZ%XiKQP$0Ih^Y{KTZeZPswSdUswI~J>7Q_wSe^2EOS9g(l| z;jzrd4Pi9|gkZEl;;9-g?NI6h;b%9I&B<)fCv5!>R1E2g3NLViL$|D9ZvICQnO^hO z{Fw^=?o=f5b+j~4reT-pc&~vH)`8WkeOvHt$@8aGj!Tl8=+~xAA_@RoJ`}Hj)E{A}7HHI4)R0nkDRIdX*wH2yjo3N%b)#H__fPo3~XdBFEpkls8 z*THp5Rg{$|==F+54uUkn9A6;1_8Ma2a}NVA7_v4*Z9k;h@L3TUF=Qds-HqB|VOj29 z_2f5ki~AeYM28zbPmN03v|((-kvA?l^-c%H-?^lt&m?Qhd)@KvhZf~-ejYKIfIEhh?^|j-CYX}iyu%Jgg)7v~ zOMRFAQ~|EDBsNfSJtBu25dbOqcX`G;@d(Qh5W1NnGHm8O{{o>k!2nu?a9Ut-V<3`W zP>-n!mV}=!;?plM1yCpdk>wTZxiLh4>IXG+$zoL@JzL z$-O+zkIMXd0$HGabb{srWQNyDfyHMfJHaW=6Oe?RI9#XqZWWoW?j}ikW*A0OL#;p3P!%#AS)d4&fYJ0Gku(+Ww6m!L^w z0WT1A`|kFS%YtzcqBm699{tAA=vKG1L-;2~!QF8~P4V8?n5ez>WpDDhO825vqmbjC zhp*hWAt&0;Ccd8FlK7+E(iR!$9tbQ z43z)DSjwd!aJY%o@1`ogqmpRV^ao+yo2{``tGA^{vySagU$Dis1^vE6TarMxUVfYP zrkDRMraDgN`xL0WUisVj5XJgry5_OziSvet1r3wKbjtXJJicTnJe<7v@;qw9Y_T`~ zRN{f`Adb?6HqF-=uh&=vn$P=4$(R9Fso@Vkpt6la*%ODT@i3TbMHyRpo9ep_W4-#1 zAi``j%tD6uGo01_4jeVjouj(tkU;eQ&BW;z`xFp^%49K^whZV=x4_(cqEss|I>w-D zzi-D9v_RQ9cIAKsoN$IeSzoSn-Vtwnp3tsz7-72ly#T8U+*@sQ^9kPLIgD9Y&zaJ- z*g>m=@Ye1&n%RHfgZ&b1eOq85q%r@;)!@{R)A9Mlv+w^j$*E>A%im2|3$u43Jc%(A z{}KMw-QHX?TyRgokxMJfyOT#M!Sc*Q=lz0yvGWz3p~nB?a<>tJwu$w6avvUx7upDE z0n34qbBWmTcmS%dW)my~Y|`mOtYW-HF6MJw)`h9Rl8U_YxL&9J{GoL6X3x8~zTNZQ z-HDd5T?}zP;k1MMax5;|eyeBOw*-HVy#55jvGq!hA_zPgRQg65t6=|;Tc zlDq8T`7WIdKr)k}hcmBCo=y~oo^7Az_pjbuuREXQtvxabw|8gEu=#c3q$hr~`}^kW zTswBQ5u8`|gJ5aPgMH6?%j!57?jwuPf-HAKkJx4P7yc3_( zV0QwS$>|p3!J@!oK$uAtPpm+7(D#Of4*~BE4=FL7|Itw-q0fHqVS69f?}cP(2H_%) zI=ORTySfb)F*5zn`Ehm-A=Ws9XNbBly!d3pU37~l20`1c)6pJpM2h~iijl)ERr>+5 z((TGx#VltBat>8xctW27mYmf3B<)S4Ne;j$U|{sA9Z4l$Q8ov9w3Rl_QlS0d{K zk~BiYTx8})jk`Uk{VV(Np>iJQRNmz939yVkwkl&Ayjo;VxI%qNIvgs6JyNqgxwtMj z>OH3f)9qcoef?kjfK$ZkzE{yq&q^MIhwov(T^jARe^`k0dV%0}40WS6eN+rh?%%7+ zk!{T0FW4^?u%zxcbt!c_1c62&H$C$!PxE8tAmDu*-5+p%6lIT$D}*_n4~m%^R}!WV z>JNUO4tzY{f>^o4&xhDllb6Vvo;XsZ33unRu-r28U3zzGVn(T@j5%=3AAW44)4A0|UZboPu9l9XXj@?pxmKeAwE@8HsC+#X| zrlbpk?gL?|WA!hp?OTPmcB6NhGOZ><<|yXJb*<^*cAe~;(^krDuV<1}#QF3GmZ%#N z+BEiWu4*Qbpf6AGzK1#mTE>>NjH*!9fDC?RY}zFr0T(<+f&2oy_oqt^-S04}oAO7l8|Iy!bapHIr`4si zuFuttMW^B2O33c;su*plDpgEHHg0qHl)$8xJ+9t{IovN__>Z$9R9~?EDk@@HbO}sy zTa$fB<8Tl1SlA|(Yh9GiNX0>9Qt<^9X$c9)5O`eG*b+(vJfAdMS9mu{;!4XrcE{XY zv15`b^~fiKFLAb6zx}P?{C{)I18o|%2`bemf5kO08wtGh)UxLc34L%mZ3lqL_0OS= zzWi|0V~CK9i#y{EUn~l9W*8W~k3g$@#@-;tEq6ioeeybe+AOW|7*R{4S)#0+etUQ} z)cV6u-|3vTGO|V_pLi)FZIJ~9t%=q|8hD%WzEnokRDr%FcQsG(+I7$4q{!P^Q;9ZV zBdR;s+MT-3WEcf2xyTK{QP#Yx^Wd}_bvpe=aOD{cI*2M?gQh7;ETz%FK(21E)`ho1 z6KtQ<^3xS~C;+(ToJLkptlL>?}Y15 z{6PM?P85EQPW1f>q@A1JGb)5Idq$mkl%Fb!klU-kA1ZLauD$v9eB|9iEkT5Zl zEsCv2mgv(#&fA);nQrc&tcT~F{RwiWSuIq9PG>yp)6I@}X*KUkPH$x+^-kxjmt+*f=?j=8FOE_?{rJ9j+OXEOMf>%Q%PE!$ozHYYwr~j(lc~c_{I{)9Z`I4Abc@0|J815r1I6TRUIfA*| zPR)4%Tg0QiA_q*P_n=!RH`sAIJ#OiW?(4>sf`1wqpMm7Bznw%DR*<1MTC|sDfZ3Ms zFa@DK#RG+6Ro=J#*f_ot-Ub|9Sd6QhqnOJ6VB$UNB4;C6Fgs@UF7$Yr{F4q zY~$x4I~&2KH?jhklzMB+NuX80)qV_t6e_?z-#v0V(#xr_jT(-%<0IFehC9Y-Ymr9f z^R^0_Tf7p_Ar%H#6lTJLp1mJJ6b%O?xc&iSE6vho*~b2xu+$`CZvrz-w1u4qDpSmA zlMif5^hb4lGCW@0rfg%0U|yZLU;#M2w%K+w{D=CA6<=cRvSZ_`>AQCPWx{$v+Qf@w z$)OcR4G6h{AYpErB_z9U{RMMfMkt*FUfmSW>=pdn`Q#{5L%8*a0N%m;PV6P6 z{Z#)#Q2B^^E3z>8`;N7NHX7EGSn4yl(sY?wU7C{|>vn3=bn!Y6xzI0m94TiWv7$78^islQ@R*#|4G$egoJb zpL`SF1V@@$D+6hS;#H=o^1jo9%`vL-QY@j{7X+Li801P*Mi0gN^d$HU52=Pd?Ga)+ z>v?3~%O#su^<$}~Ir}+=^x7*S^on5rn5H`$zvmZwz^ZLnc_EXEG_>z-07^!z7nsZO z(-bUM>>*i7?jKQv2P#c6qI}J%!^PxqV5qb1A$lYChF_oM&V-EKcwaBGKKFV+&LFXj4n zkyKP8f4ter`qAaUBu(=Z#K^=SO|YVbd2Uc`D!W&NYHRtUz>YGFx%`vCW|K!|>;Tk2 zMu(Xj^rHZ1AkuEbI9o8SbjKaK-_4t|?#T&U&EtD0*;itjzegMSMq^@MMp#K%6*3I1 zmFnk<`BW_E`Hxje^B3HQcP;5=X9PoJGyF8~67zfQV4bgm{a|P6LgeP#3a>T%@nmaaf0q9A8Gnvg7)U*S{_JsBe3JA7ixhk zP0}V#j5fd`&aD`z&QTde#N7l)+AmGfX-ZDw0w|nto0~$kk79j_RA50#Zi=RT&RSZU zsrF1lUa(VkA96Y@>`U4sC2hN{T>Vx_)_=CA%hetVRR}n!#}e8r2$H-A#5EU4JDemI zfFU3(r*0zX=VmEXj|r7pK`OcoZJoHADY|1+chRmZ%=WerY^80$V|d2o8MeW0y^~#c zxdj)&hVx-zVyFfo-y2w0RBd1=Xob{fY%5^}0Ty{1ZY19+t#J?;%M@sp#!tY(0ABS4 zuT5XmhE4!Zu9bAH{7LRRV)~{^pBh4H{AX<3xGJn!_IhsODSyC6^r$ zfaoW?l-Ncuw$S?A+M) zmawq{b8)PsL;(b`yr4bS)&Lm}mbrIbPjZHXB6*r%mUD7{0;Q#4`hyaGgk7ItkwHPQd22DcAVxEg zu~niSP5D5*elEf)CwI-uLdk?XZ7x$CCn~>iQWSth!lvJpq}7TP{}B8cFj7>yS;a?i zQ_a56PccXUQA$auIwiBx23!(?nw5T(lDH&1oUKb#rbSg+vwF!*>xT9^ZE8#)MnDq~ z(94C=w)%|^BL{IZp0+~;z>YnU<2}nU=cZ3}=Gr2Jm(}|;M?}SwR3bts9^}q_*2+c+ zw6X#6ifB_3LwR>J&KGS0-E_GNHMTQ_jj*L;^ogA!gY=8(2pI4>EEAZ0(_ z0>mNO0yet(Q7Y%IC?$n-hH0rFRexcLs>2H&2RsgV95~cDKyLym`^#EQ)spix{I;6x zP;Y$iwvUqtXT`kWi@elU^%duq(ufRIah8gUwlsif%iX!4Owv5z@pc5pz1ns-mc^u2pV|_ZtzHQ%dQv zQnh>4r=}Xxm8F!B_Ej;0o2bAG?`OK>RIRcTZD5*q@yVzi$wha6t0*IJs`S3sQ4TxD zxI#&=f1P&wc+VJIvyY@*E_}L+uE@*6=UVYjNutyX9tS)QcpUh6aDYQERYueSX|j{Q zDB2s_{)cE3O}iM+R-?8DK@uJ6RA@IuUUC9YagsD%8Vcv1Tw!Ud+X?^%V_fqnB^y-D z(WQZfm1>xvR8JyG^`o^)OZ7`V)JWSX3(%yDzpSi)3~k92S4vvGo`Uv}uneUa#_fbl zq9L0sNy`ePRlH+@4VVh0bBZZ_t^y78RxW-KUF6aeoP~(S;*kXH+6Y7h_y|DqAx~@m z9?yt83Mcr2SYB9aN%!3V4b$^|XSLPr(^u?U%$=#|ynvT3(yI5VD`6SFSXEHU#6<$K z5ww6g_eCRL0tAzC_rbMhkbttA(mkoI&0-2S_%(|kxX?quh;Mu4_k9oL6tvez;V1iy z3k15jpJAHMyICQqMHRHIGan*0Lv)bMq5MKwKQ4fX>AB8JISsfsD)@p*ybF&59tS)Q z9P%9CO7{>VlnQW>nh6c`KV(z{oJ1uR%kJ$?p@%#R?FQ1utCjXklCT(FYIUScUF><* zOhzUtr%h{#BO)pKu4>UlYRLft4OGbyNJZ(kKq1I8N=t=!-xj1l6Be8lW0!2Oy|O7 zx$KG)L~AIf*MR-P=gLRbp8!|FPk&Q65;)P*i+Pq4cFg97C5_4$vI}U<*UB>);e)no zRBTEetdJ|yy0p}il!n>QyxP#hzKc73$7BMA#@7|ki<@9w^M6r_twg1 zQmcWZ1jcs9Kr5NQ_23D!2urH*JV!zbUG0L9< z1nzoxUn?~0DNETamgg>eU!K=XH>E$FM8XyQomHW=koQ#*))`e;Zuvfy|ExGyXx96@ z29j7Os&f2I`~vbPf@9Ll%t$g%PK0m@KXNjxJZH$+xp*< z!jilzj{_bDJPsU;9LPz+4o2b+IGU+~OLFTcvy}r4%Y!u5DQhovz!82362!hp(fx|{ zDD=XC$^oe&GgSRL(&Gb_PLD2+10DxF4(uBa$T3~fX4xk10DxF4tN|mKsn%numiMX rJhD6vcpUIJ;Bmm?fX9JDn*;v~vKlqvFA>F|00000NkvXXu0mjfhQIH- diff --git a/doc/gitian-building/create_vm_file_location_size.png b/doc/gitian-building/create_vm_file_location_size.png deleted file mode 100644 index 5f77206b6fa8f9f0f1bbd6d02819d67bf18327ad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 111942 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∾;f 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


_e3SbcU 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

1NzC{}=|46{ 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

T6`uHKZ9H{(6`1K|X4$NrfrI zn2&0e_!y*n$E@vPr=jcoAK3jQBH-&MiY{tNpeU}I7DIeeooogTeJh)b@gD;bNW}`~ zUp)&OA>+dw#2d3b!@gkQ8ggLM(OiqJPi7=^8532-9J)5Cr)`@E0lTYl_Uhj81_Gw~ zW^jlkv@Q}`uUw#gvuyz|*JT|@pXEM&QnDJUy61IdSESK@ zwQ;2Bu>ab>EsxSpuy5g2Imp`kOI-z6i|9H1EeM=ggDe*-<+Uz+bwe$78 z@UHA*he}g?IF#GZWpx&XRG|YSdkJ6zkfgSAxxe7tg`5z|#q;Af1`PXhe)#wFU~Wu} zUzFow3Sjh#m9b8hqUDiVr^zB>r~vO3x~}TsIHq?|LduwN%G}k?kvZS}aD9k%C%l_# zx)b?sEH(&(FkLXFlt}Emb9Oa4^-IUO2pvTXhMQ^fcC%gr45-mmysnC3-NqWP7k4jv zmqi>)Xteh90tFNj(q<(1a9?duG2HeZuP+29G`)RMR#!S6X}hdtetC zcBJ2?O_6gQU;oB|H#)HP?@43$JHWe7T2pr~B# zql-M32fuhw{myNd&2WNU*amm@Ha!Po51?a!b+L+bD$*o=- z^mWCPGwRc)WY4fwiM@oD{M9)9eHManJ=6I$PxO9{s39IQB?_-rz5-N}%@6mdy@d^!Yr6T`Anl z>&Y#Lsal^x(Z@gfLJzxO6OAA5i6m%k2t`@Da@zD&5YX#~66LD^p8&dY$9&mEW< zu0{QX<8euLcss+)?kHzQa97))iPz*3~ZblbTNr%>TN=)2)8WH8Awv zb>lf*tteu+$aWq0!sAgMf@){|HwVDgg)}5Y7GRHpY|J~-ysF3FO-YlW+>pyEdSR9} zBdA62jUIG~@EX3K0pNTIJ4qsp=En~H|F{4s`8>iuuDk2beLYOXU4>oX>a=XQBz}!6 zvzUlacEd^y&*6U%^C-*z@`eXGau!=1AB3IUM*?>wmMU&BRYHT^MSqBwV6HIXUu>Sq z`O3kbz-r9rTHUReaUS9iEIDrygGoZvh&v^x>ahYFjil#`EYxT^xcToqVzrPnDWi` zr#jqjJ3Y@8Z8`rPA0TE13MJD1OX$=c@%!IV5&xpHjyZvCs7v6%jb!Wgh2CJ zEJ(Rn^vFVwXSXjzBxPuv0twYdNT>3ptSjCrM()=E`)VplcmOBxVs(*YbFImqx4V2%bR10yhgSU%&gn}fVh$SuG0xLteNh|lro;lOqhieFR;D_7m&7k z#%1va+0H}D=Vs&dT;41C>Ec{~>!&iGx{B9Z4?{p0JL6EHmOcxip?eKkNKSen%fO1U zY|H>K5?1SuHD;SH4>~Y6&%mVQd(Sg*s!WN1O8PxsAMM%D`Mu^^uIQSM|9-#H{%#D$ zOWd8l*Wn{TJ<744+N1f4=K}a5txWk1SdVI)?d!Gb^2Y8wMbaRkmn1raC8y)$bJ|R; z-mZj4S9?3MB);!RjYbn&);n2Efye|fVGdPXw(H{gN!dlu2D9Vwp5;(8R5R`G)wJQ0 zbs8@ZbwrN$`#BS%U+H_ND5!@3l{U@Fu9weg6EiGMIbMpb@Q$}{N~x%6HXZwV`46K}wuvIOYBrfKx&je4*Cg z(`(+(tc5AUis#OVmC6BB(t21{313|*#Gp$fXE%D!Zfw@?2sN4s8OIc@A_-!q!=p*)1o_o!y-C zI&xRr)z$)y3o|wwHNgR7t%4a1Z(AwkAeD*Yee8oHC0LvzTS%uPNU} zd~aPjU}rIEOKOiv;56V~EI+mubn$B!sL~X7x+g|nh!nl0^LWm4LwdKxv$b#ud&yFV6pa%gG@QF(AQGOX?>D+JhY1*zZ#VGSfBn2jVs?_bvmUDN0Z+x;M5DssJ zChR14wG1gd#@~q9>U$_(iuw|Mg$_l-#VqCVpz|II948(zJ`uD_`o(x8nkt=i+&inX zt+rEEao&9z0lv|WL-Af?+j0{vj>Mji<^J2Um;KJi)HMJ6w*ITH!9eeDjt?Js*G1;x z2LzvqX%roo+NVe-&M~5{vH;)BN9p^V!s8@6aC(+Wp@l zRtGINEL0USbv>khNB+E1Qw})4h0FmtrUdf=?B2d*IvZ>A8fKMVRFYYYHHNm=$x3Nw zVbp7oEXIp3De^NeTP=u)Or|xB@xh}0O4ENXBNMt>FnFUIaH`|^ z5bQ7NBXZm67mGkAR-zhQ9{}+)eL&3I})?GZ~S;%gLQR*4l`J1)&w! zx2Z4l!&APV3Kn7=+8h%7HdaM=0#2crInLVAq8a22j7p)y2T6CN=!?e>+e%_{mSRDe zlff$wwbUUL{>`WJ?)TDmpuT>vh<-J?>tK^&$544L`#a8 z(O}N!;JRoYN#ZdkcB}8P zUzjqA3@=eZEeH|!T2DubouQ2Tc`sEk4DX)LSz)MuMK-`WN1`AibH+O9>H~D8+JeOX zNmj8<2^8V~Twoe7L$_SOpV`y9tLwEtXDsDY!K~M}{&SeHFacn^N^}+wojHrt7ips_ zrH$7>wI2%|tH<37=y+MP*gugY=t!B?Dj27VX;_FdS~_erWhqB3l!)2{=||wp-xj~k zpRVUG#~Oj}#&?`wVY$NzgfRYYqc%;g3`7f3ZU~mtz=2-O5gotM>bsGxEJ*1ecD#Jq(;7+0PdW zz2AwT&Ll`ZvrZIepSL(P>4;;4DG3cH{KY;6OA@qk*2GU9g@5dBJ}%RGi+K?xOP+y{ z{o6E}XD)|a+o+!LBp0pNI7q#W&HeRIQ~4%wLiE*wYBn(J-sk?S(Y2JH=)i&4QEL9V zp?Iuf_i2azF;TfqIz-hsTJ)NwNGKyW{!;+&hgA`NniCrP9j$q5jrL>Qlr7cJP8J{|n`6mx0k*Syo6RVL19eCRiwY)h9A-L=;Z_na+iW{E zkZK1Sd92?{qrVi=pVJd`4uLWSfBr$$s$m(D7IG*ins-0%)qv-!Tf=~i3ajLeslXFd zRUW?q8!_^&>J@OT0!IkS^;{a7L#inN<#PZoe=dZ6_j{I3?@@e}>;z{}o;!0fLOC&= z_s5rJG}gSB;3+i$*16+Ew`K}VD}O+(vt4JeHzFF8|2SNpb!_LtLIC4uL}>iT?H+N9 zlh8BDryNPrRtG^=Vd=j}H?i*@4MgLTRsm+KYn}&XyOWGrk-GE0`sxgO(p%YUcW$wV zO*Xh}k}ojKx@NZQNOR>)ONY$2I2;5c&r;{UelwsSU%5?$dtbw$i}DAg%u0T#}7X|dHLTC^RiPD?O! zIRQWX0iwASL<&yvcai-Yh*0nWYQ#1zyN3r(61kxRM9hv4muqQmY5gV8xw^`)B`Zy{ zKbQnGcr5kCS~{>Ou!R<$8~!E@8QRb0SggHx>#-A~U%yT*0x#*X?XTl_%K1Xo=3fLm zV`_OgZ2D!_v}yraY3g!dhp?_)Huy$lg_)Y)#o<*$OZ9yI8u>!knStP!ODAZk5Zv^9 z&}pG&w1|26r$c=Y0`H;5U=D)e9%Bb)-Jyl!eIOARC1LrY6b zqgSwc@N^`RzKAQ8u+NB)Rxt7cxAO#3K)YbrpuY%tAEHm@_bb&UVHFISAxw6oUtiIR zO&4flj-RU&gWwiu7UF;VE%)oJA;exah!(fs$x~;_ZOs=yYnq5K4 zl1F)8szAcF1(8t9)$bb^Qy177uFUBM&`450_2Tu|b@3Hi0qQFRr)sUG#bkV><3%;zzF*fYQQMAhpqz*e&wzY?YR7H*IQYwc?4T?q$R zzQLct?$X}mI%GbG2Q6qDa~Pcxg8bB!HayiJngX&?SWN(#!uKn?bQK0A<^ zocR*A%4f&pN;VG05Uz&}mzm$kSQrb>w9<|}wr5g$eliT~)KoXJH!ap@Pl-m@>6t0a z%B6c3w&F%RXXU}O}81GAwF z?uUCD1J8)?g^h`ama}ZDH(Ibrr`k$5lH5lBNL~dGnx^PQqrx4n`~)F2RJrGO0;n337%7dCXY8Q~F8iYn3k)wl47k32-o5 z);0=ZzxhkAWpqi*+2G5$zzaoV)2m6H7q;i;@$}yoyPyobb{^+q;>&75&G`7iqK_d; z>cf#Y&pKV2au-EX7vGBqNi+^WMMA&T_zgOtn zrL{)lROog5Bi|Sf?hKBcy;7zsyB+`cu!*jpJ+hG*+EvVVup}j#`SzQ^le&lRbK22; zC-P*jq?Hu{&%;JE0Zx!u1Ve(eyUWkPHQPKuD&5E&=71-Pm0MkmY}jY*+Db>BhlFC( zmEH=1I~p zR(Yh!Qi(13cnY3`Y0u32#7y)KM8Wxvi!k$K=k~21(fHf#E4rL7WtzmhuS`VmEP?UC zb)V&v+2Rf>+rEO{rl?xal{3N)%BpbSd#zgyyYfqaJ$3KGC~t;s!@hMl22ipaA-=Wv zgouZe`j_GxTXo7uO!b&l^j;-dV5?{+HD>oms`nd(F>nljY_HG`xcmvwVJNXgdDWYW z)-{@pCwT;FrU}{3eN$>>76g`cI827~#ZbHpFj_uRG?nc)p@S+sql=6sdtn@( zO3$ue7^SQ<7E(SnpGtVh7i>W)%FOUF#Oo#!bOWWw0#P_}_eFVn9BHAo-|jTXq6*CK zFH#&F?>z621Z7-1nz}PLe0KXMle=)owttK&C`lnQ3z`vhF{D|yYM>&3rJ-=}6mICzhh=_#eufooXj;mEEhuN+&h ziZJT^08D=xD_7|gDg}KZRFNwWKHpv*P=)=kq4X=8Z4WWwuG42n`mgeWHw&hUH*Ke# zti_p*;Wg>FnOV+Le)k;YIDLm^gQ_<1onDG{kk2Tb*GcHx4LuqJ+PGYS_Db<-u87Ko zW0MfJjNZ~H?Y7l@RMu2DeI~+P&L^6U5$tv%j>N%)0537B*1%)mu zF{?Jp#DdDHEyIWYn9ST3e9{SLBc2*SPtS#m$f$?W+!V_DDkVzrmjFa+y=8l0F@mlJ z&??TEnVf9R;}AbAocH%nzqx7(QK2mSGNhoElHM!gOUDQYR?X^m-mg=Cj>6@f@KVN> z8|~|mZgtPdxSSEtD0x*o&u5Km|1>n2uP!yvplr97jc3xDh6rrn`*u28cMqG3-y7$n zx=RJLqsJ>63Q@jv#cZrA3BgzkjlEGK%P3iM+Dy{h+uF$Y42&I?;WnTOZNhGd2XIth zzkED`YMm!4`%>&R-urB71YHiMpcSKmMHlb}UB+G_+K6`>K67^7{V*Q697RRlch6-l zmfv?DDu?Gyum^YxQU#|JB82@NSu4WTUTgwiwr`SW1Dyy$cY3r=R`H0q-{%~5jB?{; zz3EvB=TbpJx%gyf0C&fe8$rM+{&V5z$EYH^akp7IdSclSawv)*J#nvzUfm_oQMON; zF?oPbpwq>~z#)&UF>mcygEkVbfQ^v zZSukr(Kt?frhWfQG4)~N*9%m29NBE~=ih0iTh$QvCdrN^-zQ$rHs9Z=u)0&$BRJkc%qk3Po3o4e#Vh=FyCR1 z?r##7$FkzgDf(0(RT@t*)l+L08ga-#!*13UHM}{{;$20m>33O7y`}53Ig^hJSf>a! z^Y{hCD#5yURl1cxbpgBTP`ibC>ENvtkkh-H=f}*n%D`?_CP;E`?zA|r0yY%GMU@C+ ziihIJbHK_UjKGYyv$>^&poH+)gzTP(NO*SHRy?B?^xTF-S>a4+Lj{$3k5U2BHo~}M zS3w`4I-$ol*2Rnw-gQC+8k0;wI5`@XO^RvNz}M)eBwTNPDT~At9@1F##(u3ZROpis zX#k^qoWl34H@sGSj`gcYG<6jtaezM<2W6%G73Hqp{n}2EI!aOvzcPd+<1K|szBm7M zaPI0_1al()$eTcI^Wl=&O)-mn1I=(U_=Su;DC@l5)>?Vk%2t%A(QtJ9gOIh}vj+(S zRu5SAge8B9g!?q+3Qd7imfDl3>*cQUY@DgkS+%0lD2K{zVuM;xJYSFhTOXfrhP!Ut zvubzm3;(%~(wg+KfdN`BR&|tX0r#`rL#n^0DR@@g{4sZ*RmPRy53I?1L+?VOX*8X6 z`tJ&)B-UA*9|o+&DK6$Y?!;L=4EZATN4%PEPyLYw+114u&``B}nOH3&u{C?O8Wqk7 zZi}ww18S3$d_UPczbs4bpaDPOsn{Gxjg3O}s2!u&U99g4oHr^!MXfZVVs)G%@o6@q zx_O9wZq)lCHZ4z2Q`UKs*R3S-uLGB4bx-Vb&T1167bgz}wLW@na31Jai59uyhnVwH zzJk$xL(sZ9^ee;kcN&pxObNS!5t~uX(z&T;ep`l@2qpJcjD{?3LnJ!k=cXj47h~ZDxcIyDr~oqv=7&hgIh1NUKhYJku?yWd?p+e zr!$Q@k;QK$BgvU0UeBy;=6;AsJvOW7nwjzss90)fHe|}!rylKDwae}4E+LP*n(OWE z8B-GI{m}B%{Y&_2^Kh#Ap@%nWx;THd;0sQX9f$%h2!;pM(cp^10d=P>!7(l2frXm$ z&Su|@$4H>|wtiQ;;CGci=|x&;=j7oOYdQAYrerT#@8(76d@oAP`>QVNNe#d118l*H z;B>qHYifYiBuOYOXK8W3A6?=U&_|47>K>`I-65>{KDq)AU)s4X>1INOG!-hW6wrb) z&$@9>m3&|Hvkb-(VB4#&*%epT*WQlmWUn6^1)+b!auJ37%*4pdBWmw4_eN{FzzeB} zvW9=?n$ z)knuREo#p9PgCDQW$3!dhFJr^a;?IE43atw6<8BN&4zL93klOc|B754W{T^eD*y0h z;S(Yz=|(`KO=}q5cXvO~N+KKxCM*Qep+Z0fo=r6@+M{AWBTfMv<78Ao&S>7GLbE7D zphpP(Pcwv0{`z9GBPkbm&Tg07Y1x5;amKRM%=o}|HXE{}&z4?+QP?VhA4ESeXZGwM z2qex511@Z1%pAm27=*G-xrp{s=!&S|r3S0x6Wt zz9pVTj!Q~clv_dZ2IOhb(WIonI&CenvP6Aqw*o#3Ag&mR)N!w*FNBsEG6lkkKhH~hyKMPD6|>~-`vE{alP_|kHm(B>V{NLfDBa;2*i|nS-wc{~aQyyP^=@#kFp0h7=rk=#78Q~DTh{xvv(J*%1s^q{W0O&8}v{5{r{^Vh!WflSL1_UAsLzrg{Oqa zi`ExaqzG{GTXvfGaUEA7QGQOfq0UtVVaCR{^?=%Zs1{B`D8e+OqKI`Z|7+dTAQj9h z-b``yd$yui9d?;akm%dF@C4rl_Xsl)SGT-*D>bX4U7x$WK;+2S@fgUXs)xsKBMPVeyP2Yk( zm5H*o<;arfQ4coeH=9B4xaO?_Mz~z5*gW{-IBYI}yc#~$Xl5IdJ^@`;@f$ZqMuk?%nXqz+`__R)u&BKil-KG6tGi2YpMj2TcE=Wpk z(n}50eN66NCoH4?vAz9o9Ngdq@ohk~a~c2S<^x7^Dm>DQ916|#`#w*nRKaGomO$-Q z&KT{eMC?SM#f?4}V^u&+q?9d@EADBJFt^P7hFA4l(rR&x=xK|$`EqZ^_S6~P1=+Z! z{@^K33%{~`XEXbwxzk_0%{6@9pL=QBG=_sL(MFVWL}UstfhrT$;0`7A*a*e^*7_zCv&9`H zm4Tu8U8`jrOLJ@ugW4O`h<$8^$mVCGAYC$y*R}P3#P1cQ3Rm++cW1*~Dw=%s6I3vZ zJW{cmFlv{%qHx&P+DE)FHb)}MSfV4xvc?(spfE0h2$w0`$4ww;NF5F47S?tCt_JpS zdj7iDT9?oOftnp}q7?5mM+2Ksh;Wd{0F;v-S)(VZ1S8U!<+6WSu*u8t>v1m=^hnR4 zp{m8~j}QUne_w2@pzo-ukvtv za1}x5m#HWJ#iefXc?g1u_s`4#;w*TosFcs|`a7|w2w4wlnMtnZw;3m#Vf*74!E%Sb zKboIKFwW=&#r>mE8omzBkb#>mN_08G@sd485#T~Ma+j?fLuNqg0A<6Xw~SKxFOBV}Q9Zw!HJU>k(7AN_{+%o=m(FRRhIJ8n{|Ewp;6M45WJu z&NjF|j-+~P@$>5IKEM~J$Ow;9mraCnli8#X>g8n9=;oIQ{$6PCnIxHjoB?OEpHNR2QJ|1HvS96AGzj_WNAoZm^(c;tz6ex^e^#2Cv<(q9sDB; zq_5GWT%p1dgyGNbt15DKug-#5aH7NSGucSu>9wmi;AuIlKQ*kF8 z5#S4-psT-Rc&!Yce&YJEU)3jIAosfO-#j>4Xo+bc*V2xCWF+#@7E9o zp^JDnR4p>bBm=Y@tzELQwab=Uo&?%hbo}!RxkU_mm#8H)@LaDeFwJ|kXFRAQ z0>D%FBUM#@^08(k(@jy+X_P+q=P&l*LFN}h{~t2(g1~YHlx}CmR3TVaF0%P(g9r}) zJiiIN;IJ*5D$YAS6fXD?T}yXC2s!{Gy6tR;&4Dtf;1Di#6W_eTQrl!2t^dK+hrs;S;>FCXWziep%97;cO?kAwq zfTRku<}BH0_$;}Css7;%*R!y;(Hnh6&9@-~*V-T62GK_%v~Iv3k&sR4B1q;P_Babq zy}E{OQJi3VAkm#!q0+tmgOUOAt;Za_2%X_OB25|yw}MmhF%@BjNxN3&;lL7HuNC-U zyv6`ZA`_9i$Xt>OeXy-Qm<_}lQYB#ti00)l$mu_=kE|(2GFxdmIKNQT3U(-QClMan zpmBibY0Lmb*c>)%R&ZJtF(?jD7$r8%iS{c>`8-e#_Ydyg3@pR`2P^lAmNPm+dPH zp=h#ZhH}=lw`&e-$5~l(mO2L!e*tV!NSKbaAc8*`&5RP#48E5{ZzfV*CoHjc`!F7D z%vyE(jx;KftOyD$07E?b6WAD3gqOt*-{`A8Z%xvs;g8YwIWwFd7O+W^kNOC8ymzI= zgWa0ddKAgE@Fsn^Uv#w^z5nm0^;Aayh8`;(`+RH{`m>8{`VEI)5_9lFpt}_dP-hiy z@EU}ugNTExKt5M+4#ER)e$c{!KA@(e_LhIE0QnpenX3qWp@7PLV8u!Pn-2ukJg|+u z!Zd(=#?^l|-7&PoN5`buXF`;pdio6A+z>WYH+;zaD0UIx@^e`^%2C8Xka8p+usQ9cOU>&8j{ z(BP1;d^uAma0$)1+-29f= z!^dK0>5xx6Y4N&jTLSPfv`0+bz#4)e@qtiw0K9!3-{?>bms$RirpyCDd#W>nGjHW_ zHSfUP@R8w~SkLrVwh^Wj(pnY#!Mdwxu*S2GzE)5w|jq*my`rTTMTfBBkohn!iBl`hK- zHs4+m9pt4s0flu?C-VSkgW@r&0kf*26O9Tm>6YLl=F|^*w}x4K#XM&+F*PQxeJW zTcg-+D!rnrmS-26P^~5PztXdTSEc8ZW(5m*PDcqG`OrS8JQ_<~Hx$~Ugz04PHmrN# z5YgDV{h*ltSHcBa>_Oz)NXaml{mP*g4Z6Uy04^lZ947}ni~&$>v&x1AtLkxPdERx$ zB``AG;bn6x@3Dz`B0H!6TZ zi488zZ^b4|r@1N=nvE4k9N=eN2I4wsKS5tdeHn(=H)&1 z;`4lVaU%QC?%Y&=FUgKI1bY+N8o6A(v~P znM|df7V6-CShVYWC{QP~#h!i!53K1Kr!)7cxqn9BxfIV! z5lZ5`Bpws#^19a`bo?(PStel;BRM@zC;#i63+InyBey9juL1@4ebQ! zK2B~$b!DELBgIJ;iHA?-iDz1v5)QvFP#2ovOq?CID!IrZC|7YUTKr&`RuplYvAwFz z_%?}Xj5q?+>P$7J8$A)tTVagclPeA&L}+J&NhQOUi7xP?}|`xz>(?&3rbP zfkPMhT~l>Rz6+cP(*a|RVoqW z?<|+$@K+PAsk?*sV%9~*7NUWQamG1R$@dM{s^#eNkTF6(c$IPHZ|5i zoSXsa|JQlKrF+CwoYSI+6Z68-kuGF&3Fm(QVsIrtzpMHUSg=paS}1P@3@iE`&$;b7Hh^(CuvS%4%@e}P7M%FjWn)@EOwOZ|{_BQ{v z`1*C??BPT4e>$ibV0}ywgzpB*2St7$g4npLJMxjUmhy~Phr8uoZh;Oce?AAWP7^c6 z8aKCau#q?46jRJqhB7nq2rkhEScBLxwv}W}k+#X>2=1!O#b?yHMwyknFm`Q-3ypoa z2d_GpK|Mqxy24YIX4(u4BXf;gl_NhfDduc@MP1&tBy5t9Kp*ombxut>#oWBoRwYy&I;Ibl#wa& z`7VO%|LZYxA4ozOYL3|yC(JFq+erki`m{9_se<`r4YC|czI`oaSw1M+>E0fK;}^tv zoOT8vzwmSiVytJ_)J#`mq{y4f(tIB2i2{S+h0#I|;d=iNiqC{!oIj#}(y)0EWzPqb zXf3^XC7ZTA!R$!2dAbbC8ykVn4swni+O<+9e=L80NS9PUVnkl{GvxwISiej4k5#&K@+KG8ZLR@kcY#Oh%cZAv8lpp4&brW?V%Rgdus3NUd24g(e_H36 z$Sl*4wDj_-!RTgqDox^q+Mhy@NwQrm6Z0g8k8f=xQKqS0j}XA4j!wbZg3xP|r#q0+&Fg?E<_yI6h_2c8h=L8tHOF;;D#ad}lWtj7IUx?;p z&2O4|DCeF&dnM1i*6DNmygaG#>RQ%ZJSR=o{(t}CNx~&r-nBS_I44r4+pf4Op@AJorqO9;NyJ# zhnu8q4c!=tBjw9t8$nd2RHZ@3(di2hVUa<_5pr??stNADRB}Vf+11XCZW&R?FQ)9a z%-Vf`Plt8f3FG-(qb*kT!5+s90MjfVDvRv_U+JG-GN@jN+v$cSRbCgz96P!9K_#%k zn|iN%KB3S5HI39ry18S!_1epT$ZwP$wL=~c+C`qG1dW)pDwCi^48OK^&WTPQJep)`7)2gQnHp-FrG;Ag)t=5gAeD zGTrEs>>8(-77ysi8&05EskSOEolC$(zRVrFj8AObA!kRBs;J*YnF`*5fO_JM zFjKf_p3o~$N*bJcXo#`m8JlT%?g1%jyYvgwyap$EbzmXSf?o(6_B>w3z)U7S#_aqj zn(RbO2oZ;6&jZELnhjl))9e%k*h<#USKcnv=wuyJdAniwzabY6y5RmBfFiR{sK_ z5}+DUFoc2f&Q$v@271PUWEAvbv4I{zz^@nzg@=Gg`zwI>;gdPWUF)Y7k_otZ32hXV zZ-tZ2QFTDy7892T@Uw9tyMW^U4d?1+(2G z{LR_ZX*Yv@z7KJ=v4qoq*ofXqUG|9jK9gwXe0;I*G;{gTBdj6&qU+E5BKrSZJifdj z!g+kjiq0TbL_;ZE1&8kuJ&M3+G^&ZdyCgRf;Q0_BPCI@u{N_`0zbcn93wd&!DaxZC?5o}T}a z#1c4!{x|v%N+wB+F9|g%#%DF#%F*vpXd*v#bn7ZA5{S@=h%J!-*tBVg5UL|g+zgd5 zr{gwOhom2j^~(avvzf*!S%M!#ImVWqu>OdsPi*b|^mwWF3oIKb)?VFFTxZ#95tU`!vo(K4eN6a~&hs#zD zY;>Zg=LcW>hteBN^p>Z19;jA8z&+x|dMm%vZ|d9tixI*Xp7RuD&@Rn?FQ$a>Y}Ktt zJBR&6Xwd>RP6U9)RaW&xAMpsH(^B-6yrj0;I!0WLOxk||WxujdpSKlFI{X8&JREr@mXN(=*6gQG-WRW-ie zGjBBT(Hd7@Q9UGR`m1KGk=-#LJkpy&K-%sp9~^RbDi1S}sY8+&lTEggnH4PuA2y(w zO3dSa`e_qnSR*){`47v=|H#je2wxCP{0(9S3)*c7q0h-KU%h=6AC{H_r6y{c4wuzm z5(QAZ{m((X{8=D1HAOWY^yZk4IX z&DjZUxuhf{1!y7Rmkg19PtO%A#L3bbw6#C@Xha+?p@YB4HB#cv*}+236ZMfIKQCTx z98Q~N4?+qb3})v-lS;b_MX+A67fgW5d3CnmIYwUv?+Ra2myy9qAuJUHL__Tqn?!Ew z875+3=b!2d5+n;9o=vtZ&66rvl;SIZI-hWSv#J%`V3m- zpDYIQ4yeiACM6Zw>xzqrQ*Q@rAw%`M-Y!o2ENHElW=oe>>FQ@scN@+?{Anfbrg$sn za*DwNPavT}=A_E|*J#Zoi%=g}!>O3cl}F|%i-3hwexcLp)lx$@^zL{n9D8e7*b>L1`s1>hy=)QL?;ANq^V~f{d?_gfW!(!kN2YiS}57Jk@+` ztZd*l^5*`x?~8u6`z~Kxz~Hc|rPt@mmlXve%N4*}#=!hY)!%LE#c0#Oi%KaOauGR> z<{f8Zz#v)1-9rj&-RH$m-*ZsWk&aDm)*fM!^7cJWAWZr1NXT#!bIZ1i&bvK<+RZ5G zX0%<9koyIngmmYQ_f^OHrJTN79uR zpSY)oJ?!D>KXuD_UHe+TN}cZ0maw~I78gukbMwIYF0U@p`WL$UT|%Wnti_}@v02$_ zXw3ReTrRTy6Q?c$qV@wKY3V*N5Q%Sl%Tpc!_QJ*V#doiE(5$~1?6|yIw)0fA#*))! zU2EI+=$huR#JIzFsVD!IH7k3G8r%1lkO{Ba4rHQy~w zkCSiW>^4mUEW7iT&j#{4=NaQ6v!#wg^5=?hZrq%E#jGel5GLC?Tj!*2M~nw^7@O8R z6vw)*^)4!^loEv8R%tAwvKZ>-2a{}b9z@o>-=DOa*Zd)uOw#ORd|=X?m+S^{i@#3Y zL0zvSFa5`7R!8`o5J@hUQ1zUPLf#962&_<-H;#+n{hu3VJDa> zJo+Uu4g63jHDps|e`8D>6(K%IXKMZO?rql?E(FVng9?%H-GblMM52i#!n5|S&2xbZ zF5Ih^<_lj#!r9i3YMsk6fFOuR#sHz+tKuNskfu+#T0rZbnHOvjTvnSfFvM-34g${q zEs!iAqw<8#de6xlv9j>9UN%b5ib`~X`1z7HAjG}#)lu+@U+|x77;dy(!XFXT;&s^{ zba5(f+Hg0qyqDef&quh3Y$2oYyi8Vj-T8;XQz%`EEk3=bn2VD;b5>L2(?@7U^LnWa31sC3c%L!Rb zOiyuFgJECV=tAQ;* zE~<;onw1*RU|yXCSX32KE&yNl67##8qa!X6h%8{ki&0;xg3K1&s9d#eZT*8v#=1UR zJQyp($)^a(9eU$KLXJ*62i?oh1t)}C8*ymUkh`!Rt^G#6s$E}{b?r2SAV5UK)T*Ye z@M3{?@$9G!%l|thIr!-sZlGAmcz@~y6JfegA`ysn)oDo(O$*WiyRD^HIA|~pR+@`V z>^4Cvoe-XK|7+YFCy}GYPC-UcZd^t}Z9v|N2kC2*p%yJ>@an`B|4$Qw7dF0sw1Zu* zPyW<&@G{k-|7Pmg#W&F=7j^MHaxPnZ6Apt#+o_Sb^47F)odn~FQ%}#6ZMY+B_7YF} zk%sg9Qga9In!x9R#!@);`i-R4cpbB}LBnXo&tDWd_0u@OMIa#!2J>3w4 z1e*j09n%^Afsfg&IhG;Eif*H(-d}Rs4-(1==RrFCw!L2n7jV12fK{t~^iasO;h=aU zJ4F}DAd$!W-_qgNgW+Z`CSf=~kB{c2DX$)ltd0fm!5p82-x!bX0Vaf#dMvqen+`N9 zz2_gGk475T5}qZ&Y}LyOd-xNAmyS2F#I`vXUn56_jSvv4A98OM+);^oxsPa^J4fV5 ze-j7zb-FqlzuJ6#4sGRMmHrxWmf!RaSsj_dm&iT8i1?(DaCPAt8pHD}ck6Us9v)jkv@Uh*qf!5k zZVAB~;ON9P&};A1UVp@(w|S%9*|1>3R=btdFE>?)>S|0OBt~G3T?D7YCY0Vd*>B;C z3Uhu3Y9IeSzVkD2w)EP@`?I-{({{b>v3ufDrs(~P)Jgv-6D60KTcL+;@J*_B*z^Lc8_YIn*uXMMsKY z=sQ0TFq7-@>?SW6XA+NOVc=yLMI9?0mN-_u&8W)b+BH-D$MU3g%=K;Ei)3}Li_Ck1 z?JCqDuoYuFGef~@;~8nV5H#KpDlW*Kr5S1Cq%?YrvA3O;pjEun&xI~07PZ+M`M^DW zt=)PNrcTn?A?y-iG2q<#Sf_;L`O(;in*|pON$9%1*i+JuBD?c1Cw;YWrx*^RE?Q^C z0BndU&~UIuCUrwe9r>FZ2M_SpuCt*%lae=ql?=ecnc|_#i|=vSIasRNd1X*3fh~X= znp7U`oPTrzPqYlEpJOd=0-E;yn?hPA+G6d^X{OhZ^U=UPo8siyoTvT2V#VU-PPs;< zkspcYGD}J-`n$Gz*RVT(th(R6-*vzg#urZzH6@*|Iqzc|8!+{@X}QhKW;3s;#16RjHv=|5K%gLf}K@k288+`9}FxfIkzt1ARy4bW; zC24seceAwj!Jrs4S&zivIx{!y<-r=<78$2f;V{CT3b|ClFHn!&gGD9lS@s~>XuMPk z>#9nj+$W|%?E9g2XMB-=*5OLOfcVCp!6dP|Tb|f2C&-8$FlV`)wiW}uc48+@6sqrF zC+4h_7#qfqXQ_HWn))kmK7*mM#s0*U&HEgBp<%Z!&Wb(0(TIQQUaDnk+yk5>wo>C!o0hn=#<2&?$e}t zoF7#H8fF*gE(As{^0@?}|287qg0tU@UHuMH1Bd60GUfExFH3Jo>Td_E81AF>qgj6G zg7Cx`qqD z$+;-9Q2~erC9r;(901uN4*ju+GMsgbpX8VZ77Tt9s@$o-men6z$5V**m0h~$YTc91 zGcRpE7mqL^ zwCep=QkmL2ZC`n*I;t{e###h&e61oUgT`!fge1(L%+;x~l1C;kLmQN4g7NwRlbY#T ziCMT{1K-QN%j6Ste1fL5UqS>6#JuL*s$nZ-FD~NJ#shq0R9NOysGJ8gCD_?nOG4k| z^qiD(Bx+n>0|GEHAqlCqS<%b9RGY*0@ZcA&Uq|0Me1RRswkC7@Bvi1u$t=I9$EHp8 zH8t+Ui7)u&&)xi-V))s7H8TehJiz}gcvc+SQUZ+=|0%8IDy6oH&A{0q*Oln;9rOEr zJ;2m}3FG=ES-8K(5CV7=+=znN!bMYG{5re)2A-&@!W}^b% zo$anDN;_>W_d2)-3j5xlD=d_eLv6hrtP`ivo5SG9u8W?wGzK2`Ql`B(C)h{6m+(Ng zo#w4iXJ{kQq4`~G`akCn9DBROel zTINl+8p0M(>kUG?J>*uOXsT_`#Xtc>tEfr1)bOtSsU_6s_d3)b+GLjTUwb@Mhg4to z1Lv?sto(h+#7MAvKKz&%O%5qT|I`Ufer zY$fUoouq4_)Cgr%9pQ+1G_dJMA%DjzpPBw*%HY|8JT8 z`Cnh=Bl1_OC&kd#1>$mIhve5e7s5XF5o#!qb0>pi2e;3+^`mmHwH~+G4R2B2@uzu1 zN3%^mA!t^j zObB8#1Rd(P)CvZ2*@jBkxRY4OFvfmWZNVDvss~UY-tidm&>c&m#RHkwuvN{=;CGM< z)oT<4>_I=SQ)218n1-61BkWC#A+HIK?R(1K)L&T8k3)jUCNRvJ-+Rn)m;yk=6BJ?M(D^x-veW z8+*o3_&R_zmMcvmaClqQj7Q$CtL9;1OexC@OO!fejhXMNOieRUK->f8n#GucINgU?@Z4zGewR zx%rNT+R-`aPI2F*l0nNe+fnnSF8%g4{Jzs6%}x#*k; zzd}=DOBetWqhZGa!$}T7ISHPu#C5!EZLDz;F$=uB_P*s5?6RKsDQvsGNq#{vFfD=U=_E?>s;ltcb!@ zA6`$@eQ3&AtmAt7pE_fzfOVr07bf@)pXYY!~;zmp9Yk_~e`>7$#r-u@z!xnK1L#Ap){ zj$UDb35_eX7_r6_(|xqH%vv!>`L2D}>NA2J6=7d774=1YK?0wtf+)_CL^zR^xYEy6 zK9A2udy$+f8mz@klSaaBLPZpNB3%emzIWf>jkk^kv7*VC&MFCt7$V!c~% zW^L>4KYffMlOyG#`Wl_BxbhC%vzl*4Y(tl&%;p&6@<&}?84<*$| z(S`I~KBQsyhxU;fEZekU-sJas_^xH5l}$L|*Jv1#q(Wt%gXJpMsAZj5>-mSokZUB-f63xFNgY~8Ope@r4#VIWG7sn~7Qkdf4078Yf zHN@w}Q9<1BOVLk-`o+_r_vPBuizknj-~6}Jz`q@gEko9iXh{-zfX4N;m9&k)2E@Wr zzW_*{B>YfrO8y#}Ie&bQ9KT(6*p{G=BL$h>AvhtJ(a>o%v`I)&*7_R0;SQ5 zGs5*awowKbeqHm|HLheBT*2?>9d<&gy6HKo4uqm1nZWl#YHf0k zM9^*jTuYHVZ)m#!58cU~AHjZ&i6^&eSE6$e^cfSHmihs(tB9x3w6+*iVtn=SC~n>? zPNE^xZ2PjtuV&DZ=S{#%gbQ{73wM6)?1)pjXcOEKRi{FZo08CnnT)avCZpP zds#kPrY!G<;4aH3@4XKXB|~Pj*LE#Q_$~h0J@GgC`jFvHiD!52|J>-#X4M+;>OcK` zcX5*Je!d}~D?H>IH@yxl-r&ainf|EoO6q!xW0A)5@Ou#E#`!2nll9M=hvwF1h9;WD zf-mHk*my0fTD%lem{bhDj7cmrD#oZ*jcf29v>zw~O%K1Xf1c4vklv-sBHeNaefsn6 zmQd@f<7UiP#cYjWo*M$P6+gr(k`S*z!zYn^CH>*oQ$qh*n;$1a_*fvWkDdn;mQKfY zl7nlHJavFzc6e5dZ$8gA1f9mw-{GOyVmNiy_kR`jl%b`exp!0{{UB*vH7V&=4k8?s zw~Q+L+`&^%zxZ5G)4wmVV^(=NbiY44X4OppW9ChT`dMKB$8%-M-H2qJWo@Rrs+?ki zjFdy-U=Bw=u1bcNfXds4R`G3O$jKYO1+3(bs?*WI$(Z#qg|FFfpzgv*@%UP;xddkc zaig5Bw_V&2(bxN!!5qOALGq8~Ga3f#L2O*!vtPS7G~2HIYj*rsV(MIvS?{_}@LLxrh(vpNto1*%nX2QLBoLZDF zu1J%_{GR1i{=j$70Uh3p$v`8$PgRPDZ#3{KABSzLh<7)gOGl;3Q0Ptt)feiZRM~<8 zBmD&&UFdM{mst$%jJH6nk%Ww^hD*m4zNKfKhUzu+WlvIIpQA|6)Hwvma3-ZGO=RPb zsxsXr*i7ucKXGv#4(V~aTdpd>Y>1h)QCe>XOA+6?_t#P)<+pe~s4i2R(q`2x=r5C< ze#&MY;}OL?=gPgP7Im1lg?MN0e4v?t7G-)K1ikVaBmOqCxgNu2@suY_EInc==@_PA zQ&^1u*WJ}QB~>2waFZCK3|L;CS$ns%dyo1tW`b2>VsXZ8HmHU}PKj@mU8JKb=C7kI ztI%}ml4H<^G?{ZfX@y4GFNh(xrRe{@^Id96Lb+;%TckQFWZ_4aTi4tq(ros^t*j>V zJ%yHKt>f%@eF*taxBtb3JI_6nkMB_}dpT}YyGTQZXIXE8ETL*sHCq3^KZ_g0ki3H7 z1Ec>||Ih8pw<*3Nk)xIT-xIm_M(CPoSN{KZ(UKJYScUs^Ajz5N>QGbBRxVYvc>8}` CmSZCT diff --git a/doc/gitian-building/create_vm_hard_disk_file_type.png b/doc/gitian-building/create_vm_hard_disk_file_type.png deleted file mode 100644 index a157211cf5baf909149e73fddfc2556305f4b123..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 170503 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 z%0A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�fhm#MN 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%0A-nL7Zl65qAwp5y$_efz^}#|Q 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 FV1lysU>^Vg diff --git a/doc/gitian-building/create_vm_storage_physical_hard_disk.png b/doc/gitian-building/create_vm_storage_physical_hard_disk.png deleted file mode 100644 index cee16a6c63b4f2dcd592ae8c9cdc02f278fbad81..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 181681 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⪙EBe)ptd%4#bSc4(R)#^VgfnvX(sk)l! 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Ξnrtda)r}IBOkBL)C3F}vs%`L{I|LW!)R;mump43 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 diff --git a/doc/gitian-building/debian_install_12_choose_disk.png b/doc/gitian-building/debian_install_12_choose_disk.png deleted file mode 100644 index 0f3acc498e3e9cc04692a9c557a8eecb5029dc25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6613 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 Ro_*^(ddTY_@j%$+e*!Zl+HL>< diff --git a/doc/gitian-building/debian_install_14_finish.png b/doc/gitian-building/debian_install_14_finish.png deleted file mode 100644 index c8ef0b37ad6232f87593ea448beaba089b568a91..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10794 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 diff --git a/doc/gitian-building/debian_install_18_proxy_settings.png b/doc/gitian-building/debian_install_18_proxy_settings.png deleted file mode 100644 index 2c19919f64b62d89883dc8e1308f179978b95ca4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7582 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 diff --git a/doc/gitian-building/debian_install_1_boot_menu.png b/doc/gitian-building/debian_install_1_boot_menu.png deleted file mode 100644 index 216502e1c6eb6930ce60c1a36e7476e6fb8781d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 110818 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%0Aov@;*Bqa@^y$+3vf4cK^tvHPiSqMpIS@yr7c zf98RQx94BK=c7aZt_K~yYoGtMd;X{p?RL#>J__wDUov^t)+!%pq`_@BS^*z;a}4ES91InO`x z*rDWkM;-ILqmJczo`3YQ&p-OO_WW-?^nUJ`?c@Lc*f-i&oBZorZvOL2FCtA}(K?&A z`*9}@sojr1Y4_t#Vq?lo_NSk^@Vp;B`t=_^`t>&Xg_~}a3IT>&TJw{n2Ky-h01xR& zL_t*c_@}Rby?wRG&);~%Q1A2a-T1KoF!p123=d?f@|0m0+HM-#RzCYRr$Nc~q3D18?suMh>=|P39zM68 zJy`dOm z7oA5$Pu^u0BKqN@kNPSKQMV}_9E!(AycWmXaq+FRYu~!Y0;i>xeer2$?0MzYpWPVH z*VAs5MUP@{rAKP+%&YC|bV`cz9(n*=fcJnQB**v4_mK+xx`GesnLuT}Xt)^vO-NkI zo#4>RiUEk|HgB6>+>7Tw^_TW0A`y)W?R4<5-}|!%fAoXzK6dB(cRJ*_q18Jddi;0) z^!^`x|GVF~ zj(Z3jE8nv973WgXXNC}Dv2M!mD+SInz=+pQx`=7lo-X~t3(Cm|nvFJ0JH$ra|gyajSOxX8Qe@nBZHey3~nZ( z@w47{=|^rMq7yqFBckIw9y`9{Q3^ESnj79SZxIm<|7gJS_Y8CN2M<#`3HD^R070ul zM1T6;L)1A5c3=RAr8qRO;n2Va#UF*4y7p_?*tV~U%>561gNJ2lYQL#*f$a-TM_sF- z29N`Zp1F7Z+P&-7*2=FVJ7fa}qCNe6DkckHq8yd?tGn;wVOfFr5nd1m)HdvTuQd<_ zenMPNZtEw*I#JU@E0+(gT#m%XY1i^)yOuBW=P|#BnZwlfDO7*LIs7vkk-51UpQ_&QSs)v3r7Yw^FZhe$4#JuF8$~& zC!!Bd`4>$nh`f#HW7{8z7cmISCTQ{_kC!{r|2lgn#zi05x-sQ&%EMb8WOv31fQ{%t zRJ?uBXa4Z$1Ft-Hnuuad))lwXp+`2vxagQ-C>F<&b}xCoa^_ULh#^-~mwxWqjoEuX^i&o}KH|ab$ZAzzY>WbKO{;R*VYVX(j zUm7_**WSH0M6Av~wM)0xUG`RfkW8KJ=(HeKo^*EqeNV4i8BdF6_pf_; z)ruF69h-fx_-aZdkH(sF@4BHcuSloA)Y*;}3u5Dp=d4>lv|?GT73mYg3rCN}IO>?* z`_=WkmM@F((l`b6e`S+b*T%C=0bJD(c2REa*Yw%I4<$A@p?J+z*INP`&+mMWSlp-z z@)lHra23Uv{5*sFuQ25~9U`O>R8=&b8N~Cy8A^2H-Z>|s{cZ2AM03%*uH#Jkc`y;Z zVP@G_f8Fy7o&e;u8zi7$MniEJ}-r=p73d#g;g@rF;;a z^2x;qw+s-`l!c39)n_*kB(SOFIdLi8!OkZ;s<15Qha3(Jl;3fiws^^bflXBK9ciX* zLDY%Ty7JZ~>SFqBx9@*gyzD-GNf~&!f75rVY54R0SH3hy7RgZCp1E}#$-h{zSm+0&BvtzCF^fI{B0nq^`*n4l#8M+;zgv_9Ch zrCkSk`u>3*&$EOaN zh-hD3_qae{T8369dpZE({#o1|#tKXeN%;l=O)fi5O|BR3l}~lBHynfC3TzdG|lzum*yaOhupE&ax`f<~m$;$B=?v!i?zLLdv3gpdK zg(=t5F_>3yCF#Aa7PhvK+x1%t&B~8FmdKWE15|Kw4U8wEHaEsp3RgfF)^A*L+C_4P zvcsNceK4HR&ubqH?nSop*ku_rm#naiUDS<$_FDN1XAp1HZ z7pPffk<}8Yiwfr|wOO&7bmxW@0Ml=~8vrmvMAgw^Z!7cdnot7YNIGG-sf z%DqpN$r5^IOo_=1O9xHc!!e-#z)~RZaZcw7BT8c;v%oG7Ut)0|OA$l(t_~8w4i_ss zRLQiELhqoVv4}Lc*2SFmnmR=1yqs{7N!)C-P`5WRub zo$%a#wFX4S*Ce<-15vo(f;Z}Kwea-+xd5XVxvA2t0nc-i0_EaJx zKvxTv(d9<8BJ6%6T54o<8bv^sS%278S9GRld%}>TDZq02g@QGsOwd8y)1@845~;yD z7;;17JWnL0!1AC$oJty*!M}6NEAer;V+&ujdXaJ^ZCA$iEIc!vzeztqW+}Qtmk+Yd zM9Gjl4^cvo8V78Lqn$7O2{`3t_aL1ce?B&DB0Fc{sl$%cjJJqF=(*KS8AvS82dkkC z&@9ugHliISK+(d_Rc*vEjUs}=)x|WcY6Ik!oJkWSyY2|7W(7~Vqw~Qv4}Tad+n;%z z%rtF{%L_CopknX0qp4CIniLwVUZ`{9hFubmkt-dd&v3WpT|48Z?4aq#HaS}Zt?RVp zs<&L)aP*hNxy7=sTIYbVtLR+m96~b|@FTX8{YP+pH7|m#bRDu zvu&+@jHI~1~~zBfdq zI9hU60GGmUGv?vpb&jZ%fRzo{j0}ut4 zumX5Q8XUcY0Poe2Fv2&j-1t;DqzYPOhKA${08r-1QgB!On~@f$%b2qjfYEYIg6Q$qT6}bX^I907+19foye~E8Y^P zZ9tZ1-~fw}r-Q%_Ko5Md(_3r8vU2?owxP@lgr=4d?cIH3RwW|@R{(nEs^kKS_rwhY zrW`TkJh8}iNODnDLDUK!2CmTQd_8r^`4n3I8PlW{@AR@J`~i_$DH|$>n$M$X{d>R( zpeDl%0<1ikYnrwatXk%1ewIHLIGDS7S#S%8Y=^T$rU3EtPmtvv0=mL;A*NKh!j4iV zC1qzOAd|p^WrQ&TSe~FsywrUkR2%~at)xiGN=s(lD4rj1{lxvKG^nc&Q4nd z_6}#jpsrobKD}59LsN9_$IK0j1+kap6mLp-L4Z_sAX%T;aGSS|o)5u*PBa0Z465I&A<00000NkvXXu0mjfu0WCO diff --git a/doc/gitian-building/debian_install_20_install_grub.png b/doc/gitian-building/debian_install_20_install_grub.png deleted file mode 100644 index d853c1587188c5fd4ede94ab00f9d8b6731674ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9784 zcmeHtdsNbCyEb;Rw6fmH%2b+h%E>ab(mXRA%`vs|EGnHT5%GK$oGHi3)G70jDO9GW zsCWYMh+0Z&ibqgTNKr8n86=Q{z^2*X`o8^s>sxEDwZFapds&P1EFPZU?_oXM_jO(O zb^jh+c6HpMrKbe|0Jbn~4d+@0ZgX1Trf#dxO&O>~w0 zvuL2CaTYo`&vUS^=R|aT6yQuDZ-+r!*(? zY%H2|izgk4f7vXiM3}+~Ez4%4q;}V(Kj~)vlLJQUEw@!m&QaD?t3JzPk3ie;oE4&O zUI=lNL#scEdL{e@%I>!-c=da7Cr#o6pwun?q;J=UoBwQvJaQCR5dm+(p(ZQQ6(ut8 zigS#4rKp1UoUz!$qB}2eo$&6`XZcE0fqzxCWBbFF19r$lzy^q=_lcc zjk>sx6#*L#`)fS@*&hijC*6yZ-z_Iak%AAE;L)^0RRbzm=F5dm1b5-H{4i0pG~fM~ zIH`0OnaCJ*_^F`0bvOh>F<;QiVeOWl*_~Gz^vCVtq?*F?z$k8ZCUw@#+|(oFV&7di zIDh_Qv*g(sTMP2V;oyO=wJ{Nmjn-~S#r72UknPJNbXLN!@qk+~4lUi1Pfa_!TejIN zi^ocS$&mg!6~bF21f;OPWpx<>&$ayyBE1`n1kFZgRFB3)$4xGD3t5R>3uC0x1?-5# zj%S4dmO>nESRW?c@099WTyZe8Cz>SNn;Mp8`1d=uO(#a9eO;4V5cE3OW$v8-MP<1O1eZ)>RL!Pl+$TE9wT16~F<3-|8 zNMP$+fyZ4E`|MI0aMdrkG>WwIh#1uu4+eTu>+YVj1s^Bpgw=*gOB5D};g&P~Q#4-A z-ZWHyqcVFLG1AQ9aOkFK8Xgg7nJZ47^lg`$ceRx(*?Sc5MQh!W8_fpmsyY%od4%Xq zhfi20elqwU0e5i(iA8B(7HPqvvTeZW%&OBo)WVWgL59J?&AAWQ_bfhyQLL$1A;%m< z*_4+*^iWjt{99NTxbu|G-1>e!Z z`07q-i|bKb?PdVt$cSOP_q^zSIkKf}76NQG(6Qj1+VV%I%oxkXlA;26 z@9OT^RgA`g`HeiX^L1&7(wD)+D@JP|Hgj38{U~fsOjokVra36bZ1ejP4hM_e+FtUg z>9Xq9vI%XNu2acPY7G*+S)!|s2GOfZa-|3z$A8(<(RU9cNP`U~M~>vwmW4)O_(Im0DUBw(z5 zVb4}_@7+qacpGql5`o!P9b$G}D&{QtGCY&`XEyMZ(gOp*a)YakgvDJCmz;-_4%<&5 z{ZB;N=>!Qk=M!MxVq@!3$?AKpn5yC~0L_X5is!H^!U;#kRw`SN$2v{AEQdyjcrEdY zq9h+xb4V{acmxcxtzjR)O;t=FfR?U}UsID3uYyo#+R7((OcJ$MCWfJVJnsjM!G{SI z^ZS-|E%`IhSow=wB5E_CWUhp#N87YQf|p&dVOdfW=gz+B{!MXgdcp(M*!=Na4XV&( zYwLL*$(z#(gK;N%(ovQy5hl;Z1qGqd6RF7X8#O99fq1$5Z6QsI%b)J!1_$sHdBRy^ zTmYEQKw>8{&&ojzrI>AwU*%0U3&gVsT2t%_yf+%YR|hs1cb6SD*qAtF6%S5~DV(h2 zSprM?ZHbS=d`?u=DjMG1o2dHAC1ydmvZ$uw)?5LF&ExOaAk3pvs*We&H&nFVv3_iD zr3_!Rgi=~{_FR2|R{g2du=WYE6lr2q6&Vt&86Ca6$K;v@@WTzhK#=>!WWP4)K=}juHrOnjTM7)tVZ3!PzWfHENjB%-qI}c~@1NCT&NB!x zH{^2|xC5lli*w*HYksE&Y;ZhYWl|K^M@iKnvT7W_#`RQpEBecPJ-W`y?liZf%AAig zC|VB7F^E*)QvS&~c+sU0nw?K^+!w&+&s z=&$cMaCv2+yXslz1!_Och11@u0KZ$Xl@8H>#&oftVRf7=dd4zI}lU|q`vK9 zY-K~2J2+*BYk}^H@y3>Rfvzta7U8P?F{_b9M>P=Kt5PdtCC@=CrH@c>myG zFuTKHW-B1nWQ{K1m$9cBumPHxrlRAFV=tgBCVc(h)mhM&qTd0(q_a^a*Og}@h}pPo z=XIk6aXu8E?A<2oUfkxXYq3Iblc+_cDgpu^<#MO_KfR1JI7za~Cnzpb8ykhP}Yqs>OUy@TnXO(?Ok?+^m#Ynx1bkv>s}nhCNCHL0X%0sLBS#xGJT@ zk9%mRH3V^;gg~f7ZZ3t^h|;o8*HAe+kGnB8R0h4DVS|X06K}S-@ai+L9tr_((t(c; z;opi%66NNiLe4kPzr_Ar?;e=Y7v_EUwJ4DmSx{vU@j+Z4rdqykRb)nXC3`wterd=G z%;>^<+5va#Il*b%`$QfE8xJq{SJAb1(}o5?I8~Du6k;D?z`kBtNYk`1{)6$W>f~rc zbct`)0tGKB5#NN_T155tG99*p8Hel){ZOF^2qR95DZ9Evc}AX<``o)wPp6VCZ4f)D zVm_z3aGI#>`Y8PjhbqvJ8uHPWOv4i*3XzbN)O|zyso-S%Ij@6ty!7A^o7u@68H)0u z$)PGBq-An)EUa16J4`9sK;}Grjr3rJm^WgYVDJ3zIF~(bNGdw<km`<-205bx zS@T6pJ*yAnFJz|LbM5X^Z5$D_D~jk5yWvKrdpedH_oXeaXmosz(sKusxtnWNvEspA zFf!1u4m9)b$0REG4xM&YsGG5v7{l!0bD^NElU3M}y^-bEt&RqKB3tY|9PhZ|MsB8^ z)QL1B)fseMhk?G~AtWaDvo|ej=&+Bb-@XWT#^c^iOzyh=S913qILx4L;)iC!D$S-G z_)VqUs(kSW2Yr;BZ$GIb2;6^E4i{%?dH65$Hr)d~tfL za#J*W`^*{NyluAL;P_7&dYjviqj8nNGizPrj3c`a`(!y&+fRGBF3lq1>H|k{5QMLy zQ0@d7t*9^S50?vVwB=#&mNZICuy@vAlsKIej$-PO&%XYnMBxK*W4*8ZirLPs^ldFF z>}*1mDXik|m=6bIR(eFA_hCqu7EIaOuIov*#0fiUM|H)Cxlh;aT#ai8kh z*41Zy1#3$NaD{^0dT%Up*%dcKU!&r^rBR+R?$s9YIdUz@->X5$F?vioD)Jfd-;(!r?6` zHjsT4z6fq~_Ni#)zA%LRdh1XLgmHMawf}~31suWZ4>*FKK=zpszHz2I&XUg#iwV>Q zLKjgUcGJEr>^N{=R;A(gmo{A#zrG}Pv|0(;7;5oOre|f$vO@R|rKSF=?+JUVu+(Z` zFRJ8q;tizy8L>C_K7IN|)_PsYa&$`?>=2GvrjU7pN}mxG$52h;JVjR+`9afIf8wY? zp&e_iL@f8Z!osLC^+W7kI#V6$I3ga{!zR?yOFIQGhE5`>=z9jlGWMG&yWsImaE=+x zIz)MI(yU$jIy8QI8!$XAwD*|J0v>u7bhS-gko9ler`;Nfq8j%OVjot((gqnmFOqS^ zUr#oFD36){@F{jH)%r#jF8uTHj=i{R@W@ULKVhKsF(YbM8EjV?&yJ28Lk-2|H8Tw9 z%F#*@#jjB>CLN9u6DwlAyS>oH28F$gX$O2BAx3QhWw6Z5x|BB0KJ}gEx1f=3-L;pWWXO_Iw#+u)YY0~!I#Oo^G_(R8qkpGLTml7w zo4Au3)THU(hK=D^u%-*E+_$ z(yp^EPP1Y#cjD@2aeNPtrf+})pB%5p3&v%MS@|*ue^*GsDN8YQMiQilX}3x32O%7k+v*KDQx^}YE0*k;e|KdQgk{Heo3Ae z{s20u1qe0#u1WHecZEJP>oN4~a80q~?u8ce8f23$H&|i+c2Y(P|BKjfjNr1Y zBt}9v7)jKJK!Ep|vswfAH`p$`VPLbg`w)1dnhl~)xIhp>JQCZm1a z9ufRPmXVLLq2tCMND{%8EPj%DGl9y(^&Iz4KmXQ&Pvd_q`q`i5t|lw3!SYIe9?}2N zi)QCTX2bNkjb%1&$E{#JKYw;U>sByniik_tJ5f*+EpYlnw=`-f8PBnJ-QD!KWR~O- zL|R1k@i))K?C_%$mK`=PlBn5XDZhBdsYMNiHtq@1ApE(WpUe4I>4Jk3*$E&Jw} zVwvxUCNug*THdC2MgmC;@5!3Rj`zHu?|hVu5B2XozO*;!UF2f344*+dFUdh!Vi^JVFYd66twC6R&=n~}5D{gV5&yc2A?;ABh6B_tlAct|lm>a;T!Xp&* zL6ey&%CCkLVxwZrBz(ftk~xB~$VAv0l871#_5VhH?~?vu#LhRwI6Uy@F;(-h4NLbq z8JcAKcP1*B18Pa@YCT!{#=#bMboR|{{+FNZv$T8?-wtPJvURlrGarhHx$x8>fp>N5 zuYVbgmD*6G`{fxgbV45qu1$0}C7x+(9M~NG`f6r)`B58BbNwX~_LNCf2ehdzbwUGB zb^gD==0llsn_W$J|9uL(zk}<9MF)#7m(FGvW)6#CXKY@<>uwC zP^WBD9yOjC-2;!FMeq)!+_20b4zfA;lx~x&a`PdKFsPRoGoohJlDLv0G?9l%{0D$A zrdPe8Zzqn=3`9hkwtfINZMKvdT%TG7yDMC<49D-cz3r?vyPMHcEd@y&dnKrucMN&Z zED^nIBsU+$R5JGIBc6%~;tzDmPQsTD>MDwT=RtnX%cV44@V9a>XZE`@P4iYe(_6@O zaQ;BN$v)QAvs_r_f=bPaCmc;P+*UNF))okKr}CwR8N`0^J#=gQT<#_?t3s#cSK*cx(9(bFYPA^Qz}^mr|)-MF?i%-!`?~ z?wCaucE$6p@(jts08$q3I`2-@9veP@(&P$aJ(w~LKkL?v1I%{l`>SpA<7~`d!Gj({ z^p5+k4~!S9YVp5o?+b5$7d#vRN&oRnd6_-L){i_uACtUDSoUwm4lrfF!3*>;_5jW3F7}A)iH6zKGyZRZ;rNj=7+& zeUU-*GCpQROnvSV0)N z@3x+QuPgDdSp$$vY5mPK|KF9K!l4lWc>i3k$P+mjk}FoYw`AkH{;8)aAEI2Fge<)R z#q)z$s^%@E;=(1`n$RCJ$=`PP386>thNHvj<%09x)#$i+=qrdCRV}wGtGUrz?^%W@P6LYQ5pltKIwPQjQ4NWpAs4<~9rU>(eG!E-W z{l@!zvp`4BJ{YS6%ehg7UsEu610dUU0fsVtc|;W)1Su2FN@*}O zj(vwGs9d`5n}RD9<5hm8gPTG%ELOCPt%H|6+oeKB)rAjmMd0Zyg$G3Kd~s(hR`6%cXqdywVF-^qdP~7_mXNh6`Kt0PQXrwh{2QQH9wxctgq+#O1Ddc(igU0xo5Iatk6c zd*3zxJl4qUfE<=L4Csuq!W*s zcoS5+D)6KD4X@6~Q_@XXO>qnJl#&fB!U=wAy7$XQULAzMg*C`^6UmJ>>&VxGK!&pm zqP5V!DEw=qD6b;QCcchRBxeaLl@yd}bVMi3^wt7*XpoYo2z_;0KI>zwuPQ2p`{Ntq z6>T+2$?L(4@wdmpPaK+7Jpu!J>j=aZnv5D)H^b=E(%vZ(i^7r=W%-A5S+zW3yZm-q zzGq~nDnoFY%@Yg_#?A^xiH-K|8RyoYyQDT)=9oR)I9T}Bm#benr%eL-XrshLn5_t} z5Umiaw16e(6^b828t8bk8+5_35w9I@d?w6INnfqXBUGzQ40y``rubc%vOvx zGV$X3WbfgyD`U(O8`$Hr3gERRHAGuA{t>Y^riQ1D_{aLM_5jzft&$4VVqA~!YQf@e i^}}8N_bd60O49&(Cl23XYxDg(r}M7os?J>d{oep$nuvb@ diff --git a/doc/gitian-building/debian_install_21_install_grub_bootloader.png b/doc/gitian-building/debian_install_21_install_grub_bootloader.png deleted file mode 100644 index 493ab806a673f5c8ee47db0bb5de6af0d5042b03..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8878 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 diff --git a/doc/gitian-building/debian_install_2_select_a_language.png b/doc/gitian-building/debian_install_2_select_a_language.png deleted file mode 100644 index 0228ae2c011daeababc75b915a12b28b702456c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13131 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 ChHGyC diff --git a/doc/gitian-building/debian_install_3_select_location.png b/doc/gitian-building/debian_install_3_select_location.png deleted file mode 100644 index 7b18fba97577b521944f909ed57fd251942a9eab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10388 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 diff --git a/doc/gitian-building/debian_install_4_configure_keyboard.png b/doc/gitian-building/debian_install_4_configure_keyboard.png deleted file mode 100644 index 8e46117de4879a6de8b276813bef12c219cb4e8b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10224 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 G0{;s$nf7Y{ diff --git a/doc/gitian-building/debian_install_5_configure_the_network.png b/doc/gitian-building/debian_install_5_configure_the_network.png deleted file mode 100644 index 8e3720f24322a54ef33893e8eff95383158bf3d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7612 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 F`Y-khTNydUoS`S!3D`&_^}kArm_$KL1u|Mq@* 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

jWr9v@z0ivdDMx9PEs!hN{lI$N9NlC_pq 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 diff --git a/doc/gitian-building/network_settings.png b/doc/gitian-building/network_settings.png deleted file mode 100644 index 9e714fd15451837980b9d12a931c294fc962b2a2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 72185 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 !LL;wH@0^a>s+;)8a@6| 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 O*kNV%i)poqXTo1+UQf;d diff --git a/doc/gitian-building/port_forwarding_rules.png b/doc/gitian-building/port_forwarding_rules.png deleted file mode 100644 index 9e1fa2af20f48d482892b722b7288600bece322a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44052 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 diff --git a/doc/gitian-building/select_startup_disk.png b/doc/gitian-building/select_startup_disk.png deleted file mode 100644 index 59bc093e2c3d35bee0d1bdc50f11f2cf2a6fdafe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 72785 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

Yv36);|zQ7@X|zmOcox3QHk-s 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 diff --git a/doc/gitian-building/system_settings.png b/doc/gitian-building/system_settings.png deleted file mode 100644 index a5720ef3a345d9a52a64298fd68c3c8402f86b38..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76448 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

gI}JOanh-atZ1Dy|<~gJCmQ!_M9Y)4zQe zdoMjieqJv2&w3YD?@h+lYnSldYgYJ=FUHgUbxe5g|G|c1H;|Q^+hzB4U)On5TEOG< z^+!;t5(=dzs?gL__asLYJ!&u7*C z3-EEb$KTQZT{^DUm16GbZxLTmf%qrZ_PYdSaZS_TB=Z4OM778kt(_x9sF&W<@gQUeBglCqztOG4OFq{krL^TS;L26&$(+z z&#yqHDR16)_Q0)^TVVD3sVI`F5g+t`A3t!OfHET&aRK%iJ#8t1BcpJ9<7{j=Wle>< z0{iBFh6yY8?t+RTW|7gzs?O-V6SZ?%|K`{eY}8H6m?K^Ki~xyN{&Y5-KYk@!nTc z@Hi-x;BGbM?YKbiiVij>*W$l_F^3vG?pQzVFR=P57>cH|V6AdmIn z6cj_JQ^Eei4*bK{Kf=fNF;-7}3yb&PL3OPL7k16V-N!M|Yo*vU@i&;d<_J6B=5VT)G2cVZoHnU*VKZ0JPFF%%*eRpS~LI{$a?=&BT&#{)FpJzVLCl zh7nWOAeS1*LYMjg!ef{NrY+o0@ThBrN0&?cu;-2^L2fzbfAnv->fi%E=Ue#dhb1VG zS0gjb4S)V%G&}=BaO1>Qj9+;Ksv0eB9bG`_{uhFK7i8uY3tiMEcs#mx8e9TXP^+%O z;-6XJ%SAi+v;2te`MO64vXjFweZ(i&dxGktqH<(J`QWX$zoWDW#_iKP@xzimkk`~B z-t8uTwx_P#LvVBy?qA%8ug5Kft4|2ZD@w3`@g$tQ<%CdQ7rgV@dx*-cgr*`3-~WQ% z!!x(x;c$!l4@oUSLBbQf{>E2udlHPh=l1YxlvBfeaC&h>4H`VM?lA+kXs&FZhg;6U zXlSZO+!J^F@b%Z2ICeBHy9OaE+8_T$58mA`7zr_9nDZwq?0@hWg^7WfG

Nbw;$* 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�ez%1= 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⋘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%0DS(mf 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 H_RD_(VGmy2 From 22f816ef4dd57785627d801c247714cace7fc5b5 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 23 Sep 2017 10:36:23 +0200 Subject: [PATCH 276/382] net: Improve and document SOCKS code Make the SOCKS code more consistent, and document the constants used. --- src/netbase.cpp | 125 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 89 insertions(+), 36 deletions(-) diff --git a/src/netbase.cpp b/src/netbase.cpp index 05f9f6961..914124cb1 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -184,6 +184,48 @@ struct timeval MillisToTimeval(int64_t nTimeout) return timeout; } +/** SOCKS version */ +enum SOCKSVersion: uint8_t { + SOCKS4 = 0x04, + SOCKS5 = 0x05 +}; + +/** Values defined for METHOD in RFC1928 */ +enum SOCKS5Method: uint8_t { + NOAUTH = 0x00, //! No authentication required + GSSAPI = 0x01, //! GSSAPI + USER_PASS = 0x02, //! Username/password + NO_ACCEPTABLE = 0xff, //! No acceptable methods +}; + +/** Values defined for CMD in RFC1928 */ +enum SOCKS5Command: uint8_t { + CONNECT = 0x01, + BIND = 0x02, + UDP_ASSOCIATE = 0x03 +}; + +/** Values defined for REP in RFC1928 */ +enum SOCKS5Reply: uint8_t { + SUCCEEDED = 0x00, //! Succeeded + GENFAILURE = 0x01, //! General failure + NOTALLOWED = 0x02, //! Connection not allowed by ruleset + NETUNREACHABLE = 0x03, //! Network unreachable + HOSTUNREACHABLE = 0x04, //! Network unreachable + CONNREFUSED = 0x05, //! Connection refused + TTLEXPIRED = 0x06, //! TTL expired + CMDUNSUPPORTED = 0x07, //! Command not supported + ATYPEUNSUPPORTED = 0x08, //! Address type not supported +}; + +/** Values defined for ATYPE in RFC1928 */ +enum SOCKS5Atyp: uint8_t { + IPV4 = 0x01, + DOMAINNAME = 0x03, + IPV6 = 0x04, +}; + +/** Status codes that can be returned by InterruptibleRecv */ enum class IntrRecvError { OK, Timeout, @@ -203,7 +245,7 @@ enum class IntrRecvError { * * @note This function requires that hSocket is in non-blocking mode. */ -static IntrRecvError InterruptibleRecv(char* data, size_t len, int timeout, const SOCKET& hSocket) +static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, const SOCKET& hSocket) { int64_t curTime = GetTimeMillis(); int64_t endTime = curTime + timeout; @@ -211,7 +253,7 @@ static IntrRecvError InterruptibleRecv(char* data, size_t len, int timeout, cons // to break off in case of an interruption. const int64_t maxWait = 1000; while (len > 0 && curTime < endTime) { - ssize_t ret = recv(hSocket, data, len, 0); // Optimistically try the recv first + ssize_t ret = recv(hSocket, (char*)data, len, 0); // Optimistically try the recv first if (ret > 0) { len -= ret; data += ret; @@ -242,24 +284,35 @@ static IntrRecvError InterruptibleRecv(char* data, size_t len, int timeout, cons return len == 0 ? IntrRecvError::OK : IntrRecvError::Timeout; } +/** Credentials for proxy authentication */ struct ProxyCredentials { std::string username; std::string password; }; -std::string Socks5ErrorString(int err) +/** Convert SOCKS5 reply to a an error message */ +std::string Socks5ErrorString(uint8_t err) { switch(err) { - case 0x01: return "general failure"; - case 0x02: return "connection not allowed"; - case 0x03: return "network unreachable"; - case 0x04: return "host unreachable"; - case 0x05: return "connection refused"; - case 0x06: return "TTL expired"; - case 0x07: return "protocol error"; - case 0x08: return "address type not supported"; - default: return "unknown"; + case SOCKS5Reply::GENFAILURE: + return "general failure"; + case SOCKS5Reply::NOTALLOWED: + return "connection not allowed"; + case SOCKS5Reply::NETUNREACHABLE: + return "network unreachable"; + case SOCKS5Reply::HOSTUNREACHABLE: + return "host unreachable"; + case SOCKS5Reply::CONNREFUSED: + return "connection refused"; + case SOCKS5Reply::TTLEXPIRED: + return "TTL expired"; + case SOCKS5Reply::CMDUNSUPPORTED: + return "protocol error"; + case SOCKS5Reply::ATYPEUNSUPPORTED: + return "address type not supported"; + default: + return "unknown"; } } @@ -274,34 +327,34 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials } // Accepted authentication methods std::vector vSocks5Init; - vSocks5Init.push_back(0x05); + vSocks5Init.push_back(SOCKSVersion::SOCKS5); if (auth) { - vSocks5Init.push_back(0x02); // # METHODS - vSocks5Init.push_back(0x00); // X'00' NO AUTHENTICATION REQUIRED - vSocks5Init.push_back(0x02); // X'02' USERNAME/PASSWORD (RFC1929) + vSocks5Init.push_back(0x02); // Number of methods + vSocks5Init.push_back(SOCKS5Method::NOAUTH); + vSocks5Init.push_back(SOCKS5Method::USER_PASS); } else { - vSocks5Init.push_back(0x01); // # METHODS - vSocks5Init.push_back(0x00); // X'00' NO AUTHENTICATION REQUIRED + vSocks5Init.push_back(0x01); // Number of methods + vSocks5Init.push_back(SOCKS5Method::NOAUTH); } ssize_t ret = send(hSocket, (const char*)vSocks5Init.data(), vSocks5Init.size(), MSG_NOSIGNAL); if (ret != (ssize_t)vSocks5Init.size()) { CloseSocket(hSocket); return error("Error sending to proxy"); } - char pchRet1[2]; + uint8_t pchRet1[2]; if ((recvr = InterruptibleRecv(pchRet1, 2, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) { CloseSocket(hSocket); LogPrintf("Socks5() connect to %s:%d failed: InterruptibleRecv() timeout or other failure\n", strDest, port); return false; } - if (pchRet1[0] != 0x05) { + if (pchRet1[0] != SOCKSVersion::SOCKS5) { CloseSocket(hSocket); return error("Proxy failed to initialize"); } - if (pchRet1[1] == 0x02 && auth) { + if (pchRet1[1] == SOCKS5Method::USER_PASS && auth) { // Perform username/password authentication (as described in RFC1929) std::vector vAuth; - vAuth.push_back(0x01); + vAuth.push_back(0x01); // Current (and only) version of user/pass subnegotiation if (auth->username.size() > 255 || auth->password.size() > 255) return error("Proxy username or password too long"); vAuth.push_back(auth->username.size()); @@ -314,7 +367,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials return error("Error sending authentication to proxy"); } LogPrint(BCLog::PROXY, "SOCKS5 sending proxy authentication %s:%s\n", auth->username, auth->password); - char pchRetA[2]; + uint8_t pchRetA[2]; if ((recvr = InterruptibleRecv(pchRetA, 2, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) { CloseSocket(hSocket); return error("Error reading proxy authentication response"); @@ -323,17 +376,17 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials CloseSocket(hSocket); return error("Proxy authentication unsuccessful"); } - } else if (pchRet1[1] == 0x00) { + } else if (pchRet1[1] == SOCKS5Method::NOAUTH) { // Perform no authentication } else { CloseSocket(hSocket); return error("Proxy requested wrong authentication method %02x", pchRet1[1]); } std::vector vSocks5; - vSocks5.push_back(0x05); // VER protocol version - vSocks5.push_back(0x01); // CMD CONNECT - vSocks5.push_back(0x00); // RSV Reserved - vSocks5.push_back(0x03); // ATYP DOMAINNAME + vSocks5.push_back(SOCKSVersion::SOCKS5); // VER protocol version + vSocks5.push_back(SOCKS5Command::CONNECT); // CMD CONNECT + vSocks5.push_back(0x00); // RSV Reserved must be 0 + vSocks5.push_back(SOCKS5Atyp::DOMAINNAME); // ATYP DOMAINNAME vSocks5.push_back(strDest.size()); // Length<=255 is checked at beginning of function vSocks5.insert(vSocks5.end(), strDest.begin(), strDest.end()); vSocks5.push_back((port >> 8) & 0xFF); @@ -343,7 +396,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials CloseSocket(hSocket); return error("Error sending to proxy"); } - char pchRet2[4]; + uint8_t pchRet2[4]; if ((recvr = InterruptibleRecv(pchRet2, 4, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) { CloseSocket(hSocket); if (recvr == IntrRecvError::Timeout) { @@ -355,26 +408,26 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials return error("Error while reading proxy response"); } } - if (pchRet2[0] != 0x05) { + if (pchRet2[0] != SOCKSVersion::SOCKS5) { CloseSocket(hSocket); return error("Proxy failed to accept request"); } - if (pchRet2[1] != 0x00) { + if (pchRet2[1] != SOCKS5Reply::SUCCEEDED) { // Failures to connect to a peer that are not proxy errors CloseSocket(hSocket); LogPrintf("Socks5() connect to %s:%d failed: %s\n", strDest, port, Socks5ErrorString(pchRet2[1])); return false; } - if (pchRet2[2] != 0x00) { + if (pchRet2[2] != 0x00) { // Reserved field must be 0 CloseSocket(hSocket); return error("Error: malformed proxy response"); } - char pchRet3[256]; + uint8_t pchRet3[256]; switch (pchRet2[3]) { - case 0x01: recvr = InterruptibleRecv(pchRet3, 4, SOCKS5_RECV_TIMEOUT, hSocket); break; - case 0x04: recvr = InterruptibleRecv(pchRet3, 16, SOCKS5_RECV_TIMEOUT, hSocket); break; - case 0x03: + case SOCKS5Atyp::IPV4: recvr = InterruptibleRecv(pchRet3, 4, SOCKS5_RECV_TIMEOUT, hSocket); break; + case SOCKS5Atyp::IPV6: recvr = InterruptibleRecv(pchRet3, 16, SOCKS5_RECV_TIMEOUT, hSocket); break; + case SOCKS5Atyp::DOMAINNAME: { recvr = InterruptibleRecv(pchRet3, 1, SOCKS5_RECV_TIMEOUT, hSocket); if (recvr != IntrRecvError::OK) { From 0cd9273fd959c6742574259d026039f7da0309a2 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 7 Mar 2017 09:50:41 +0100 Subject: [PATCH 277/382] rpc: Prevent `dumpwallet` from overwriting files Prevent arbitrary files from being overwritten. There have been reports that users have overwritten wallet files this way. It may also avoid other security issues. Fixes #9934. Adds mention to release notes and adds a test. --- doc/release-notes.md | 3 +++ src/wallet/rpcdump.cpp | 14 ++++++++++++-- test/functional/wallet-dump.py | 5 ++++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/doc/release-notes.md b/doc/release-notes.md index 04fb0f333..4ecca7897 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -83,6 +83,9 @@ Low-level RPC changes * `getwalletinfo` * `getmininginfo` +- `dumpwallet` no longer allows overwriting files. This is a security measure + as well as prevents dangerous user mistakes. + Credits ======= diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 9539cc9f4..1123fd6db 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -600,7 +600,7 @@ UniValue dumpwallet(const JSONRPCRequest& request) if (request.fHelp || request.params.size() != 1) throw std::runtime_error( "dumpwallet \"filename\"\n" - "\nDumps all wallet keys in a human-readable format.\n" + "\nDumps all wallet keys in a human-readable format to a server-side file. This does not allow overwriting existing files.\n" "\nArguments:\n" "1. \"filename\" (string, required) The filename with path (either absolute or relative to bitcoind)\n" "\nResult:\n" @@ -616,9 +616,19 @@ UniValue dumpwallet(const JSONRPCRequest& request) EnsureWalletIsUnlocked(pwallet); - std::ofstream file; boost::filesystem::path filepath = request.params[0].get_str(); filepath = boost::filesystem::absolute(filepath); + + /* Prevent arbitrary files from being overwritten. There have been reports + * that users have overwritten wallet files this way: + * https://github.com/bitcoin/bitcoin/issues/9934 + * It may also avoid other security issues. + */ + if (boost::filesystem::exists(filepath)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, filepath.string() + " already exists. If you are sure this is what you want, move it out of the way first"); + } + + std::ofstream file; file.open(filepath.string().c_str()); if (!file.is_open()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); diff --git a/test/functional/wallet-dump.py b/test/functional/wallet-dump.py index e4757c8c0..12db95e5d 100755 --- a/test/functional/wallet-dump.py +++ b/test/functional/wallet-dump.py @@ -7,7 +7,7 @@ import os from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal +from test_framework.util import (assert_equal, assert_raises_jsonrpc) def read_dump(file_name, addrs, hd_master_addr_old): @@ -105,5 +105,8 @@ def run_test (self): assert_equal(found_addr_chg, 90*2 + 50) # old reserve keys are marked as change now assert_equal(found_addr_rsv, 90*2) + # Overwriting should fail + assert_raises_jsonrpc(-8, "already exists", self.nodes[0].dumpwallet, tmpdir + "/node0/wallet.unencrypted.dump") + if __name__ == '__main__': WalletDumpTest().main () From d4cdbd6fb6ac3663d069307c4fd0078f4ecf0245 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Fri, 11 Aug 2017 11:25:06 -0400 Subject: [PATCH 278/382] [rpc] Deprecate estimatefee RPC Deprecate estimatefee in v0.16, for final removal in v0.17. This commit introduces a phased removal of RPC methods. RPC method is disabled by default in version x, but can be enabled by using the `-deprecatedrpc=` argument. RPC method is removed entirely in version (x+1). --- src/init.cpp | 1 + src/rpc/mining.cpp | 6 ++++++ src/rpc/protocol.h | 1 + src/rpc/server.cpp | 7 +++++++ src/rpc/server.h | 2 ++ test/functional/smartfees.py | 2 +- 6 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/init.cpp b/src/init.cpp index a997f9740..a1c49a099 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -430,6 +430,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-checkmempool=", strprintf("Run checks every transactions (default: %u)", defaultChainParams->DefaultConsistencyChecks())); strUsage += HelpMessageOpt("-checkpoints", strprintf("Disable expensive verification for known chain history (default: %u)", DEFAULT_CHECKPOINTS_ENABLED)); strUsage += HelpMessageOpt("-disablesafemode", strprintf("Disable safemode, override a real safe mode event (default: %u)", DEFAULT_DISABLE_SAFEMODE)); + strUsage += HelpMessageOpt("-deprecatedrpc=", "Allows deprecated RPC method(s) to be used"); strUsage += HelpMessageOpt("-testsafemode", strprintf("Force safe mode (default: %u)", DEFAULT_TESTSAFEMODE)); strUsage += HelpMessageOpt("-dropmessagestest=", "Randomly drop 1 of every network messages"); strUsage += HelpMessageOpt("-fuzzmessagestest=", "Randomly fuzz 1 of every network messages"); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index f0ffa07e1..5ac32dc97 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -789,6 +789,12 @@ UniValue estimatefee(const JSONRPCRequest& request) + HelpExampleCli("estimatefee", "6") ); + if (!IsDeprecatedRPCEnabled("estimatefee")) { + throw JSONRPCError(RPC_METHOD_DEPRECATED, "estimatefee is deprecated and will be fully removed in v0.17. " + "To use estimatefee in v0.16, restart bitcoind with -deprecatedrpc=estimatefee.\n" + "Projects should transition to using estimatesmartfee before upgrading to v0.17"); + } + RPCTypeCheck(request.params, {UniValue::VNUM}); int nBlocks = request.params[0].get_int(); diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h index 5c9c64f67..056f93e7d 100644 --- a/src/rpc/protocol.h +++ b/src/rpc/protocol.h @@ -57,6 +57,7 @@ enum RPCErrorCode RPC_VERIFY_REJECTED = -26, //!< Transaction or block was rejected by network rules RPC_VERIFY_ALREADY_IN_CHAIN = -27, //!< Transaction already in chain RPC_IN_WARMUP = -28, //!< Client still warming up + RPC_METHOD_DEPRECATED = -32, //!< RPC method is deprecated //! Aliases for backward compatibility RPC_TRANSACTION_ERROR = RPC_VERIFY_ERROR, diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 428ab3b9b..a73b697e0 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -382,6 +382,13 @@ void JSONRPCRequest::parse(const UniValue& valRequest) throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array or object"); } +bool IsDeprecatedRPCEnabled(const std::string& method) +{ + const std::vector enabled_methods = gArgs.GetArgs("-deprecatedrpc"); + + return find(enabled_methods.begin(), enabled_methods.end(), method) != enabled_methods.end(); +} + static UniValue JSONRPCExecOne(const UniValue& req) { UniValue rpc_result(UniValue::VOBJ); diff --git a/src/rpc/server.h b/src/rpc/server.h index 777acbcb9..31d630427 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -171,6 +171,8 @@ class CRPCTable bool appendCommand(const std::string& name, const CRPCCommand* pcmd); }; +bool IsDeprecatedRPCEnabled(const std::string& method); + extern CRPCTable tableRPC; /** diff --git a/test/functional/smartfees.py b/test/functional/smartfees.py index 76632fc57..986f4546a 100755 --- a/test/functional/smartfees.py +++ b/test/functional/smartfees.py @@ -151,7 +151,7 @@ def setup_network(self): which we will use to generate our transactions. """ self.add_nodes(3, extra_args=[["-maxorphantx=1000", "-whitelist=127.0.0.1"], - ["-blockmaxsize=17000", "-maxorphantx=1000"], + ["-blockmaxsize=17000", "-maxorphantx=1000", "-deprecatedrpc=estimatefee"], ["-blockmaxsize=8000", "-maxorphantx=1000"]]) # Use node0 to mine blocks for input splitting # Node1 mines small blocks but that are bigger than the expected transaction rate. From 048e0c3e26051e66e027a84128923ea341d5337b Mon Sep 17 00:00:00 2001 From: Cristian Mircea Messel Date: Wed, 23 Aug 2017 17:58:59 +0300 Subject: [PATCH 279/382] [rpc] [tests] Add deprecated RPC test --- test/functional/deprecated_rpc.py | 23 +++++++++++++++++++++++ test/functional/test_runner.py | 1 + 2 files changed, 24 insertions(+) create mode 100755 test/functional/deprecated_rpc.py diff --git a/test/functional/deprecated_rpc.py b/test/functional/deprecated_rpc.py new file mode 100755 index 000000000..ec500ccbf --- /dev/null +++ b/test/functional/deprecated_rpc.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test deprecation of RPC calls.""" +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_raises_jsonrpc + +class DeprecatedRpcTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 2 + self.setup_clean_chain = True + self.extra_args = [[], ["-deprecatedrpc=estimatefee"]] + + def run_test(self): + self.log.info("estimatefee: Shows deprecated message") + assert_raises_jsonrpc(-32, 'estimatefee is deprecated', self.nodes[0].estimatefee, 1) + + self.log.info("Using -deprecatedrpc=estimatefee bypasses the error") + self.nodes[1].estimatefee(1) + +if __name__ == '__main__': + DeprecatedRpcTest().main() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 8dbe6247e..5c8740d7c 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -98,6 +98,7 @@ 'disconnect_ban.py', 'decodescript.py', 'blockchain.py', + 'deprecated_rpc.py', 'disablewallet.py', 'net.py', 'keypool.py', From 6643b80d1c0134bc8ef5edd65fbe09c8b63b01d3 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Tue, 26 Sep 2017 16:23:41 -0400 Subject: [PATCH 280/382] Add state message print to AcceptBlock failure message. This should make it easier to debug issues where the CheckBlock at the top of ProcessNewBlock fails (which does not print, in contrast to AcceptBlock, which always prints). --- src/validation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/validation.cpp b/src/validation.cpp index bd9640e74..7a0db1c50 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -3189,7 +3189,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr Date: Tue, 26 Sep 2017 18:02:09 -0400 Subject: [PATCH 281/382] Fix parameter name typo in ErasePurpose walletdb method. --- src/wallet/walletdb.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 52370a8eb..b7f873c1e 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -41,9 +41,9 @@ bool CWalletDB::WritePurpose(const std::string& strAddress, const std::string& s return WriteIC(std::make_pair(std::string("purpose"), strAddress), strPurpose); } -bool CWalletDB::ErasePurpose(const std::string& strPurpose) +bool CWalletDB::ErasePurpose(const std::string& strAddress) { - return EraseIC(std::make_pair(std::string("purpose"), strPurpose)); + return EraseIC(std::make_pair(std::string("purpose"), strAddress)); } bool CWalletDB::WriteTx(const CWalletTx& wtx) From 49f869fe91716785b3276925d64bf8955feff69f Mon Sep 17 00:00:00 2001 From: Johnson Lau Date: Mon, 25 Sep 2017 18:14:03 +0800 Subject: [PATCH 282/382] Fix bip68-sequence rpc test --- test/functional/bip68-sequence.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/functional/bip68-sequence.py b/test/functional/bip68-sequence.py index 381828720..74f51d8cf 100755 --- a/test/functional/bip68-sequence.py +++ b/test/functional/bip68-sequence.py @@ -369,11 +369,14 @@ def test_bip68_not_consensus(self): def activateCSV(self): # activation should happen at block height 432 (3 periods) + # getblockchaininfo will show CSV as active at block 431 (144 * 3 -1) since it's returning whether CSV is active for the next block. min_activation_height = 432 height = self.nodes[0].getblockcount() - assert(height < min_activation_height) - self.nodes[0].generate(min_activation_height-height) - assert(get_bip9_status(self.nodes[0], 'csv')['status'] == 'active') + assert_greater_than(min_activation_height - height, 2) + self.nodes[0].generate(min_activation_height - height - 2) + assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], "locked_in") + self.nodes[0].generate(1) + assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], "active") sync_blocks(self.nodes) # Use self.nodes[1] to test that version 2 transactions are standard. From f77f0e4825e17fdda5984ce556a1102e32e7af72 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Mon, 17 Jul 2017 15:43:12 -0700 Subject: [PATCH 283/382] Add warnings field to getblockchaininfo --- src/rpc/blockchain.cpp | 2 ++ test/functional/blockchain.py | 1 + 2 files changed, 3 insertions(+) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 46f4f1632..099e4825d 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1162,6 +1162,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) " }\n" " }\n" " }\n" + " \"warnings\" : \"...\", (string) any network and blockchain errors.\n" "}\n" "\nExamples:\n" + HelpExampleCli("getblockchaininfo", "") @@ -1201,6 +1202,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) obj.push_back(Pair("pruneheight", block->nHeight)); } + obj.push_back(Pair("warnings", GetWarnings("statusbar"))); return obj; } diff --git a/test/functional/blockchain.py b/test/functional/blockchain.py index 63c56d0e9..c5967aa10 100755 --- a/test/functional/blockchain.py +++ b/test/functional/blockchain.py @@ -60,6 +60,7 @@ def _test_getblockchaininfo(self): 'pruned', 'softforks', 'verificationprogress', + 'warnings', ] res = self.nodes[0].getblockchaininfo() # result should have pruneheight and default keys if pruning is enabled From 8502b2085288bcf5b5ff96b77236a3b04c65f082 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Mon, 17 Jul 2017 15:55:14 -0700 Subject: [PATCH 284/382] Unify help text for GetWarnings output in get*info RPCs --- src/rpc/blockchain.cpp | 3 ++- src/rpc/mining.cpp | 4 ++-- src/rpc/net.cpp | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 099e4825d..8c0268e26 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -24,6 +24,7 @@ #include "util.h" #include "utilstrencodings.h" #include "hash.h" +#include "warnings.h" #include @@ -1162,7 +1163,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) " }\n" " }\n" " }\n" - " \"warnings\" : \"...\", (string) any network and blockchain errors.\n" + " \"warnings\" : \"...\", (string) any network and blockchain warnings.\n" "}\n" "\nExamples:\n" + HelpExampleCli("getblockchaininfo", "") diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 5ac32dc97..85cc906b7 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -199,10 +199,10 @@ UniValue getmininginfo(const JSONRPCRequest& request) " \"currentblockweight\": nnn, (numeric) The last block weight\n" " \"currentblocktx\": nnn, (numeric) The last block transaction\n" " \"difficulty\": xxx.xxxxx (numeric) The current difficulty\n" - " \"errors\": \"...\" (string) Current errors\n" " \"networkhashps\": nnn, (numeric) The network hashes per second\n" " \"pooledtx\": n (numeric) The size of the mempool\n" " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n" + " \"errors\": \"...\" (string) (string) any network and blockchain warnings\n" "}\n" "\nExamples:\n" + HelpExampleCli("getmininginfo", "") @@ -217,10 +217,10 @@ UniValue getmininginfo(const JSONRPCRequest& request) obj.push_back(Pair("currentblockweight", (uint64_t)nLastBlockWeight)); obj.push_back(Pair("currentblocktx", (uint64_t)nLastBlockTx)); obj.push_back(Pair("difficulty", (double)GetDifficulty())); - obj.push_back(Pair("errors", GetWarnings("statusbar"))); obj.push_back(Pair("networkhashps", getnetworkhashps(request))); obj.push_back(Pair("pooledtx", (uint64_t)mempool.size())); obj.push_back(Pair("chain", Params().NetworkIDString())); + obj.push_back(Pair("errors", GetWarnings("statusbar"))); return obj; } diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 7faf21604..018444821 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -447,7 +447,7 @@ UniValue getnetworkinfo(const JSONRPCRequest& request) " }\n" " ,...\n" " ]\n" - " \"warnings\": \"...\" (string) any network warnings\n" + " \"warnings\": \"...\" (string) any network and blockchain warnings\n" "}\n" "\nExamples:\n" + HelpExampleCli("getnetworkinfo", "") From 395cef7601479b97f5794b0c98067c859f00fc7f Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Tue, 26 Sep 2017 12:03:34 -0400 Subject: [PATCH 285/382] Change getmininginfo errors field to warnings Changes the errors field to warnings. To maintain compatibility, the errors field is deprecated and enabled by starting bitcoind with -deprecatedrpc=getmininginfo --- src/rpc/mining.cpp | 9 +++++++-- test/functional/p2p-versionbits-warning.py | 6 +++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 85cc906b7..f79439f03 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -202,7 +202,8 @@ UniValue getmininginfo(const JSONRPCRequest& request) " \"networkhashps\": nnn, (numeric) The network hashes per second\n" " \"pooledtx\": n (numeric) The size of the mempool\n" " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n" - " \"errors\": \"...\" (string) (string) any network and blockchain warnings\n" + " \"warnings\": \"...\" (string) any network and blockchain warnings\n" + " \"errors\": \"...\" (string) DEPRECATED. Same as warnings. Only shown when bitcoind is started with -deprecatedrpc=getmininginfo\n" "}\n" "\nExamples:\n" + HelpExampleCli("getmininginfo", "") @@ -220,7 +221,11 @@ UniValue getmininginfo(const JSONRPCRequest& request) obj.push_back(Pair("networkhashps", getnetworkhashps(request))); obj.push_back(Pair("pooledtx", (uint64_t)mempool.size())); obj.push_back(Pair("chain", Params().NetworkIDString())); - obj.push_back(Pair("errors", GetWarnings("statusbar"))); + if (IsDeprecatedRPCEnabled("getmininginfo")) { + obj.push_back(Pair("errors", GetWarnings("statusbar"))); + } else { + obj.push_back(Pair("warnings", GetWarnings("statusbar"))); + } return obj; } diff --git a/test/functional/p2p-versionbits-warning.py b/test/functional/p2p-versionbits-warning.py index 0848fcde6..f9bef2580 100755 --- a/test/functional/p2p-versionbits-warning.py +++ b/test/functional/p2p-versionbits-warning.py @@ -87,7 +87,7 @@ def run_test(self): self.nodes[0].generate(VB_PERIOD - VB_THRESHOLD + 1) # Check that we're not getting any versionbit-related errors in # get*info() - assert(not VB_PATTERN.match(self.nodes[0].getmininginfo()["errors"])) + assert(not VB_PATTERN.match(self.nodes[0].getmininginfo()["warnings"])) assert(not VB_PATTERN.match(self.nodes[0].getnetworkinfo()["warnings"])) # 3. Now build one period of blocks with >= VB_THRESHOLD blocks signaling @@ -98,7 +98,7 @@ def run_test(self): # have gotten a different alert due to more than 51/100 blocks # being of unexpected version. # Check that get*info() shows some kind of error. - assert(WARN_UNKNOWN_RULES_MINED in self.nodes[0].getmininginfo()["errors"]) + assert(WARN_UNKNOWN_RULES_MINED in self.nodes[0].getmininginfo()["warnings"]) assert(WARN_UNKNOWN_RULES_MINED in self.nodes[0].getnetworkinfo()["warnings"]) # Mine a period worth of expected blocks so the generic block-version warning @@ -113,7 +113,7 @@ def run_test(self): # Connecting one block should be enough to generate an error. self.nodes[0].generate(1) - assert(WARN_UNKNOWN_RULES_ACTIVE in self.nodes[0].getmininginfo()["errors"]) + assert(WARN_UNKNOWN_RULES_ACTIVE in self.nodes[0].getmininginfo()["warnings"]) assert(WARN_UNKNOWN_RULES_ACTIVE in self.nodes[0].getnetworkinfo()["warnings"]) self.stop_nodes() self.test_versionbits_in_alert_file() From 382625318d8734fd71a6c9620d3e88b5617da0c5 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 29 Sep 2016 16:45:19 +0200 Subject: [PATCH 286/382] rpc: Handle `getinfo` locally in bitcoin-cli w/ `-getinfo` This adds the infrastructure `BaseRequestHandler` class that takes care of converting bitcoin-cli arguments into a JSON-RPC request object, and converting the reply into a JSON object that can be shown as result. This is subsequently used to handle the `-getinfo` option, which sends a JSON-RPC batch request to the RPC server with `["getnetworkinfo", "getblockchaininfo", "getwalletinfo"]`, and after reply combines the result into what looks like a `getinfo` result. There have been some requests for a client-side `getinfo` and this is my PoC of how to do it. If this is considered a good idea some of the logic could be moved up to rpcclient.cpp and used in the GUI console as well. Extra-Author: Andrew Chow --- src/bitcoin-cli.cpp | 119 ++++++++++++++++++++++++++++++++++++++----- src/rpc/protocol.cpp | 21 +++++++- src/rpc/protocol.h | 2 + 3 files changed, 127 insertions(+), 15 deletions(-) diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 3c94c99b3..e21a26922 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -37,6 +37,7 @@ std::string HelpMessageCli() strUsage += HelpMessageOpt("-?", _("This help message")); strUsage += HelpMessageOpt("-conf=", strprintf(_("Specify configuration file (default: %s)"), BITCOIN_CONF_FILENAME)); strUsage += HelpMessageOpt("-datadir=

", _("Specify data directory")); + strUsage += HelpMessageOpt("-getinfo", _("Get general information from the remote server. Note that unlike server-side RPC calls, the results of -getinfo is the result of multiple non-atomic requests. Some entries in the result may represent results from different states (e.g. wallet balance may be as of a different block from the chain state reported)")); AppendParamsHelpMessages(strUsage); strUsage += HelpMessageOpt("-named", strprintf(_("Pass named instead of positional arguments (default: %s)"), DEFAULT_NAMED)); strUsage += HelpMessageOpt("-rpcconnect=", strprintf(_("Send commands to node running on (default: %s)"), DEFAULT_RPCCONNECT)); @@ -191,7 +192,96 @@ static void http_error_cb(enum evhttp_request_error err, void *ctx) } #endif -static UniValue CallRPC(const std::string& strMethod, const UniValue& params) +/** Class that handles the conversion from a command-line to a JSON-RPC request, + * as well as converting back to a JSON object that can be shown as result. + */ +class BaseRequestHandler +{ +public: + virtual UniValue PrepareRequest(const std::string& method, const std::vector& args) = 0; + virtual UniValue ProcessReply(const UniValue &batch_in) = 0; +}; + +/** Process getinfo requests */ +class GetinfoRequestHandler: public BaseRequestHandler +{ +public: + const int ID_NETWORKINFO = 0; + const int ID_BLOCKCHAININFO = 1; + const int ID_WALLETINFO = 2; + + /** Create a simulated `getinfo` request. */ + UniValue PrepareRequest(const std::string& method, const std::vector& args) override + { + UniValue result(UniValue::VARR); + result.push_back(JSONRPCRequestObj("getnetworkinfo", NullUniValue, ID_NETWORKINFO)); + result.push_back(JSONRPCRequestObj("getblockchaininfo", NullUniValue, ID_BLOCKCHAININFO)); + result.push_back(JSONRPCRequestObj("getwalletinfo", NullUniValue, ID_WALLETINFO)); + return result; + } + + /** Collect values from the batch and form a simulated `getinfo` reply. */ + UniValue ProcessReply(const UniValue &batch_in) override + { + UniValue result(UniValue::VOBJ); + std::vector batch = JSONRPCProcessBatchReply(batch_in, 3); + // Errors in getnetworkinfo() and getblockchaininfo() are fatal, pass them on + // getwalletinfo() is allowed to fail in case there is no wallet. + if (!batch[ID_NETWORKINFO]["error"].isNull()) { + return batch[ID_NETWORKINFO]; + } + if (!batch[ID_BLOCKCHAININFO]["error"].isNull()) { + return batch[ID_BLOCKCHAININFO]; + } + result.pushKV("version", batch[ID_NETWORKINFO]["result"]["version"]); + result.pushKV("protocolversion", batch[ID_NETWORKINFO]["result"]["protocolversion"]); + if (!batch[ID_WALLETINFO].isNull()) { + result.pushKV("walletversion", batch[ID_WALLETINFO]["result"]["walletversion"]); + result.pushKV("balance", batch[ID_WALLETINFO]["result"]["balance"]); + } + result.pushKV("blocks", batch[ID_BLOCKCHAININFO]["result"]["blocks"]); + result.pushKV("timeoffset", batch[ID_NETWORKINFO]["result"]["timeoffset"]); + result.pushKV("connections", batch[ID_NETWORKINFO]["result"]["connections"]); + result.pushKV("proxy", batch[ID_NETWORKINFO]["result"]["networks"][0]["proxy"]); + result.pushKV("difficulty", batch[ID_BLOCKCHAININFO]["result"]["difficulty"]); + result.pushKV("testnet", UniValue(batch[ID_BLOCKCHAININFO]["result"]["chain"].get_str() == "test")); + if (!batch[ID_WALLETINFO].isNull()) { + result.pushKV("walletversion", batch[ID_WALLETINFO]["result"]["walletversion"]); + result.pushKV("balance", batch[ID_WALLETINFO]["result"]["balance"]); + result.pushKV("keypoololdest", batch[ID_WALLETINFO]["result"]["keypoololdest"]); + result.pushKV("keypoolsize", batch[ID_WALLETINFO]["result"]["keypoolsize"]); + if (!batch[ID_WALLETINFO]["result"]["unlocked_until"].isNull()) { + result.pushKV("unlocked_until", batch[ID_WALLETINFO]["result"]["unlocked_until"]); + } + result.pushKV("paytxfee", batch[ID_WALLETINFO]["result"]["paytxfee"]); + } + result.pushKV("relayfee", batch[ID_NETWORKINFO]["result"]["relayfee"]); + result.pushKV("warnings", batch[ID_NETWORKINFO]["result"]["warnings"]); + return JSONRPCReplyObj(result, NullUniValue, 1); + } +}; + +/** Process default single requests */ +class DefaultRequestHandler: public BaseRequestHandler { +public: + UniValue PrepareRequest(const std::string& method, const std::vector& args) override + { + UniValue params; + if(gArgs.GetBoolArg("-named", DEFAULT_NAMED)) { + params = RPCConvertNamedValues(method, args); + } else { + params = RPCConvertValues(method, args); + } + return JSONRPCRequestObj(method, params, 1); + } + + UniValue ProcessReply(const UniValue &reply) override + { + return reply.get_obj(); + } +}; + +static UniValue CallRPC(BaseRequestHandler *rh, const std::string& strMethod, const std::vector& args) { std::string host; // In preference order, we choose the following for the port: @@ -238,7 +328,7 @@ static UniValue CallRPC(const std::string& strMethod, const UniValue& params) evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str()); // Attach request data - std::string strRequest = JSONRPCRequestObj(strMethod, params, 1).write() + "\n"; + std::string strRequest = rh->PrepareRequest(strMethod, args).write() + "\n"; struct evbuffer* output_buffer = evhttp_request_get_output_buffer(req.get()); assert(output_buffer); evbuffer_add(output_buffer, strRequest.data(), strRequest.size()); @@ -277,7 +367,7 @@ static UniValue CallRPC(const std::string& strMethod, const UniValue& params) UniValue valReply(UniValue::VSTR); if (!valReply.read(response.body)) throw std::runtime_error("couldn't parse reply from server"); - const UniValue& reply = valReply.get_obj(); + const UniValue reply = rh->ProcessReply(valReply); if (reply.empty()) throw std::runtime_error("expected reply to have result, error and id properties"); @@ -309,24 +399,25 @@ int CommandLineRPC(int argc, char *argv[]) args.push_back(line); } } - if (args.size() < 1) { - throw std::runtime_error("too few parameters (need at least command)"); - } - std::string strMethod = args[0]; - args.erase(args.begin()); // Remove trailing method name from arguments vector - - UniValue params; - if(gArgs.GetBoolArg("-named", DEFAULT_NAMED)) { - params = RPCConvertNamedValues(strMethod, args); + std::unique_ptr rh; + std::string method; + if (gArgs.GetBoolArg("-getinfo", false)) { + rh.reset(new GetinfoRequestHandler()); + method = ""; } else { - params = RPCConvertValues(strMethod, args); + rh.reset(new DefaultRequestHandler()); + if (args.size() < 1) { + throw std::runtime_error("too few parameters (need at least command)"); + } + method = args[0]; + args.erase(args.begin()); // Remove trailing method name from arguments vector } // Execute and handle connection failures with -rpcwait const bool fWait = gArgs.GetBoolArg("-rpcwait", false); do { try { - const UniValue reply = CallRPC(strMethod, params); + const UniValue reply = CallRPC(rh.get(), method, args); // Parse reply const UniValue& result = find_value(reply, "result"); diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp index dc6bcec38..1f4ae75b1 100644 --- a/src/rpc/protocol.cpp +++ b/src/rpc/protocol.cpp @@ -19,7 +19,7 @@ * JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility, * but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were * unspecified (HTTP errors and contents of 'error'). - * + * * 1.0 spec: http://json-rpc.org/wiki/specification * 1.2 spec: http://jsonrpc.org/historical/json-rpc-over-http.html */ @@ -135,3 +135,22 @@ void DeleteAuthCookie() } } +std::vector JSONRPCProcessBatchReply(const UniValue &in, size_t num) +{ + if (!in.isArray()) { + throw std::runtime_error("Batch must be an array"); + } + std::vector batch(num); + for (size_t i=0; i= num) { + throw std::runtime_error("Batch member id larger than size"); + } + batch[id] = rec; + } + return batch; +} diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h index 5c9c64f67..bba7438c9 100644 --- a/src/rpc/protocol.h +++ b/src/rpc/protocol.h @@ -97,5 +97,7 @@ bool GenerateAuthCookie(std::string *cookie_out); bool GetAuthCookie(std::string *cookie_out); /** Delete RPC authentication cookie from disk */ void DeleteAuthCookie(); +/** Parse JSON-RPC batch reply into a vector */ +std::vector JSONRPCProcessBatchReply(const UniValue &in, size_t num); #endif // BITCOIN_RPCPROTOCOL_H From 5e69a430ee260950b69e0c36394671381add2b94 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Wed, 23 Aug 2017 14:30:03 -0400 Subject: [PATCH 287/382] Add test for bitcoin-cli -getinfo Extra-Author: Andrew Chow --- test/functional/bitcoin_cli.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/functional/bitcoin_cli.py b/test/functional/bitcoin_cli.py index ffed5b0d3..996cbb8a1 100755 --- a/test/functional/bitcoin_cli.py +++ b/test/functional/bitcoin_cli.py @@ -35,5 +35,28 @@ def run_test(self): assert_equal(["foo", "bar"], self.nodes[0].cli('-rpcuser=%s' % user, '-stdin', '-stdinrpcpass', input=password + "\nfoo\nbar").echo()) assert_raises_process_error(1, "incorrect rpcuser or rpcpassword", self.nodes[0].cli('-rpcuser=%s' % user, '-stdin', '-stdinrpcpass', input="foo").echo) + self.log.info("Compare responses from `bitcoin-cli -getinfo` and the RPCs data is retrieved from.") + cli_get_info = self.nodes[0].cli('-getinfo').help() + wallet_info = self.nodes[0].getwalletinfo() + network_info = self.nodes[0].getnetworkinfo() + blockchain_info = self.nodes[0].getblockchaininfo() + + assert_equal(cli_get_info['version'], network_info['version']) + assert_equal(cli_get_info['protocolversion'], network_info['protocolversion']) + assert_equal(cli_get_info['walletversion'], wallet_info['walletversion']) + assert_equal(cli_get_info['balance'], wallet_info['balance']) + assert_equal(cli_get_info['blocks'], blockchain_info['blocks']) + assert_equal(cli_get_info['timeoffset'], network_info['timeoffset']) + assert_equal(cli_get_info['connections'], network_info['connections']) + assert_equal(cli_get_info['proxy'], network_info['networks'][0]['proxy']) + assert_equal(cli_get_info['difficulty'], blockchain_info['difficulty']) + assert_equal(cli_get_info['testnet'], blockchain_info['chain'] == "test") + assert_equal(cli_get_info['balance'], wallet_info['balance']) + assert_equal(cli_get_info['keypoololdest'], wallet_info['keypoololdest']) + assert_equal(cli_get_info['keypoolsize'], wallet_info['keypoolsize']) + assert_equal(cli_get_info['paytxfee'], wallet_info['paytxfee']) + assert_equal(cli_get_info['relayfee'], network_info['relayfee']) + # unlocked_until is not tested because the wallet is not encrypted + if __name__ == '__main__': TestBitcoinCli().main() From 5ddf56045ad65162c7cd5c757c81d9446299a5aa Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Wed, 27 Sep 2017 19:49:18 -0700 Subject: [PATCH 288/382] script: Change SignatureHash input index check to an assert. In the SignatureHash function, the input index must refer to a valid index. This is not enforced equally in the segwit/non-segwit branches and should be an assertion rather than returning a error hash. --- src/script/interpreter.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 7315500e3..77314e8cb 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1175,6 +1175,8 @@ PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo) uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache) { + assert(nIn < txTo.vin.size()); + if (sigversion == SIGVERSION_WITNESS_V0) { uint256 hashPrevouts; uint256 hashSequence; @@ -1221,10 +1223,6 @@ uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsig } static const uint256 one(uint256S("0000000000000000000000000000000000000000000000000000000000000001")); - if (nIn >= txTo.vin.size()) { - // nIn out of range - return one; - } // Check for invalid use of SIGHASH_SINGLE if ((nHashType & 0x1f) == SIGHASH_SINGLE) { From bb174e19bc0e91854c40269cf8214943d70049a2 Mon Sep 17 00:00:00 2001 From: fanquake Date: Thu, 28 Sep 2017 21:04:22 +0800 Subject: [PATCH 289/382] [docs] Remove partial gitian instructions from descriptors dir --- contrib/README.md | 2 +- contrib/gitian-descriptors/README.md | 65 ---------------------------- 2 files changed, 1 insertion(+), 66 deletions(-) delete mode 100644 contrib/gitian-descriptors/README.md diff --git a/contrib/README.md b/contrib/README.md index 34c4cfc55..a582a724f 100644 --- a/contrib/README.md +++ b/contrib/README.md @@ -26,7 +26,7 @@ Contains files used to package bitcoind/bitcoin-qt for Debian-based Linux systems. If you compile bitcoind/bitcoin-qt yourself, there are some useful files here. ### [Gitian-descriptors](/contrib/gitian-descriptors) ### -Notes on getting Gitian builds up and running using KVM. +Files used during the gitian build process. For more information about gitian, see the [the Bitcoin Core documentation repository](https://github.com/bitcoin-core/docs). ### [Gitian-keys](/contrib/gitian-keys) PGP keys used for signing Bitcoin Core [Gitian release](/doc/release-process.md) results. diff --git a/contrib/gitian-descriptors/README.md b/contrib/gitian-descriptors/README.md deleted file mode 100644 index d9dbfd3cb..000000000 --- a/contrib/gitian-descriptors/README.md +++ /dev/null @@ -1,65 +0,0 @@ -### Gavin's notes on getting Gitian builds up and running using KVM - -These instructions distilled from -[https://help.ubuntu.com/community/KVM/Installation](https://help.ubuntu.com/community/KVM/Installation). - -You need the right hardware: you need a 64-bit-capable CPU with hardware virtualization support (Intel VT-x or AMD-V). Not all modern CPUs support hardware virtualization. - -You probably need to enable hardware virtualization in your machine's BIOS. - -You need to be running a recent version of 64-bit-Ubuntu, and you need to install several prerequisites: - - sudo apt-get install ruby apache2 git apt-cacher-ng python-vm-builder qemu-kvm - -Sanity checks: - - sudo service apt-cacher-ng status # Should return apt-cacher-ng is running - ls -l /dev/kvm # Should show a /dev/kvm device - - -Once you've got the right hardware and software: - - git clone git://github.com/bitcoin/bitcoin.git - git clone git://github.com/devrandom/gitian-builder.git - mkdir gitian-builder/inputs - cd gitian-builder/inputs - - # Create base images - cd gitian-builder - bin/make-base-vm --suite trusty --arch amd64 - cd .. - - # Get inputs (see doc/release-process.md for exact inputs needed and where to get them) - ... - - # For further build instructions see doc/release-process.md - ... - ---------------------- - -`gitian-builder` now also supports building using LXC. See -[help.ubuntu.com](https://help.ubuntu.com/14.04/serverguide/lxc.html) -for how to get LXC up and running under Ubuntu. - -If your main machine is a 64-bit Mac or PC with a few gigabytes of memory -and at least 10 gigabytes of free disk space, you can `gitian-build` using -LXC running inside a virtual machine. - -Here's a description of Gavin's setup on OSX 10.6: - -1. Download and install VirtualBox from [https://www.virtualbox.org/](https://www.virtualbox.org/) - -2. Download the 64-bit Ubuntu Desktop 12.04 LTS .iso CD image from - [http://www.ubuntu.com/](http://www.ubuntu.com/) - -3. Run VirtualBox and create a new virtual machine, using the Ubuntu .iso (see the [VirtualBox documentation](https://www.virtualbox.org/wiki/Documentation) for details). Create it with at least 2 gigabytes of memory and a disk that is at least 20 gigabytes big. - -4. Inside the running Ubuntu desktop, install: - - sudo apt-get install debootstrap lxc ruby apache2 git apt-cacher-ng python-vm-builder - -5. Still inside Ubuntu, tell gitian-builder to use LXC, then follow the "Once you've got the right hardware and software" instructions above: - - export USE_LXC=1 - git clone git://github.com/bitcoin/bitcoin.git - ... etc From 8fd226705347c2b3955d0d98f8ca21e4325e6765 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 25 Aug 2017 18:12:39 -0700 Subject: [PATCH 290/382] Import Bech32 C++ reference code & tests This includes a reformatted version of the Bech32 reference code (see https://github.com/sipa/bech32/tree/master/ref/c%2B%2B), with extra documentation. --- src/Makefile.am | 2 + src/Makefile.test.include | 1 + src/bech32.cpp | 191 ++++++++++++++++++++++++++++++++++++++ src/bech32.h | 25 +++++ src/test/bech32_tests.cpp | 67 +++++++++++++ 5 files changed, 286 insertions(+) create mode 100644 src/bech32.cpp create mode 100644 src/bech32.h create mode 100644 src/test/bech32_tests.cpp diff --git a/src/Makefile.am b/src/Makefile.am index ebae53a8c..c71e457eb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -78,6 +78,7 @@ BITCOIN_CORE_H = \ addrdb.h \ addrman.h \ base58.h \ + bech32.h \ bloom.h \ blockencodings.h \ chain.h \ @@ -316,6 +317,7 @@ libbitcoin_common_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_common_a_SOURCES = \ base58.cpp \ + bech32.cpp \ chainparams.cpp \ coins.cpp \ compressor.cpp \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 3a932f460..ed95f345b 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -31,6 +31,7 @@ BITCOIN_TESTS =\ test/base32_tests.cpp \ test/base58_tests.cpp \ test/base64_tests.cpp \ + test/bech32_tests.cpp \ test/bip32_tests.cpp \ test/blockencodings_tests.cpp \ test/bloom_tests.cpp \ diff --git a/src/bech32.cpp b/src/bech32.cpp new file mode 100644 index 000000000..573eac58b --- /dev/null +++ b/src/bech32.cpp @@ -0,0 +1,191 @@ +// Copyright (c) 2017 Pieter Wuille +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "bech32.h" + +namespace +{ + +typedef std::vector data; + +/** The Bech32 character set for encoding. */ +const char* CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; + +/** The Bech32 character set for decoding. */ +const int8_t CHARSET_REV[128] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1, + -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, + 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1, + -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, + 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1 +}; + +/** Concatenate two byte arrays. */ +data Cat(data x, const data& y) +{ + x.insert(x.end(), y.begin(), y.end()); + return x; +} + +/** This function will compute what 6 5-bit values to XOR into the last 6 input values, in order to + * make the checksum 0. These 6 values are packed together in a single 30-bit integer. The higher + * bits correspond to earlier values. */ +uint32_t PolyMod(const data& v) +{ + // The input is interpreted as a list of coefficients of a polynomial over F = GF(32), with an + // implicit 1 in front. If the input is [v0,v1,v2,v3,v4], that polynomial is v(x) = + // 1*x^5 + v0*x^4 + v1*x^3 + v2*x^2 + v3*x + v4. The implicit 1 guarantees that + // [v0,v1,v2,...] has a distinct checksum from [0,v0,v1,v2,...]. + + // The output is a 30-bit integer whose 5-bit groups are the coefficients of the remainder of + // v(x) mod g(x), where g(x) is the Bech32 generator, + // x^6 + {29}x^5 + {22}x^4 + {20}x^3 + {21}x^2 + {29}x + {18}. g(x) is chosen in such a way + // that the resulting code is a BCH code, guaranteeing detection of up to 3 errors within a + // window of 1023 characters. Among the various possible BCH codes, one was selected to in + // fact guarantee detection of up to 4 errors within a window of 89 characters. + + // Note that the coefficients are elements of GF(32), here represented as decimal numbers + // between {}. In this finite field, addition is just XOR of the corresponding numbers. For + // example, {27} + {13} = {27 ^ 13} = {22}. Multiplication is more complicated, and requires + // treating the bits of values themselves as coefficients of a polynomial over a smaller field, + // GF(2), and multiplying those polynomials mod a^5 + a^3 + 1. For example, {5} * {26} = + // (a^2 + 1) * (a^4 + a^3 + a) = (a^4 + a^3 + a) * a^2 + (a^4 + a^3 + a) = a^6 + a^5 + a^4 + a + // = a^3 + 1 (mod a^5 + a^3 + 1) = {9}. + + // During the course of the loop below, `c` contains the bitpacked coefficients of the + // polynomial constructed from just the values of v that were processed so far, mod g(x). In + // the above example, `c` initially corresponds to 1 mod (x), and after processing 2 inputs of + // v, it corresponds to x^2 + v0*x + v1 mod g(x). As 1 mod g(x) = 1, that is the starting value + // for `c`. + uint32_t c = 1; + for (auto v_i : v) { + // We want to update `c` to correspond to a polynomial with one extra term. If the initial + // value of `c` consists of the coefficients of c(x) = f(x) mod g(x), we modify it to + // correspond to c'(x) = (f(x) * x + v_i) mod g(x), where v_i is the next input to + // process. Simplifying: + // c'(x) = (f(x) * x + v_i) mod g(x) + // ((f(x) mod g(x)) * x + v_i) mod g(x) + // (c(x) * x + v_i) mod g(x) + // If c(x) = c0*x^5 + c1*x^4 + c2*x^3 + c3*x^2 + c4*x + c5, we want to compute + // c'(x) = (c0*x^5 + c1*x^4 + c2*x^3 + c3*x^2 + c4*x + c5) * x + v_i mod g(x) + // = c0*x^6 + c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i mod g(x) + // = c0*(x^6 mod g(x)) + c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i + // If we call (x^6 mod g(x)) = k(x), this can be written as + // c'(x) = (c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i) + c0*k(x) + + // First, determine the value of c0: + uint8_t c0 = c >> 25; + + // Then compute c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i: + c = ((c & 0x1ffffff) << 5) ^ v_i; + + // Finally, for each set bit n in c0, conditionally add {2^n}k(x): + if (c0 & 1) c ^= 0x3b6a57b2; // k(x) = {29}x^5 + {22}x^4 + {20}x^3 + {21}x^2 + {29}x + {18} + if (c0 & 2) c ^= 0x26508e6d; // {2}k(x) = {19}x^5 + {5}x^4 + x^3 + {3}x^2 + {19}x + {13} + if (c0 & 4) c ^= 0x1ea119fa; // {4}k(x) = {15}x^5 + {10}x^4 + {2}x^3 + {6}x^2 + {15}x + {26} + if (c0 & 8) c ^= 0x3d4233dd; // {8}k(x) = {30}x^5 + {20}x^4 + {4}x^3 + {12}x^2 + {30}x + {29} + if (c0 & 16) c ^= 0x2a1462b3; // {16}k(x) = {21}x^5 + x^4 + {8}x^3 + {24}x^2 + {21}x + {19} + } + return c; +} + +/** Convert to lower case. */ +inline unsigned char LowerCase(unsigned char c) +{ + return (c >= 'A' && c <= 'Z') ? (c - 'A') + 'a' : c; +} + +/** Expand a HRP for use in checksum computation. */ +data ExpandHRP(const std::string& hrp) +{ + data ret; + ret.reserve(hrp.size() + 90); + ret.resize(hrp.size() * 2 + 1); + for (size_t i = 0; i < hrp.size(); ++i) { + unsigned char c = hrp[i]; + ret[i] = c >> 5; + ret[i + hrp.size() + 1] = c & 0x1f; + } + ret[hrp.size()] = 0; + return ret; +} + +/** Verify a checksum. */ +bool VerifyChecksum(const std::string& hrp, const data& values) +{ + // PolyMod computes what value to xor into the final values to make the checksum 0. However, + // if we required that the checksum was 0, it would be the case that appending a 0 to a valid + // list of values would result in a new valid list. For that reason, Bech32 requires the + // resulting checksum to be 1 instead. + return PolyMod(Cat(ExpandHRP(hrp), values)) == 1; +} + +/** Create a checksum. */ +data CreateChecksum(const std::string& hrp, const data& values) +{ + data enc = Cat(ExpandHRP(hrp), values); + enc.resize(enc.size() + 6); // Append 6 zeroes + uint32_t mod = PolyMod(enc) ^ 1; // Determine what to XOR into those 6 zeroes. + data ret(6); + for (size_t i = 0; i < 6; ++i) { + // Convert the 5-bit groups in mod to checksum values. + ret[i] = (mod >> (5 * (5 - i))) & 31; + } + return ret; +} + +} // namespace + +namespace bech32 +{ + +/** Encode a Bech32 string. */ +std::string Encode(const std::string& hrp, const data& values) { + data checksum = CreateChecksum(hrp, values); + data combined = Cat(values, checksum); + std::string ret = hrp + '1'; + ret.reserve(ret.size() + combined.size()); + for (auto c : combined) { + ret += CHARSET[c]; + } + return ret; +} + +/** Decode a Bech32 string. */ +std::pair Decode(const std::string& str) { + bool lower = false, upper = false; + for (size_t i = 0; i < str.size(); ++i) { + unsigned char c = str[i]; + if (c < 33 || c > 126) return {}; + if (c >= 'a' && c <= 'z') lower = true; + if (c >= 'A' && c <= 'Z') upper = true; + } + if (lower && upper) return {}; + size_t pos = str.rfind('1'); + if (str.size() > 90 || pos == str.npos || pos == 0 || pos + 7 > str.size()) { + return {}; + } + data values(str.size() - 1 - pos); + for (size_t i = 0; i < str.size() - 1 - pos; ++i) { + unsigned char c = str[i + pos + 1]; + int8_t rev = (c < 33 || c > 126) ? -1 : CHARSET_REV[c]; + if (rev == -1) { + return {}; + } + values[i] = rev; + } + std::string hrp; + for (size_t i = 0; i < pos; ++i) { + hrp += LowerCase(str[i]); + } + if (!VerifyChecksum(hrp, values)) { + return {}; + } + return {hrp, data(values.begin(), values.end() - 6)}; +} + +} // namespace bech32 diff --git a/src/bech32.h b/src/bech32.h new file mode 100644 index 000000000..7ef7b2221 --- /dev/null +++ b/src/bech32.h @@ -0,0 +1,25 @@ +// Copyright (c) 2017 Pieter Wuille +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +// Bech32 is a string encoding format used in newer address types. +// The output consists of a human-readable part (alphanumeric), a +// separator character (1), and a base32 data section, the last +// 6 characters of which are a checksum. +// +// For more information, see BIP 173. + +#include +#include +#include + +namespace bech32 +{ + +/** Encode a Bech32 string. Returns the empty string in case of failure. */ +std::string Encode(const std::string& hrp, const std::vector& values); + +/** Decode a Bech32 string. Returns (hrp, data). Empty hrp means failure. */ +std::pair> Decode(const std::string& str); + +} // namespace bech32 diff --git a/src/test/bech32_tests.cpp b/src/test/bech32_tests.cpp new file mode 100644 index 000000000..ce4cddd64 --- /dev/null +++ b/src/test/bech32_tests.cpp @@ -0,0 +1,67 @@ +// Copyright (c) 2017 Pieter Wuille +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "bech32.h" +#include "test/test_bitcoin.h" + +#include + +BOOST_FIXTURE_TEST_SUITE(bech32_tests, BasicTestingSetup) + +bool CaseInsensitiveEqual(const std::string &s1, const std::string &s2) +{ + if (s1.size() != s2.size()) return false; + for (size_t i = 0; i < s1.size(); ++i) { + char c1 = s1[i]; + if (c1 >= 'A' && c1 <= 'Z') c1 -= ('A' - 'a'); + char c2 = s2[i]; + if (c2 >= 'A' && c2 <= 'Z') c2 -= ('A' - 'a'); + if (c1 != c2) return false; + } + return true; +} + +BOOST_AUTO_TEST_CASE(bip173_testvectors_valid) +{ + static const std::string CASES[] = { + "A12UEL5L", + "a12uel5l", + "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs", + "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", + "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", + "split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", + "?1ezyfcl", + }; + for (const std::string& str : CASES) { + auto ret = bech32::Decode(str); + BOOST_CHECK(!ret.first.empty()); + std::string recode = bech32::Encode(ret.first, ret.second); + BOOST_CHECK(!recode.empty()); + BOOST_CHECK(CaseInsensitiveEqual(str, recode)); + } +} + +BOOST_AUTO_TEST_CASE(bip173_testvectors_invalid) +{ + static const std::string CASES[] = { + " 1nwldj5", + "\x7f""1axkwrx", + "\x80""1eym55h", + "an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx", + "pzry9x0s0muk", + "1pzry9x0s0muk", + "x1b4n0q5v", + "li1dgmt3", + "de1lg7wt\xff", + "A1G7SGD8", + "10a06t8", + "1qzzfhee", + }; + for (const std::string& str : CASES) { + auto ret = bech32::Decode(str); + BOOST_CHECK(ret.first.empty()); + } +} + +BOOST_AUTO_TEST_SUITE_END() From 6565c5501cfe7755585fd72f6329eea4c5eb2bf1 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 19 Sep 2017 15:30:11 -0700 Subject: [PATCH 291/382] Convert base58_tests from type/payload to scriptPubKey comparison --- src/test/base58_tests.cpp | 107 +---- src/test/data/base58_keys_valid.json | 572 +++++++++++++-------------- 2 files changed, 294 insertions(+), 385 deletions(-) diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index 4829590c5..f6613b0b9 100644 --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -10,14 +10,15 @@ #include "key.h" #include "script/script.h" +#include "test/test_bitcoin.h" #include "uint256.h" #include "util.h" #include "utilstrencodings.h" -#include "test/test_bitcoin.h" + +#include #include -#include extern UniValue read_json(const std::string& jsondata); @@ -72,50 +73,6 @@ BOOST_AUTO_TEST_CASE(base58_DecodeBase58) BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end()); } -// Visitor to check address type -class TestAddrTypeVisitor : public boost::static_visitor -{ -private: - std::string exp_addrType; -public: - explicit TestAddrTypeVisitor(const std::string &_exp_addrType) : exp_addrType(_exp_addrType) { } - bool operator()(const CKeyID &id) const - { - return (exp_addrType == "pubkey"); - } - bool operator()(const CScriptID &id) const - { - return (exp_addrType == "script"); - } - bool operator()(const CNoDestination &no) const - { - return (exp_addrType == "none"); - } -}; - -// Visitor to check address payload -class TestPayloadVisitor : public boost::static_visitor -{ -private: - std::vector exp_payload; -public: - explicit TestPayloadVisitor(std::vector &_exp_payload) : exp_payload(_exp_payload) { } - bool operator()(const CKeyID &id) const - { - uint160 exp_key(exp_payload); - return exp_key == id; - } - bool operator()(const CScriptID &id) const - { - uint160 exp_key(exp_payload); - return exp_key == id; - } - bool operator()(const CNoDestination &no) const - { - return exp_payload.size() == 0; - } -}; - // Goal: check that parsed keys match test payload BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) { @@ -127,8 +84,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) for (unsigned int idx = 0; idx < tests.size(); idx++) { UniValue test = tests[idx]; std::string strTest = test.write(); - if (test.size() < 3) // Allow for extra stuff (useful for comments) - { + if (test.size() < 3) { // Allow for extra stuff (useful for comments) BOOST_ERROR("Bad test: " << strTest); continue; } @@ -136,13 +92,13 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) std::vector exp_payload = ParseHex(test[1].get_str()); const UniValue &metadata = test[2].get_obj(); bool isPrivkey = find_value(metadata, "isPrivkey").get_bool(); - bool isTestnet = find_value(metadata, "isTestnet").get_bool(); - if (isTestnet) + bool isTestnet = find_value(metadata, "chain").get_str() == "testnet"; + if (isTestnet) { SelectParams(CBaseChainParams::TESTNET); - else + } else { SelectParams(CBaseChainParams::MAIN); - if(isPrivkey) - { + } + if (isPrivkey) { bool isCompressed = find_value(metadata, "isCompressed").get_bool(); // Must be valid private key BOOST_CHECK_MESSAGE(secret.SetString(exp_base58string), "!SetString:"+ strTest); @@ -154,15 +110,12 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) // Private key must be invalid public key destination = DecodeDestination(exp_base58string); BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid privkey as pubkey:" + strTest); - } - else - { - std::string exp_addrType = find_value(metadata, "addrType").get_str(); // "script" or "pubkey" + } else { // Must be valid public key destination = DecodeDestination(exp_base58string); + CScript script = GetScriptForDestination(destination); BOOST_CHECK_MESSAGE(IsValidDestination(destination), "!IsValid:" + strTest); - BOOST_CHECK_MESSAGE((boost::get(&destination) != nullptr) == (exp_addrType == "script"), "isScript mismatch" + strTest); - BOOST_CHECK_MESSAGE(boost::apply_visitor(TestAddrTypeVisitor(exp_addrType), destination), "addrType mismatch" + strTest); + BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload)); // Public key must be invalid private key secret.SetString(exp_base58string); @@ -188,13 +141,13 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen) std::vector exp_payload = ParseHex(test[1].get_str()); const UniValue &metadata = test[2].get_obj(); bool isPrivkey = find_value(metadata, "isPrivkey").get_bool(); - bool isTestnet = find_value(metadata, "isTestnet").get_bool(); - if (isTestnet) + bool isTestnet = find_value(metadata, "chain").get_str() == "testnet"; + if (isTestnet) { SelectParams(CBaseChainParams::TESTNET); - else + } else { SelectParams(CBaseChainParams::MAIN); - if(isPrivkey) - { + } + if (isPrivkey) { bool isCompressed = find_value(metadata, "isCompressed").get_bool(); CKey key; key.Set(exp_payload.begin(), exp_payload.end(), isCompressed); @@ -202,30 +155,12 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen) CBitcoinSecret secret; secret.SetKey(key); BOOST_CHECK_MESSAGE(secret.ToString() == exp_base58string, "result mismatch: " + strTest); - } - else - { - std::string exp_addrType = find_value(metadata, "addrType").get_str(); + } else { CTxDestination dest; - if(exp_addrType == "pubkey") - { - dest = CKeyID(uint160(exp_payload)); - } - else if(exp_addrType == "script") - { - dest = CScriptID(uint160(exp_payload)); - } - else if(exp_addrType == "none") - { - dest = CNoDestination(); - } - else - { - BOOST_ERROR("Bad addrtype: " << strTest); - continue; - } + CScript exp_script(exp_payload.begin(), exp_payload.end()); + ExtractDestination(exp_script, dest); std::string address = EncodeDestination(dest); - BOOST_CHECK_MESSAGE(address == exp_base58string, "mismatch: " + strTest); + BOOST_CHECK_EQUAL(address, exp_base58string); } } diff --git a/src/test/data/base58_keys_valid.json b/src/test/data/base58_keys_valid.json index e1e252e22..e1e4d0a30 100644 --- a/src/test/data/base58_keys_valid.json +++ b/src/test/data/base58_keys_valid.json @@ -1,452 +1,426 @@ [ [ - "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", - "65a16059864a2fdbc7c99a4723a8395bc6f188eb", + "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", + "76a91465a16059864a2fdbc7c99a4723a8395bc6f188eb88ac", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": false + "isPrivkey": false, + "chain": "mainnet" } - ], + ], [ - "3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou", - "74f209f6ea907e2ea48f74fae05782ae8a665257", + "3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou", + "a91474f209f6ea907e2ea48f74fae05782ae8a66525787", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": false + "isPrivkey": false, + "chain": "mainnet" } - ], + ], [ - "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs", - "53c0307d6851aa0ce7825ba883c6bd9ad242b486", + "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs", + "76a91453c0307d6851aa0ce7825ba883c6bd9ad242b48688ac", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": true + "isPrivkey": false, + "chain": "testnet" } - ], + ], [ - "2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br", - "6349a418fc4578d10a372b54b45c280cc8c4382f", + "2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br", + "a9146349a418fc4578d10a372b54b45c280cc8c4382f87", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": true + "isPrivkey": false, + "chain": "testnet" } - ], + ], [ - "5Kd3NBUAdUnhyzenEwVLy9pBKxSwXvE9FMPyR4UKZvpe6E3AgLr", - "eddbdc1168f1daeadbd3e44c1e3f8f5a284c2029f78ad26af98583a499de5b19", + "5Kd3NBUAdUnhyzenEwVLy9pBKxSwXvE9FMPyR4UKZvpe6E3AgLr", + "eddbdc1168f1daeadbd3e44c1e3f8f5a284c2029f78ad26af98583a499de5b19", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": false + "isCompressed": false, + "isPrivkey": true, + "chain": "mainnet" } - ], + ], [ - "Kz6UJmQACJmLtaQj5A3JAge4kVTNQ8gbvXuwbmCj7bsaabudb3RD", - "55c9bccb9ed68446d1b75273bbce89d7fe013a8acd1625514420fb2aca1a21c4", + "Kz6UJmQACJmLtaQj5A3JAge4kVTNQ8gbvXuwbmCj7bsaabudb3RD", + "55c9bccb9ed68446d1b75273bbce89d7fe013a8acd1625514420fb2aca1a21c4", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": false + "isCompressed": true, + "isPrivkey": true, + "chain": "mainnet" } - ], + ], [ - "9213qJab2HNEpMpYNBa7wHGFKKbkDn24jpANDs2huN3yi4J11ko", - "36cb93b9ab1bdabf7fb9f2c04f1b9cc879933530ae7842398eef5a63a56800c2", + "9213qJab2HNEpMpYNBa7wHGFKKbkDn24jpANDs2huN3yi4J11ko", + "36cb93b9ab1bdabf7fb9f2c04f1b9cc879933530ae7842398eef5a63a56800c2", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": true + "isCompressed": false, + "isPrivkey": true, + "chain": "testnet" } - ], + ], [ - "cTpB4YiyKiBcPxnefsDpbnDxFDffjqJob8wGCEDXxgQ7zQoMXJdH", - "b9f4892c9e8282028fea1d2667c4dc5213564d41fc5783896a0d843fc15089f3", + "cTpB4YiyKiBcPxnefsDpbnDxFDffjqJob8wGCEDXxgQ7zQoMXJdH", + "b9f4892c9e8282028fea1d2667c4dc5213564d41fc5783896a0d843fc15089f3", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": true + "isCompressed": true, + "isPrivkey": true, + "chain": "testnet" } - ], + ], [ - "1Ax4gZtb7gAit2TivwejZHYtNNLT18PUXJ", - "6d23156cbbdcc82a5a47eee4c2c7c583c18b6bf4", + "1Ax4gZtb7gAit2TivwejZHYtNNLT18PUXJ", + "76a9146d23156cbbdcc82a5a47eee4c2c7c583c18b6bf488ac", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": false + "isPrivkey": false, + "chain": "mainnet" } - ], + ], [ - "3QjYXhTkvuj8qPaXHTTWb5wjXhdsLAAWVy", - "fcc5460dd6e2487c7d75b1963625da0e8f4c5975", + "3QjYXhTkvuj8qPaXHTTWb5wjXhdsLAAWVy", + "a914fcc5460dd6e2487c7d75b1963625da0e8f4c597587", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": false + "isPrivkey": false, + "chain": "mainnet" } - ], + ], [ - "n3ZddxzLvAY9o7184TB4c6FJasAybsw4HZ", - "f1d470f9b02370fdec2e6b708b08ac431bf7a5f7", + "n3ZddxzLvAY9o7184TB4c6FJasAybsw4HZ", + "76a914f1d470f9b02370fdec2e6b708b08ac431bf7a5f788ac", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": true + "isPrivkey": false, + "chain": "testnet" } - ], + ], [ - "2NBFNJTktNa7GZusGbDbGKRZTxdK9VVez3n", - "c579342c2c4c9220205e2cdc285617040c924a0a", + "2NBFNJTktNa7GZusGbDbGKRZTxdK9VVez3n", + "a914c579342c2c4c9220205e2cdc285617040c924a0a87", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": true + "isPrivkey": false, + "chain": "testnet" } - ], + ], [ - "5K494XZwps2bGyeL71pWid4noiSNA2cfCibrvRWqcHSptoFn7rc", - "a326b95ebae30164217d7a7f57d72ab2b54e3be64928a19da0210b9568d4015e", + "5K494XZwps2bGyeL71pWid4noiSNA2cfCibrvRWqcHSptoFn7rc", + "a326b95ebae30164217d7a7f57d72ab2b54e3be64928a19da0210b9568d4015e", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": false + "isCompressed": false, + "isPrivkey": true, + "chain": "mainnet" } - ], + ], [ - "L1RrrnXkcKut5DEMwtDthjwRcTTwED36thyL1DebVrKuwvohjMNi", - "7d998b45c219a1e38e99e7cbd312ef67f77a455a9b50c730c27f02c6f730dfb4", + "L1RrrnXkcKut5DEMwtDthjwRcTTwED36thyL1DebVrKuwvohjMNi", + "7d998b45c219a1e38e99e7cbd312ef67f77a455a9b50c730c27f02c6f730dfb4", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": false + "isCompressed": true, + "isPrivkey": true, + "chain": "mainnet" } - ], + ], [ - "93DVKyFYwSN6wEo3E2fCrFPUp17FtrtNi2Lf7n4G3garFb16CRj", - "d6bca256b5abc5602ec2e1c121a08b0da2556587430bcf7e1898af2224885203", + "93DVKyFYwSN6wEo3E2fCrFPUp17FtrtNi2Lf7n4G3garFb16CRj", + "d6bca256b5abc5602ec2e1c121a08b0da2556587430bcf7e1898af2224885203", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": true + "isCompressed": false, + "isPrivkey": true, + "chain": "testnet" } - ], + ], [ - "cTDVKtMGVYWTHCb1AFjmVbEbWjvKpKqKgMaR3QJxToMSQAhmCeTN", - "a81ca4e8f90181ec4b61b6a7eb998af17b2cb04de8a03b504b9e34c4c61db7d9", + "cTDVKtMGVYWTHCb1AFjmVbEbWjvKpKqKgMaR3QJxToMSQAhmCeTN", + "a81ca4e8f90181ec4b61b6a7eb998af17b2cb04de8a03b504b9e34c4c61db7d9", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": true + "isCompressed": true, + "isPrivkey": true, + "chain": "testnet" } - ], + ], [ - "1C5bSj1iEGUgSTbziymG7Cn18ENQuT36vv", - "7987ccaa53d02c8873487ef919677cd3db7a6912", + "1C5bSj1iEGUgSTbziymG7Cn18ENQuT36vv", + "76a9147987ccaa53d02c8873487ef919677cd3db7a691288ac", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": false + "isPrivkey": false, + "chain": "mainnet" } - ], + ], [ - "3AnNxabYGoTxYiTEZwFEnerUoeFXK2Zoks", - "63bcc565f9e68ee0189dd5cc67f1b0e5f02f45cb", + "3AnNxabYGoTxYiTEZwFEnerUoeFXK2Zoks", + "a91463bcc565f9e68ee0189dd5cc67f1b0e5f02f45cb87", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": false + "isPrivkey": false, + "chain": "mainnet" } - ], + ], [ - "n3LnJXCqbPjghuVs8ph9CYsAe4Sh4j97wk", - "ef66444b5b17f14e8fae6e7e19b045a78c54fd79", + "n3LnJXCqbPjghuVs8ph9CYsAe4Sh4j97wk", + "76a914ef66444b5b17f14e8fae6e7e19b045a78c54fd7988ac", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": true + "isPrivkey": false, + "chain": "testnet" } - ], + ], [ - "2NB72XtkjpnATMggui83aEtPawyyKvnbX2o", - "c3e55fceceaa4391ed2a9677f4a4d34eacd021a0", + "2NB72XtkjpnATMggui83aEtPawyyKvnbX2o", + "a914c3e55fceceaa4391ed2a9677f4a4d34eacd021a087", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": true + "isPrivkey": false, + "chain": "testnet" } - ], + ], [ - "5KaBW9vNtWNhc3ZEDyNCiXLPdVPHCikRxSBWwV9NrpLLa4LsXi9", - "e75d936d56377f432f404aabb406601f892fd49da90eb6ac558a733c93b47252", + "5KaBW9vNtWNhc3ZEDyNCiXLPdVPHCikRxSBWwV9NrpLLa4LsXi9", + "e75d936d56377f432f404aabb406601f892fd49da90eb6ac558a733c93b47252", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": false + "isCompressed": false, + "isPrivkey": true, + "chain": "mainnet" } - ], + ], [ - "L1axzbSyynNYA8mCAhzxkipKkfHtAXYF4YQnhSKcLV8YXA874fgT", - "8248bd0375f2f75d7e274ae544fb920f51784480866b102384190b1addfbaa5c", + "L1axzbSyynNYA8mCAhzxkipKkfHtAXYF4YQnhSKcLV8YXA874fgT", + "8248bd0375f2f75d7e274ae544fb920f51784480866b102384190b1addfbaa5c", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": false + "isCompressed": true, + "isPrivkey": true, + "chain": "mainnet" } - ], + ], [ - "927CnUkUbasYtDwYwVn2j8GdTuACNnKkjZ1rpZd2yBB1CLcnXpo", - "44c4f6a096eac5238291a94cc24c01e3b19b8d8cef72874a079e00a242237a52", + "927CnUkUbasYtDwYwVn2j8GdTuACNnKkjZ1rpZd2yBB1CLcnXpo", + "44c4f6a096eac5238291a94cc24c01e3b19b8d8cef72874a079e00a242237a52", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": true + "isCompressed": false, + "isPrivkey": true, + "chain": "testnet" } - ], + ], [ - "cUcfCMRjiQf85YMzzQEk9d1s5A4K7xL5SmBCLrezqXFuTVefyhY7", - "d1de707020a9059d6d3abaf85e17967c6555151143db13dbb06db78df0f15c69", + "cUcfCMRjiQf85YMzzQEk9d1s5A4K7xL5SmBCLrezqXFuTVefyhY7", + "d1de707020a9059d6d3abaf85e17967c6555151143db13dbb06db78df0f15c69", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": true + "isCompressed": true, + "isPrivkey": true, + "chain": "testnet" } - ], + ], [ - "1Gqk4Tv79P91Cc1STQtU3s1W6277M2CVWu", - "adc1cc2081a27206fae25792f28bbc55b831549d", + "1Gqk4Tv79P91Cc1STQtU3s1W6277M2CVWu", + "76a914adc1cc2081a27206fae25792f28bbc55b831549d88ac", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": false + "isPrivkey": false, + "chain": "mainnet" } - ], + ], [ - "33vt8ViH5jsr115AGkW6cEmEz9MpvJSwDk", - "188f91a931947eddd7432d6e614387e32b244709", + "33vt8ViH5jsr115AGkW6cEmEz9MpvJSwDk", + "a914188f91a931947eddd7432d6e614387e32b24470987", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": false + "isPrivkey": false, + "chain": "mainnet" } - ], + ], [ - "mhaMcBxNh5cqXm4aTQ6EcVbKtfL6LGyK2H", - "1694f5bc1a7295b600f40018a618a6ea48eeb498", + "mhaMcBxNh5cqXm4aTQ6EcVbKtfL6LGyK2H", + "76a9141694f5bc1a7295b600f40018a618a6ea48eeb49888ac", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": true + "isPrivkey": false, + "chain": "testnet" } - ], + ], [ - "2MxgPqX1iThW3oZVk9KoFcE5M4JpiETssVN", - "3b9b3fd7a50d4f08d1a5b0f62f644fa7115ae2f3", + "2MxgPqX1iThW3oZVk9KoFcE5M4JpiETssVN", + "a9143b9b3fd7a50d4f08d1a5b0f62f644fa7115ae2f387", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": true + "isPrivkey": false, + "chain": "testnet" } - ], + ], [ - "5HtH6GdcwCJA4ggWEL1B3jzBBUB8HPiBi9SBc5h9i4Wk4PSeApR", - "091035445ef105fa1bb125eccfb1882f3fe69592265956ade751fd095033d8d0", + "5HtH6GdcwCJA4ggWEL1B3jzBBUB8HPiBi9SBc5h9i4Wk4PSeApR", + "091035445ef105fa1bb125eccfb1882f3fe69592265956ade751fd095033d8d0", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": false + "isCompressed": false, + "isPrivkey": true, + "chain": "mainnet" } - ], + ], [ - "L2xSYmMeVo3Zek3ZTsv9xUrXVAmrWxJ8Ua4cw8pkfbQhcEFhkXT8", - "ab2b4bcdfc91d34dee0ae2a8c6b6668dadaeb3a88b9859743156f462325187af", + "L2xSYmMeVo3Zek3ZTsv9xUrXVAmrWxJ8Ua4cw8pkfbQhcEFhkXT8", + "ab2b4bcdfc91d34dee0ae2a8c6b6668dadaeb3a88b9859743156f462325187af", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": false + "isCompressed": true, + "isPrivkey": true, + "chain": "mainnet" } - ], + ], [ - "92xFEve1Z9N8Z641KQQS7ByCSb8kGjsDzw6fAmjHN1LZGKQXyMq", - "b4204389cef18bbe2b353623cbf93e8678fbc92a475b664ae98ed594e6cf0856", + "92xFEve1Z9N8Z641KQQS7ByCSb8kGjsDzw6fAmjHN1LZGKQXyMq", + "b4204389cef18bbe2b353623cbf93e8678fbc92a475b664ae98ed594e6cf0856", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": true + "isCompressed": false, + "isPrivkey": true, + "chain": "testnet" } - ], + ], [ - "cVM65tdYu1YK37tNoAyGoJTR13VBYFva1vg9FLuPAsJijGvG6NEA", - "e7b230133f1b5489843260236b06edca25f66adb1be455fbd38d4010d48faeef", + "cVM65tdYu1YK37tNoAyGoJTR13VBYFva1vg9FLuPAsJijGvG6NEA", + "e7b230133f1b5489843260236b06edca25f66adb1be455fbd38d4010d48faeef", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": true + "isCompressed": true, + "isPrivkey": true, + "chain": "testnet" } - ], + ], [ - "1JwMWBVLtiqtscbaRHai4pqHokhFCbtoB4", - "c4c1b72491ede1eedaca00618407ee0b772cad0d", + "1JwMWBVLtiqtscbaRHai4pqHokhFCbtoB4", + "76a914c4c1b72491ede1eedaca00618407ee0b772cad0d88ac", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": false + "isPrivkey": false, + "chain": "mainnet" } - ], + ], [ - "3QCzvfL4ZRvmJFiWWBVwxfdaNBT8EtxB5y", - "f6fe69bcb548a829cce4c57bf6fff8af3a5981f9", + "3QCzvfL4ZRvmJFiWWBVwxfdaNBT8EtxB5y", + "a914f6fe69bcb548a829cce4c57bf6fff8af3a5981f987", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": false + "isPrivkey": false, + "chain": "mainnet" } - ], + ], [ - "mizXiucXRCsEriQCHUkCqef9ph9qtPbZZ6", - "261f83568a098a8638844bd7aeca039d5f2352c0", + "mizXiucXRCsEriQCHUkCqef9ph9qtPbZZ6", + "76a914261f83568a098a8638844bd7aeca039d5f2352c088ac", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": true + "isPrivkey": false, + "chain": "testnet" } - ], + ], [ - "2NEWDzHWwY5ZZp8CQWbB7ouNMLqCia6YRda", - "e930e1834a4d234702773951d627cce82fbb5d2e", + "2NEWDzHWwY5ZZp8CQWbB7ouNMLqCia6YRda", + "a914e930e1834a4d234702773951d627cce82fbb5d2e87", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": true + "isPrivkey": false, + "chain": "testnet" } - ], + ], [ - "5KQmDryMNDcisTzRp3zEq9e4awRmJrEVU1j5vFRTKpRNYPqYrMg", - "d1fab7ab7385ad26872237f1eb9789aa25cc986bacc695e07ac571d6cdac8bc0", + "5KQmDryMNDcisTzRp3zEq9e4awRmJrEVU1j5vFRTKpRNYPqYrMg", + "d1fab7ab7385ad26872237f1eb9789aa25cc986bacc695e07ac571d6cdac8bc0", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": false + "isCompressed": false, + "isPrivkey": true, + "chain": "mainnet" } - ], + ], [ - "L39Fy7AC2Hhj95gh3Yb2AU5YHh1mQSAHgpNixvm27poizcJyLtUi", - "b0bbede33ef254e8376aceb1510253fc3550efd0fcf84dcd0c9998b288f166b3", + "L39Fy7AC2Hhj95gh3Yb2AU5YHh1mQSAHgpNixvm27poizcJyLtUi", + "b0bbede33ef254e8376aceb1510253fc3550efd0fcf84dcd0c9998b288f166b3", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": false + "isCompressed": true, + "isPrivkey": true, + "chain": "mainnet" } - ], + ], [ - "91cTVUcgydqyZLgaANpf1fvL55FH53QMm4BsnCADVNYuWuqdVys", - "037f4192c630f399d9271e26c575269b1d15be553ea1a7217f0cb8513cef41cb", + "91cTVUcgydqyZLgaANpf1fvL55FH53QMm4BsnCADVNYuWuqdVys", + "037f4192c630f399d9271e26c575269b1d15be553ea1a7217f0cb8513cef41cb", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": true + "isCompressed": false, + "isPrivkey": true, + "chain": "testnet" } - ], + ], [ - "cQspfSzsgLeiJGB2u8vrAiWpCU4MxUT6JseWo2SjXy4Qbzn2fwDw", - "6251e205e8ad508bab5596bee086ef16cd4b239e0cc0c5d7c4e6035441e7d5de", + "cQspfSzsgLeiJGB2u8vrAiWpCU4MxUT6JseWo2SjXy4Qbzn2fwDw", + "6251e205e8ad508bab5596bee086ef16cd4b239e0cc0c5d7c4e6035441e7d5de", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": true + "isCompressed": true, + "isPrivkey": true, + "chain": "testnet" } - ], + ], [ - "19dcawoKcZdQz365WpXWMhX6QCUpR9SY4r", - "5eadaf9bb7121f0f192561a5a62f5e5f54210292", + "19dcawoKcZdQz365WpXWMhX6QCUpR9SY4r", + "76a9145eadaf9bb7121f0f192561a5a62f5e5f5421029288ac", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": false + "isPrivkey": false, + "chain": "mainnet" } - ], + ], [ - "37Sp6Rv3y4kVd1nQ1JV5pfqXccHNyZm1x3", - "3f210e7277c899c3a155cc1c90f4106cbddeec6e", + "37Sp6Rv3y4kVd1nQ1JV5pfqXccHNyZm1x3", + "a9143f210e7277c899c3a155cc1c90f4106cbddeec6e87", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": false + "isPrivkey": false, + "chain": "mainnet" } - ], + ], [ - "myoqcgYiehufrsnnkqdqbp69dddVDMopJu", - "c8a3c2a09a298592c3e180f02487cd91ba3400b5", + "myoqcgYiehufrsnnkqdqbp69dddVDMopJu", + "76a914c8a3c2a09a298592c3e180f02487cd91ba3400b588ac", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": true + "isPrivkey": false, + "chain": "testnet" } - ], + ], [ - "2N7FuwuUuoTBrDFdrAZ9KxBmtqMLxce9i1C", - "99b31df7c9068d1481b596578ddbb4d3bd90baeb", + "2N7FuwuUuoTBrDFdrAZ9KxBmtqMLxce9i1C", + "a91499b31df7c9068d1481b596578ddbb4d3bd90baeb87", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": true + "isPrivkey": false, + "chain": "testnet" } - ], + ], [ - "5KL6zEaMtPRXZKo1bbMq7JDjjo1bJuQcsgL33je3oY8uSJCR5b4", - "c7666842503db6dc6ea061f092cfb9c388448629a6fe868d068c42a488b478ae", + "5KL6zEaMtPRXZKo1bbMq7JDjjo1bJuQcsgL33je3oY8uSJCR5b4", + "c7666842503db6dc6ea061f092cfb9c388448629a6fe868d068c42a488b478ae", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": false + "isCompressed": false, + "isPrivkey": true, + "chain": "mainnet" } - ], + ], [ - "KwV9KAfwbwt51veZWNscRTeZs9CKpojyu1MsPnaKTF5kz69H1UN2", - "07f0803fc5399e773555ab1e8939907e9badacc17ca129e67a2f5f2ff84351dd", + "KwV9KAfwbwt51veZWNscRTeZs9CKpojyu1MsPnaKTF5kz69H1UN2", + "07f0803fc5399e773555ab1e8939907e9badacc17ca129e67a2f5f2ff84351dd", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": false + "isCompressed": true, + "isPrivkey": true, + "chain": "mainnet" } - ], + ], [ - "93N87D6uxSBzwXvpokpzg8FFmfQPmvX4xHoWQe3pLdYpbiwT5YV", - "ea577acfb5d1d14d3b7b195c321566f12f87d2b77ea3a53f68df7ebf8604a801", + "93N87D6uxSBzwXvpokpzg8FFmfQPmvX4xHoWQe3pLdYpbiwT5YV", + "ea577acfb5d1d14d3b7b195c321566f12f87d2b77ea3a53f68df7ebf8604a801", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": true + "isCompressed": false, + "isPrivkey": true, + "chain": "testnet" } - ], + ], [ - "cMxXusSihaX58wpJ3tNuuUcZEQGt6DKJ1wEpxys88FFaQCYjku9h", - "0b3b34f0958d8a268193a9814da92c3e8b58b4a4378a542863e34ac289cd830c", + "cMxXusSihaX58wpJ3tNuuUcZEQGt6DKJ1wEpxys88FFaQCYjku9h", + "0b3b34f0958d8a268193a9814da92c3e8b58b4a4378a542863e34ac289cd830c", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": true + "isCompressed": true, + "isPrivkey": true, + "chain": "testnet" } - ], + ], [ - "13p1ijLwsnrcuyqcTvJXkq2ASdXqcnEBLE", - "1ed467017f043e91ed4c44b4e8dd674db211c4e6", + "13p1ijLwsnrcuyqcTvJXkq2ASdXqcnEBLE", + "76a9141ed467017f043e91ed4c44b4e8dd674db211c4e688ac", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": false + "isPrivkey": false, + "chain": "mainnet" } - ], + ], [ - "3ALJH9Y951VCGcVZYAdpA3KchoP9McEj1G", - "5ece0cadddc415b1980f001785947120acdb36fc", + "3ALJH9Y951VCGcVZYAdpA3KchoP9McEj1G", + "a9145ece0cadddc415b1980f001785947120acdb36fc87", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": false + "isPrivkey": false, + "chain": "mainnet" } ] ] From bd355b8db9ffaacaafd10eb14f6b74cf00d8fc06 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 20 Sep 2017 00:13:44 -0700 Subject: [PATCH 292/382] Add regtest testing to base58_tests --- src/test/base58_tests.cpp | 25 ++--- src/test/data/base58_keys_valid.json | 135 +++++++++++++++++---------- 2 files changed, 94 insertions(+), 66 deletions(-) diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index f6613b0b9..65092f593 100644 --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -92,12 +92,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) std::vector exp_payload = ParseHex(test[1].get_str()); const UniValue &metadata = test[2].get_obj(); bool isPrivkey = find_value(metadata, "isPrivkey").get_bool(); - bool isTestnet = find_value(metadata, "chain").get_str() == "testnet"; - if (isTestnet) { - SelectParams(CBaseChainParams::TESTNET); - } else { - SelectParams(CBaseChainParams::MAIN); - } + SelectParams(find_value(metadata, "chain").get_str()); if (isPrivkey) { bool isCompressed = find_value(metadata, "isCompressed").get_bool(); // Must be valid private key @@ -141,12 +136,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen) std::vector exp_payload = ParseHex(test[1].get_str()); const UniValue &metadata = test[2].get_obj(); bool isPrivkey = find_value(metadata, "isPrivkey").get_bool(); - bool isTestnet = find_value(metadata, "chain").get_str() == "testnet"; - if (isTestnet) { - SelectParams(CBaseChainParams::TESTNET); - } else { - SelectParams(CBaseChainParams::MAIN); - } + SelectParams(find_value(metadata, "chain").get_str()); if (isPrivkey) { bool isCompressed = find_value(metadata, "isCompressed").get_bool(); CKey key; @@ -185,10 +175,13 @@ BOOST_AUTO_TEST_CASE(base58_keys_invalid) std::string exp_base58string = test[0].get_str(); // must be invalid as public and as private key - destination = DecodeDestination(exp_base58string); - BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid pubkey:" + strTest); - secret.SetString(exp_base58string); - BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid privkey:" + strTest); + for (auto chain : { CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::REGTEST }) { + SelectParams(chain); + destination = DecodeDestination(exp_base58string); + BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid pubkey in mainnet:" + strTest); + secret.SetString(exp_base58string); + BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid privkey in mainnet:" + strTest); + } } } diff --git a/src/test/data/base58_keys_valid.json b/src/test/data/base58_keys_valid.json index e1e4d0a30..175b297a1 100644 --- a/src/test/data/base58_keys_valid.json +++ b/src/test/data/base58_keys_valid.json @@ -4,7 +4,7 @@ "76a91465a16059864a2fdbc7c99a4723a8395bc6f188eb88ac", { "isPrivkey": false, - "chain": "mainnet" + "chain": "main" } ], [ @@ -12,7 +12,7 @@ "a91474f209f6ea907e2ea48f74fae05782ae8a66525787", { "isPrivkey": false, - "chain": "mainnet" + "chain": "main" } ], [ @@ -20,7 +20,15 @@ "76a91453c0307d6851aa0ce7825ba883c6bd9ad242b48688ac", { "isPrivkey": false, - "chain": "testnet" + "chain": "test" + } + ], + [ + "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs", + "76a91453c0307d6851aa0ce7825ba883c6bd9ad242b48688ac", + { + "isPrivkey": false, + "chain": "regtest" } ], [ @@ -28,7 +36,7 @@ "a9146349a418fc4578d10a372b54b45c280cc8c4382f87", { "isPrivkey": false, - "chain": "testnet" + "chain": "test" } ], [ @@ -37,7 +45,7 @@ { "isCompressed": false, "isPrivkey": true, - "chain": "mainnet" + "chain": "main" } ], [ @@ -46,7 +54,16 @@ { "isCompressed": true, "isPrivkey": true, - "chain": "mainnet" + "chain": "main" + } + ], + [ + "9213qJab2HNEpMpYNBa7wHGFKKbkDn24jpANDs2huN3yi4J11ko", + "36cb93b9ab1bdabf7fb9f2c04f1b9cc879933530ae7842398eef5a63a56800c2", + { + "isCompressed": false, + "isPrivkey": true, + "chain": "test" } ], [ @@ -55,7 +72,7 @@ { "isCompressed": false, "isPrivkey": true, - "chain": "testnet" + "chain": "regtest" } ], [ @@ -64,7 +81,16 @@ { "isCompressed": true, "isPrivkey": true, - "chain": "testnet" + "chain": "test" + } + ], + [ + "cTpB4YiyKiBcPxnefsDpbnDxFDffjqJob8wGCEDXxgQ7zQoMXJdH", + "b9f4892c9e8282028fea1d2667c4dc5213564d41fc5783896a0d843fc15089f3", + { + "isCompressed": true, + "isPrivkey": true, + "chain": "regtest" } ], [ @@ -72,7 +98,7 @@ "76a9146d23156cbbdcc82a5a47eee4c2c7c583c18b6bf488ac", { "isPrivkey": false, - "chain": "mainnet" + "chain": "main" } ], [ @@ -80,7 +106,7 @@ "a914fcc5460dd6e2487c7d75b1963625da0e8f4c597587", { "isPrivkey": false, - "chain": "mainnet" + "chain": "main" } ], [ @@ -88,7 +114,7 @@ "76a914f1d470f9b02370fdec2e6b708b08ac431bf7a5f788ac", { "isPrivkey": false, - "chain": "testnet" + "chain": "test" } ], [ @@ -96,7 +122,7 @@ "a914c579342c2c4c9220205e2cdc285617040c924a0a87", { "isPrivkey": false, - "chain": "testnet" + "chain": "test" } ], [ @@ -105,7 +131,7 @@ { "isCompressed": false, "isPrivkey": true, - "chain": "mainnet" + "chain": "main" } ], [ @@ -114,7 +140,7 @@ { "isCompressed": true, "isPrivkey": true, - "chain": "mainnet" + "chain": "main" } ], [ @@ -123,7 +149,7 @@ { "isCompressed": false, "isPrivkey": true, - "chain": "testnet" + "chain": "test" } ], [ @@ -132,7 +158,7 @@ { "isCompressed": true, "isPrivkey": true, - "chain": "testnet" + "chain": "test" } ], [ @@ -140,7 +166,7 @@ "76a9147987ccaa53d02c8873487ef919677cd3db7a691288ac", { "isPrivkey": false, - "chain": "mainnet" + "chain": "main" } ], [ @@ -148,7 +174,7 @@ "a91463bcc565f9e68ee0189dd5cc67f1b0e5f02f45cb87", { "isPrivkey": false, - "chain": "mainnet" + "chain": "main" } ], [ @@ -156,7 +182,7 @@ "76a914ef66444b5b17f14e8fae6e7e19b045a78c54fd7988ac", { "isPrivkey": false, - "chain": "testnet" + "chain": "test" } ], [ @@ -164,7 +190,7 @@ "a914c3e55fceceaa4391ed2a9677f4a4d34eacd021a087", { "isPrivkey": false, - "chain": "testnet" + "chain": "test" } ], [ @@ -173,7 +199,7 @@ { "isCompressed": false, "isPrivkey": true, - "chain": "mainnet" + "chain": "main" } ], [ @@ -182,7 +208,7 @@ { "isCompressed": true, "isPrivkey": true, - "chain": "mainnet" + "chain": "main" } ], [ @@ -191,7 +217,7 @@ { "isCompressed": false, "isPrivkey": true, - "chain": "testnet" + "chain": "test" } ], [ @@ -200,7 +226,7 @@ { "isCompressed": true, "isPrivkey": true, - "chain": "testnet" + "chain": "test" } ], [ @@ -208,7 +234,7 @@ "76a914adc1cc2081a27206fae25792f28bbc55b831549d88ac", { "isPrivkey": false, - "chain": "mainnet" + "chain": "main" } ], [ @@ -216,7 +242,7 @@ "a914188f91a931947eddd7432d6e614387e32b24470987", { "isPrivkey": false, - "chain": "mainnet" + "chain": "main" } ], [ @@ -224,7 +250,7 @@ "76a9141694f5bc1a7295b600f40018a618a6ea48eeb49888ac", { "isPrivkey": false, - "chain": "testnet" + "chain": "test" } ], [ @@ -232,7 +258,7 @@ "a9143b9b3fd7a50d4f08d1a5b0f62f644fa7115ae2f387", { "isPrivkey": false, - "chain": "testnet" + "chain": "test" } ], [ @@ -241,7 +267,7 @@ { "isCompressed": false, "isPrivkey": true, - "chain": "mainnet" + "chain": "main" } ], [ @@ -250,7 +276,16 @@ { "isCompressed": true, "isPrivkey": true, - "chain": "mainnet" + "chain": "main" + } + ], + [ + "92xFEve1Z9N8Z641KQQS7ByCSb8kGjsDzw6fAmjHN1LZGKQXyMq", + "b4204389cef18bbe2b353623cbf93e8678fbc92a475b664ae98ed594e6cf0856", + { + "isCompressed": false, + "isPrivkey": true, + "chain": "test" } ], [ @@ -259,7 +294,7 @@ { "isCompressed": false, "isPrivkey": true, - "chain": "testnet" + "chain": "regtest" } ], [ @@ -268,7 +303,7 @@ { "isCompressed": true, "isPrivkey": true, - "chain": "testnet" + "chain": "test" } ], [ @@ -276,7 +311,7 @@ "76a914c4c1b72491ede1eedaca00618407ee0b772cad0d88ac", { "isPrivkey": false, - "chain": "mainnet" + "chain": "main" } ], [ @@ -284,7 +319,7 @@ "a914f6fe69bcb548a829cce4c57bf6fff8af3a5981f987", { "isPrivkey": false, - "chain": "mainnet" + "chain": "main" } ], [ @@ -292,7 +327,7 @@ "76a914261f83568a098a8638844bd7aeca039d5f2352c088ac", { "isPrivkey": false, - "chain": "testnet" + "chain": "test" } ], [ @@ -300,7 +335,7 @@ "a914e930e1834a4d234702773951d627cce82fbb5d2e87", { "isPrivkey": false, - "chain": "testnet" + "chain": "test" } ], [ @@ -309,7 +344,7 @@ { "isCompressed": false, "isPrivkey": true, - "chain": "mainnet" + "chain": "main" } ], [ @@ -318,7 +353,7 @@ { "isCompressed": true, "isPrivkey": true, - "chain": "mainnet" + "chain": "main" } ], [ @@ -327,7 +362,7 @@ { "isCompressed": false, "isPrivkey": true, - "chain": "testnet" + "chain": "test" } ], [ @@ -336,7 +371,7 @@ { "isCompressed": true, "isPrivkey": true, - "chain": "testnet" + "chain": "test" } ], [ @@ -344,7 +379,7 @@ "76a9145eadaf9bb7121f0f192561a5a62f5e5f5421029288ac", { "isPrivkey": false, - "chain": "mainnet" + "chain": "main" } ], [ @@ -352,7 +387,7 @@ "a9143f210e7277c899c3a155cc1c90f4106cbddeec6e87", { "isPrivkey": false, - "chain": "mainnet" + "chain": "main" } ], [ @@ -360,7 +395,7 @@ "76a914c8a3c2a09a298592c3e180f02487cd91ba3400b588ac", { "isPrivkey": false, - "chain": "testnet" + "chain": "test" } ], [ @@ -368,7 +403,7 @@ "a91499b31df7c9068d1481b596578ddbb4d3bd90baeb87", { "isPrivkey": false, - "chain": "testnet" + "chain": "test" } ], [ @@ -377,7 +412,7 @@ { "isCompressed": false, "isPrivkey": true, - "chain": "mainnet" + "chain": "main" } ], [ @@ -386,7 +421,7 @@ { "isCompressed": true, "isPrivkey": true, - "chain": "mainnet" + "chain": "main" } ], [ @@ -395,7 +430,7 @@ { "isCompressed": false, "isPrivkey": true, - "chain": "testnet" + "chain": "test" } ], [ @@ -404,7 +439,7 @@ { "isCompressed": true, "isPrivkey": true, - "chain": "testnet" + "chain": "test" } ], [ @@ -412,7 +447,7 @@ "76a9141ed467017f043e91ed4c44b4e8dd674db211c4e688ac", { "isPrivkey": false, - "chain": "mainnet" + "chain": "main" } ], [ @@ -420,7 +455,7 @@ "a9145ece0cadddc415b1980f001785947120acdb36fc87", { "isPrivkey": false, - "chain": "mainnet" + "chain": "main" } ] ] From c091b99379b97cb314c9fa123beabdbc324cf7a4 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 25 Aug 2017 19:55:52 -0700 Subject: [PATCH 293/382] Implement BIP173 addresses and tests --- src/base58.cpp | 62 +++++++++++++++++++++- src/chainparams.cpp | 6 +++ src/chainparams.h | 2 + src/policy/policy.cpp | 2 +- src/rpc/misc.cpp | 44 ++++++++++++++++ src/script/ismine.cpp | 1 + src/script/sign.cpp | 2 + src/script/standard.cpp | 45 ++++++++++++++++ src/script/standard.h | 37 +++++++++++-- src/test/base58_tests.cpp | 19 ++++++- src/test/data/base58_keys_invalid.json | 30 +++++++++++ src/test/data/base58_keys_valid.json | 72 ++++++++++++++++++++++++++ src/test/script_standard_tests.cpp | 39 +++++++------- src/utilstrencodings.h | 24 +++++++++ src/wallet/rpcwallet.cpp | 5 +- src/wallet/wallet.cpp | 21 +++++++- test/util/data/txcreatemultisig3.json | 6 ++- test/util/data/txcreateoutpubkey2.json | 6 ++- test/util/data/txcreatescript3.json | 6 ++- 19 files changed, 397 insertions(+), 32 deletions(-) diff --git a/src/base58.cpp b/src/base58.cpp index 027271157..c2cc5d979 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -4,9 +4,11 @@ #include "base58.h" +#include "bech32.h" #include "hash.h" #include "script/script.h" #include "uint256.h" +#include "utilstrencodings.h" #include #include @@ -235,7 +237,31 @@ class DestinationEncoder : public boost::static_visitor return EncodeBase58Check(data); } - std::string operator()(const CNoDestination& no) const { return ""; } + std::string operator()(const WitnessV0KeyHash& id) const + { + std::vector data = {0}; + ConvertBits<8, 5, true>(data, id.begin(), id.end()); + return bech32::Encode(m_params.Bech32HRP(), data); + } + + std::string operator()(const WitnessV0ScriptHash& id) const + { + std::vector data = {0}; + ConvertBits<8, 5, true>(data, id.begin(), id.end()); + return bech32::Encode(m_params.Bech32HRP(), data); + } + + std::string operator()(const WitnessUnknown& id) const + { + if (id.version < 1 || id.version > 16 || id.length < 2 || id.length > 40) { + return {}; + } + std::vector data = {(unsigned char)id.version}; + ConvertBits<8, 5, true>(data, id.program, id.program + id.length); + return bech32::Encode(m_params.Bech32HRP(), data); + } + + std::string operator()(const CNoDestination& no) const { return {}; } }; CTxDestination DecodeDestination(const std::string& str, const CChainParams& params) @@ -259,6 +285,40 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par return CScriptID(hash); } } + data.clear(); + auto bech = bech32::Decode(str); + if (bech.second.size() > 0 && bech.first == params.Bech32HRP()) { + // Bech32 decoding + int version = bech.second[0]; // The first 5 bit symbol is the witness version (0-16) + // The rest of the symbols are converted witness program bytes. + if (ConvertBits<5, 8, false>(data, bech.second.begin() + 1, bech.second.end())) { + if (version == 0) { + { + WitnessV0KeyHash keyid; + if (data.size() == keyid.size()) { + std::copy(data.begin(), data.end(), keyid.begin()); + return keyid; + } + } + { + WitnessV0ScriptHash scriptid; + if (data.size() == scriptid.size()) { + std::copy(data.begin(), data.end(), scriptid.begin()); + return scriptid; + } + } + return CNoDestination(); + } + if (version > 16 || data.size() < 2 || data.size() > 40) { + return CNoDestination(); + } + WitnessUnknown unk; + unk.version = version; + std::copy(data.begin(), data.end(), unk.program); + unk.length = data.size(); + return unk; + } + } return CNoDestination(); } } // namespace diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 2021ec51d..85c9cd693 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -137,6 +137,8 @@ class CMainParams : public CChainParams { base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x88, 0xB2, 0x1E}; base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x88, 0xAD, 0xE4}; + bech32_hrp = "bc"; + vFixedSeeds = std::vector(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main)); fDefaultConsistencyChecks = false; @@ -236,6 +238,8 @@ class CTestNetParams : public CChainParams { base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF}; base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94}; + bech32_hrp = "tb"; + vFixedSeeds = std::vector(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test)); fDefaultConsistencyChecks = false; @@ -330,6 +334,8 @@ class CRegTestParams : public CChainParams { base58Prefixes[SECRET_KEY] = std::vector(1,239); base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF}; base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94}; + + bech32_hrp = "bcrt"; } }; diff --git a/src/chainparams.h b/src/chainparams.h index f55ae4cf7..3948c9163 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -73,6 +73,7 @@ class CChainParams std::string NetworkIDString() const { return strNetworkID; } const std::vector& DNSSeeds() const { return vSeeds; } const std::vector& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; } + const std::string& Bech32HRP() const { return bech32_hrp; } const std::vector& FixedSeeds() const { return vFixedSeeds; } const CCheckpointData& Checkpoints() const { return checkpointData; } const ChainTxData& TxData() const { return chainTxData; } @@ -86,6 +87,7 @@ class CChainParams uint64_t nPruneAfterHeight; std::vector vSeeds; std::vector base58Prefixes[MAX_BASE58_TYPES]; + std::string bech32_hrp; std::string strNetworkID; CBlock genesis; std::vector vFixedSeeds; diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 56912d037..b2fb28450 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -76,7 +76,7 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType, const bool w else if (!witnessEnabled && (whichType == TX_WITNESS_V0_KEYHASH || whichType == TX_WITNESS_V0_SCRIPTHASH)) return false; - return whichType != TX_NONSTANDARD; + return whichType != TX_NONSTANDARD && whichType != TX_WITNESS_UNKNOWN; } bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnessEnabled) diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index f8cdf57d1..521b49e2a 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -7,6 +7,7 @@ #include "chain.h" #include "clientversion.h" #include "core_io.h" +#include "crypto/ripemd160.h" #include "init.h" #include "validation.h" #include "httpserver.h" @@ -45,6 +46,7 @@ class DescribeAddressVisitor : public boost::static_visitor UniValue obj(UniValue::VOBJ); CPubKey vchPubKey; obj.push_back(Pair("isscript", false)); + obj.push_back(Pair("iswitness", false)); if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) { obj.push_back(Pair("pubkey", HexStr(vchPubKey))); obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed())); @@ -56,6 +58,7 @@ class DescribeAddressVisitor : public boost::static_visitor UniValue obj(UniValue::VOBJ); CScript subscript; obj.push_back(Pair("isscript", true)); + obj.push_back(Pair("iswitness", false)); if (pwallet && pwallet->GetCScript(scriptID, subscript)) { std::vector addresses; txnouttype whichType; @@ -73,6 +76,47 @@ class DescribeAddressVisitor : public boost::static_visitor } return obj; } + + UniValue operator()(const WitnessV0KeyHash& id) const + { + UniValue obj(UniValue::VOBJ); + CPubKey pubkey; + obj.push_back(Pair("isscript", false)); + obj.push_back(Pair("iswitness", true)); + obj.push_back(Pair("witness_version", 0)); + obj.push_back(Pair("witness_program", HexStr(id.begin(), id.end()))); + if (pwallet && pwallet->GetPubKey(CKeyID(id), pubkey)) { + obj.push_back(Pair("pubkey", HexStr(pubkey))); + } + return obj; + } + + UniValue operator()(const WitnessV0ScriptHash& id) const + { + UniValue obj(UniValue::VOBJ); + CScript subscript; + obj.push_back(Pair("isscript", true)); + obj.push_back(Pair("iswitness", true)); + obj.push_back(Pair("witness_version", 0)); + obj.push_back(Pair("witness_program", HexStr(id.begin(), id.end()))); + CRIPEMD160 hasher; + uint160 hash; + hasher.Write(id.begin(), 32).Finalize(hash.begin()); + if (pwallet && pwallet->GetCScript(CScriptID(hash), subscript)) { + obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end()))); + } + return obj; + } + + UniValue operator()(const WitnessUnknown& id) const + { + UniValue obj(UniValue::VOBJ); + CScript subscript; + obj.push_back(Pair("iswitness", true)); + obj.push_back(Pair("witness_version", (int)id.version)); + obj.push_back(Pair("witness_program", HexStr(id.program, id.program + id.length))); + return obj; + } }; #endif diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp index c3aade177..6b68f0679 100644 --- a/src/script/ismine.cpp +++ b/src/script/ismine.cpp @@ -61,6 +61,7 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& { case TX_NONSTANDARD: case TX_NULL_DATA: + case TX_WITNESS_UNKNOWN: break; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); diff --git a/src/script/sign.cpp b/src/script/sign.cpp index dc50467d3..ac58b690a 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -79,6 +79,7 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP { case TX_NONSTANDARD: case TX_NULL_DATA: + case TX_WITNESS_UNKNOWN: return false; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); @@ -309,6 +310,7 @@ static Stacks CombineSignatures(const CScript& scriptPubKey, const BaseSignature { case TX_NONSTANDARD: case TX_NULL_DATA: + case TX_WITNESS_UNKNOWN: // Don't know anything about this, assume bigger one is correct: if (sigs1.script.size() >= sigs2.script.size()) return sigs1; diff --git a/src/script/standard.cpp b/src/script/standard.cpp index b6e2232ab..f57f1f61b 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -30,6 +30,7 @@ const char* GetTxnOutputType(txnouttype t) case TX_NULL_DATA: return "nulldata"; case TX_WITNESS_V0_KEYHASH: return "witness_v0_keyhash"; case TX_WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash"; + case TX_WITNESS_UNKNOWN: return "witness_unknown"; } return nullptr; } @@ -75,6 +76,12 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector{(unsigned char)witnessversion}); + vSolutionsRet.push_back(std::move(witnessprogram)); + return true; + } return false; } @@ -198,6 +205,23 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) { addressRet = CScriptID(uint160(vSolutions[0])); return true; + } else if (whichType == TX_WITNESS_V0_KEYHASH) { + WitnessV0KeyHash hash; + std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin()); + addressRet = hash; + return true; + } else if (whichType == TX_WITNESS_V0_SCRIPTHASH) { + WitnessV0ScriptHash hash; + std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin()); + addressRet = hash; + return true; + } else if (whichType == TX_WITNESS_UNKNOWN) { + WitnessUnknown unk; + unk.version = vSolutions[0][0]; + std::copy(vSolutions[1].begin(), vSolutions[1].end(), unk.program); + unk.length = vSolutions[1].size(); + addressRet = unk; + return true; } // Multisig txns have more than one address... return false; @@ -268,6 +292,27 @@ class CScriptVisitor : public boost::static_visitor *script << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL; return true; } + + bool operator()(const WitnessV0KeyHash& id) const + { + script->clear(); + *script << OP_0 << ToByteVector(id); + return true; + } + + bool operator()(const WitnessV0ScriptHash& id) const + { + script->clear(); + *script << OP_0 << ToByteVector(id); + return true; + } + + bool operator()(const WitnessUnknown& id) const + { + script->clear(); + *script << CScript::EncodeOP_N(id.version) << std::vector(id.program, id.program + id.length); + return true; + } }; } // namespace diff --git a/src/script/standard.h b/src/script/standard.h index 8df143a3a..fa07ea88c 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -64,6 +64,7 @@ enum txnouttype TX_NULL_DATA, //!< unspendable OP_RETURN script that carries data TX_WITNESS_V0_SCRIPTHASH, TX_WITNESS_V0_KEYHASH, + TX_WITNESS_UNKNOWN, //!< Only for Witness versions not already defined above }; class CNoDestination { @@ -72,14 +73,42 @@ class CNoDestination { friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; } }; +struct WitnessV0ScriptHash : public uint256 {}; +struct WitnessV0KeyHash : public uint160 {}; + +//! CTxDestination subtype to encode any future Witness version +struct WitnessUnknown +{ + unsigned int version; + unsigned int length; + unsigned char program[40]; + + friend bool operator==(const WitnessUnknown& w1, const WitnessUnknown& w2) { + if (w1.version != w2.version) return false; + if (w1.length != w2.length) return false; + return std::equal(w1.program, w1.program + w1.length, w2.program); + } + + friend bool operator<(const WitnessUnknown& w1, const WitnessUnknown& w2) { + if (w1.version < w2.version) return true; + if (w1.version > w2.version) return false; + if (w1.length < w2.length) return true; + if (w1.length > w2.length) return false; + return std::lexicographical_compare(w1.program, w1.program + w1.length, w2.program, w2.program + w2.length); + } +}; + /** * A txout script template with a specific destination. It is either: * * CNoDestination: no destination set - * * CKeyID: TX_PUBKEYHASH destination - * * CScriptID: TX_SCRIPTHASH destination + * * CKeyID: TX_PUBKEYHASH destination (P2PKH) + * * CScriptID: TX_SCRIPTHASH destination (P2SH) + * * WitnessV0ScriptHash: TX_WITNESS_V0_SCRIPTHASH destination (P2WSH) + * * WitnessV0KeyHash: TX_WITNESS_V0_KEYHASH destination (P2WPKH) + * * WitnessUnknown: TX_WITNESS_UNKNOWN destination (P2W???) * A CTxDestination is the internal data type encoded in a bitcoin address */ -typedef boost::variant CTxDestination; +typedef boost::variant CTxDestination; /** Check whether a CTxDestination is a CNoDestination. */ bool IsValidDestination(const CTxDestination& dest); @@ -104,7 +133,7 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector= 'a' && c <= 'z') { + c = (c - 'a') + 'A'; + } else if (c >= 'A' && c <= 'Z') { + c = (c - 'A') + 'a'; + } + } + destination = DecodeDestination(exp_base58string); + BOOST_CHECK_MESSAGE(IsValidDestination(destination) == try_case_flip, "!IsValid case flipped:" + strTest); + if (IsValidDestination(destination)) { + script = GetScriptForDestination(destination); + BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload)); + } + // Public key must be invalid private key secret.SetString(exp_base58string); BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid pubkey as privkey:" + strTest); @@ -150,6 +166,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen) CScript exp_script(exp_payload.begin(), exp_payload.end()); ExtractDestination(exp_script, dest); std::string address = EncodeDestination(dest); + BOOST_CHECK_EQUAL(address, exp_base58string); } } @@ -157,6 +174,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen) SelectParams(CBaseChainParams::MAIN); } + // Goal: check that base58 parsing code is robust against a variety of corrupted data BOOST_AUTO_TEST_CASE(base58_keys_invalid) { @@ -187,4 +205,3 @@ BOOST_AUTO_TEST_CASE(base58_keys_invalid) BOOST_AUTO_TEST_SUITE_END() - diff --git a/src/test/data/base58_keys_invalid.json b/src/test/data/base58_keys_invalid.json index a088620f1..2056c7491 100644 --- a/src/test/data/base58_keys_invalid.json +++ b/src/test/data/base58_keys_invalid.json @@ -148,5 +148,35 @@ ], [ "2A1q1YsMZowabbvta7kTy2Fd6qN4r5ZCeG3qLpvZBMzCixMUdkN2Y4dHB1wPsZAeVXUGD83MfRED" + ], + [ + "tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty" + ], + [ + "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5" + ], + [ + "BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2" + ], + [ + "bc1rw5uspcuh" + ], + [ + "bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90" + ], + [ + "BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P" + ], + [ + "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7" + ], + [ + "bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du" + ], + [ + "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv" + ], + [ + "bc1gmk9yu" ] ] diff --git a/src/test/data/base58_keys_valid.json b/src/test/data/base58_keys_valid.json index 175b297a1..8418a6002 100644 --- a/src/test/data/base58_keys_valid.json +++ b/src/test/data/base58_keys_valid.json @@ -457,5 +457,77 @@ "isPrivkey": false, "chain": "main" } + ], + [ + "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4", + "0014751e76e8199196d454941c45d1b3a323f1433bd6", + { + "isPrivkey": false, + "chain": "main", + "tryCaseFlip": true + } + ], + [ + "bcrt1qw508d6qejxtdg4y5r3zarvary0c5xw7kygt080", + "0014751e76e8199196d454941c45d1b3a323f1433bd6", + { + "isPrivkey": false, + "chain": "regtest", + "tryCaseFlip": true + } + ], + [ + "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", + "00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262", + { + "isPrivkey": false, + "chain": "test", + "tryCaseFlip": true + } + ], + [ + "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx", + "5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6", + { + "isPrivkey": false, + "chain": "main", + "tryCaseFlip": true + } + ], + [ + "bc1sw50qa3jx3s", + "6002751e", + { + "isPrivkey": false, + "chain": "main", + "tryCaseFlip": true + } + ], + [ + "bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj", + "5210751e76e8199196d454941c45d1b3a323", + { + "isPrivkey": false, + "chain": "main", + "tryCaseFlip": true + } + ], + [ + "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy", + "0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433", + { + "isPrivkey": false, + "chain": "test", + "tryCaseFlip": true + } + ], + [ + "bcrt1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvseswlauz7", + "0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433", + { + "isPrivkey": false, + "chain": "regtest", + "tryCaseFlip": true + } ] ] diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp index 3d17a0dbb..bd2d9ed11 100644 --- a/src/test/script_standard_tests.cpp +++ b/src/test/script_standard_tests.cpp @@ -170,11 +170,6 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_failure) s << OP_RETURN << std::vector({75}) << OP_ADD; BOOST_CHECK(!Solver(s, whichType, solutions)); - // TX_WITNESS with unknown version - s.clear(); - s << OP_1 << ToByteVector(pubkey); - BOOST_CHECK(!Solver(s, whichType, solutions)); - // TX_WITNESS with incorrect program size s.clear(); s << OP_0 << std::vector(19, 0x01); @@ -225,13 +220,29 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination) // TX_WITNESS_V0_KEYHASH s.clear(); - s << OP_0 << ToByteVector(pubkey); - BOOST_CHECK(!ExtractDestination(s, address)); + s << OP_0 << ToByteVector(pubkey.GetID()); + BOOST_CHECK(ExtractDestination(s, address)); + WitnessV0KeyHash keyhash; + CHash160().Write(pubkey.begin(), pubkey.size()).Finalize(keyhash.begin()); + BOOST_CHECK(boost::get(&address) && *boost::get(&address) == keyhash); // TX_WITNESS_V0_SCRIPTHASH s.clear(); - s << OP_0 << ToByteVector(CScriptID(redeemScript)); - BOOST_CHECK(!ExtractDestination(s, address)); + WitnessV0ScriptHash scripthash; + CSHA256().Write(redeemScript.data(), redeemScript.size()).Finalize(scripthash.begin()); + s << OP_0 << ToByteVector(scripthash); + BOOST_CHECK(ExtractDestination(s, address)); + BOOST_CHECK(boost::get(&address) && *boost::get(&address) == scripthash); + + // TX_WITNESS with unknown version + s.clear(); + s << OP_1 << ToByteVector(pubkey); + BOOST_CHECK(ExtractDestination(s, address)); + WitnessUnknown unk; + unk.length = 33; + unk.version = 1; + std::copy(pubkey.begin(), pubkey.end(), unk.program); + BOOST_CHECK(boost::get(&address) && *boost::get(&address) == unk); } BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations) @@ -298,16 +309,6 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations) s.clear(); s << OP_RETURN << std::vector({75}); BOOST_CHECK(!ExtractDestinations(s, whichType, addresses, nRequired)); - - // TX_WITNESS_V0_KEYHASH - s.clear(); - s << OP_0 << ToByteVector(pubkeys[0].GetID()); - BOOST_CHECK(!ExtractDestinations(s, whichType, addresses, nRequired)); - - // TX_WITNESS_V0_SCRIPTHASH - s.clear(); - s << OP_0 << ToByteVector(CScriptID(redeemScript)); - BOOST_CHECK(!ExtractDestinations(s, whichType, addresses, nRequired)); } BOOST_AUTO_TEST_CASE(script_standard_GetScriptFor_) diff --git a/src/utilstrencodings.h b/src/utilstrencodings.h index 192f33fb2..af33f0e5f 100644 --- a/src/utilstrencodings.h +++ b/src/utilstrencodings.h @@ -149,4 +149,28 @@ bool TimingResistantEqual(const T& a, const T& b) */ bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out); +/** Convert from one power-of-2 number base to another. */ +template +bool ConvertBits(O& out, I it, I end) { + size_t acc = 0; + size_t bits = 0; + constexpr size_t maxv = (1 << tobits) - 1; + constexpr size_t max_acc = (1 << (frombits + tobits - 1)) - 1; + while (it != end) { + acc = ((acc << frombits) | *it) & max_acc; + bits += frombits; + while (bits >= tobits) { + bits -= tobits; + out.push_back((acc >> bits) & maxv); + } + ++it; + } + if (pad) { + if (bits) out.push_back((acc << (tobits - bits)) & maxv); + } else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)) { + return false; + } + return true; +} + #endif // BITCOIN_UTILSTRENCODINGS_H diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index ae89b3c0a..8e2b7d04a 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1158,8 +1158,6 @@ class Witnessifier : public boost::static_visitor explicit Witnessifier(CWallet *_pwallet) : pwallet(_pwallet) {} - bool operator()(const CNoDestination &dest) const { return false; } - bool operator()(const CKeyID &keyID) { if (pwallet) { CScript basescript = GetScriptForDestination(keyID); @@ -1203,6 +1201,9 @@ class Witnessifier : public boost::static_visitor } return false; } + + template + bool operator()(const T& dest) { return false; } }; UniValue addwitnessaddress(const JSONRPCRequest& request) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index fd2c1dfbe..594b82a09 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -111,7 +111,26 @@ class CAffectedKeysVisitor : public boost::static_visitor { Process(script); } - void operator()(const CNoDestination &none) {} + void operator()(const WitnessV0ScriptHash& scriptID) + { + CScriptID id; + CRIPEMD160().Write(scriptID.begin(), 32).Finalize(id.begin()); + CScript script; + if (keystore.GetCScript(id, script)) { + Process(script); + } + } + + void operator()(const WitnessV0KeyHash& keyid) + { + CKeyID id(keyid); + if (keystore.HaveKey(id)) { + vKeys.push_back(id); + } + } + + template + void operator()(const X &none) {} }; const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const diff --git a/test/util/data/txcreatemultisig3.json b/test/util/data/txcreatemultisig3.json index 06e093e22..6c5b49d87 100644 --- a/test/util/data/txcreatemultisig3.json +++ b/test/util/data/txcreatemultisig3.json @@ -14,7 +14,11 @@ "scriptPubKey": { "asm": "0 e15a86a23178f433d514dbbce042e87d72662b8b5edcacfd2e37ab7a2d135f05", "hex": "0020e15a86a23178f433d514dbbce042e87d72662b8b5edcacfd2e37ab7a2d135f05", - "type": "witness_v0_scripthash" + "reqSigs": 1, + "type": "witness_v0_scripthash", + "addresses": [ + "bc1qu9dgdg330r6r84g5mw7wqshg04exv2uttmw2elfwx74h5tgntuzs44gyfg" + ] } } ], diff --git a/test/util/data/txcreateoutpubkey2.json b/test/util/data/txcreateoutpubkey2.json index 514472223..4ba5dcb28 100644 --- a/test/util/data/txcreateoutpubkey2.json +++ b/test/util/data/txcreateoutpubkey2.json @@ -14,7 +14,11 @@ "scriptPubKey": { "asm": "0 a2516e770582864a6a56ed21a102044e388c62e3", "hex": "0014a2516e770582864a6a56ed21a102044e388c62e3", - "type": "witness_v0_keyhash" + "reqSigs": 1, + "type": "witness_v0_keyhash", + "addresses": [ + "bc1q5fgkuac9s2ry56jka5s6zqsyfcugcchry5cwu0" + ] } } ], diff --git a/test/util/data/txcreatescript3.json b/test/util/data/txcreatescript3.json index 980da2fb3..31b645921 100644 --- a/test/util/data/txcreatescript3.json +++ b/test/util/data/txcreatescript3.json @@ -14,7 +14,11 @@ "scriptPubKey": { "asm": "0 0bfe935e70c321c7ca3afc75ce0d0ca2f98b5422e008bb31c00c6d7f1f1c0ad6", "hex": "00200bfe935e70c321c7ca3afc75ce0d0ca2f98b5422e008bb31c00c6d7f1f1c0ad6", - "type": "witness_v0_scripthash" + "reqSigs": 1, + "type": "witness_v0_scripthash", + "addresses": [ + "bc1qp0lfxhnscvsu0j36l36uurgv5tuck4pzuqytkvwqp3kh78cupttqyf705v" + ] } } ], From e278f12ca71c2d11916352b91594b068183b605e Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sat, 26 Aug 2017 14:52:03 -0700 Subject: [PATCH 294/382] Support BIP173 in addwitnessaddress --- src/rpc/client.cpp | 1 + src/wallet/rpcwallet.cpp | 59 ++++++++++++++++++++++++++++++---------- 2 files changed, 46 insertions(+), 14 deletions(-) diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 406ad2f6e..f54f24e2a 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -129,6 +129,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "logging", 0, "include" }, { "logging", 1, "exclude" }, { "disconnectnode", 1, "nodeid" }, + { "addwitnessaddress", 1, "p2sh" }, // Echo with conversion (For testing only) { "echojson", 0, "arg0" }, { "echojson", 1, "arg1" }, diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 8e2b7d04a..5d98498a4 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1154,9 +1154,10 @@ class Witnessifier : public boost::static_visitor { public: CWallet * const pwallet; - CScriptID result; + CTxDestination result; + bool already_witness; - explicit Witnessifier(CWallet *_pwallet) : pwallet(_pwallet) {} + explicit Witnessifier(CWallet *_pwallet) : pwallet(_pwallet), already_witness(false) {} bool operator()(const CKeyID &keyID) { if (pwallet) { @@ -1170,9 +1171,7 @@ class Witnessifier : public boost::static_visitor !VerifyScript(sigs.scriptSig, witscript, &sigs.scriptWitness, MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, DummySignatureCreator(pwallet).Checker())) { return false; } - pwallet->AddCScript(witscript); - result = CScriptID(witscript); - return true; + return ExtractDestination(witscript, result); } return false; } @@ -1183,7 +1182,8 @@ class Witnessifier : public boost::static_visitor int witnessversion; std::vector witprog; if (subscript.IsWitnessProgram(witnessversion, witprog)) { - result = scriptID; + ExtractDestination(subscript, result); + already_witness = true; return true; } CScript witscript = GetScriptForWitness(subscript); @@ -1195,13 +1195,25 @@ class Witnessifier : public boost::static_visitor !VerifyScript(sigs.scriptSig, witscript, &sigs.scriptWitness, MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, DummySignatureCreator(pwallet).Checker())) { return false; } - pwallet->AddCScript(witscript); - result = CScriptID(witscript); - return true; + return ExtractDestination(witscript, result); } return false; } + bool operator()(const WitnessV0KeyHash& id) + { + already_witness = true; + result = id; + return true; + } + + bool operator()(const WitnessV0ScriptHash& id) + { + already_witness = true; + result = id; + return true; + } + template bool operator()(const T& dest) { return false; } }; @@ -1213,17 +1225,18 @@ UniValue addwitnessaddress(const JSONRPCRequest& request) return NullUniValue; } - if (request.fHelp || request.params.size() < 1 || request.params.size() > 1) + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { - std::string msg = "addwitnessaddress \"address\"\n" + std::string msg = "addwitnessaddress \"address\" ( p2sh )\n" "\nAdd a witness address for a script (with pubkey or redeemscript known).\n" "It returns the witness script.\n" "\nArguments:\n" "1. \"address\" (string, required) An address known to the wallet\n" + "2. p2sh (bool, optional, default=true) Embed inside P2SH\n" "\nResult:\n" - "\"witnessaddress\", (string) The value of the new address (P2SH of witness script).\n" + "\"witnessaddress\", (string) The value of the new address (P2SH or BIP173).\n" "}\n" ; throw std::runtime_error(msg); @@ -1241,13 +1254,31 @@ UniValue addwitnessaddress(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); } + bool p2sh = true; + if (!request.params[1].isNull()) { + p2sh = request.params[1].get_bool(); + } + Witnessifier w(pwallet); bool ret = boost::apply_visitor(w, dest); if (!ret) { throw JSONRPCError(RPC_WALLET_ERROR, "Public key or redeemscript not known to wallet, or the key is uncompressed"); } - pwallet->SetAddressBook(w.result, "", "receive"); + CScript witprogram = GetScriptForDestination(w.result); + + if (p2sh) { + w.result = CScriptID(witprogram); + } + + if (w.already_witness) { + if (!(dest == w.result)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Cannot convert between witness address types"); + } + } else { + pwallet->AddCScript(witprogram); + pwallet->SetAddressBook(w.result, "", "receive"); + } return EncodeDestination(w.result); } @@ -3200,7 +3231,7 @@ static const CRPCCommand commands[] = { "wallet", "abandontransaction", &abandontransaction, {"txid"} }, { "wallet", "abortrescan", &abortrescan, {} }, { "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","account"} }, - { "wallet", "addwitnessaddress", &addwitnessaddress, {"address"} }, + { "wallet", "addwitnessaddress", &addwitnessaddress, {"address","p2sh"} }, { "wallet", "backupwallet", &backupwallet, {"destination"} }, { "wallet", "bumpfee", &bumpfee, {"txid", "options"} }, { "wallet", "dumpprivkey", &dumpprivkey, {"address"} }, From fd0041aa27cd0571af0ec018605067137d27ce43 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 27 Aug 2017 00:41:46 -0700 Subject: [PATCH 295/382] Use BIP173 addresses in segwit.py test --- test/functional/segwit.py | 40 ++++--- test/functional/test_framework/address.py | 28 +++++ test/functional/test_framework/segwit_addr.py | 107 ++++++++++++++++++ 3 files changed, 162 insertions(+), 13 deletions(-) create mode 100644 test/functional/test_framework/segwit_addr.py diff --git a/test/functional/segwit.py b/test/functional/segwit.py index f465c1683..4888bbd30 100755 --- a/test/functional/segwit.py +++ b/test/functional/segwit.py @@ -7,7 +7,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * from test_framework.mininode import sha256, CTransaction, CTxIn, COutPoint, CTxOut, COIN, ToHex, FromHex -from test_framework.address import script_to_p2sh, key_to_p2pkh +from test_framework.address import script_to_p2sh, key_to_p2pkh, key_to_p2sh_p2wpkh, key_to_p2wpkh, script_to_p2sh_p2wsh, script_to_p2wsh, program_to_witness from test_framework.script import CScript, OP_HASH160, OP_CHECKSIG, OP_0, hash160, OP_EQUAL, OP_DUP, OP_EQUALVERIFY, OP_1, OP_2, OP_CHECKMULTISIG, OP_TRUE from io import BytesIO @@ -33,15 +33,15 @@ def witness_script(use_p2wsh, pubkey): # Return a transaction (in hex) that spends the given utxo to a segwit output, # optionally wrapping the segwit output using P2SH. -def create_witnessprogram(use_p2wsh, utxo, pubkey, encode_p2sh, amount): - pkscript = hex_str_to_bytes(witness_script(use_p2wsh, pubkey)) - if (encode_p2sh): - p2sh_hash = hash160(pkscript) - pkscript = CScript([OP_HASH160, p2sh_hash, OP_EQUAL]) - tx = CTransaction() - tx.vin.append(CTxIn(COutPoint(int(utxo["txid"], 16), utxo["vout"]), b"")) - tx.vout.append(CTxOut(int(amount*COIN), pkscript)) - return ToHex(tx) +def create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount): + if use_p2wsh: + program = CScript([OP_1, hex_str_to_bytes(pubkey), OP_1, OP_CHECKMULTISIG]) + addr = script_to_p2sh_p2wsh(program) if encode_p2sh else script_to_p2wsh(program) + else: + addr = key_to_p2sh_p2wpkh(pubkey) if encode_p2sh else key_to_p2wpkh(pubkey) + if not encode_p2sh: + assert_equal(node.validateaddress(addr)['scriptPubKey'], witness_script(use_p2wsh, pubkey)) + return node.createrawtransaction([utxo], {addr: amount}) # Create a transaction spending a given utxo to a segwit output corresponding # to the given pubkey: use_p2wsh determines whether to use P2WPKH or P2WSH; @@ -49,7 +49,7 @@ def create_witnessprogram(use_p2wsh, utxo, pubkey, encode_p2sh, amount): # sign=True will have the given node sign the transaction. # insert_redeem_script will be added to the scriptSig, if given. def send_to_witness(use_p2wsh, node, utxo, pubkey, encode_p2sh, amount, sign=True, insert_redeem_script=""): - tx_to_witness = create_witnessprogram(use_p2wsh, utxo, pubkey, encode_p2sh, amount) + tx_to_witness = create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount) if (sign): signed = node.signrawtransaction(tx_to_witness) assert("errors" not in signed or len(["errors"]) == 0) @@ -133,8 +133,15 @@ def run_test(self): newaddress = self.nodes[i].getnewaddress() self.pubkey.append(self.nodes[i].validateaddress(newaddress)["pubkey"]) multiaddress = self.nodes[i].addmultisigaddress(1, [self.pubkey[-1]]) - self.nodes[i].addwitnessaddress(newaddress) - self.nodes[i].addwitnessaddress(multiaddress) + multiscript = CScript([OP_1, hex_str_to_bytes(self.pubkey[-1]), OP_1, OP_CHECKMULTISIG]) + p2sh_addr = self.nodes[i].addwitnessaddress(newaddress, True) + bip173_addr = self.nodes[i].addwitnessaddress(newaddress, False) + p2sh_ms_addr = self.nodes[i].addwitnessaddress(multiaddress, True) + bip173_ms_addr = self.nodes[i].addwitnessaddress(multiaddress, False) + assert_equal(p2sh_addr, key_to_p2sh_p2wpkh(self.pubkey[-1])) + assert_equal(bip173_addr, key_to_p2wpkh(self.pubkey[-1])) + assert_equal(p2sh_ms_addr, script_to_p2sh_p2wsh(multiscript)) + assert_equal(bip173_ms_addr, script_to_p2wsh(multiscript)) p2sh_ids.append([]) wit_ids.append([]) for v in range(2): @@ -558,6 +565,13 @@ def run_test(self): solvable_txid.append(self.mine_and_test_listunspent(solvable_after_addwitnessaddress, 1)) self.mine_and_test_listunspent(unseen_anytime, 0) + # Check that createrawtransaction/decoderawtransaction with non-v0 Bech32 works + v1_addr = program_to_witness(1, [3,5]) + v1_tx = self.nodes[0].createrawtransaction([getutxo(spendable_txid[0])],{v1_addr: 1}) + v1_decoded = self.nodes[1].decoderawtransaction(v1_tx) + assert_equal(v1_decoded['vout'][0]['scriptPubKey']['addresses'][0], v1_addr) + assert_equal(v1_decoded['vout'][0]['scriptPubKey']['hex'], "51020305") + # Check that spendable outputs are really spendable self.create_and_mine_tx_from_txids(spendable_txid) diff --git a/test/functional/test_framework/address.py b/test/functional/test_framework/address.py index 180dac197..2e2db5ffb 100644 --- a/test/functional/test_framework/address.py +++ b/test/functional/test_framework/address.py @@ -7,6 +7,8 @@ from .script import hash256, hash160, sha256, CScript, OP_0 from .util import bytes_to_hex_str, hex_str_to_bytes +from . import segwit_addr + chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' def byte_to_base58(b, version): @@ -44,6 +46,32 @@ def script_to_p2sh(script, main = False): script = check_script(script) return scripthash_to_p2sh(hash160(script), main) +def key_to_p2sh_p2wpkh(key, main = False): + key = check_key(key) + p2shscript = CScript([OP_0, hash160(key)]) + return script_to_p2sh(p2shscript, main) + +def program_to_witness(version, program, main = False): + if (type(program) is str): + program = hex_str_to_bytes(program) + assert 0 <= version <= 16 + assert 2 <= len(program) <= 40 + assert version > 0 or len(program) in [20, 32] + return segwit_addr.encode("bc" if main else "bcrt", version, program) + +def script_to_p2wsh(script, main = False): + script = check_script(script) + return program_to_witness(0, sha256(script), main) + +def key_to_p2wpkh(key, main = False): + key = check_key(key) + return program_to_witness(0, hash160(key), main) + +def script_to_p2sh_p2wsh(script, main = False): + script = check_script(script) + p2shscript = CScript([OP_0, sha256(script)]) + return script_to_p2sh(p2shscript, main) + def check_key(key): if (type(key) is str): key = hex_str_to_bytes(key) # Assuming this is hex string diff --git a/test/functional/test_framework/segwit_addr.py b/test/functional/test_framework/segwit_addr.py new file mode 100644 index 000000000..02368e938 --- /dev/null +++ b/test/functional/test_framework/segwit_addr.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017 Pieter Wuille +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Reference implementation for Bech32 and segwit addresses.""" + + +CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" + + +def bech32_polymod(values): + """Internal function that computes the Bech32 checksum.""" + generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3] + chk = 1 + for value in values: + top = chk >> 25 + chk = (chk & 0x1ffffff) << 5 ^ value + for i in range(5): + chk ^= generator[i] if ((top >> i) & 1) else 0 + return chk + + +def bech32_hrp_expand(hrp): + """Expand the HRP into values for checksum computation.""" + return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp] + + +def bech32_verify_checksum(hrp, data): + """Verify a checksum given HRP and converted data characters.""" + return bech32_polymod(bech32_hrp_expand(hrp) + data) == 1 + + +def bech32_create_checksum(hrp, data): + """Compute the checksum values given HRP and data.""" + values = bech32_hrp_expand(hrp) + data + polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ 1 + return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)] + + +def bech32_encode(hrp, data): + """Compute a Bech32 string given HRP and data values.""" + combined = data + bech32_create_checksum(hrp, data) + return hrp + '1' + ''.join([CHARSET[d] for d in combined]) + + +def bech32_decode(bech): + """Validate a Bech32 string, and determine HRP and data.""" + if ((any(ord(x) < 33 or ord(x) > 126 for x in bech)) or + (bech.lower() != bech and bech.upper() != bech)): + return (None, None) + bech = bech.lower() + pos = bech.rfind('1') + if pos < 1 or pos + 7 > len(bech) or len(bech) > 90: + return (None, None) + if not all(x in CHARSET for x in bech[pos+1:]): + return (None, None) + hrp = bech[:pos] + data = [CHARSET.find(x) for x in bech[pos+1:]] + if not bech32_verify_checksum(hrp, data): + return (None, None) + return (hrp, data[:-6]) + + +def convertbits(data, frombits, tobits, pad=True): + """General power-of-2 base conversion.""" + acc = 0 + bits = 0 + ret = [] + maxv = (1 << tobits) - 1 + max_acc = (1 << (frombits + tobits - 1)) - 1 + for value in data: + if value < 0 or (value >> frombits): + return None + acc = ((acc << frombits) | value) & max_acc + bits += frombits + while bits >= tobits: + bits -= tobits + ret.append((acc >> bits) & maxv) + if pad: + if bits: + ret.append((acc << (tobits - bits)) & maxv) + elif bits >= frombits or ((acc << (tobits - bits)) & maxv): + return None + return ret + + +def decode(hrp, addr): + """Decode a segwit address.""" + hrpgot, data = bech32_decode(addr) + if hrpgot != hrp: + return (None, None) + decoded = convertbits(data[1:], 5, 8, False) + if decoded is None or len(decoded) < 2 or len(decoded) > 40: + return (None, None) + if data[0] > 16: + return (None, None) + if data[0] == 0 and len(decoded) != 20 and len(decoded) != 32: + return (None, None) + return (data[0], decoded) + + +def encode(hrp, witver, witprog): + """Encode a segwit address.""" + ret = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5)) + if decode(hrp, ret) == (None, None): + return None + return ret From 06eaca63139ac02abf82b0245b42493c9b383905 Mon Sep 17 00:00:00 2001 From: NicolasDorier Date: Sun, 10 Sep 2017 12:37:38 -0700 Subject: [PATCH 296/382] [RPC] Wallet: test importing of native witness scripts Integration into segwit.py test by Pieter Wuille. --- test/functional/segwit.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/functional/segwit.py b/test/functional/segwit.py index 4888bbd30..de5c8c6c8 100755 --- a/test/functional/segwit.py +++ b/test/functional/segwit.py @@ -584,6 +584,29 @@ def run_test(self): self.nodes[0].importprivkey("cTW5mR5M45vHxXkeChZdtSPozrFwFgmEvTNnanCW6wrqwaCZ1X7K") self.create_and_mine_tx_from_txids(solvable_txid) + # Test that importing native P2WPKH/P2WSH scripts works + for use_p2wsh in [False, True]: + if use_p2wsh: + scriptPubKey = "00203a59f3f56b713fdcf5d1a57357f02c44342cbf306ffe0c4741046837bf90561a" + transaction = "01000000000100e1f505000000002200203a59f3f56b713fdcf5d1a57357f02c44342cbf306ffe0c4741046837bf90561a00000000" + else: + scriptPubKey = "a9142f8c469c2f0084c48e11f998ffbe7efa7549f26d87" + transaction = "01000000000100e1f5050000000017a9142f8c469c2f0084c48e11f998ffbe7efa7549f26d8700000000" + + self.nodes[1].importaddress(scriptPubKey, "", False) + rawtxfund = self.nodes[1].fundrawtransaction(transaction)['hex'] + rawtxfund = self.nodes[1].signrawtransaction(rawtxfund)["hex"] + txid = self.nodes[1].sendrawtransaction(rawtxfund) + + assert_equal(self.nodes[1].gettransaction(txid, True)["txid"], txid) + assert_equal(self.nodes[1].listtransactions("*", 1, 0, True)[0]["txid"], txid) + + # Assert it is properly saved + self.stop_node(1) + self.start_node(1) + assert_equal(self.nodes[1].gettransaction(txid, True)["txid"], txid) + assert_equal(self.nodes[1].listtransactions("*", 1, 0, True)[0]["txid"], txid) + def mine_and_test_listunspent(self, script_list, ismine): utxo = find_unspent(self.nodes[0], 50) tx = CTransaction() From 8213838db2e0625b7a74e5f9b6837e59da6cbcb3 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Fri, 22 Sep 2017 20:48:14 -0600 Subject: [PATCH 297/382] [Qt] tolerate BIP173/bech32 addresses during input validation This eases the during-type validation to allow Bech32 chars. Once the focus has been lost, the address will be properly verified through IsValidDestinationString --- src/qt/bitcoinaddressvalidator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/bitcoinaddressvalidator.cpp b/src/qt/bitcoinaddressvalidator.cpp index 4dd109280..362a71f04 100644 --- a/src/qt/bitcoinaddressvalidator.cpp +++ b/src/qt/bitcoinaddressvalidator.cpp @@ -67,7 +67,7 @@ QValidator::State BitcoinAddressEntryValidator::validate(QString &input, int &po if (((ch >= '0' && ch<='9') || (ch >= 'a' && ch<='z') || (ch >= 'A' && ch<='Z')) && - ch != 'l' && ch != 'I' && ch != '0' && ch != 'O') + ch != 'I' && ch != 'O') // Characters invalid in both Base58 and Bech32 { // Alphanumeric and not a 'forbidden' character } From 6f33d8c79129a049c0f8a0f25e09b310e17dd252 Mon Sep 17 00:00:00 2001 From: Johnson Lau Date: Fri, 29 Sep 2017 14:50:30 +0800 Subject: [PATCH 298/382] Correct typo in comments --- src/clientversion.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clientversion.cpp b/src/clientversion.cpp index d2344ded0..8a4b40883 100644 --- a/src/clientversion.cpp +++ b/src/clientversion.cpp @@ -10,7 +10,7 @@ /** * Name of client reported in the 'version' message. Report the same name - * for both bitcoind and bitcoin-core, to make it harder for attackers to + * for both bitcoind and bitcoin-qt, to make it harder for attackers to * target servers or GUI users specifically. */ const std::string CLIENT_NAME("Satoshi"); From cee28fbc3f0555ca41bdeff4ceec29fa62257ab8 Mon Sep 17 00:00:00 2001 From: Mark Friedenbach Date: Fri, 29 Sep 2017 01:48:43 -0700 Subject: [PATCH 299/382] Add error string for CLEANSTACK script violation, preventing an "unknown error" if the CLEANSTACK error condition is set. --- src/script/script_error.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp index c9d13c92a..6c590f53e 100644 --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -73,6 +73,8 @@ const char* ScriptErrorString(const ScriptError serror) return "Witness version reserved for soft-fork upgrades"; case SCRIPT_ERR_PUBKEYTYPE: return "Public key is neither compressed or uncompressed"; + case SCRIPT_ERR_CLEANSTACK: + return "Extra items left on stack after execution"; case SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH: return "Witness program has incorrect length"; case SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY: From ab5bba7783d2927e513af91e6c89c41bdfa47964 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Avil=C3=A9s?= Date: Fri, 29 Sep 2017 10:54:10 +0200 Subject: [PATCH 300/382] Fix launchctl not being able to stop bitcoind bitcoind should not be launched as daemon from launchctl. Otherwise, the process cannot be stopped from launchctl. --- contrib/init/org.bitcoin.bitcoind.plist | 1 - 1 file changed, 1 deletion(-) diff --git a/contrib/init/org.bitcoin.bitcoind.plist b/contrib/init/org.bitcoin.bitcoind.plist index e94cd4466..95b5342f1 100644 --- a/contrib/init/org.bitcoin.bitcoind.plist +++ b/contrib/init/org.bitcoin.bitcoind.plist @@ -7,7 +7,6 @@ ProgramArguments /usr/local/bin/bitcoind - -daemon RunAtLoad From 619bb05037a55c4b73973965989d199d8cb62f74 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Fri, 29 Sep 2017 14:27:20 +0200 Subject: [PATCH 301/382] Squashed 'src/univalue/' changes from 16a1f7f6e..fe805ea74 fe805ea74 Declare single-argument (non-converting) constructors "explicit" 8a2d6f1e3 Merge pull request #41 from jgarzik/get-obj-map ba341a20d Add getObjMap() helper method. Also, constify checkObject(). ceb119413 Handle .pushKV() and .checkObject() edge cases. 107db9829 Add ::push_back(double) method for feature parity. d41530031 Move one-line implementation of UniValue::read() to header. 52e85b35b Move exception-throwing get_* methods into separate implementation module. dac529675 README.md: update code quotes 3e31dcffb README.md: close code quote d09b8429d Update README.md f1b86edb4 Convert README to markdown style. 1dfe464ef Import UniValue class unit tests from bitcoin project. 0d3e74dd1 operator[] takes size_t index parameter (versus unsigned int) 640158fa2 Private findKey() method becomes size_t clean, and returns bool on failure. 709913585 Merge pull request #36 from ryanofsky/pr/end-str a31231b51 Version 1.0.3 4fd5444d1 Reject unterminated strings 81eba332b Merge pull request #26 from isle2983/pushBackHelpers 36405413e Merge PR #32 from branch 'nul-not-special' of git://github.com/ryanofsky/univalue into merge 89bb07322 Merge pull request #31 from ryanofsky/raw-literals 511008c36 Merge pull request #30 from ryanofsky/test-driver 77974f3a9 Merge pull request #34 from paveljanik/20161116_Wshadow_codepoint a38fcd355 Do not shadow member variable codepoint. fd32d1ab8 Don't require nul-terminated string inputs 0bb1439d0 Support parsing raw literals in UniValue 28876d045 Merge pull request #29 from btcdrak/exportspace 839ccd71f Add test driver for JSONTestSuite 26ef3fff1 Remove trailing whitespace from JSON export cfa0384d6 Convenience wrappers for push_back-ing integer types REVERT: 16a1f7f6e Merge #3: Pull upstream REVERT: daf1285af Merge pull request #2 from jgarzik/master REVERT: f32df99e9 Merge branch '2016_04_unicode' into bitcoin REVERT: 280b191cb Merge remote-tracking branch 'jgarzik/master' into bitcoin REVERT: 2740c4f71 Merge branch '2015_11_escape_plan' into bitcoin git-subtree-dir: src/univalue git-subtree-split: fe805ea74f8919382720b09a905a14e81311b3ad --- Makefile.am | 29 ++- README | 7 - README.md | 32 ++++ configure.ac | 6 +- include/univalue.h | 34 +++- lib/univalue.cpp | 193 ++++--------------- lib/univalue_get.cpp | 147 +++++++++++++++ lib/univalue_read.cpp | 72 ++++--- lib/univalue_utffilter.h | 44 ++--- lib/univalue_write.cpp | 2 - test/.gitignore | 4 + test/fail1.json | 2 +- test/fail42.json | Bin 0 -> 37 bytes test/fail44.json | 1 + test/no_nul.cpp | 8 + test/object.cpp | 395 +++++++++++++++++++++++++++++++++++++++ test/round3.json | 1 + test/round4.json | 1 + test/round5.json | 1 + test/round6.json | 1 + test/round7.json | 1 + test/test_json.cpp | 24 +++ test/unitester.cpp | 7 + 23 files changed, 783 insertions(+), 229 deletions(-) delete mode 100644 README create mode 100644 README.md create mode 100644 lib/univalue_get.cpp create mode 100644 test/fail42.json create mode 100644 test/fail44.json create mode 100644 test/no_nul.cpp create mode 100644 test/object.cpp create mode 100644 test/round3.json create mode 100644 test/round4.json create mode 100644 test/round5.json create mode 100644 test/round6.json create mode 100644 test/round7.json create mode 100644 test/test_json.cpp diff --git a/Makefile.am b/Makefile.am index 6c1ec81e6..e283fc890 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,6 +12,7 @@ pkgconfig_DATA = pc/libunivalue.pc libunivalue_la_SOURCES = \ lib/univalue.cpp \ + lib/univalue_get.cpp \ lib/univalue_read.cpp \ lib/univalue_write.cpp @@ -20,7 +21,7 @@ libunivalue_la_LDFLAGS = \ -no-undefined libunivalue_la_CXXFLAGS = -I$(top_srcdir)/include -TESTS = test/unitester +TESTS = test/object test/unitester test/no_nul GENBIN = gen/gen$(BUILD_EXEEXT) GEN_SRCS = gen/gen.cpp @@ -33,7 +34,7 @@ gen: lib/univalue_escapes.h $(GENBIN) @echo Updating $< $(AM_V_at)$(GENBIN) > lib/univalue_escapes.h -noinst_PROGRAMS = $(TESTS) +noinst_PROGRAMS = $(TESTS) test/test_json TEST_DATA_DIR=test @@ -42,6 +43,21 @@ test_unitester_LDADD = libunivalue.la test_unitester_CXXFLAGS = -I$(top_srcdir)/include -DJSON_TEST_SRC=\"$(srcdir)/$(TEST_DATA_DIR)\" test_unitester_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS) +test_test_json_SOURCES = test/test_json.cpp +test_test_json_LDADD = libunivalue.la +test_test_json_CXXFLAGS = -I$(top_srcdir)/include +test_test_json_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS) + +test_no_nul_SOURCES = test/no_nul.cpp +test_no_nul_LDADD = libunivalue.la +test_no_nul_CXXFLAGS = -I$(top_srcdir)/include +test_no_nul_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS) + +test_object_SOURCES = test/object.cpp +test_object_LDADD = libunivalue.la +test_object_CXXFLAGS = -I$(top_srcdir)/include +test_object_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS) + TEST_FILES = \ $(TEST_DATA_DIR)/fail10.json \ $(TEST_DATA_DIR)/fail11.json \ @@ -77,6 +93,8 @@ TEST_FILES = \ $(TEST_DATA_DIR)/fail39.json \ $(TEST_DATA_DIR)/fail40.json \ $(TEST_DATA_DIR)/fail41.json \ + $(TEST_DATA_DIR)/fail42.json \ + $(TEST_DATA_DIR)/fail44.json \ $(TEST_DATA_DIR)/fail3.json \ $(TEST_DATA_DIR)/fail4.json \ $(TEST_DATA_DIR)/fail5.json \ @@ -88,6 +106,11 @@ TEST_FILES = \ $(TEST_DATA_DIR)/pass2.json \ $(TEST_DATA_DIR)/pass3.json \ $(TEST_DATA_DIR)/round1.json \ - $(TEST_DATA_DIR)/round2.json + $(TEST_DATA_DIR)/round2.json \ + $(TEST_DATA_DIR)/round3.json \ + $(TEST_DATA_DIR)/round4.json \ + $(TEST_DATA_DIR)/round5.json \ + $(TEST_DATA_DIR)/round6.json \ + $(TEST_DATA_DIR)/round7.json EXTRA_DIST=$(TEST_FILES) $(GEN_SRCS) diff --git a/README b/README deleted file mode 100644 index 48167b083..000000000 --- a/README +++ /dev/null @@ -1,7 +0,0 @@ - - UniValue - -A universal value object, with JSON encoding (output) and decoding (input). - -Built as a single dynamic RAII C++ object class, and no templates. - diff --git a/README.md b/README.md new file mode 100644 index 000000000..36aa786a4 --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ + +# UniValue + +## Summary + +A universal value class, with JSON encoding and decoding. + +UniValue is an abstract data type that may be a null, boolean, string, +number, array container, or a key/value dictionary container, nested to +an arbitrary depth. + +This class is aligned with the JSON standard, [RFC +7159](https://tools.ietf.org/html/rfc7159.html). + +## Installation + +This project is a standard GNU +[autotools](https://www.gnu.org/software/automake/manual/html_node/Autotools-Introduction.html) +project. Build and install instructions are available in the `INSTALL` +file provided with GNU autotools. + +``` +$ ./autogen.sh +$ ./configure +$ make +``` + +## Design + +UniValue provides a single dynamic RAII C++ object class, +and minimizes template use (contra json_spirit). + diff --git a/configure.ac b/configure.ac index 93d3ba945..8298332ac 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ m4_define([libunivalue_major_version], [1]) m4_define([libunivalue_minor_version], [1]) -m4_define([libunivalue_micro_version], [2]) -m4_define([libunivalue_interface_age], [2]) +m4_define([libunivalue_micro_version], [3]) +m4_define([libunivalue_interface_age], [3]) # If you need a modifier for the version number. # Normally empty, but can be used to make "fixup" releases. m4_define([libunivalue_extraversion], []) @@ -14,7 +14,7 @@ m4_define([libunivalue_age], [m4_eval(libunivalue_binary_age - libunivalue_inter m4_define([libunivalue_version], [libunivalue_major_version().libunivalue_minor_version().libunivalue_micro_version()libunivalue_extraversion()]) -AC_INIT([univalue], [1.0.2], +AC_INIT([univalue], [1.0.3], [http://github.com/jgarzik/univalue/]) dnl make the compilation flags quiet unless V=1 is used diff --git a/include/univalue.h b/include/univalue.h index e8ce28351..4fd2223b3 100644 --- a/include/univalue.h +++ b/include/univalue.h @@ -7,6 +7,7 @@ #define __UNIVALUE_H__ #include +#include #include #include @@ -69,10 +70,11 @@ class UniValue { size_t size() const { return values.size(); } bool getBool() const { return isTrue(); } - bool checkObject(const std::map& memberTypes); + void getObjMap(std::map& kv) const; + bool checkObject(const std::map& memberTypes) const; const UniValue& operator[](const std::string& key) const; - const UniValue& operator[](unsigned int index) const; - bool exists(const std::string& key) const { return (findKey(key) >= 0); } + const UniValue& operator[](size_t index) const; + bool exists(const std::string& key) const { size_t i; return findKey(key, i); } bool isNull() const { return (typ == VNULL); } bool isTrue() const { return (typ == VBOOL) && (val == "1"); } @@ -92,8 +94,25 @@ class UniValue { std::string s(val_); return push_back(s); } + bool push_back(uint64_t val_) { + UniValue tmpVal(val_); + return push_back(tmpVal); + } + bool push_back(int64_t val_) { + UniValue tmpVal(val_); + return push_back(tmpVal); + } + bool push_back(int val_) { + UniValue tmpVal(val_); + return push_back(tmpVal); + } + bool push_back(double val_) { + UniValue tmpVal(val_); + return push_back(tmpVal); + } bool push_backV(const std::vector& vec); + void __pushKV(const std::string& key, const UniValue& val); bool pushKV(const std::string& key, const UniValue& val); bool pushKV(const std::string& key, const std::string& val_) { UniValue tmpVal(VSTR, val_); @@ -124,9 +143,10 @@ class UniValue { std::string write(unsigned int prettyIndent = 0, unsigned int indentLevel = 0) const; - bool read(const char *raw); + bool read(const char *raw, size_t len); + bool read(const char *raw) { return read(raw, strlen(raw)); } bool read(const std::string& rawStr) { - return read(rawStr.c_str()); + return read(rawStr.data(), rawStr.size()); } private: @@ -135,7 +155,7 @@ class UniValue { std::vector keys; std::vector values; - int findKey(const std::string& key) const; + bool findKey(const std::string& key, size_t& retIdx) const; void writeArray(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const; void writeObject(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const; @@ -240,7 +260,7 @@ enum jtokentype { }; extern enum jtokentype getJsonToken(std::string& tokenVal, - unsigned int& consumed, const char *raw); + unsigned int& consumed, const char *raw, const char *end); extern const char *uvTypeName(UniValue::VType t); static inline bool jsonTokenIsValue(enum jtokentype jtt) diff --git a/lib/univalue.cpp b/lib/univalue.cpp index 5a2860c13..d8ad7c4b9 100644 --- a/lib/univalue.cpp +++ b/lib/univalue.cpp @@ -4,75 +4,12 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include -#include #include -#include #include -#include #include -#include #include "univalue.h" -namespace -{ -static bool ParsePrechecks(const std::string& str) -{ - if (str.empty()) // No empty string allowed - return false; - if (str.size() >= 1 && (json_isspace(str[0]) || json_isspace(str[str.size()-1]))) // No padding allowed - return false; - if (str.size() != strlen(str.c_str())) // No embedded NUL characters allowed - return false; - return true; -} - -bool ParseInt32(const std::string& str, int32_t *out) -{ - if (!ParsePrechecks(str)) - return false; - char *endp = NULL; - errno = 0; // strtol will not set errno if valid - long int n = strtol(str.c_str(), &endp, 10); - if(out) *out = (int32_t)n; - // Note that strtol returns a *long int*, so even if strtol doesn't report a over/underflow - // we still have to check that the returned value is within the range of an *int32_t*. On 64-bit - // platforms the size of these types may be different. - return endp && *endp == 0 && !errno && - n >= std::numeric_limits::min() && - n <= std::numeric_limits::max(); -} - -bool ParseInt64(const std::string& str, int64_t *out) -{ - if (!ParsePrechecks(str)) - return false; - char *endp = NULL; - errno = 0; // strtoll will not set errno if valid - long long int n = strtoll(str.c_str(), &endp, 10); - if(out) *out = (int64_t)n; - // Note that strtoll returns a *long long int*, so even if strtol doesn't report a over/underflow - // we still have to check that the returned value is within the range of an *int64_t*. - return endp && *endp == 0 && !errno && - n >= std::numeric_limits::min() && - n <= std::numeric_limits::max(); -} - -bool ParseDouble(const std::string& str, double *out) -{ - if (!ParsePrechecks(str)) - return false; - if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') // No hexadecimal floats allowed - return false; - std::istringstream text(str); - text.imbue(std::locale::classic()); - double result; - text >> result; - if(out) *out = result; - return text.eof() && !text.fail(); -} -} - using namespace std; const UniValue NullUniValue; @@ -104,7 +41,7 @@ static bool validNumStr(const string& s) { string tokenVal; unsigned int consumed; - enum jtokentype tt = getJsonToken(tokenVal, consumed, s.c_str()); + enum jtokentype tt = getJsonToken(tokenVal, consumed, s.data(), s.data() + s.size()); return (tt == JTOK_NUMBER); } @@ -189,13 +126,22 @@ bool UniValue::push_backV(const std::vector& vec) return true; } +void UniValue::__pushKV(const std::string& key, const UniValue& val_) +{ + keys.push_back(key); + values.push_back(val_); +} + bool UniValue::pushKV(const std::string& key, const UniValue& val_) { if (typ != VOBJ) return false; - keys.push_back(key); - values.push_back(val_); + size_t idx; + if (findKey(key, idx)) + values[idx] = val_; + else + __pushKV(key, val_); return true; } @@ -204,30 +150,43 @@ bool UniValue::pushKVs(const UniValue& obj) if (typ != VOBJ || obj.typ != VOBJ) return false; - for (unsigned int i = 0; i < obj.keys.size(); i++) { - keys.push_back(obj.keys[i]); - values.push_back(obj.values.at(i)); - } + for (size_t i = 0; i < obj.keys.size(); i++) + __pushKV(obj.keys[i], obj.values.at(i)); return true; } -int UniValue::findKey(const std::string& key) const +void UniValue::getObjMap(std::map& kv) const +{ + if (typ != VOBJ) + return; + + kv.clear(); + for (size_t i = 0; i < keys.size(); i++) + kv[keys[i]] = values[i]; +} + +bool UniValue::findKey(const std::string& key, size_t& retIdx) const { - for (unsigned int i = 0; i < keys.size(); i++) { - if (keys[i] == key) - return (int) i; + for (size_t i = 0; i < keys.size(); i++) { + if (keys[i] == key) { + retIdx = i; + return true; + } } - return -1; + return false; } -bool UniValue::checkObject(const std::map& t) +bool UniValue::checkObject(const std::map& t) const { + if (typ != VOBJ) + return false; + for (std::map::const_iterator it = t.begin(); it != t.end(); ++it) { - int idx = findKey(it->first); - if (idx < 0) + size_t idx = 0; + if (!findKey(it->first, idx)) return false; if (values.at(idx).getType() != it->second) @@ -242,14 +201,14 @@ const UniValue& UniValue::operator[](const std::string& key) const if (typ != VOBJ) return NullUniValue; - int index = findKey(key); - if (index < 0) + size_t index = 0; + if (!findKey(key, index)) return NullUniValue; return values.at(index); } -const UniValue& UniValue::operator[](unsigned int index) const +const UniValue& UniValue::operator[](size_t index) const { if (typ != VOBJ && typ != VARR) return NullUniValue; @@ -283,75 +242,3 @@ const UniValue& find_value(const UniValue& obj, const std::string& name) return NullUniValue; } -const std::vector& UniValue::getKeys() const -{ - if (typ != VOBJ) - throw std::runtime_error("JSON value is not an object as expected"); - return keys; -} - -const std::vector& UniValue::getValues() const -{ - if (typ != VOBJ && typ != VARR) - throw std::runtime_error("JSON value is not an object or array as expected"); - return values; -} - -bool UniValue::get_bool() const -{ - if (typ != VBOOL) - throw std::runtime_error("JSON value is not a boolean as expected"); - return getBool(); -} - -const std::string& UniValue::get_str() const -{ - if (typ != VSTR) - throw std::runtime_error("JSON value is not a string as expected"); - return getValStr(); -} - -int UniValue::get_int() const -{ - if (typ != VNUM) - throw std::runtime_error("JSON value is not an integer as expected"); - int32_t retval; - if (!ParseInt32(getValStr(), &retval)) - throw std::runtime_error("JSON integer out of range"); - return retval; -} - -int64_t UniValue::get_int64() const -{ - if (typ != VNUM) - throw std::runtime_error("JSON value is not an integer as expected"); - int64_t retval; - if (!ParseInt64(getValStr(), &retval)) - throw std::runtime_error("JSON integer out of range"); - return retval; -} - -double UniValue::get_real() const -{ - if (typ != VNUM) - throw std::runtime_error("JSON value is not a number as expected"); - double retval; - if (!ParseDouble(getValStr(), &retval)) - throw std::runtime_error("JSON double out of range"); - return retval; -} - -const UniValue& UniValue::get_obj() const -{ - if (typ != VOBJ) - throw std::runtime_error("JSON value is not an object as expected"); - return *this; -} - -const UniValue& UniValue::get_array() const -{ - if (typ != VARR) - throw std::runtime_error("JSON value is not an array as expected"); - return *this; -} - diff --git a/lib/univalue_get.cpp b/lib/univalue_get.cpp new file mode 100644 index 000000000..eabcf2dad --- /dev/null +++ b/lib/univalue_get.cpp @@ -0,0 +1,147 @@ +// Copyright 2014 BitPay Inc. +// Copyright 2015 Bitcoin Core Developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "univalue.h" + +namespace +{ +static bool ParsePrechecks(const std::string& str) +{ + if (str.empty()) // No empty string allowed + return false; + if (str.size() >= 1 && (json_isspace(str[0]) || json_isspace(str[str.size()-1]))) // No padding allowed + return false; + if (str.size() != strlen(str.c_str())) // No embedded NUL characters allowed + return false; + return true; +} + +bool ParseInt32(const std::string& str, int32_t *out) +{ + if (!ParsePrechecks(str)) + return false; + char *endp = NULL; + errno = 0; // strtol will not set errno if valid + long int n = strtol(str.c_str(), &endp, 10); + if(out) *out = (int32_t)n; + // Note that strtol returns a *long int*, so even if strtol doesn't report a over/underflow + // we still have to check that the returned value is within the range of an *int32_t*. On 64-bit + // platforms the size of these types may be different. + return endp && *endp == 0 && !errno && + n >= std::numeric_limits::min() && + n <= std::numeric_limits::max(); +} + +bool ParseInt64(const std::string& str, int64_t *out) +{ + if (!ParsePrechecks(str)) + return false; + char *endp = NULL; + errno = 0; // strtoll will not set errno if valid + long long int n = strtoll(str.c_str(), &endp, 10); + if(out) *out = (int64_t)n; + // Note that strtoll returns a *long long int*, so even if strtol doesn't report a over/underflow + // we still have to check that the returned value is within the range of an *int64_t*. + return endp && *endp == 0 && !errno && + n >= std::numeric_limits::min() && + n <= std::numeric_limits::max(); +} + +bool ParseDouble(const std::string& str, double *out) +{ + if (!ParsePrechecks(str)) + return false; + if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') // No hexadecimal floats allowed + return false; + std::istringstream text(str); + text.imbue(std::locale::classic()); + double result; + text >> result; + if(out) *out = result; + return text.eof() && !text.fail(); +} +} + +const std::vector& UniValue::getKeys() const +{ + if (typ != VOBJ) + throw std::runtime_error("JSON value is not an object as expected"); + return keys; +} + +const std::vector& UniValue::getValues() const +{ + if (typ != VOBJ && typ != VARR) + throw std::runtime_error("JSON value is not an object or array as expected"); + return values; +} + +bool UniValue::get_bool() const +{ + if (typ != VBOOL) + throw std::runtime_error("JSON value is not a boolean as expected"); + return getBool(); +} + +const std::string& UniValue::get_str() const +{ + if (typ != VSTR) + throw std::runtime_error("JSON value is not a string as expected"); + return getValStr(); +} + +int UniValue::get_int() const +{ + if (typ != VNUM) + throw std::runtime_error("JSON value is not an integer as expected"); + int32_t retval; + if (!ParseInt32(getValStr(), &retval)) + throw std::runtime_error("JSON integer out of range"); + return retval; +} + +int64_t UniValue::get_int64() const +{ + if (typ != VNUM) + throw std::runtime_error("JSON value is not an integer as expected"); + int64_t retval; + if (!ParseInt64(getValStr(), &retval)) + throw std::runtime_error("JSON integer out of range"); + return retval; +} + +double UniValue::get_real() const +{ + if (typ != VNUM) + throw std::runtime_error("JSON value is not a number as expected"); + double retval; + if (!ParseDouble(getValStr(), &retval)) + throw std::runtime_error("JSON double out of range"); + return retval; +} + +const UniValue& UniValue::get_obj() const +{ + if (typ != VOBJ) + throw std::runtime_error("JSON value is not an object as expected"); + return *this; +} + +const UniValue& UniValue::get_array() const +{ + if (typ != VARR) + throw std::runtime_error("JSON value is not an array as expected"); + return *this; +} + diff --git a/lib/univalue_read.cpp b/lib/univalue_read.cpp index 95bac6958..ae75cb462 100644 --- a/lib/univalue_read.cpp +++ b/lib/univalue_read.cpp @@ -43,21 +43,21 @@ static const char *hatoui(const char *first, const char *last, } enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, - const char *raw) + const char *raw, const char *end) { tokenVal.clear(); consumed = 0; const char *rawStart = raw; - while ((*raw) && (json_isspace(*raw))) // skip whitespace + while (raw < end && (json_isspace(*raw))) // skip whitespace raw++; - switch (*raw) { - - case 0: + if (raw >= end) return JTOK_NONE; + switch (*raw) { + case '{': raw++; consumed = (raw - rawStart); @@ -127,40 +127,40 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, numStr += *raw; // copy first char raw++; - if ((*first == '-') && (!json_isdigit(*raw))) + if ((*first == '-') && (raw < end) && (!json_isdigit(*raw))) return JTOK_ERR; - while ((*raw) && json_isdigit(*raw)) { // copy digits + while (raw < end && json_isdigit(*raw)) { // copy digits numStr += *raw; raw++; } // part 2: frac - if (*raw == '.') { + if (raw < end && *raw == '.') { numStr += *raw; // copy . raw++; - if (!json_isdigit(*raw)) + if (raw >= end || !json_isdigit(*raw)) return JTOK_ERR; - while ((*raw) && json_isdigit(*raw)) { // copy digits + while (raw < end && json_isdigit(*raw)) { // copy digits numStr += *raw; raw++; } } // part 3: exp - if (*raw == 'e' || *raw == 'E') { + if (raw < end && (*raw == 'e' || *raw == 'E')) { numStr += *raw; // copy E raw++; - if (*raw == '-' || *raw == '+') { // copy +/- + if (raw < end && (*raw == '-' || *raw == '+')) { // copy +/- numStr += *raw; raw++; } - if (!json_isdigit(*raw)) + if (raw >= end || !json_isdigit(*raw)) return JTOK_ERR; - while ((*raw) && json_isdigit(*raw)) { // copy digits + while (raw < end && json_isdigit(*raw)) { // copy digits numStr += *raw; raw++; } @@ -177,13 +177,16 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, string valStr; JSONUTF8StringFilter writer(valStr); - while (*raw) { - if ((unsigned char)*raw < 0x20) + while (true) { + if (raw >= end || (unsigned char)*raw < 0x20) return JTOK_ERR; else if (*raw == '\\') { raw++; // skip backslash + if (raw >= end) + return JTOK_ERR; + switch (*raw) { case '"': writer.push_back('\"'); break; case '\\': writer.push_back('\\'); break; @@ -196,7 +199,8 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, case 'u': { unsigned int codepoint; - if (hatoui(raw + 1, raw + 1 + 4, codepoint) != + if (raw + 1 + 4 >= end || + hatoui(raw + 1, raw + 1 + 4, codepoint) != raw + 1 + 4) return JTOK_ERR; writer.push_back_u(codepoint); @@ -246,7 +250,7 @@ enum expect_bits { #define setExpect(bit) (expectMask |= EXP_##bit) #define clearExpect(bit) (expectMask &= ~EXP_##bit) -bool UniValue::read(const char *raw) +bool UniValue::read(const char *raw, size_t size) { clear(); @@ -257,10 +261,11 @@ bool UniValue::read(const char *raw) unsigned int consumed; enum jtokentype tok = JTOK_NONE; enum jtokentype last_tok = JTOK_NONE; + const char* end = raw + size; do { last_tok = tok; - tok = getJsonToken(tokenVal, consumed, raw); + tok = getJsonToken(tokenVal, consumed, raw, end); if (tok == JTOK_NONE || tok == JTOK_ERR) return false; raw += consumed; @@ -371,9 +376,6 @@ bool UniValue::read(const char *raw) case JTOK_KW_NULL: case JTOK_KW_TRUE: case JTOK_KW_FALSE: { - if (!stack.size()) - return false; - UniValue tmpVal; switch (tok) { case JTOK_KW_NULL: @@ -388,6 +390,11 @@ bool UniValue::read(const char *raw) default: /* impossible */ break; } + if (!stack.size()) { + *this = tmpVal; + break; + } + UniValue *top = stack.back(); top->values.push_back(tmpVal); @@ -396,10 +403,12 @@ bool UniValue::read(const char *raw) } case JTOK_NUMBER: { - if (!stack.size()) - return false; - UniValue tmpVal(VNUM, tokenVal); + if (!stack.size()) { + *this = tmpVal; + break; + } + UniValue *top = stack.back(); top->values.push_back(tmpVal); @@ -408,17 +417,18 @@ bool UniValue::read(const char *raw) } case JTOK_STRING: { - if (!stack.size()) - return false; - - UniValue *top = stack.back(); - if (expect(OBJ_NAME)) { + UniValue *top = stack.back(); top->keys.push_back(tokenVal); clearExpect(OBJ_NAME); setExpect(COLON); } else { UniValue tmpVal(VSTR, tokenVal); + if (!stack.size()) { + *this = tmpVal; + break; + } + UniValue *top = stack.back(); top->values.push_back(tmpVal); } @@ -432,7 +442,7 @@ bool UniValue::read(const char *raw) } while (!stack.empty ()); /* Check that nothing follows the initial construct (parsed above). */ - tok = getJsonToken(tokenVal, consumed, raw); + tok = getJsonToken(tokenVal, consumed, raw, end); if (tok != JTOK_NONE) return false; diff --git a/lib/univalue_utffilter.h b/lib/univalue_utffilter.h index 0e330dce9..20d404300 100644 --- a/lib/univalue_utffilter.h +++ b/lib/univalue_utffilter.h @@ -13,7 +13,7 @@ class JSONUTF8StringFilter { public: - JSONUTF8StringFilter(std::string &s): + explicit JSONUTF8StringFilter(std::string &s): str(s), is_valid(true), codepoint(0), state(0), surpair(0) { } @@ -46,19 +46,19 @@ class JSONUTF8StringFilter } } // Write codepoint directly, possibly collating surrogate pairs - void push_back_u(unsigned int codepoint) + void push_back_u(unsigned int codepoint_) { if (state) // Only accept full codepoints in open state is_valid = false; - if (codepoint >= 0xD800 && codepoint < 0xDC00) { // First half of surrogate pair + if (codepoint_ >= 0xD800 && codepoint_ < 0xDC00) { // First half of surrogate pair if (surpair) // Two subsequent surrogate pair openers - fail is_valid = false; else - surpair = codepoint; - } else if (codepoint >= 0xDC00 && codepoint < 0xE000) { // Second half of surrogate pair + surpair = codepoint_; + } else if (codepoint_ >= 0xDC00 && codepoint_ < 0xE000) { // Second half of surrogate pair if (surpair) { // Open surrogate pair, expect second half // Compute code point from UTF-16 surrogate pair - append_codepoint(0x10000 | ((surpair - 0xD800)<<10) | (codepoint - 0xDC00)); + append_codepoint(0x10000 | ((surpair - 0xD800)<<10) | (codepoint_ - 0xDC00)); surpair = 0; } else // Second half doesn't follow a first half - fail is_valid = false; @@ -66,7 +66,7 @@ class JSONUTF8StringFilter if (surpair) // First half of surrogate pair not followed by second - fail is_valid = false; else - append_codepoint(codepoint); + append_codepoint(codepoint_); } } // Check that we're in a state where the string can be ended @@ -96,22 +96,22 @@ class JSONUTF8StringFilter // Two subsequent \u.... may have to be replaced with one actual codepoint. unsigned int surpair; // First half of open UTF-16 surrogate pair, or 0 - void append_codepoint(unsigned int codepoint) + void append_codepoint(unsigned int codepoint_) { - if (codepoint <= 0x7f) - str.push_back((char)codepoint); - else if (codepoint <= 0x7FF) { - str.push_back((char)(0xC0 | (codepoint >> 6))); - str.push_back((char)(0x80 | (codepoint & 0x3F))); - } else if (codepoint <= 0xFFFF) { - str.push_back((char)(0xE0 | (codepoint >> 12))); - str.push_back((char)(0x80 | ((codepoint >> 6) & 0x3F))); - str.push_back((char)(0x80 | (codepoint & 0x3F))); - } else if (codepoint <= 0x1FFFFF) { - str.push_back((char)(0xF0 | (codepoint >> 18))); - str.push_back((char)(0x80 | ((codepoint >> 12) & 0x3F))); - str.push_back((char)(0x80 | ((codepoint >> 6) & 0x3F))); - str.push_back((char)(0x80 | (codepoint & 0x3F))); + if (codepoint_ <= 0x7f) + str.push_back((char)codepoint_); + else if (codepoint_ <= 0x7FF) { + str.push_back((char)(0xC0 | (codepoint_ >> 6))); + str.push_back((char)(0x80 | (codepoint_ & 0x3F))); + } else if (codepoint_ <= 0xFFFF) { + str.push_back((char)(0xE0 | (codepoint_ >> 12))); + str.push_back((char)(0x80 | ((codepoint_ >> 6) & 0x3F))); + str.push_back((char)(0x80 | (codepoint_ & 0x3F))); + } else if (codepoint_ <= 0x1FFFFF) { + str.push_back((char)(0xF0 | (codepoint_ >> 18))); + str.push_back((char)(0x80 | ((codepoint_ >> 12) & 0x3F))); + str.push_back((char)(0x80 | ((codepoint_ >> 6) & 0x3F))); + str.push_back((char)(0x80 | (codepoint_ & 0x3F))); } } }; diff --git a/lib/univalue_write.cpp b/lib/univalue_write.cpp index cfbdad328..cf2783599 100644 --- a/lib/univalue_write.cpp +++ b/lib/univalue_write.cpp @@ -79,8 +79,6 @@ void UniValue::writeArray(unsigned int prettyIndent, unsigned int indentLevel, s s += values[i].write(prettyIndent, indentLevel + 1); if (i != (values.size() - 1)) { s += ","; - if (prettyIndent) - s += " "; } if (prettyIndent) s += "\n"; diff --git a/test/.gitignore b/test/.gitignore index 3d9347fe7..7b27cf0da 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -1,4 +1,8 @@ + +object unitester +test_json +no_nul *.trs *.log diff --git a/test/fail1.json b/test/fail1.json index 6216b865f..8feb01a6d 100644 --- a/test/fail1.json +++ b/test/fail1.json @@ -1 +1 @@ -"A JSON payload should be an object or array, not a string." \ No newline at end of file +"This is a string that never ends, yes it goes on and on, my friends. diff --git a/test/fail42.json b/test/fail42.json new file mode 100644 index 0000000000000000000000000000000000000000..9c7565adbddf645df5edfbdcd630c7a0f94aa2eb GIT binary patch literal 37 kcma!6N=i-3FG^L&E6q_zsw_!Wie*qrOe;w(LWpny0Pvs;RsaA1 literal 0 HcmV?d00001 diff --git a/test/fail44.json b/test/fail44.json new file mode 100644 index 000000000..80edceddf --- /dev/null +++ b/test/fail44.json @@ -0,0 +1 @@ +"This file ends without a newline or close-quote. \ No newline at end of file diff --git a/test/no_nul.cpp b/test/no_nul.cpp new file mode 100644 index 000000000..83d292200 --- /dev/null +++ b/test/no_nul.cpp @@ -0,0 +1,8 @@ +#include "univalue.h" + +int main (int argc, char *argv[]) +{ + char buf[] = "___[1,2,3]___"; + UniValue val; + return val.read(buf + 3, 7) ? 0 : 1; +} diff --git a/test/object.cpp b/test/object.cpp new file mode 100644 index 000000000..02446292a --- /dev/null +++ b/test/object.cpp @@ -0,0 +1,395 @@ +// Copyright (c) 2014 BitPay Inc. +// Copyright (c) 2014-2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#include +#include +#include +#include + +#define BOOST_FIXTURE_TEST_SUITE(a, b) +#define BOOST_AUTO_TEST_CASE(funcName) void funcName() +#define BOOST_AUTO_TEST_SUITE_END() +#define BOOST_CHECK(expr) assert(expr) +#define BOOST_CHECK_EQUAL(v1, v2) assert((v1) == (v2)) +#define BOOST_CHECK_THROW(stmt, excMatch) { \ + try { \ + (stmt); \ + } catch (excMatch & e) { \ + } catch (...) { \ + assert(0); \ + } \ + } +#define BOOST_CHECK_NO_THROW(stmt) { \ + try { \ + (stmt); \ + } catch (...) { \ + assert(0); \ + } \ + } + +BOOST_FIXTURE_TEST_SUITE(univalue_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(univalue_constructor) +{ + UniValue v1; + BOOST_CHECK(v1.isNull()); + + UniValue v2(UniValue::VSTR); + BOOST_CHECK(v2.isStr()); + + UniValue v3(UniValue::VSTR, "foo"); + BOOST_CHECK(v3.isStr()); + BOOST_CHECK_EQUAL(v3.getValStr(), "foo"); + + UniValue numTest; + BOOST_CHECK(numTest.setNumStr("82")); + BOOST_CHECK(numTest.isNum()); + BOOST_CHECK_EQUAL(numTest.getValStr(), "82"); + + uint64_t vu64 = 82; + UniValue v4(vu64); + BOOST_CHECK(v4.isNum()); + BOOST_CHECK_EQUAL(v4.getValStr(), "82"); + + int64_t vi64 = -82; + UniValue v5(vi64); + BOOST_CHECK(v5.isNum()); + BOOST_CHECK_EQUAL(v5.getValStr(), "-82"); + + int vi = -688; + UniValue v6(vi); + BOOST_CHECK(v6.isNum()); + BOOST_CHECK_EQUAL(v6.getValStr(), "-688"); + + double vd = -7.21; + UniValue v7(vd); + BOOST_CHECK(v7.isNum()); + BOOST_CHECK_EQUAL(v7.getValStr(), "-7.21"); + + std::string vs("yawn"); + UniValue v8(vs); + BOOST_CHECK(v8.isStr()); + BOOST_CHECK_EQUAL(v8.getValStr(), "yawn"); + + const char *vcs = "zappa"; + UniValue v9(vcs); + BOOST_CHECK(v9.isStr()); + BOOST_CHECK_EQUAL(v9.getValStr(), "zappa"); +} + +BOOST_AUTO_TEST_CASE(univalue_typecheck) +{ + UniValue v1; + BOOST_CHECK(v1.setNumStr("1")); + BOOST_CHECK(v1.isNum()); + BOOST_CHECK_THROW(v1.get_bool(), std::runtime_error); + + UniValue v2; + BOOST_CHECK(v2.setBool(true)); + BOOST_CHECK_EQUAL(v2.get_bool(), true); + BOOST_CHECK_THROW(v2.get_int(), std::runtime_error); + + UniValue v3; + BOOST_CHECK(v3.setNumStr("32482348723847471234")); + BOOST_CHECK_THROW(v3.get_int64(), std::runtime_error); + BOOST_CHECK(v3.setNumStr("1000")); + BOOST_CHECK_EQUAL(v3.get_int64(), 1000); + + UniValue v4; + BOOST_CHECK(v4.setNumStr("2147483648")); + BOOST_CHECK_EQUAL(v4.get_int64(), 2147483648); + BOOST_CHECK_THROW(v4.get_int(), std::runtime_error); + BOOST_CHECK(v4.setNumStr("1000")); + BOOST_CHECK_EQUAL(v4.get_int(), 1000); + BOOST_CHECK_THROW(v4.get_str(), std::runtime_error); + BOOST_CHECK_EQUAL(v4.get_real(), 1000); + BOOST_CHECK_THROW(v4.get_array(), std::runtime_error); + BOOST_CHECK_THROW(v4.getKeys(), std::runtime_error); + BOOST_CHECK_THROW(v4.getValues(), std::runtime_error); + BOOST_CHECK_THROW(v4.get_obj(), std::runtime_error); + + UniValue v5; + BOOST_CHECK(v5.read("[true, 10]")); + BOOST_CHECK_NO_THROW(v5.get_array()); + std::vector vals = v5.getValues(); + BOOST_CHECK_THROW(vals[0].get_int(), std::runtime_error); + BOOST_CHECK_EQUAL(vals[0].get_bool(), true); + + BOOST_CHECK_EQUAL(vals[1].get_int(), 10); + BOOST_CHECK_THROW(vals[1].get_bool(), std::runtime_error); +} + +BOOST_AUTO_TEST_CASE(univalue_set) +{ + UniValue v(UniValue::VSTR, "foo"); + v.clear(); + BOOST_CHECK(v.isNull()); + BOOST_CHECK_EQUAL(v.getValStr(), ""); + + BOOST_CHECK(v.setObject()); + BOOST_CHECK(v.isObject()); + BOOST_CHECK_EQUAL(v.size(), 0); + BOOST_CHECK_EQUAL(v.getType(), UniValue::VOBJ); + BOOST_CHECK(v.empty()); + + BOOST_CHECK(v.setArray()); + BOOST_CHECK(v.isArray()); + BOOST_CHECK_EQUAL(v.size(), 0); + + BOOST_CHECK(v.setStr("zum")); + BOOST_CHECK(v.isStr()); + BOOST_CHECK_EQUAL(v.getValStr(), "zum"); + + BOOST_CHECK(v.setFloat(-1.01)); + BOOST_CHECK(v.isNum()); + BOOST_CHECK_EQUAL(v.getValStr(), "-1.01"); + + BOOST_CHECK(v.setInt((int)1023)); + BOOST_CHECK(v.isNum()); + BOOST_CHECK_EQUAL(v.getValStr(), "1023"); + + BOOST_CHECK(v.setInt((int64_t)-1023LL)); + BOOST_CHECK(v.isNum()); + BOOST_CHECK_EQUAL(v.getValStr(), "-1023"); + + BOOST_CHECK(v.setInt((uint64_t)1023ULL)); + BOOST_CHECK(v.isNum()); + BOOST_CHECK_EQUAL(v.getValStr(), "1023"); + + BOOST_CHECK(v.setNumStr("-688")); + BOOST_CHECK(v.isNum()); + BOOST_CHECK_EQUAL(v.getValStr(), "-688"); + + BOOST_CHECK(v.setBool(false)); + BOOST_CHECK_EQUAL(v.isBool(), true); + BOOST_CHECK_EQUAL(v.isTrue(), false); + BOOST_CHECK_EQUAL(v.isFalse(), true); + BOOST_CHECK_EQUAL(v.getBool(), false); + + BOOST_CHECK(v.setBool(true)); + BOOST_CHECK_EQUAL(v.isBool(), true); + BOOST_CHECK_EQUAL(v.isTrue(), true); + BOOST_CHECK_EQUAL(v.isFalse(), false); + BOOST_CHECK_EQUAL(v.getBool(), true); + + BOOST_CHECK(!v.setNumStr("zombocom")); + + BOOST_CHECK(v.setNull()); + BOOST_CHECK(v.isNull()); +} + +BOOST_AUTO_TEST_CASE(univalue_array) +{ + UniValue arr(UniValue::VARR); + + UniValue v((int64_t)1023LL); + BOOST_CHECK(arr.push_back(v)); + + std::string vStr("zippy"); + BOOST_CHECK(arr.push_back(vStr)); + + const char *s = "pippy"; + BOOST_CHECK(arr.push_back(s)); + + std::vector vec; + v.setStr("boing"); + vec.push_back(v); + + v.setStr("going"); + vec.push_back(v); + + BOOST_CHECK(arr.push_backV(vec)); + + BOOST_CHECK(arr.push_back((uint64_t) 400ULL)); + BOOST_CHECK(arr.push_back((int64_t) -400LL)); + BOOST_CHECK(arr.push_back((int) -401)); + BOOST_CHECK(arr.push_back(-40.1)); + + BOOST_CHECK_EQUAL(arr.empty(), false); + BOOST_CHECK_EQUAL(arr.size(), 9); + + BOOST_CHECK_EQUAL(arr[0].getValStr(), "1023"); + BOOST_CHECK_EQUAL(arr[1].getValStr(), "zippy"); + BOOST_CHECK_EQUAL(arr[2].getValStr(), "pippy"); + BOOST_CHECK_EQUAL(arr[3].getValStr(), "boing"); + BOOST_CHECK_EQUAL(arr[4].getValStr(), "going"); + BOOST_CHECK_EQUAL(arr[5].getValStr(), "400"); + BOOST_CHECK_EQUAL(arr[6].getValStr(), "-400"); + BOOST_CHECK_EQUAL(arr[7].getValStr(), "-401"); + BOOST_CHECK_EQUAL(arr[8].getValStr(), "-40.1"); + + BOOST_CHECK_EQUAL(arr[999].getValStr(), ""); + + arr.clear(); + BOOST_CHECK(arr.empty()); + BOOST_CHECK_EQUAL(arr.size(), 0); +} + +BOOST_AUTO_TEST_CASE(univalue_object) +{ + UniValue obj(UniValue::VOBJ); + std::string strKey, strVal; + UniValue v; + + strKey = "age"; + v.setInt(100); + BOOST_CHECK(obj.pushKV(strKey, v)); + + strKey = "first"; + strVal = "John"; + BOOST_CHECK(obj.pushKV(strKey, strVal)); + + strKey = "last"; + const char *cVal = "Smith"; + BOOST_CHECK(obj.pushKV(strKey, cVal)); + + strKey = "distance"; + BOOST_CHECK(obj.pushKV(strKey, (int64_t) 25)); + + strKey = "time"; + BOOST_CHECK(obj.pushKV(strKey, (uint64_t) 3600)); + + strKey = "calories"; + BOOST_CHECK(obj.pushKV(strKey, (int) 12)); + + strKey = "temperature"; + BOOST_CHECK(obj.pushKV(strKey, (double) 90.012)); + + UniValue obj2(UniValue::VOBJ); + BOOST_CHECK(obj2.pushKV("cat1", 9000)); + BOOST_CHECK(obj2.pushKV("cat2", 12345)); + + BOOST_CHECK(obj.pushKVs(obj2)); + + BOOST_CHECK_EQUAL(obj.empty(), false); + BOOST_CHECK_EQUAL(obj.size(), 9); + + BOOST_CHECK_EQUAL(obj["age"].getValStr(), "100"); + BOOST_CHECK_EQUAL(obj["first"].getValStr(), "John"); + BOOST_CHECK_EQUAL(obj["last"].getValStr(), "Smith"); + BOOST_CHECK_EQUAL(obj["distance"].getValStr(), "25"); + BOOST_CHECK_EQUAL(obj["time"].getValStr(), "3600"); + BOOST_CHECK_EQUAL(obj["calories"].getValStr(), "12"); + BOOST_CHECK_EQUAL(obj["temperature"].getValStr(), "90.012"); + BOOST_CHECK_EQUAL(obj["cat1"].getValStr(), "9000"); + BOOST_CHECK_EQUAL(obj["cat2"].getValStr(), "12345"); + + BOOST_CHECK_EQUAL(obj["nyuknyuknyuk"].getValStr(), ""); + + BOOST_CHECK(obj.exists("age")); + BOOST_CHECK(obj.exists("first")); + BOOST_CHECK(obj.exists("last")); + BOOST_CHECK(obj.exists("distance")); + BOOST_CHECK(obj.exists("time")); + BOOST_CHECK(obj.exists("calories")); + BOOST_CHECK(obj.exists("temperature")); + BOOST_CHECK(obj.exists("cat1")); + BOOST_CHECK(obj.exists("cat2")); + + BOOST_CHECK(!obj.exists("nyuknyuknyuk")); + + std::map objTypes; + objTypes["age"] = UniValue::VNUM; + objTypes["first"] = UniValue::VSTR; + objTypes["last"] = UniValue::VSTR; + objTypes["distance"] = UniValue::VNUM; + objTypes["time"] = UniValue::VNUM; + objTypes["calories"] = UniValue::VNUM; + objTypes["temperature"] = UniValue::VNUM; + objTypes["cat1"] = UniValue::VNUM; + objTypes["cat2"] = UniValue::VNUM; + BOOST_CHECK(obj.checkObject(objTypes)); + + objTypes["cat2"] = UniValue::VSTR; + BOOST_CHECK(!obj.checkObject(objTypes)); + + obj.clear(); + BOOST_CHECK(obj.empty()); + BOOST_CHECK_EQUAL(obj.size(), 0); + BOOST_CHECK_EQUAL(obj.getType(), UniValue::VNULL); + + BOOST_CHECK_EQUAL(obj.setObject(), true); + UniValue uv; + uv.setInt(42); + obj.__pushKV("age", uv); + BOOST_CHECK_EQUAL(obj.size(), 1); + BOOST_CHECK_EQUAL(obj["age"].getValStr(), "42"); + + uv.setInt(43); + obj.pushKV("age", uv); + BOOST_CHECK_EQUAL(obj.size(), 1); + BOOST_CHECK_EQUAL(obj["age"].getValStr(), "43"); + + obj.pushKV("name", "foo bar"); + + std::map kv; + obj.getObjMap(kv); + BOOST_CHECK_EQUAL(kv["age"].getValStr(), "43"); + BOOST_CHECK_EQUAL(kv["name"].getValStr(), "foo bar"); + +} + +static const char *json1 = +"[1.10000000,{\"key1\":\"str\\u0000\",\"key2\":800,\"key3\":{\"name\":\"martian http://test.com\"}}]"; + +BOOST_AUTO_TEST_CASE(univalue_readwrite) +{ + UniValue v; + BOOST_CHECK(v.read(json1)); + + std::string strJson1(json1); + BOOST_CHECK(v.read(strJson1)); + + BOOST_CHECK(v.isArray()); + BOOST_CHECK_EQUAL(v.size(), 2); + + BOOST_CHECK_EQUAL(v[0].getValStr(), "1.10000000"); + + UniValue obj = v[1]; + BOOST_CHECK(obj.isObject()); + BOOST_CHECK_EQUAL(obj.size(), 3); + + BOOST_CHECK(obj["key1"].isStr()); + std::string correctValue("str"); + correctValue.push_back('\0'); + BOOST_CHECK_EQUAL(obj["key1"].getValStr(), correctValue); + BOOST_CHECK(obj["key2"].isNum()); + BOOST_CHECK_EQUAL(obj["key2"].getValStr(), "800"); + BOOST_CHECK(obj["key3"].isObject()); + + BOOST_CHECK_EQUAL(strJson1, v.write()); + + /* Check for (correctly reporting) a parsing error if the initial + JSON construct is followed by more stuff. Note that whitespace + is, of course, exempt. */ + + BOOST_CHECK(v.read(" {}\n ")); + BOOST_CHECK(v.isObject()); + BOOST_CHECK(v.read(" []\n ")); + BOOST_CHECK(v.isArray()); + + BOOST_CHECK(!v.read("@{}")); + BOOST_CHECK(!v.read("{} garbage")); + BOOST_CHECK(!v.read("[]{}")); + BOOST_CHECK(!v.read("{}[]")); + BOOST_CHECK(!v.read("{} 42")); +} + +BOOST_AUTO_TEST_SUITE_END() + +int main (int argc, char *argv[]) +{ + univalue_constructor(); + univalue_typecheck(); + univalue_set(); + univalue_array(); + univalue_object(); + univalue_readwrite(); + return 0; +} + diff --git a/test/round3.json b/test/round3.json new file mode 100644 index 000000000..7182dc2f9 --- /dev/null +++ b/test/round3.json @@ -0,0 +1 @@ +"abcdefghijklmnopqrstuvwxyz" diff --git a/test/round4.json b/test/round4.json new file mode 100644 index 000000000..7f8f011eb --- /dev/null +++ b/test/round4.json @@ -0,0 +1 @@ +7 diff --git a/test/round5.json b/test/round5.json new file mode 100644 index 000000000..27ba77dda --- /dev/null +++ b/test/round5.json @@ -0,0 +1 @@ +true diff --git a/test/round6.json b/test/round6.json new file mode 100644 index 000000000..c508d5366 --- /dev/null +++ b/test/round6.json @@ -0,0 +1 @@ +false diff --git a/test/round7.json b/test/round7.json new file mode 100644 index 000000000..19765bd50 --- /dev/null +++ b/test/round7.json @@ -0,0 +1 @@ +null diff --git a/test/test_json.cpp b/test/test_json.cpp new file mode 100644 index 000000000..2943bae2b --- /dev/null +++ b/test/test_json.cpp @@ -0,0 +1,24 @@ +// Test program that can be called by the JSON test suite at +// https://github.com/nst/JSONTestSuite. +// +// It reads JSON input from stdin and exits with code 0 if it can be parsed +// successfully. It also pretty prints the parsed JSON value to stdout. + +#include +#include +#include "univalue.h" + +using namespace std; + +int main (int argc, char *argv[]) +{ + UniValue val; + if (val.read(string(istreambuf_iterator(cin), + istreambuf_iterator()))) { + cout << val.write(1 /* prettyIndent */, 4 /* indentLevel */) << endl; + return 0; + } else { + cerr << "JSON Parse Error." << endl; + return 1; + } +} diff --git a/test/unitester.cpp b/test/unitester.cpp index 05f3842cd..2c37794a4 100644 --- a/test/unitester.cpp +++ b/test/unitester.cpp @@ -113,6 +113,8 @@ static const char *filenames[] = { "fail39.json", // invalid unicode: only second half of surrogate pair "fail40.json", // invalid unicode: broken UTF-8 "fail41.json", // invalid unicode: unfinished UTF-8 + "fail42.json", // valid json with garbage following a nul byte + "fail44.json", // unterminated string "fail3.json", "fail4.json", // extra comma "fail5.json", @@ -125,6 +127,11 @@ static const char *filenames[] = { "pass3.json", "round1.json", // round-trip test "round2.json", // unicode + "round3.json", // bare string + "round4.json", // bare number + "round5.json", // bare true + "round6.json", // bare false + "round7.json", // bare null }; // Test \u handling From fd86f998fcfd25d823d67a2920814e22445655f9 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Fri, 29 Sep 2017 16:00:20 +0200 Subject: [PATCH 302/382] Squashed 'src/secp256k1/' changes from 84973d393..0b7024185 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 0b7024185 Merge #474: Fix header guards using reserved identifiers ab1f89f00 Merge #478: Fixed multiple typos 8c7ea22d5 Fixed multiple typos abe2d3e84 Fix header guards using reserved identifiers f532bdc9f Merge #459: Add pubkey prefix constants to include/secp256k1.h cac7c5559 Merge #470: Fix wnaf_const documentation 768514bac Fix wnaf_const documentation with respect to return value and number of words set b8c26a399 Merge #458: Fix typo in API documentation 817fb2013 Merge #440: Fix typos 12230f90e Merge #468: Remove redundant conditional expression 2e1ccdca0 Remove redundant conditional expression bc61b91ac add pubkey prefix constants to include/secp256k1.h b0452e664 Fix typo in API documentation 4c0f32ed5 Fix typo: "Agressive" → "Aggressive" 73aca8364 Fix typo: "exectured" → "executed" git-subtree-dir: src/secp256k1 git-subtree-split: 0b7024185045a49a1a6a4c5615bf31c94f63d9c4 --- contrib/lax_der_parsing.h | 10 +++++----- contrib/lax_der_privatekey_parsing.h | 10 +++++----- include/secp256k1.h | 23 +++++++++++++++-------- include/secp256k1_ecdh.h | 16 ++++++++-------- include/secp256k1_recovery.h | 16 ++++++++-------- sage/group_prover.sage | 4 ++-- src/asm/field_10x26_arm.s | 4 ++-- src/basic-config.h | 9 +++++---- src/bench.h | 6 +++--- src/ecdsa.h | 6 +++--- src/ecdsa_impl.h | 8 +++----- src/eckey.h | 6 +++--- src/eckey_impl.h | 17 +++++++++-------- src/ecmult.h | 6 +++--- src/ecmult_const.h | 6 +++--- src/ecmult_const_impl.h | 13 +++++++------ src/ecmult_gen.h | 6 +++--- src/ecmult_gen_impl.h | 6 +++--- src/ecmult_impl.h | 6 +++--- src/field.h | 6 +++--- src/field_10x26.h | 7 ++++--- src/field_10x26_impl.h | 6 +++--- src/field_5x52.h | 6 +++--- src/field_5x52_asm_impl.h | 6 +++--- src/field_5x52_impl.h | 6 +++--- src/field_5x52_int128_impl.h | 6 +++--- src/field_impl.h | 6 +++--- src/group.h | 6 +++--- src/group_impl.h | 6 +++--- src/hash.h | 6 +++--- src/hash_impl.h | 6 +++--- src/modules/ecdh/main_impl.h | 6 +++--- src/modules/ecdh/tests_impl.h | 6 +++--- src/modules/recovery/main_impl.h | 6 +++--- src/modules/recovery/tests_impl.h | 6 +++--- src/num.h | 6 +++--- src/num_gmp.h | 6 +++--- src/num_gmp_impl.h | 6 +++--- src/num_impl.h | 6 +++--- src/scalar.h | 6 +++--- src/scalar_4x64.h | 6 +++--- src/scalar_4x64_impl.h | 6 +++--- src/scalar_8x32.h | 6 +++--- src/scalar_8x32_impl.h | 6 +++--- src/scalar_impl.h | 6 +++--- src/scalar_low.h | 6 +++--- src/scalar_low_impl.h | 6 +++--- src/testrand.h | 6 +++--- src/testrand_impl.h | 6 +++--- src/util.h | 6 +++--- 50 files changed, 187 insertions(+), 178 deletions(-) diff --git a/contrib/lax_der_parsing.h b/contrib/lax_der_parsing.h index 6d27871a7..7eaf63bf6 100644 --- a/contrib/lax_der_parsing.h +++ b/contrib/lax_der_parsing.h @@ -48,14 +48,14 @@ * 8.3.1. */ -#ifndef _SECP256K1_CONTRIB_LAX_DER_PARSING_H_ -#define _SECP256K1_CONTRIB_LAX_DER_PARSING_H_ +#ifndef SECP256K1_CONTRIB_LAX_DER_PARSING_H +#define SECP256K1_CONTRIB_LAX_DER_PARSING_H #include -# ifdef __cplusplus +#ifdef __cplusplus extern "C" { -# endif +#endif /** Parse a signature in "lax DER" format * @@ -88,4 +88,4 @@ int ecdsa_signature_parse_der_lax( } #endif -#endif +#endif /* SECP256K1_CONTRIB_LAX_DER_PARSING_H */ diff --git a/contrib/lax_der_privatekey_parsing.h b/contrib/lax_der_privatekey_parsing.h index 2fd088f8a..fece261fb 100644 --- a/contrib/lax_der_privatekey_parsing.h +++ b/contrib/lax_der_privatekey_parsing.h @@ -25,14 +25,14 @@ * library are sufficient. */ -#ifndef _SECP256K1_CONTRIB_BER_PRIVATEKEY_H_ -#define _SECP256K1_CONTRIB_BER_PRIVATEKEY_H_ +#ifndef SECP256K1_CONTRIB_BER_PRIVATEKEY_H +#define SECP256K1_CONTRIB_BER_PRIVATEKEY_H #include -# ifdef __cplusplus +#ifdef __cplusplus extern "C" { -# endif +#endif /** Export a private key in DER format. * @@ -87,4 +87,4 @@ SECP256K1_WARN_UNUSED_RESULT int ec_privkey_import_der( } #endif -#endif +#endif /* SECP256K1_CONTRIB_BER_PRIVATEKEY_H */ diff --git a/include/secp256k1.h b/include/secp256k1.h index fc4c5cefb..3e9c098d1 100644 --- a/include/secp256k1.h +++ b/include/secp256k1.h @@ -1,9 +1,9 @@ -#ifndef _SECP256K1_ -# define _SECP256K1_ +#ifndef SECP256K1_H +#define SECP256K1_H -# ifdef __cplusplus +#ifdef __cplusplus extern "C" { -# endif +#endif #include @@ -61,7 +61,7 @@ typedef struct { * however guaranteed to be 64 bytes in size, and can be safely copied/moved. * If you need to convert to a format suitable for storage, transmission, or * comparison, use the secp256k1_ecdsa_signature_serialize_* and - * secp256k1_ecdsa_signature_serialize_* functions. + * secp256k1_ecdsa_signature_parse_* functions. */ typedef struct { unsigned char data[64]; @@ -159,6 +159,13 @@ typedef int (*secp256k1_nonce_function)( #define SECP256K1_EC_COMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION) #define SECP256K1_EC_UNCOMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION) +/** Prefix byte used to tag various encoded curvepoints for specific purposes */ +#define SECP256K1_TAG_PUBKEY_EVEN 0x02 +#define SECP256K1_TAG_PUBKEY_ODD 0x03 +#define SECP256K1_TAG_PUBKEY_UNCOMPRESSED 0x04 +#define SECP256K1_TAG_PUBKEY_HYBRID_EVEN 0x06 +#define SECP256K1_TAG_PUBKEY_HYBRID_ODD 0x07 + /** Create a secp256k1 context object. * * Returns: a newly created context object. @@ -607,8 +614,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_combine( size_t n ) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); -# ifdef __cplusplus +#ifdef __cplusplus } -# endif - #endif + +#endif /* SECP256K1_H */ diff --git a/include/secp256k1_ecdh.h b/include/secp256k1_ecdh.h index 4b84d7a96..88492dc1a 100644 --- a/include/secp256k1_ecdh.h +++ b/include/secp256k1_ecdh.h @@ -1,11 +1,11 @@ -#ifndef _SECP256K1_ECDH_ -# define _SECP256K1_ECDH_ +#ifndef SECP256K1_ECDH_H +#define SECP256K1_ECDH_H -# include "secp256k1.h" +#include "secp256k1.h" -# ifdef __cplusplus +#ifdef __cplusplus extern "C" { -# endif +#endif /** Compute an EC Diffie-Hellman secret in constant time * Returns: 1: exponentiation was successful @@ -24,8 +24,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdh( const unsigned char *privkey ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); -# ifdef __cplusplus +#ifdef __cplusplus } -# endif - #endif + +#endif /* SECP256K1_ECDH_H */ diff --git a/include/secp256k1_recovery.h b/include/secp256k1_recovery.h index 055379725..cf6c5ed7f 100644 --- a/include/secp256k1_recovery.h +++ b/include/secp256k1_recovery.h @@ -1,11 +1,11 @@ -#ifndef _SECP256K1_RECOVERY_ -# define _SECP256K1_RECOVERY_ +#ifndef SECP256K1_RECOVERY_H +#define SECP256K1_RECOVERY_H -# include "secp256k1.h" +#include "secp256k1.h" -# ifdef __cplusplus +#ifdef __cplusplus extern "C" { -# endif +#endif /** Opaque data structured that holds a parsed ECDSA signature, * supporting pubkey recovery. @@ -103,8 +103,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_recover( const unsigned char *msg32 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); -# ifdef __cplusplus +#ifdef __cplusplus } -# endif - #endif + +#endif /* SECP256K1_RECOVERY_H */ diff --git a/sage/group_prover.sage b/sage/group_prover.sage index ab580c5b2..8521f0799 100644 --- a/sage/group_prover.sage +++ b/sage/group_prover.sage @@ -3,7 +3,7 @@ # to independently set assumptions on input or intermediary variables. # # The general approach is: -# * A constraint is a tuple of two sets of of symbolic expressions: +# * A constraint is a tuple of two sets of symbolic expressions: # the first of which are required to evaluate to zero, the second of which # are required to evaluate to nonzero. # - A constraint is said to be conflicting if any of its nonzero expressions @@ -17,7 +17,7 @@ # - A constraint describing the requirements of the law, called "require" # * Implementations are transliterated into functions that operate as well on # algebraic input points, and are called once per combination of branches -# exectured. Each execution returns: +# executed. Each execution returns: # - A constraint describing the assumptions this implementation requires # (such as Z1=1), called "assumeFormula" # - A constraint describing the assumptions this specific branch requires, diff --git a/src/asm/field_10x26_arm.s b/src/asm/field_10x26_arm.s index 5df561f2f..5a9cc3ffc 100644 --- a/src/asm/field_10x26_arm.s +++ b/src/asm/field_10x26_arm.s @@ -11,7 +11,7 @@ Note: - To avoid unnecessary loads and make use of available registers, two 'passes' have every time been interleaved, with the odd passes accumulating c' and d' - which will be added to c and d respectively in the the even passes + which will be added to c and d respectively in the even passes */ @@ -23,7 +23,7 @@ Note: .eabi_attribute 10, 0 @ Tag_FP_arch = none .eabi_attribute 24, 1 @ Tag_ABI_align_needed = 8-byte .eabi_attribute 25, 1 @ Tag_ABI_align_preserved = 8-byte, except leaf SP - .eabi_attribute 30, 2 @ Tag_ABI_optimization_goals = Agressive Speed + .eabi_attribute 30, 2 @ Tag_ABI_optimization_goals = Aggressive Speed .eabi_attribute 34, 1 @ Tag_CPU_unaligned_access = v6 .text diff --git a/src/basic-config.h b/src/basic-config.h index c4c16eb7c..fc588061c 100644 --- a/src/basic-config.h +++ b/src/basic-config.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_BASIC_CONFIG_ -#define _SECP256K1_BASIC_CONFIG_ +#ifndef SECP256K1_BASIC_CONFIG_H +#define SECP256K1_BASIC_CONFIG_H #ifdef USE_BASIC_CONFIG @@ -28,5 +28,6 @@ #define USE_FIELD_10X26 1 #define USE_SCALAR_8X32 1 -#endif // USE_BASIC_CONFIG -#endif // _SECP256K1_BASIC_CONFIG_ +#endif /* USE_BASIC_CONFIG */ + +#endif /* SECP256K1_BASIC_CONFIG_H */ diff --git a/src/bench.h b/src/bench.h index d67f08a42..d5ebe0130 100644 --- a/src/bench.h +++ b/src/bench.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_BENCH_H_ -#define _SECP256K1_BENCH_H_ +#ifndef SECP256K1_BENCH_H +#define SECP256K1_BENCH_H #include #include @@ -63,4 +63,4 @@ void run_benchmark(char *name, void (*benchmark)(void*), void (*setup)(void*), v printf("us\n"); } -#endif +#endif /* SECP256K1_BENCH_H */ diff --git a/src/ecdsa.h b/src/ecdsa.h index 54ae101b9..80590c7cc 100644 --- a/src/ecdsa.h +++ b/src/ecdsa.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECDSA_ -#define _SECP256K1_ECDSA_ +#ifndef SECP256K1_ECDSA_H +#define SECP256K1_ECDSA_H #include @@ -18,4 +18,4 @@ static int secp256k1_ecdsa_sig_serialize(unsigned char *sig, size_t *size, const static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const secp256k1_scalar* r, const secp256k1_scalar* s, const secp256k1_ge *pubkey, const secp256k1_scalar *message); static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, secp256k1_scalar* r, secp256k1_scalar* s, const secp256k1_scalar *seckey, const secp256k1_scalar *message, const secp256k1_scalar *nonce, int *recid); -#endif +#endif /* SECP256K1_ECDSA_H */ diff --git a/src/ecdsa_impl.h b/src/ecdsa_impl.h index 453bb1188..c3400042d 100644 --- a/src/ecdsa_impl.h +++ b/src/ecdsa_impl.h @@ -5,8 +5,8 @@ **********************************************************************/ -#ifndef _SECP256K1_ECDSA_IMPL_H_ -#define _SECP256K1_ECDSA_IMPL_H_ +#ifndef SECP256K1_ECDSA_IMPL_H +#define SECP256K1_ECDSA_IMPL_H #include "scalar.h" #include "field.h" @@ -81,8 +81,6 @@ static int secp256k1_der_read_len(const unsigned char **sigp, const unsigned cha return -1; } while (lenleft > 0) { - if ((ret >> ((sizeof(size_t) - 1) * 8)) != 0) { - } ret = (ret << 8) | **sigp; if (ret + lenleft > (size_t)(sigend - *sigp)) { /* Result exceeds the length of the passed array. */ @@ -312,4 +310,4 @@ static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, sec return 1; } -#endif +#endif /* SECP256K1_ECDSA_IMPL_H */ diff --git a/src/eckey.h b/src/eckey.h index 42739a3be..b621f1e6c 100644 --- a/src/eckey.h +++ b/src/eckey.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECKEY_ -#define _SECP256K1_ECKEY_ +#ifndef SECP256K1_ECKEY_H +#define SECP256K1_ECKEY_H #include @@ -22,4 +22,4 @@ static int secp256k1_eckey_pubkey_tweak_add(const secp256k1_ecmult_context *ctx, static int secp256k1_eckey_privkey_tweak_mul(secp256k1_scalar *key, const secp256k1_scalar *tweak); static int secp256k1_eckey_pubkey_tweak_mul(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak); -#endif +#endif /* SECP256K1_ECKEY_H */ diff --git a/src/eckey_impl.h b/src/eckey_impl.h index ce38071ac..1ab9a68ec 100644 --- a/src/eckey_impl.h +++ b/src/eckey_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECKEY_IMPL_H_ -#define _SECP256K1_ECKEY_IMPL_H_ +#ifndef SECP256K1_ECKEY_IMPL_H +#define SECP256K1_ECKEY_IMPL_H #include "eckey.h" @@ -15,16 +15,17 @@ #include "ecmult_gen.h" static int secp256k1_eckey_pubkey_parse(secp256k1_ge *elem, const unsigned char *pub, size_t size) { - if (size == 33 && (pub[0] == 0x02 || pub[0] == 0x03)) { + if (size == 33 && (pub[0] == SECP256K1_TAG_PUBKEY_EVEN || pub[0] == SECP256K1_TAG_PUBKEY_ODD)) { secp256k1_fe x; - return secp256k1_fe_set_b32(&x, pub+1) && secp256k1_ge_set_xo_var(elem, &x, pub[0] == 0x03); + return secp256k1_fe_set_b32(&x, pub+1) && secp256k1_ge_set_xo_var(elem, &x, pub[0] == SECP256K1_TAG_PUBKEY_ODD); } else if (size == 65 && (pub[0] == 0x04 || pub[0] == 0x06 || pub[0] == 0x07)) { secp256k1_fe x, y; if (!secp256k1_fe_set_b32(&x, pub+1) || !secp256k1_fe_set_b32(&y, pub+33)) { return 0; } secp256k1_ge_set_xy(elem, &x, &y); - if ((pub[0] == 0x06 || pub[0] == 0x07) && secp256k1_fe_is_odd(&y) != (pub[0] == 0x07)) { + if ((pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_EVEN || pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_ODD) && + secp256k1_fe_is_odd(&y) != (pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_ODD)) { return 0; } return secp256k1_ge_is_valid_var(elem); @@ -42,10 +43,10 @@ static int secp256k1_eckey_pubkey_serialize(secp256k1_ge *elem, unsigned char *p secp256k1_fe_get_b32(&pub[1], &elem->x); if (compressed) { *size = 33; - pub[0] = 0x02 | (secp256k1_fe_is_odd(&elem->y) ? 0x01 : 0x00); + pub[0] = secp256k1_fe_is_odd(&elem->y) ? SECP256K1_TAG_PUBKEY_ODD : SECP256K1_TAG_PUBKEY_EVEN; } else { *size = 65; - pub[0] = 0x04; + pub[0] = SECP256K1_TAG_PUBKEY_UNCOMPRESSED; secp256k1_fe_get_b32(&pub[33], &elem->y); } return 1; @@ -96,4 +97,4 @@ static int secp256k1_eckey_pubkey_tweak_mul(const secp256k1_ecmult_context *ctx, return 1; } -#endif +#endif /* SECP256K1_ECKEY_IMPL_H */ diff --git a/src/ecmult.h b/src/ecmult.h index 20484134f..6d44aba60 100644 --- a/src/ecmult.h +++ b/src/ecmult.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECMULT_ -#define _SECP256K1_ECMULT_ +#ifndef SECP256K1_ECMULT_H +#define SECP256K1_ECMULT_H #include "num.h" #include "group.h" @@ -28,4 +28,4 @@ static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx /** Double multiply: R = na*A + ng*G */ static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng); -#endif +#endif /* SECP256K1_ECMULT_H */ diff --git a/src/ecmult_const.h b/src/ecmult_const.h index 2b0097655..72bf7d758 100644 --- a/src/ecmult_const.h +++ b/src/ecmult_const.h @@ -4,12 +4,12 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECMULT_CONST_ -#define _SECP256K1_ECMULT_CONST_ +#ifndef SECP256K1_ECMULT_CONST_H +#define SECP256K1_ECMULT_CONST_H #include "scalar.h" #include "group.h" static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q); -#endif +#endif /* SECP256K1_ECMULT_CONST_H */ diff --git a/src/ecmult_const_impl.h b/src/ecmult_const_impl.h index 0db314c48..7d7a172b7 100644 --- a/src/ecmult_const_impl.h +++ b/src/ecmult_const_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECMULT_CONST_IMPL_ -#define _SECP256K1_ECMULT_CONST_IMPL_ +#ifndef SECP256K1_ECMULT_CONST_IMPL_H +#define SECP256K1_ECMULT_CONST_IMPL_H #include "scalar.h" #include "group.h" @@ -42,11 +42,12 @@ } while(0) -/** Convert a number to WNAF notation. The number becomes represented by sum(2^{wi} * wnaf[i], i=0..return_val) - * with the following guarantees: +/** Convert a number to WNAF notation. + * The number becomes represented by sum(2^{wi} * wnaf[i], i=0..WNAF_SIZE(w)+1) - return_val. + * It has the following guarantees: * - each wnaf[i] an odd integer between -(1 << w) and (1 << w) * - each wnaf[i] is nonzero - * - the number of words set is returned; this is always (WNAF_BITS + w - 1) / w + * - the number of words set is always WNAF_SIZE(w) + 1 * * Adapted from `The Width-w NAF Method Provides Small Memory and Fast Elliptic Scalar * Multiplications Secure against Side Channel Attacks`, Okeya and Tagaki. M. Joye (Ed.) @@ -236,4 +237,4 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons } } -#endif +#endif /* SECP256K1_ECMULT_CONST_IMPL_H */ diff --git a/src/ecmult_gen.h b/src/ecmult_gen.h index eb2cc9ead..7564b7015 100644 --- a/src/ecmult_gen.h +++ b/src/ecmult_gen.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECMULT_GEN_ -#define _SECP256K1_ECMULT_GEN_ +#ifndef SECP256K1_ECMULT_GEN_H +#define SECP256K1_ECMULT_GEN_H #include "scalar.h" #include "group.h" @@ -40,4 +40,4 @@ static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context* ctx, secp25 static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const unsigned char *seed32); -#endif +#endif /* SECP256K1_ECMULT_GEN_H */ diff --git a/src/ecmult_gen_impl.h b/src/ecmult_gen_impl.h index 35f254607..9615b932d 100644 --- a/src/ecmult_gen_impl.h +++ b/src/ecmult_gen_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECMULT_GEN_IMPL_H_ -#define _SECP256K1_ECMULT_GEN_IMPL_H_ +#ifndef SECP256K1_ECMULT_GEN_IMPL_H +#define SECP256K1_ECMULT_GEN_IMPL_H #include "scalar.h" #include "group.h" @@ -207,4 +207,4 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const secp256k1_gej_clear(&gb); } -#endif +#endif /* SECP256K1_ECMULT_GEN_IMPL_H */ diff --git a/src/ecmult_impl.h b/src/ecmult_impl.h index 4e40104ad..93d3794cb 100644 --- a/src/ecmult_impl.h +++ b/src/ecmult_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECMULT_IMPL_H_ -#define _SECP256K1_ECMULT_IMPL_H_ +#ifndef SECP256K1_ECMULT_IMPL_H +#define SECP256K1_ECMULT_IMPL_H #include @@ -403,4 +403,4 @@ static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej } } -#endif +#endif /* SECP256K1_ECMULT_IMPL_H */ diff --git a/src/field.h b/src/field.h index bbb1ee866..bb6692ad5 100644 --- a/src/field.h +++ b/src/field.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_FIELD_ -#define _SECP256K1_FIELD_ +#ifndef SECP256K1_FIELD_H +#define SECP256K1_FIELD_H /** Field element module. * @@ -129,4 +129,4 @@ static void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_f /** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. */ static void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag); -#endif +#endif /* SECP256K1_FIELD_H */ diff --git a/src/field_10x26.h b/src/field_10x26.h index 61ee1e096..727c5267f 100644 --- a/src/field_10x26.h +++ b/src/field_10x26.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_FIELD_REPR_ -#define _SECP256K1_FIELD_REPR_ +#ifndef SECP256K1_FIELD_REPR_H +#define SECP256K1_FIELD_REPR_H #include @@ -44,4 +44,5 @@ typedef struct { #define SECP256K1_FE_STORAGE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{ (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }} #define SECP256K1_FE_STORAGE_CONST_GET(d) d.n[7], d.n[6], d.n[5], d.n[4],d.n[3], d.n[2], d.n[1], d.n[0] -#endif + +#endif /* SECP256K1_FIELD_REPR_H */ diff --git a/src/field_10x26_impl.h b/src/field_10x26_impl.h index 234c13a64..94f8132fc 100644 --- a/src/field_10x26_impl.h +++ b/src/field_10x26_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_FIELD_REPR_IMPL_H_ -#define _SECP256K1_FIELD_REPR_IMPL_H_ +#ifndef SECP256K1_FIELD_REPR_IMPL_H +#define SECP256K1_FIELD_REPR_IMPL_H #include "util.h" #include "num.h" @@ -1158,4 +1158,4 @@ static SECP256K1_INLINE void secp256k1_fe_from_storage(secp256k1_fe *r, const se #endif } -#endif +#endif /* SECP256K1_FIELD_REPR_IMPL_H */ diff --git a/src/field_5x52.h b/src/field_5x52.h index 8e69a560d..bccd8feb4 100644 --- a/src/field_5x52.h +++ b/src/field_5x52.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_FIELD_REPR_ -#define _SECP256K1_FIELD_REPR_ +#ifndef SECP256K1_FIELD_REPR_H +#define SECP256K1_FIELD_REPR_H #include @@ -44,4 +44,4 @@ typedef struct { (d6) | (((uint64_t)(d7)) << 32) \ }} -#endif +#endif /* SECP256K1_FIELD_REPR_H */ diff --git a/src/field_5x52_asm_impl.h b/src/field_5x52_asm_impl.h index 98cc004bf..1fc3171f6 100644 --- a/src/field_5x52_asm_impl.h +++ b/src/field_5x52_asm_impl.h @@ -11,8 +11,8 @@ * - December 2014, Pieter Wuille: converted from YASM to GCC inline assembly */ -#ifndef _SECP256K1_FIELD_INNER5X52_IMPL_H_ -#define _SECP256K1_FIELD_INNER5X52_IMPL_H_ +#ifndef SECP256K1_FIELD_INNER5X52_IMPL_H +#define SECP256K1_FIELD_INNER5X52_IMPL_H SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint64_t *r, const uint64_t *a, const uint64_t * SECP256K1_RESTRICT b) { /** @@ -499,4 +499,4 @@ __asm__ __volatile__( ); } -#endif +#endif /* SECP256K1_FIELD_INNER5X52_IMPL_H */ diff --git a/src/field_5x52_impl.h b/src/field_5x52_impl.h index 8e8b286ba..957c61b01 100644 --- a/src/field_5x52_impl.h +++ b/src/field_5x52_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_FIELD_REPR_IMPL_H_ -#define _SECP256K1_FIELD_REPR_IMPL_H_ +#ifndef SECP256K1_FIELD_REPR_IMPL_H +#define SECP256K1_FIELD_REPR_IMPL_H #if defined HAVE_CONFIG_H #include "libsecp256k1-config.h" @@ -493,4 +493,4 @@ static SECP256K1_INLINE void secp256k1_fe_from_storage(secp256k1_fe *r, const se #endif } -#endif +#endif /* SECP256K1_FIELD_REPR_IMPL_H */ diff --git a/src/field_5x52_int128_impl.h b/src/field_5x52_int128_impl.h index 0bf22bdd3..95a0d1791 100644 --- a/src/field_5x52_int128_impl.h +++ b/src/field_5x52_int128_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_FIELD_INNER5X52_IMPL_H_ -#define _SECP256K1_FIELD_INNER5X52_IMPL_H_ +#ifndef SECP256K1_FIELD_INNER5X52_IMPL_H +#define SECP256K1_FIELD_INNER5X52_IMPL_H #include @@ -274,4 +274,4 @@ SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint64_t *r, const uint64_t /* [r4 r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ } -#endif +#endif /* SECP256K1_FIELD_INNER5X52_IMPL_H */ diff --git a/src/field_impl.h b/src/field_impl.h index 5127b279b..20428648a 100644 --- a/src/field_impl.h +++ b/src/field_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_FIELD_IMPL_H_ -#define _SECP256K1_FIELD_IMPL_H_ +#ifndef SECP256K1_FIELD_IMPL_H +#define SECP256K1_FIELD_IMPL_H #if defined HAVE_CONFIG_H #include "libsecp256k1-config.h" @@ -312,4 +312,4 @@ static int secp256k1_fe_is_quad_var(const secp256k1_fe *a) { #endif } -#endif +#endif /* SECP256K1_FIELD_IMPL_H */ diff --git a/src/group.h b/src/group.h index 4957b248f..ea1302deb 100644 --- a/src/group.h +++ b/src/group.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_GROUP_ -#define _SECP256K1_GROUP_ +#ifndef SECP256K1_GROUP_H +#define SECP256K1_GROUP_H #include "num.h" #include "field.h" @@ -141,4 +141,4 @@ static void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r, const secp256k1_g /** Rescale a jacobian point by b which must be non-zero. Constant-time. */ static void secp256k1_gej_rescale(secp256k1_gej *r, const secp256k1_fe *b); -#endif +#endif /* SECP256K1_GROUP_H */ diff --git a/src/group_impl.h b/src/group_impl.h index 7d723532f..b31b6c12e 100644 --- a/src/group_impl.h +++ b/src/group_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_GROUP_IMPL_H_ -#define _SECP256K1_GROUP_IMPL_H_ +#ifndef SECP256K1_GROUP_IMPL_H +#define SECP256K1_GROUP_IMPL_H #include "num.h" #include "field.h" @@ -697,4 +697,4 @@ static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a) { return secp256k1_fe_is_quad_var(&yz); } -#endif +#endif /* SECP256K1_GROUP_IMPL_H */ diff --git a/src/hash.h b/src/hash.h index fca98cab9..e08d25d22 100644 --- a/src/hash.h +++ b/src/hash.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_HASH_ -#define _SECP256K1_HASH_ +#ifndef SECP256K1_HASH_H +#define SECP256K1_HASH_H #include #include @@ -38,4 +38,4 @@ static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha2 static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256_t *rng, unsigned char *out, size_t outlen); static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256_t *rng); -#endif +#endif /* SECP256K1_HASH_H */ diff --git a/src/hash_impl.h b/src/hash_impl.h index b47e65f83..4c9964ee0 100644 --- a/src/hash_impl.h +++ b/src/hash_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_HASH_IMPL_H_ -#define _SECP256K1_HASH_IMPL_H_ +#ifndef SECP256K1_HASH_IMPL_H +#define SECP256K1_HASH_IMPL_H #include "hash.h" @@ -278,4 +278,4 @@ static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256 #undef Maj #undef Ch -#endif +#endif /* SECP256K1_HASH_IMPL_H */ diff --git a/src/modules/ecdh/main_impl.h b/src/modules/ecdh/main_impl.h index 9e30fb73d..01ecba4d5 100644 --- a/src/modules/ecdh/main_impl.h +++ b/src/modules/ecdh/main_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_MODULE_ECDH_MAIN_ -#define _SECP256K1_MODULE_ECDH_MAIN_ +#ifndef SECP256K1_MODULE_ECDH_MAIN_H +#define SECP256K1_MODULE_ECDH_MAIN_H #include "include/secp256k1_ecdh.h" #include "ecmult_const_impl.h" @@ -51,4 +51,4 @@ int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *result, const se return ret; } -#endif +#endif /* SECP256K1_MODULE_ECDH_MAIN_H */ diff --git a/src/modules/ecdh/tests_impl.h b/src/modules/ecdh/tests_impl.h index 85a5d0a9a..cec30b67c 100644 --- a/src/modules/ecdh/tests_impl.h +++ b/src/modules/ecdh/tests_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_MODULE_ECDH_TESTS_ -#define _SECP256K1_MODULE_ECDH_TESTS_ +#ifndef SECP256K1_MODULE_ECDH_TESTS_H +#define SECP256K1_MODULE_ECDH_TESTS_H void test_ecdh_api(void) { /* Setup context that just counts errors */ @@ -102,4 +102,4 @@ void run_ecdh_tests(void) { test_bad_scalar(); } -#endif +#endif /* SECP256K1_MODULE_ECDH_TESTS_H */ diff --git a/src/modules/recovery/main_impl.h b/src/modules/recovery/main_impl.h index c6fbe2398..2f6691c5a 100755 --- a/src/modules/recovery/main_impl.h +++ b/src/modules/recovery/main_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_MODULE_RECOVERY_MAIN_ -#define _SECP256K1_MODULE_RECOVERY_MAIN_ +#ifndef SECP256K1_MODULE_RECOVERY_MAIN_H +#define SECP256K1_MODULE_RECOVERY_MAIN_H #include "include/secp256k1_recovery.h" @@ -190,4 +190,4 @@ int secp256k1_ecdsa_recover(const secp256k1_context* ctx, secp256k1_pubkey *pubk } } -#endif +#endif /* SECP256K1_MODULE_RECOVERY_MAIN_H */ diff --git a/src/modules/recovery/tests_impl.h b/src/modules/recovery/tests_impl.h index 765c7dd81..5c9bbe861 100644 --- a/src/modules/recovery/tests_impl.h +++ b/src/modules/recovery/tests_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_MODULE_RECOVERY_TESTS_ -#define _SECP256K1_MODULE_RECOVERY_TESTS_ +#ifndef SECP256K1_MODULE_RECOVERY_TESTS_H +#define SECP256K1_MODULE_RECOVERY_TESTS_H static int recovery_test_nonce_function(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { (void) msg32; @@ -390,4 +390,4 @@ void run_recovery_tests(void) { test_ecdsa_recovery_edge_cases(); } -#endif +#endif /* SECP256K1_MODULE_RECOVERY_TESTS_H */ diff --git a/src/num.h b/src/num.h index 7bb9c5be8..49f2dd791 100644 --- a/src/num.h +++ b/src/num.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_NUM_ -#define _SECP256K1_NUM_ +#ifndef SECP256K1_NUM_H +#define SECP256K1_NUM_H #ifndef USE_NUM_NONE @@ -71,4 +71,4 @@ static void secp256k1_num_negate(secp256k1_num *r); #endif -#endif +#endif /* SECP256K1_NUM_H */ diff --git a/src/num_gmp.h b/src/num_gmp.h index 7dd813088..3619844bd 100644 --- a/src/num_gmp.h +++ b/src/num_gmp.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_NUM_REPR_ -#define _SECP256K1_NUM_REPR_ +#ifndef SECP256K1_NUM_REPR_H +#define SECP256K1_NUM_REPR_H #include @@ -17,4 +17,4 @@ typedef struct { int limbs; } secp256k1_num; -#endif +#endif /* SECP256K1_NUM_REPR_H */ diff --git a/src/num_gmp_impl.h b/src/num_gmp_impl.h index 3a46495ee..0ae2a8ba0 100644 --- a/src/num_gmp_impl.h +++ b/src/num_gmp_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_NUM_REPR_IMPL_H_ -#define _SECP256K1_NUM_REPR_IMPL_H_ +#ifndef SECP256K1_NUM_REPR_IMPL_H +#define SECP256K1_NUM_REPR_IMPL_H #include #include @@ -285,4 +285,4 @@ static void secp256k1_num_negate(secp256k1_num *r) { r->neg ^= 1; } -#endif +#endif /* SECP256K1_NUM_REPR_IMPL_H */ diff --git a/src/num_impl.h b/src/num_impl.h index 0b0e3a072..c45193b03 100644 --- a/src/num_impl.h +++ b/src/num_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_NUM_IMPL_H_ -#define _SECP256K1_NUM_IMPL_H_ +#ifndef SECP256K1_NUM_IMPL_H +#define SECP256K1_NUM_IMPL_H #if defined HAVE_CONFIG_H #include "libsecp256k1-config.h" @@ -21,4 +21,4 @@ #error "Please select num implementation" #endif -#endif +#endif /* SECP256K1_NUM_IMPL_H */ diff --git a/src/scalar.h b/src/scalar.h index 27e9d8375..59304cb66 100644 --- a/src/scalar.h +++ b/src/scalar.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SCALAR_ -#define _SECP256K1_SCALAR_ +#ifndef SECP256K1_SCALAR_H +#define SECP256K1_SCALAR_H #include "num.h" @@ -103,4 +103,4 @@ static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar /** Multiply a and b (without taking the modulus!), divide by 2**shift, and round to the nearest integer. Shift must be at least 256. */ static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift); -#endif +#endif /* SECP256K1_SCALAR_H */ diff --git a/src/scalar_4x64.h b/src/scalar_4x64.h index cff406038..19c7495d1 100644 --- a/src/scalar_4x64.h +++ b/src/scalar_4x64.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SCALAR_REPR_ -#define _SECP256K1_SCALAR_REPR_ +#ifndef SECP256K1_SCALAR_REPR_H +#define SECP256K1_SCALAR_REPR_H #include @@ -16,4 +16,4 @@ typedef struct { #define SECP256K1_SCALAR_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{((uint64_t)(d1)) << 32 | (d0), ((uint64_t)(d3)) << 32 | (d2), ((uint64_t)(d5)) << 32 | (d4), ((uint64_t)(d7)) << 32 | (d6)}} -#endif +#endif /* SECP256K1_SCALAR_REPR_H */ diff --git a/src/scalar_4x64_impl.h b/src/scalar_4x64_impl.h index 56e7bd82a..db1ebf94b 100644 --- a/src/scalar_4x64_impl.h +++ b/src/scalar_4x64_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SCALAR_REPR_IMPL_H_ -#define _SECP256K1_SCALAR_REPR_IMPL_H_ +#ifndef SECP256K1_SCALAR_REPR_IMPL_H +#define SECP256K1_SCALAR_REPR_IMPL_H /* Limbs of the secp256k1 order. */ #define SECP256K1_N_0 ((uint64_t)0xBFD25E8CD0364141ULL) @@ -946,4 +946,4 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 6] >> ((shift - 1) & 0x3f)) & 1); } -#endif +#endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/scalar_8x32.h b/src/scalar_8x32.h index 1319664f6..2c9a348e2 100644 --- a/src/scalar_8x32.h +++ b/src/scalar_8x32.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SCALAR_REPR_ -#define _SECP256K1_SCALAR_REPR_ +#ifndef SECP256K1_SCALAR_REPR_H +#define SECP256K1_SCALAR_REPR_H #include @@ -16,4 +16,4 @@ typedef struct { #define SECP256K1_SCALAR_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{(d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7)}} -#endif +#endif /* SECP256K1_SCALAR_REPR_H */ diff --git a/src/scalar_8x32_impl.h b/src/scalar_8x32_impl.h index aae4f35c0..4f9ed61fe 100644 --- a/src/scalar_8x32_impl.h +++ b/src/scalar_8x32_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SCALAR_REPR_IMPL_H_ -#define _SECP256K1_SCALAR_REPR_IMPL_H_ +#ifndef SECP256K1_SCALAR_REPR_IMPL_H +#define SECP256K1_SCALAR_REPR_IMPL_H /* Limbs of the secp256k1 order. */ #define SECP256K1_N_0 ((uint32_t)0xD0364141UL) @@ -718,4 +718,4 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 5] >> ((shift - 1) & 0x1f)) & 1); } -#endif +#endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/scalar_impl.h b/src/scalar_impl.h index 2690d8655..fa790570f 100644 --- a/src/scalar_impl.h +++ b/src/scalar_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SCALAR_IMPL_H_ -#define _SECP256K1_SCALAR_IMPL_H_ +#ifndef SECP256K1_SCALAR_IMPL_H +#define SECP256K1_SCALAR_IMPL_H #include "group.h" #include "scalar.h" @@ -330,4 +330,4 @@ static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar #endif #endif -#endif +#endif /* SECP256K1_SCALAR_IMPL_H */ diff --git a/src/scalar_low.h b/src/scalar_low.h index 5574c44c7..5836febc5 100644 --- a/src/scalar_low.h +++ b/src/scalar_low.h @@ -4,12 +4,12 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SCALAR_REPR_ -#define _SECP256K1_SCALAR_REPR_ +#ifndef SECP256K1_SCALAR_REPR_H +#define SECP256K1_SCALAR_REPR_H #include /** A scalar modulo the group order of the secp256k1 curve. */ typedef uint32_t secp256k1_scalar; -#endif +#endif /* SECP256K1_SCALAR_REPR_H */ diff --git a/src/scalar_low_impl.h b/src/scalar_low_impl.h index 4f94441f4..c80e70c5a 100644 --- a/src/scalar_low_impl.h +++ b/src/scalar_low_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SCALAR_REPR_IMPL_H_ -#define _SECP256K1_SCALAR_REPR_IMPL_H_ +#ifndef SECP256K1_SCALAR_REPR_IMPL_H +#define SECP256K1_SCALAR_REPR_IMPL_H #include "scalar.h" @@ -111,4 +111,4 @@ SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const return *a == *b; } -#endif +#endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/testrand.h b/src/testrand.h index f8efa93c7..f1f9be077 100644 --- a/src/testrand.h +++ b/src/testrand.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_TESTRAND_H_ -#define _SECP256K1_TESTRAND_H_ +#ifndef SECP256K1_TESTRAND_H +#define SECP256K1_TESTRAND_H #if defined HAVE_CONFIG_H #include "libsecp256k1-config.h" @@ -35,4 +35,4 @@ static void secp256k1_rand256_test(unsigned char *b32); /** Generate pseudorandom bytes with long sequences of zero and one bits. */ static void secp256k1_rand_bytes_test(unsigned char *bytes, size_t len); -#endif +#endif /* SECP256K1_TESTRAND_H */ diff --git a/src/testrand_impl.h b/src/testrand_impl.h index 15c7b9f12..125557420 100644 --- a/src/testrand_impl.h +++ b/src/testrand_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_TESTRAND_IMPL_H_ -#define _SECP256K1_TESTRAND_IMPL_H_ +#ifndef SECP256K1_TESTRAND_IMPL_H +#define SECP256K1_TESTRAND_IMPL_H #include #include @@ -107,4 +107,4 @@ static void secp256k1_rand256_test(unsigned char *b32) { secp256k1_rand_bytes_test(b32, 32); } -#endif +#endif /* SECP256K1_TESTRAND_IMPL_H */ diff --git a/src/util.h b/src/util.h index 4092a86c9..b0441d8e3 100644 --- a/src/util.h +++ b/src/util.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_UTIL_H_ -#define _SECP256K1_UTIL_H_ +#ifndef SECP256K1_UTIL_H +#define SECP256K1_UTIL_H #if defined HAVE_CONFIG_H #include "libsecp256k1-config.h" @@ -110,4 +110,4 @@ static SECP256K1_INLINE void *checked_malloc(const secp256k1_callback* cb, size_ SECP256K1_GNUC_EXT typedef unsigned __int128 uint128_t; #endif -#endif +#endif /* SECP256K1_UTIL_H */ From 1088b5322d0e7a8366a285e2baa49c766a9ba5bd Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Tue, 26 Sep 2017 17:24:26 -0400 Subject: [PATCH 303/382] add functional test for mempoolreplacement command line arg --- test/functional/replace-by-fee.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/test/functional/replace-by-fee.py b/test/functional/replace-by-fee.py index 1d6494fe4..75f3d77c1 100755 --- a/test/functional/replace-by-fee.py +++ b/test/functional/replace-by-fee.py @@ -62,13 +62,14 @@ def make_utxo(node, amount, confirmed=True, scriptPubKey=CScript([1])): class ReplaceByFeeTest(BitcoinTestFramework): def set_test_params(self): - self.num_nodes = 1 + self.num_nodes = 2 self.extra_args= [["-maxorphantx=1000", "-whitelist=127.0.0.1", "-limitancestorcount=50", "-limitancestorsize=101", "-limitdescendantcount=200", - "-limitdescendantsize=101"]] + "-limitdescendantsize=101"], + ["-mempoolreplacement=0"]] def run_test(self): make_utxo(self.nodes[0], 1*COIN) @@ -115,6 +116,8 @@ def test_simple_doublespend(self): tx1a_hex = txToHex(tx1a) tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) + self.sync_all([self.nodes]) + # Should fail because we haven't changed the fee tx1b = CTransaction() tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)] @@ -123,12 +126,17 @@ def test_simple_doublespend(self): # This will raise an exception due to insufficient fee assert_raises_jsonrpc(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, True) + # This will raise an exception due to transaction replacement being disabled + assert_raises_jsonrpc(-26, "txn-mempool-conflict", self.nodes[1].sendrawtransaction, tx1b_hex, True) # Extra 0.1 BTC fee tx1b = CTransaction() tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)] tx1b.vout = [CTxOut(int(0.9*COIN), CScript([b'b']))] tx1b_hex = txToHex(tx1b) + # Replacement still disabled even with "enough fee" + assert_raises_jsonrpc(-26, "txn-mempool-conflict", self.nodes[1].sendrawtransaction, tx1b_hex, True) + # Works when enabled tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True) mempool = self.nodes[0].getrawmempool() @@ -138,6 +146,11 @@ def test_simple_doublespend(self): assert_equal(tx1b_hex, self.nodes[0].getrawtransaction(tx1b_txid)) + # Second node is running mempoolreplacement=0, will not replace originally-seen txn + mempool = self.nodes[1].getrawmempool() + assert tx1a_txid in mempool + assert tx1b_txid not in mempool + def test_doublespend_chain(self): """Doublespend of a long chain""" From b7dfc6c4b89b62f9bb79ea009ee103a6299ac005 Mon Sep 17 00:00:00 2001 From: Daniel Edgecumbe Date: Thu, 21 Sep 2017 00:52:20 +0100 Subject: [PATCH 304/382] [rpc] getblockchaininfo: add size_on_disk, prune_target_size, automatic_pruning Fix pruneheight help text. Move fPruneMode block to match output ordering with help text. Add functional tests for new fields in getblockchaininfo. --- src/rpc/blockchain.cpp | 30 +++++++++++++++++++++--------- src/validation.cpp | 8 +++++++- src/validation.h | 3 +++ test/functional/blockchain.py | 30 +++++++++++++++++++++++++++--- 4 files changed, 58 insertions(+), 13 deletions(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 8c0268e26..c9d46444e 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1136,8 +1136,11 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) " \"mediantime\": xxxxxx, (numeric) median time for the current best block\n" " \"verificationprogress\": xxxx, (numeric) estimate of verification progress [0..1]\n" " \"chainwork\": \"xxxx\" (string) total amount of work in active chain, in hexadecimal\n" + " \"size_on_disk\": xxxxxx, (numeric) the estimated size of the block and undo files on disk\n" " \"pruned\": xx, (boolean) if the blocks are subject to pruning\n" - " \"pruneheight\": xxxxxx, (numeric) lowest-height complete block stored\n" + " \"pruneheight\": xxxxxx, (numeric) lowest-height complete block stored (only present if pruning is enabled)\n" + " \"automatic_pruning\": xx, (boolean) whether automatic pruning is enabled (only present if pruning is enabled)\n" + " \"prune_target_size\": xxxxxx, (numeric) the target size used by pruning (only present if automatic pruning is enabled)\n" " \"softforks\": [ (array) status of softforks in progress\n" " {\n" " \"id\": \"xxxx\", (string) name of softfork\n" @@ -1181,7 +1184,24 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) obj.push_back(Pair("mediantime", (int64_t)chainActive.Tip()->GetMedianTimePast())); obj.push_back(Pair("verificationprogress", GuessVerificationProgress(Params().TxData(), chainActive.Tip()))); obj.push_back(Pair("chainwork", chainActive.Tip()->nChainWork.GetHex())); + obj.push_back(Pair("size_on_disk", CalculateCurrentUsage())); obj.push_back(Pair("pruned", fPruneMode)); + if (fPruneMode) { + CBlockIndex* block = chainActive.Tip(); + assert(block); + while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) { + block = block->pprev; + } + + obj.push_back(Pair("pruneheight", block->nHeight)); + + // if 0, execution bypasses the whole if block. + bool automatic_pruning = (gArgs.GetArg("-prune", 0) != 1); + obj.push_back(Pair("automatic_pruning", automatic_pruning)); + if (automatic_pruning) { + obj.push_back(Pair("prune_target_size", nPruneTarget)); + } + } const Consensus::Params& consensusParams = Params().GetConsensus(); CBlockIndex* tip = chainActive.Tip(); @@ -1195,14 +1215,6 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) obj.push_back(Pair("softforks", softforks)); obj.push_back(Pair("bip9_softforks", bip9_softforks)); - if (fPruneMode) - { - CBlockIndex *block = chainActive.Tip(); - while (block && block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) - block = block->pprev; - - obj.push_back(Pair("pruneheight", block->nHeight)); - } obj.push_back(Pair("warnings", GetWarnings("statusbar"))); return obj; } diff --git a/src/validation.cpp b/src/validation.cpp index a958afe84..48c323698 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -3233,8 +3233,10 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, */ /* Calculate the amount of disk space the block & undo files currently use */ -static uint64_t CalculateCurrentUsage() +uint64_t CalculateCurrentUsage() { + LOCK(cs_LastBlockFile); + uint64_t retval = 0; for (const CBlockFileInfo &file : vinfoBlockFile) { retval += file.nSize + file.nUndoSize; @@ -3245,6 +3247,8 @@ static uint64_t CalculateCurrentUsage() /* Prune a block file (modify associated database entries)*/ void PruneOneBlockFile(const int fileNumber) { + LOCK(cs_LastBlockFile); + for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); ++it) { CBlockIndex* pindex = it->second; if (pindex->nFile == fileNumber) { @@ -4247,6 +4251,8 @@ std::string CBlockFileInfo::ToString() const CBlockFileInfo* GetBlockFileInfo(size_t n) { + LOCK(cs_LastBlockFile); + return &vinfoBlockFile.at(n); } diff --git a/src/validation.h b/src/validation.h index c7ef556b4..75b897f3d 100644 --- a/src/validation.h +++ b/src/validation.h @@ -280,6 +280,9 @@ 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); +/** Calculate the amount of disk space the block & undo files currently use */ +uint64_t CalculateCurrentUsage(); + /** * Mark one block file as pruned. */ diff --git a/test/functional/blockchain.py b/test/functional/blockchain.py index c5967aa10..1918caf00 100755 --- a/test/functional/blockchain.py +++ b/test/functional/blockchain.py @@ -24,6 +24,8 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, + assert_greater_than, + assert_greater_than_or_equal, assert_raises, assert_raises_jsonrpc, assert_is_hex_string, @@ -58,21 +60,43 @@ def _test_getblockchaininfo(self): 'headers', 'mediantime', 'pruned', + 'size_on_disk', 'softforks', 'verificationprogress', 'warnings', ] res = self.nodes[0].getblockchaininfo() - # result should have pruneheight and default keys if pruning is enabled - assert_equal(sorted(res.keys()), sorted(['pruneheight'] + keys)) + + # result should have these additional pruning keys if manual pruning is enabled + assert_equal(sorted(res.keys()), sorted(['pruneheight', 'automatic_pruning'] + keys)) + + # size_on_disk should be > 0 + assert_greater_than(res['size_on_disk'], 0) + # pruneheight should be greater or equal to 0 - assert res['pruneheight'] >= 0 + assert_greater_than_or_equal(res['pruneheight'], 0) + + # check other pruning fields given that prune=1 + assert res['pruned'] + assert not res['automatic_pruning'] self.restart_node(0, ['-stopatheight=207']) res = self.nodes[0].getblockchaininfo() # should have exact keys assert_equal(sorted(res.keys()), keys) + self.restart_node(0, ['-stopatheight=207', '-prune=550']) + res = self.nodes[0].getblockchaininfo() + # result should have these additional pruning keys if prune=550 + assert_equal(sorted(res.keys()), sorted(['pruneheight', 'automatic_pruning', 'prune_target_size'] + keys)) + + # check related fields + assert res['pruned'] + assert_equal(res['pruneheight'], 0) + assert res['automatic_pruning'] + assert_equal(res['prune_target_size'], 576716800) + assert_greater_than(res['size_on_disk'], 0) + def _test_getchaintxstats(self): chaintxstats = self.nodes[0].getchaintxstats(1) # 200 txs plus genesis tx From bb8376bbc3d96af727444f90e5f60d47105609dc Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Fri, 29 Sep 2017 17:33:50 -0400 Subject: [PATCH 305/382] Verify DBWrapper iterators are taking snapshots The LevelDB docs seem to indicate that an iterator will not take snapshots (even providing instructions on how to do so yourself). In several of the places we use them, we assume snapshots to have been taken. In order to make sure LevelDB doesn't change out from under us (and to prevent the next person who reads the docs from having the same fright I did), verify that snapshots are taken in our tests. --- src/test/dbwrapper_tests.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp index 251d5a714..efddafe17 100644 --- a/src/test/dbwrapper_tests.cpp +++ b/src/test/dbwrapper_tests.cpp @@ -204,19 +204,31 @@ BOOST_AUTO_TEST_CASE(iterator_ordering) for (int x=0x00; x<256; ++x) { uint8_t key = x; uint32_t value = x*x; - BOOST_CHECK(dbw.Write(key, value)); + if (!(x & 1)) BOOST_CHECK(dbw.Write(key, value)); } + // Check that creating an iterator creates a snapshot std::unique_ptr it(const_cast(dbw).NewIterator()); + + for (int x=0x00; x<256; ++x) { + uint8_t key = x; + uint32_t value = x*x; + if (x & 1) BOOST_CHECK(dbw.Write(key, value)); + } + for (int seek_start : {0x00, 0x80}) { it->Seek((uint8_t)seek_start); - for (int x=seek_start; x<256; ++x) { + for (int x=seek_start; x<255; ++x) { uint8_t key; uint32_t value; BOOST_CHECK(it->Valid()); if (!it->Valid()) // Avoid spurious errors about invalid iterator's key and value in case of failure break; BOOST_CHECK(it->GetKey(key)); + if (x & 1) { + BOOST_CHECK_EQUAL(key, x + 1); + continue; + } BOOST_CHECK(it->GetValue(value)); BOOST_CHECK_EQUAL(key, x); BOOST_CHECK_EQUAL(value, x*x); From 92848e5058c7667c61c358ea2c46f07a6edc468b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= Date: Sat, 30 Sep 2017 22:43:55 +0100 Subject: [PATCH 306/382] Remove unused fTry from push_lock --- src/sync.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sync.cpp b/src/sync.cpp index 9c351ea48..87024ccdf 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -98,7 +98,7 @@ static void potential_deadlock_detected(const std::pair& mismatch, assert(false); } -static void push_lock(void* c, const CLockLocation& locklocation, bool fTry) +static void push_lock(void* c, const CLockLocation& locklocation) { if (lockstack.get() == nullptr) lockstack.reset(new LockStack); @@ -130,7 +130,7 @@ static void pop_lock() void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry) { - push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry), fTry); + push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry)); } void LeaveCritical() From fafff1220cf798e25f02cdd8affb70506dd366cc Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Sun, 1 Oct 2017 11:19:49 +0200 Subject: [PATCH 307/382] qa: Restore bitcoin-util-test py2 compatibility --- src/Makefile.test.include | 2 +- test/util/bitcoin-util-test.py | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 6415b3d2e..1544511e8 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -148,7 +148,7 @@ bitcoin_test_clean : FORCE check-local: @echo "Running test/util/bitcoin-util-test.py..." - $(top_builddir)/test/util/bitcoin-util-test.py + $(PYTHON) $(top_builddir)/test/util/bitcoin-util-test.py $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C secp256k1 check if EMBEDDED_UNIVALUE $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C univalue check diff --git a/test/util/bitcoin-util-test.py b/test/util/bitcoin-util-test.py index d15d6a601..ef34955d9 100755 --- a/test/util/bitcoin-util-test.py +++ b/test/util/bitcoin-util-test.py @@ -9,9 +9,14 @@ Can also be run manually.""" +from __future__ import division,print_function,unicode_literals + import argparse import binascii -import configparser +try: + import configparser +except ImportError: + import ConfigParser as configparser import difflib import json import logging @@ -22,7 +27,9 @@ def main(): config = configparser.ConfigParser() - config.read_file(open(os.path.dirname(__file__) + "/../config.ini")) + config.optionxform = str + config.readfp(open(os.path.join(os.path.dirname(__file__), "../config.ini"))) + env_conf = dict(config.items('environment')) parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('-v', '--verbose', action='store_true') @@ -37,7 +44,7 @@ def main(): # Add the format/level to the logger logging.basicConfig(format=formatter, level=level) - bctester(config["environment"]["SRCDIR"] + "/test/util/data", "bitcoin-util-test.json", config["environment"]) + bctester(os.path.join(env_conf["SRCDIR"], "test/util/data"), "bitcoin-util-test.json", env_conf) def bctester(testDir, input_basename, buildenv): """ Loads and parses the input file, runs all tests and reports results""" From fae2673d5d8dbb356423aa584a96d3ac367b5716 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Sun, 1 Oct 2017 16:42:39 +0200 Subject: [PATCH 308/382] qa: check-rpc-mapping must not run on empty lists --- contrib/devtools/check-rpc-mappings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/devtools/check-rpc-mappings.py b/contrib/devtools/check-rpc-mappings.py index d2698de04..7e96852c5 100755 --- a/contrib/devtools/check-rpc-mappings.py +++ b/contrib/devtools/check-rpc-mappings.py @@ -63,7 +63,7 @@ def process_commands(fname): else: args = [] cmds.append(RPCCommand(name, args)) - assert not in_rpcs, "Something went wrong with parsing the C++ file: update the regexps" + assert not in_rpcs and cmds, "Something went wrong with parsing the C++ file: update the regexps" return cmds def process_mapping(fname): @@ -86,7 +86,7 @@ def process_mapping(fname): idx = int(m.group(2)) argname = parse_string(m.group(3)) cmds.append((name, idx, argname)) - assert not in_rpcs + assert not in_rpcs and cmds return cmds def main(): From 4f890ba6bc8caba5394c7a5388d7f07959ced78b Mon Sep 17 00:00:00 2001 From: Donal OConnor Date: Tue, 5 Sep 2017 21:26:20 +0100 Subject: [PATCH 309/382] Add new step to clean $PATH var by removing /mnt specific Window's %PATH% paths that cause issues with the make system --- doc/build-windows.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/build-windows.md b/doc/build-windows.md index ce837a222..57e2d6834 100644 --- a/doc/build-windows.md +++ b/doc/build-windows.md @@ -72,6 +72,7 @@ To build executables for Windows 64-bit, install the following dependencies: Then build using: + PATH=$(echo "$PATH" | sed -e 's/:\/mnt.*//g') # strip out problematic Windows %PATH% imported var cd depends make HOST=x86_64-w64-mingw32 cd .. @@ -87,6 +88,7 @@ To build executables for Windows 32-bit, install the following dependencies: Then build using: + PATH=$(echo "$PATH" | sed -e 's/:\/mnt.*//g') # strip out problematic Windows %PATH% imported var cd depends make HOST=i686-w64-mingw32 cd .. From fae60e338639b3a09d2f26d8eb5feb3bb576b1fd Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Mon, 2 Oct 2017 13:35:08 +0200 Subject: [PATCH 310/382] qa: Fix lcov for out-of-tree builds --- configure.ac | 1 + 1 file changed, 1 insertion(+) diff --git a/configure.ac b/configure.ac index e3e71044d..81c84a8af 100644 --- a/configure.ac +++ b/configure.ac @@ -1231,6 +1231,7 @@ AC_SUBST(QR_LIBS) AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile share/setup.nsi share/qt/Info.plist test/config.ini]) AC_CONFIG_FILES([contrib/devtools/split-debug.sh],[chmod +x contrib/devtools/split-debug.sh]) AC_CONFIG_FILES([doc/Doxyfile]) +AC_CONFIG_LINKS([contrib/filter-lcov.py:contrib/filter-lcov.py]) AC_CONFIG_LINKS([test/functional/test_runner.py:test/functional/test_runner.py]) AC_CONFIG_LINKS([test/util/bitcoin-util-test.py:test/util/bitcoin-util-test.py]) From 55224af6bdecf79bb0d2c95b16f1ff60b16c45f8 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Fri, 21 Jul 2017 21:43:40 +0200 Subject: [PATCH 311/382] Remove redundant NULL checks after new --- src/validation.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/validation.cpp b/src/validation.cpp index 0bd1ec672..0da861eb2 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2608,7 +2608,6 @@ static CBlockIndex* AddToBlockIndex(const CBlockHeader& block) // Construct new block index object CBlockIndex* pindexNew = new CBlockIndex(block); - assert(pindexNew); // We assign the sequence id to blocks only when the full data is available, // to avoid miners withholding blocks but broadcasting headers, to get a // competitive advantage. @@ -3434,8 +3433,6 @@ CBlockIndex * InsertBlockIndex(uint256 hash) // Create new CBlockIndex* pindexNew = new CBlockIndex(); - if (!pindexNew) - throw std::runtime_error(std::string(__func__) + ": new CBlockIndex failed"); mi = mapBlockIndex.insert(std::make_pair(hash, pindexNew)).first; pindexNew->phashBlock = &((*mi).first); From 74669916709e5429990df120c6ba84ac60791b30 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Fri, 21 Jul 2017 18:42:22 +0200 Subject: [PATCH 312/382] Remove redundant check (!ecc is always true) --- src/bitcoin-tx.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index d8d7934bf..5a0ae11f0 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -682,10 +682,10 @@ static void MutateTx(CMutableTransaction& tx, const std::string& command, else if (command == "outaddr") MutateTxAddOutAddr(tx, commandVal); else if (command == "outpubkey") { - if (!ecc) { ecc.reset(new Secp256k1Init()); } + ecc.reset(new Secp256k1Init()); MutateTxAddOutPubKey(tx, commandVal); } else if (command == "outmultisig") { - if (!ecc) { ecc.reset(new Secp256k1Init()); } + ecc.reset(new Secp256k1Init()); MutateTxAddOutMultiSig(tx, commandVal); } else if (command == "outscript") MutateTxAddOutScript(tx, commandVal); @@ -693,7 +693,7 @@ static void MutateTx(CMutableTransaction& tx, const std::string& command, MutateTxAddOutData(tx, commandVal); else if (command == "sign") { - if (!ecc) { ecc.reset(new Secp256k1Init()); } + ecc.reset(new Secp256k1Init()); MutateTxSign(tx, commandVal); } From b5fb33943fd6222571d13f8247fc611ebe2ba8c6 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Fri, 21 Jul 2017 18:52:52 +0200 Subject: [PATCH 313/382] Remove duplicate uriParts.size() > 0 check --- src/rest.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/rest.cpp b/src/rest.cpp index 0b2c843d5..4d2cdfdf0 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -409,10 +409,8 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart) if (uriParts.size() > 0) { - //inputs is sent over URI scheme (/rest/getutxos/checkmempool/txid1-n/txid2-n/...) - if (uriParts.size() > 0 && uriParts[0] == "checkmempool") - fCheckMemPool = true; + if (uriParts[0] == "checkmempool") fCheckMemPool = true; for (size_t i = (fCheckMemPool) ? 1 : 0; i < uriParts.size(); i++) { From 4971a9a3c928235ef4af29a7f72379cbb567e446 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Mon, 24 Jul 2017 09:59:25 +0200 Subject: [PATCH 314/382] Use two boolean literals instead of re-using variable --- src/net_processing.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/net_processing.cpp b/src/net_processing.cpp index b8900d988..68f486d90 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -371,19 +371,17 @@ void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman* connman) { } } 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)); + connman->ForNode(lNodesAnnouncingHeaderAndIDs.front(), [connman, nCMPCTBLOCKVersion](CNode* pnodeStop){ + connman->PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetSendVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/false, nCMPCTBLOCKVersion)); return true; }); lNodesAnnouncingHeaderAndIDs.pop_front(); } - fAnnounceUsingCMPCTBLOCK = true; - connman->PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); + connman->PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/true, nCMPCTBLOCKVersion)); lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId()); return true; }); From 76fed838f381a6efba175f3650ec7e9dc73016d3 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Mon, 2 Oct 2017 17:20:08 +0200 Subject: [PATCH 315/382] Avoid NULL pointer dereference when _walletModel is NULL (which is valid) --- src/qt/walletview.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index 971f5e0e1..a56a40037 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -122,8 +122,8 @@ void WalletView::setWalletModel(WalletModel *_walletModel) overviewPage->setWalletModel(_walletModel); receiveCoinsPage->setModel(_walletModel); sendCoinsPage->setModel(_walletModel); - usedReceivingAddressesPage->setModel(_walletModel->getAddressTableModel()); - usedSendingAddressesPage->setModel(_walletModel->getAddressTableModel()); + usedReceivingAddressesPage->setModel(_walletModel ? _walletModel->getAddressTableModel() : nullptr); + usedSendingAddressesPage->setModel(_walletModel ? _walletModel->getAddressTableModel() : nullptr); if (_walletModel) { From 96c2ce9d343147608c197bc2333138b82141340f Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Mon, 2 Oct 2017 18:24:59 -0400 Subject: [PATCH 316/382] Fix validationinterface build on super old boost/clang This should fix all the non-dependancy issues for termux builds. See Github issue #11388. --- src/validationinterface.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index be5029dec..281bc04b0 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -4,7 +4,9 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "validationinterface.h" + #include "init.h" +#include "primitives/block.h" #include "scheduler.h" #include "sync.h" #include "util.h" From 696ce46306e40f48dc4b2d7010812d564a43289d Mon Sep 17 00:00:00 2001 From: fanquake Date: Mon, 2 Oct 2017 19:16:04 +0800 Subject: [PATCH 317/382] [Docs] Update Windows build instructions for using WSL and Ubuntu 17.04 --- doc/build-windows.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/doc/build-windows.md b/doc/build-windows.md index 57e2d6834..0d96af26a 100644 --- a/doc/build-windows.md +++ b/doc/build-windows.md @@ -4,10 +4,11 @@ WINDOWS BUILD NOTES Below are some notes on how to build Bitcoin Core for Windows. Most developers use cross-compilation from Ubuntu to build executables for -Windows. This is also used to build the release binaries. +Windows. Cross-compilation is also used to build the release binaries. -Currently only building on Ubuntu Trusty 14.04 is supported. -Other versions are unsupported or known to be broken (e.g. Ubuntu Xenial 16.04). +Currently only building on Ubuntu Trusty 14.04 or Ubuntu Zesty 17.04 or later is supported. +Building on Ubuntu Xenial 16.04 is known to be broken, see extensive discussion in issue [8732](https://github.com/bitcoin/bitcoin/issues/8732). +While it may be possible to do so with work arounds, it's potentially dangerous and not recommended. While there are potentially a number of ways to build on Windows (for example using msys / mingw-w64), using the Windows Subsystem For Linux is the most straightforward. If you are building with @@ -64,6 +65,13 @@ build process. See also: [dependencies.md](dependencies.md). +If you're building on Ubuntu 17.04 or later, run these two commands, selecting the 'posix' variant for both, +to work around issues with mingw-w64. See issue [8732](https://github.com/bitcoin/bitcoin/issues/8732) for more information. +``` +sudo update-alternatives --config x86_64-w64-mingw32-g++ +sudo update-alternatives --config x86_64-w64-mingw32-gcc +``` + ## Building for 64-bit Windows To build executables for Windows 64-bit, install the following dependencies: @@ -84,7 +92,7 @@ Then build using: To build executables for Windows 32-bit, install the following dependencies: - sudo apt-get install g++-mingw-w64-i686 mingw-w64-i686-dev + sudo apt-get install g++-mingw-w64-i686 mingw-w64-i686-dev Then build using: From a2be3b66b56bccc01dfa2fb992515ae56bbedd49 Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Tue, 22 Aug 2017 14:09:04 -0700 Subject: [PATCH 318/382] [net] Ignore getheaders requests for very old side blocks Sending a getheaders message with an empty locator and a stop hash is a request for a single header by hash. The node will respond with headers for blocks not in the main chain as well as those in the main chain. To avoid fingerprinting, the node should, however, ignore requests for headers on side branches that are too old. --- src/net_processing.cpp | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 7fced41d4..b5e37722d 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -61,6 +61,14 @@ static std::vector> vExtraTxnForCompact GUAR static const uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL; // SHA256("main address relay")[0:8] +/// Age after which a stale block will no longer be served if requested as +/// protection against fingerprinting. Set to one month, denominated in seconds. +static const int STALE_RELAY_AGE_LIMIT = 30 * 24 * 60 * 60; + +/// Age after which a block is considered historical for purposes of rate +/// limiting block relay. Set to one week, denominated in seconds. +static const int HISTORICAL_BLOCK_AGE = 7 * 24 * 60 * 60; + // Internal stuff namespace { /** Number of nodes with fSyncStarted. */ @@ -706,6 +714,17 @@ void Misbehaving(NodeId pnode, int howmuch) // blockchain -> download logic notification // +// To prevent fingerprinting attacks, only send blocks/headers outside of the +// active chain if they are no more than a month older (both in time, and in +// best equivalent proof of work) than the best header chain we know about. +static bool StaleBlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Params& consensusParams) +{ + AssertLockHeld(cs_main); + return (pindexBestHeader != nullptr) && + (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() < STALE_RELAY_AGE_LIMIT) && + (GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) < STALE_RELAY_AGE_LIMIT); +} + PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn) : connman(connmanIn) { // Initialize global variables that cannot be constructed at startup. recentRejects.reset(new CRollingBloomFilter(120000, 0.000001)); @@ -983,13 +1002,8 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam if (chainActive.Contains(mi->second)) { send = true; } else { - static const int nOneMonth = 30 * 24 * 60 * 60; - // To prevent fingerprinting attacks, only send blocks outside of the active - // chain if they are valid, and no more than a month older (both in time, and in - // best equivalent proof of work) than the best header chain we know about. - send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != nullptr) && - (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() < nOneMonth) && - (GetBlockProofEquivalentTime(*pindexBestHeader, *mi->second, *pindexBestHeader, consensusParams) < nOneMonth); + send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) && + StaleBlockRequestAllowed(mi->second, consensusParams); if (!send) { LogPrintf("%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom->GetId()); } @@ -997,8 +1011,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam } // disconnect node in case we have reached the outbound limit for serving historical blocks // never disconnect whitelisted nodes - static const int nOneWeek = 7 * 24 * 60 * 60; // assume > 1 week = historical - if (send && connman->OutboundTargetReached(true) && ( ((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() > nOneWeek)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted) + if (send && connman->OutboundTargetReached(true) && ( ((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted) { LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom->GetId()); @@ -1723,6 +1736,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr if (mi == mapBlockIndex.end()) return true; pindex = (*mi).second; + + if (!chainActive.Contains(pindex) && + !StaleBlockRequestAllowed(pindex, chainparams.GetConsensus())) { + LogPrintf("%s: ignoring request from peer=%i for old block header that isn't in the main chain\n", __func__, pfrom->GetId()); + return true; + } } else { From eff4bd8ab257a553ca8898ccd803630b4d44a771 Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Thu, 24 Aug 2017 17:06:42 -0700 Subject: [PATCH 319/382] [test] P2P functional test for certain fingerprinting protections --- test/functional/p2p-fingerprint.py | 158 +++++++++++++++++++++ test/functional/test_framework/mininode.py | 4 +- test/functional/test_runner.py | 1 + 3 files changed, 161 insertions(+), 2 deletions(-) create mode 100755 test/functional/p2p-fingerprint.py diff --git a/test/functional/p2p-fingerprint.py b/test/functional/p2p-fingerprint.py new file mode 100755 index 000000000..fe60c6cd4 --- /dev/null +++ b/test/functional/p2p-fingerprint.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test various fingerprinting protections. + +If an stale block more than a month old or its header are requested by a peer, +the node should pretend that it does not have it to avoid fingerprinting. +""" + +import time + +from test_framework.blocktools import (create_block, create_coinbase) +from test_framework.mininode import ( + CInv, + NetworkThread, + NodeConn, + NodeConnCB, + msg_headers, + msg_block, + msg_getdata, + msg_getheaders, + wait_until, +) +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + assert_equal, + p2p_port, +) + +class P2PFingerprintTest(BitcoinTestFramework): + def set_test_params(self): + self.setup_clean_chain = True + self.num_nodes = 1 + + # Build a chain of blocks on top of given one + def build_chain(self, nblocks, prev_hash, prev_height, prev_median_time): + blocks = [] + for _ in range(nblocks): + coinbase = create_coinbase(prev_height + 1) + block_time = prev_median_time + 1 + block = create_block(int(prev_hash, 16), coinbase, block_time) + block.solve() + + blocks.append(block) + prev_hash = block.hash + prev_height += 1 + prev_median_time = block_time + return blocks + + # Send a getdata request for a given block hash + def send_block_request(self, block_hash, node): + msg = msg_getdata() + msg.inv.append(CInv(2, block_hash)) # 2 == "Block" + node.send_message(msg) + + # Send a getheaders request for a given single block hash + def send_header_request(self, block_hash, node): + msg = msg_getheaders() + msg.hashstop = block_hash + node.send_message(msg) + + # Check whether last block received from node has a given hash + def last_block_equals(self, expected_hash, node): + block_msg = node.last_message.get("block") + return block_msg and block_msg.block.rehash() == expected_hash + + # Check whether last block header received from node has a given hash + def last_header_equals(self, expected_hash, node): + headers_msg = node.last_message.get("headers") + return (headers_msg and + headers_msg.headers and + headers_msg.headers[0].rehash() == expected_hash) + + # Checks that stale blocks timestamped more than a month ago are not served + # by the node while recent stale blocks and old active chain blocks are. + # This does not currently test that stale blocks timestamped within the + # last month but that have over a month's worth of work are also withheld. + def run_test(self): + node0 = NodeConnCB() + + connections = [] + connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0)) + node0.add_connection(connections[0]) + + NetworkThread().start() + node0.wait_for_verack() + + # Set node time to 60 days ago + self.nodes[0].setmocktime(int(time.time()) - 60 * 24 * 60 * 60) + + # Generating a chain of 10 blocks + block_hashes = self.nodes[0].generate(nblocks=10) + + # Create longer chain starting 2 blocks before current tip + height = len(block_hashes) - 2 + block_hash = block_hashes[height - 1] + block_time = self.nodes[0].getblockheader(block_hash)["mediantime"] + 1 + new_blocks = self.build_chain(5, block_hash, height, block_time) + + # Force reorg to a longer chain + node0.send_message(msg_headers(new_blocks)) + node0.wait_for_getdata() + for block in new_blocks: + node0.send_and_ping(msg_block(block)) + + # Check that reorg succeeded + assert_equal(self.nodes[0].getblockcount(), 13) + + stale_hash = int(block_hashes[-1], 16) + + # Check that getdata request for stale block succeeds + self.send_block_request(stale_hash, node0) + test_function = lambda: self.last_block_equals(stale_hash, node0) + wait_until(test_function, timeout=3) + + # Check that getheader request for stale block header succeeds + self.send_header_request(stale_hash, node0) + test_function = lambda: self.last_header_equals(stale_hash, node0) + wait_until(test_function, timeout=3) + + # Longest chain is extended so stale is much older than chain tip + self.nodes[0].setmocktime(0) + tip = self.nodes[0].generate(nblocks=1)[0] + assert_equal(self.nodes[0].getblockcount(), 14) + + # Send getdata & getheaders to refresh last received getheader message + block_hash = int(tip, 16) + self.send_block_request(block_hash, node0) + self.send_header_request(block_hash, node0) + node0.sync_with_ping() + + # Request for very old stale block should now fail + self.send_block_request(stale_hash, node0) + time.sleep(3) + assert not self.last_block_equals(stale_hash, node0) + + # Request for very old stale block header should now fail + self.send_header_request(stale_hash, node0) + time.sleep(3) + assert not self.last_header_equals(stale_hash, node0) + + # Verify we can fetch very old blocks and headers on the active chain + block_hash = int(block_hashes[2], 16) + self.send_block_request(block_hash, node0) + self.send_header_request(block_hash, node0) + node0.sync_with_ping() + + self.send_block_request(block_hash, node0) + test_function = lambda: self.last_block_equals(block_hash, node0) + wait_until(test_function, timeout=3) + + self.send_header_request(block_hash, node0) + test_function = lambda: self.last_header_equals(block_hash, node0) + wait_until(test_function, timeout=3) + +if __name__ == '__main__': + P2PFingerprintTest().main() diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py index d072969d7..c6f596156 100755 --- a/test/functional/test_framework/mininode.py +++ b/test/functional/test_framework/mininode.py @@ -1310,8 +1310,8 @@ def __repr__(self): class msg_headers(object): command = b"headers" - def __init__(self): - self.headers = [] + def __init__(self, headers=None): + self.headers = headers if headers is not None else [] def deserialize(self, f): # comment in bitcoind indicates these should be deserialized as blocks diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 5c8740d7c..434ba23b3 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -123,6 +123,7 @@ 'uptime.py', 'resendwallettransactions.py', 'minchainwork.py', + 'p2p-fingerprint.py', ] EXTENDED_SCRIPTS = [ From edafc718ad071993d10b3b9a1e1828bbd1f8ce54 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Thu, 7 Sep 2017 17:20:26 -0400 Subject: [PATCH 320/382] Fix uninitialized URI in batch RPC requests This fixes "Wallet file not specified" errors when making batch wallet RPC calls with more than one wallet loaded. This issue was reported by NicolasDorier https://github.com/bitcoin/bitcoin/issues/11257 Request URI is not used for anything except multiwallet request dispatching, so this change has no other effects. Fixes #11257 --- src/httprpc.cpp | 2 +- src/rpc/server.cpp | 7 +++---- src/rpc/server.h | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/httprpc.cpp b/src/httprpc.cpp index 91f96ef20..93f0a1866 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -192,7 +192,7 @@ static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &) // array of requests } else if (valRequest.isArray()) - strReply = JSONRPCExecBatch(valRequest.get_array()); + strReply = JSONRPCExecBatch(jreq, valRequest.get_array()); else throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error"); diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index a73b697e0..39bcfc690 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -389,11 +389,10 @@ bool IsDeprecatedRPCEnabled(const std::string& method) return find(enabled_methods.begin(), enabled_methods.end(), method) != enabled_methods.end(); } -static UniValue JSONRPCExecOne(const UniValue& req) +static UniValue JSONRPCExecOne(JSONRPCRequest jreq, const UniValue& req) { UniValue rpc_result(UniValue::VOBJ); - JSONRPCRequest jreq; try { jreq.parse(req); @@ -413,11 +412,11 @@ static UniValue JSONRPCExecOne(const UniValue& req) return rpc_result; } -std::string JSONRPCExecBatch(const UniValue& vReq) +std::string JSONRPCExecBatch(const JSONRPCRequest& jreq, const UniValue& vReq) { UniValue ret(UniValue::VARR); for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++) - ret.push_back(JSONRPCExecOne(vReq[reqIdx])); + ret.push_back(JSONRPCExecOne(jreq, vReq[reqIdx])); return ret.write() + "\n"; } diff --git a/src/rpc/server.h b/src/rpc/server.h index 31d630427..74c4a9e80 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -191,7 +191,7 @@ extern std::string HelpExampleRpc(const std::string& methodname, const std::stri bool StartRPC(); void InterruptRPC(); void StopRPC(); -std::string JSONRPCExecBatch(const UniValue& vReq); +std::string JSONRPCExecBatch(const JSONRPCRequest& jreq, const UniValue& vReq); // Retrieves any serialization flags requested in command line argument int RPCSerializationFlags(); From e02007aade3d449f030fe5c8b12beddd7df1b232 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Thu, 7 Sep 2017 17:29:26 -0400 Subject: [PATCH 321/382] Limit AuthServiceProxyWrapper.__getattr__ wrapping Change AuthServiceProxyWrapper.__getattr__ to only wrap proxied attributes, not real attributes. This way AuthServiceProxyWrapper can continue logging RPC calls without complicating other object usages, and special case handling for the .url property can be dropped. --- test/functional/test_framework/coverage.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/test/functional/test_framework/coverage.py b/test/functional/test_framework/coverage.py index 227b1a17a..c0202e560 100644 --- a/test/functional/test_framework/coverage.py +++ b/test/functional/test_framework/coverage.py @@ -31,10 +31,11 @@ def __init__(self, auth_service_proxy_instance, coverage_logfile=None): self.auth_service_proxy_instance = auth_service_proxy_instance self.coverage_logfile = coverage_logfile - def __getattr__(self, *args, **kwargs): - return_val = self.auth_service_proxy_instance.__getattr__( - *args, **kwargs) - + def __getattr__(self, name): + return_val = getattr(self.auth_service_proxy_instance, name) + if not isinstance(return_val, type(self.auth_service_proxy_instance)): + # If proxy getattr returned an unwrapped value, do the same here. + return return_val return AuthServiceProxyWrapper(return_val, self.coverage_logfile) def __call__(self, *args, **kwargs): @@ -52,10 +53,6 @@ def __call__(self, *args, **kwargs): return return_val - @property - def url(self): - return self.auth_service_proxy_instance.url - def __truediv__(self, relative_uri): return AuthServiceProxyWrapper(self.auth_service_proxy_instance / relative_uri) From 9f67646f173dd29464666b34de2ec9cfc480c11a Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Thu, 7 Sep 2017 17:38:11 -0400 Subject: [PATCH 322/382] Make AuthServiceProxy._batch method usable Split off AuthServiceProxy.get_request method to make it easier to batch RPC requests without duplicating code and remove leading underscore from _batch method. This does not change any existing behavior. --- test/functional/test_framework/authproxy.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/test/functional/test_framework/authproxy.py b/test/functional/test_framework/authproxy.py index b3671cbdc..747bda309 100644 --- a/test/functional/test_framework/authproxy.py +++ b/test/functional/test_framework/authproxy.py @@ -138,17 +138,20 @@ def _request(self, method, path, postdata): self.__conn.request(method, path, postdata, headers) return self._get_response() - def __call__(self, *args, **argsn): + def get_request(self, *args, **argsn): AuthServiceProxy.__id_count += 1 log.debug("-%s-> %s %s"%(AuthServiceProxy.__id_count, self._service_name, json.dumps(args, default=EncodeDecimal, ensure_ascii=self.ensure_ascii))) if args and argsn: raise ValueError('Cannot handle both named and positional arguments') - postdata = json.dumps({'version': '1.1', - 'method': self._service_name, - 'params': args or argsn, - 'id': AuthServiceProxy.__id_count}, default=EncodeDecimal, ensure_ascii=self.ensure_ascii) + return {'version': '1.1', + 'method': self._service_name, + 'params': args or argsn, + 'id': AuthServiceProxy.__id_count} + + def __call__(self, *args, **argsn): + postdata = json.dumps(self.get_request(*args, **argsn), default=EncodeDecimal, ensure_ascii=self.ensure_ascii) response = self._request('POST', self.__url.path, postdata.encode('utf-8')) if response['error'] is not None: raise JSONRPCException(response['error']) @@ -158,7 +161,7 @@ def __call__(self, *args, **argsn): else: return response['result'] - def _batch(self, rpc_call_list): + def batch(self, rpc_call_list): postdata = json.dumps(list(rpc_call_list), default=EncodeDecimal, ensure_ascii=self.ensure_ascii) log.debug("--> "+postdata) return self._request('POST', self.__url.path, postdata.encode('utf-8')) From ce2418fa4cef5514305cca82e74891b9d643c4c7 Mon Sep 17 00:00:00 2001 From: Cristian Mircea Messel Date: Wed, 4 Oct 2017 00:53:27 +0300 Subject: [PATCH 323/382] [gui] reset addrProxy/addrSeparateProxyTor if colon char missing If addrProxy or addrSeparateProxyTor do not have a colon in the string somewhere in the QSettings storage, then attempting to open the options dialog will cause the entire program to crash. --- src/qt/optionsmodel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index feb00a33b..fb8c60d10 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -124,7 +124,7 @@ void OptionsModel::Init(bool resetSettings) if (!settings.contains("fUseProxy")) settings.setValue("fUseProxy", false); - if (!settings.contains("addrProxy")) + if (!settings.contains("addrProxy") || !settings.value("addrProxy").toString().contains(':')) settings.setValue("addrProxy", "127.0.0.1:9050"); // Only try to set -proxy, if user has enabled fUseProxy if (settings.value("fUseProxy").toBool() && !gArgs.SoftSetArg("-proxy", settings.value("addrProxy").toString().toStdString())) @@ -134,7 +134,7 @@ void OptionsModel::Init(bool resetSettings) if (!settings.contains("fUseSeparateProxyTor")) settings.setValue("fUseSeparateProxyTor", false); - if (!settings.contains("addrSeparateProxyTor")) + if (!settings.contains("addrSeparateProxyTor") || !settings.value("addrSeparateProxyTor").toString().contains(':')) settings.setValue("addrSeparateProxyTor", "127.0.0.1:9050"); // Only try to set -onion, if user has enabled fUseSeparateProxyTor if (settings.value("fUseSeparateProxyTor").toBool() && !gArgs.SoftSetArg("-onion", settings.value("addrSeparateProxyTor").toString().toStdString())) From 505530c6cffeab8dc1f75f54ae0dfdcdb55d370b Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Wed, 4 Oct 2017 03:03:07 -0400 Subject: [PATCH 324/382] Add missing multiwallet rpc calls to python coverage logs This fixes a bug in coverage logging that's been around since the logging was introduced. --- test/functional/test_framework/coverage.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/functional/test_framework/coverage.py b/test/functional/test_framework/coverage.py index c0202e560..8101775ce 100644 --- a/test/functional/test_framework/coverage.py +++ b/test/functional/test_framework/coverage.py @@ -54,7 +54,8 @@ def __call__(self, *args, **kwargs): return return_val def __truediv__(self, relative_uri): - return AuthServiceProxyWrapper(self.auth_service_proxy_instance / relative_uri) + return AuthServiceProxyWrapper(self.auth_service_proxy_instance / relative_uri, + self.coverage_logfile) def get_filename(dirname, n_node): """ From 74182f235cd04dcac7a8b3e763bc9add549745e1 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Wed, 4 Oct 2017 03:07:01 -0400 Subject: [PATCH 325/382] Add missing batch rpc calls to python coverage logs Without this change, batch RPC calls are not included in coverage logs. --- test/functional/test_framework/coverage.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/test/functional/test_framework/coverage.py b/test/functional/test_framework/coverage.py index 8101775ce..84049e76b 100644 --- a/test/functional/test_framework/coverage.py +++ b/test/functional/test_framework/coverage.py @@ -45,18 +45,24 @@ def __call__(self, *args, **kwargs): """ return_val = self.auth_service_proxy_instance.__call__(*args, **kwargs) + self._log_call() + return return_val + + def _log_call(self): rpc_method = self.auth_service_proxy_instance._service_name if self.coverage_logfile: with open(self.coverage_logfile, 'a+', encoding='utf8') as f: f.write("%s\n" % rpc_method) - return return_val - def __truediv__(self, relative_uri): return AuthServiceProxyWrapper(self.auth_service_proxy_instance / relative_uri, self.coverage_logfile) + def get_request(self, *args, **kwargs): + self._log_call() + return self.auth_service_proxy_instance.get_request(*args, **kwargs) + def get_filename(dirname, n_node): """ Get a filename unique to the test process ID and node. From 4526d21e52aa94f12121fcf01047c04f82c4990a Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Thu, 7 Sep 2017 17:40:25 -0400 Subject: [PATCH 326/382] Add test for multiwallet batch RPC calls Tests bug reported in https://github.com/bitcoin/bitcoin/issues/11257 --- test/functional/multiwallet.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/functional/multiwallet.py b/test/functional/multiwallet.py index b4e15a332..ec808e95c 100755 --- a/test/functional/multiwallet.py +++ b/test/functional/multiwallet.py @@ -76,5 +76,9 @@ def run_test(self): assert_equal(w2.getbalance(), 1) assert_equal(w3.getbalance(), 2) + batch = w1.batch([w1.getblockchaininfo.get_request(), w1.getwalletinfo.get_request()]) + assert_equal(batch[0]["result"]["chain"], "regtest") + assert_equal(batch[1]["result"]["walletname"], "w1") + if __name__ == '__main__': MultiWalletTest().main() From f35d03336994688c7419f9d3547336e356bd2818 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Sun, 1 Oct 2017 23:07:23 +0200 Subject: [PATCH 327/382] build: Make "make clean" remove all files created when running "make check" More specifically: remove also obj/build.h and bench/data/block413567.raw.h. Before this patch: ``` $ diff -rq bitcoin-before-make/ bitcoin-after-make-and-make-clean/ | grep -E "^Only in bitcoin-after-make-and-make-clean/" | grep -v dirstamp Only in bitcoin-after-make-and-make-clean/src/bench/data: block413567.raw.h Only in bitcoin-after-make-and-make-clean/src/obj: build.h $ ``` After this patch: ``` $ diff -rq bitcoin-before-make/ bitcoin-after-make-and-make-clean/ | grep -E "^Only in bitcoin-after-make-and-make-clean/" | grep -v dirstamp $ ``` --- src/Makefile.am | 3 +-- src/Makefile.bench.include | 9 +++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index c71e457eb..90deff48b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -476,8 +476,7 @@ CLEANFILES += univalue/*.gcda univalue/*.gcno CLEANFILES += wallet/*.gcda wallet/*.gcno CLEANFILES += wallet/test/*.gcda wallet/test/*.gcno CLEANFILES += zmq/*.gcda zmq/*.gcno - -DISTCLEANFILES = obj/build.h +CLEANFILES += obj/build.h EXTRA_DIST = $(CTAES_DIST) diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index 2b1f70b25..8e2e587d3 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -6,11 +6,12 @@ bin_PROGRAMS += bench/bench_bitcoin BENCH_SRCDIR = bench BENCH_BINARY = bench/bench_bitcoin$(EXEEXT) -RAW_TEST_FILES = \ +RAW_BENCH_FILES = \ bench/data/block413567.raw -GENERATED_TEST_FILES = $(RAW_TEST_FILES:.raw=.raw.h) +GENERATED_BENCH_FILES = $(RAW_BENCH_FILES:.raw=.raw.h) bench_bench_bitcoin_SOURCES = \ + $(RAW_BENCH_FILES) \ bench/bench_bitcoin.cpp \ bench/bench.cpp \ bench/bench.h \ @@ -28,7 +29,7 @@ bench_bench_bitcoin_SOURCES = \ bench/perf.h \ bench/prevector_destructor.cpp -nodist_bench_bench_bitcoin_SOURCES = $(GENERATED_TEST_FILES) +nodist_bench_bench_bitcoin_SOURCES = $(GENERATED_BENCH_FILES) bench_bench_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CLFAGS) $(EVENT_PTHREADS_CFLAGS) -I$(builddir)/bench/ bench_bench_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) @@ -56,7 +57,7 @@ endif bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) bench_bench_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno $(GENERATED_TEST_FILES) +CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno $(GENERATED_BENCH_FILES) CLEANFILES += $(CLEAN_BITCOIN_BENCH) From 5864e9c1611e6034a8d49b78231abe6bfade686d Mon Sep 17 00:00:00 2001 From: John Newbery Date: Wed, 12 Jul 2017 10:29:02 -0400 Subject: [PATCH 328/382] [tests] remove direct testing on JSONRPCException from individual test cases --- test/functional/import-rescan.py | 30 +++++++++++--------------- test/functional/importmulti.py | 4 ++-- test/functional/segwit.py | 6 +----- test/functional/signrawtransactions.py | 2 +- test/functional/test_framework/util.py | 10 ++++++++- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/test/functional/import-rescan.py b/test/functional/import-rescan.py index cb5b65c68..aece60bc3 100755 --- a/test/functional/import-rescan.py +++ b/test/functional/import-rescan.py @@ -19,9 +19,8 @@ happened previously. """ -from test_framework.authproxy import JSONRPCException from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import (connect_nodes, sync_blocks, assert_equal, set_node_times) +from test_framework.util import (assert_raises_jsonrpc, connect_nodes, sync_blocks, assert_equal, set_node_times) import collections import enum @@ -35,21 +34,26 @@ class Variant(collections.namedtuple("Variant", "call data rescan prune")): """Helper for importing one key and verifying scanned transactions.""" + def try_rpc(self, func, *args, **kwargs): + if self.expect_disabled: + assert_raises_jsonrpc(-4, "Rescan is disabled in pruned mode", func, *args, **kwargs) + else: + return func(*args, **kwargs) + def do_import(self, timestamp): """Call one key import RPC.""" if self.call == Call.single: if self.data == Data.address: - response, error = try_rpc(self.node.importaddress, self.address["address"], self.label, - self.rescan == Rescan.yes) + response = self.try_rpc(self.node.importaddress, self.address["address"], self.label, + self.rescan == Rescan.yes) elif self.data == Data.pub: - response, error = try_rpc(self.node.importpubkey, self.address["pubkey"], self.label, - self.rescan == Rescan.yes) + response = self.try_rpc(self.node.importpubkey, self.address["pubkey"], self.label, + self.rescan == Rescan.yes) elif self.data == Data.priv: - response, error = try_rpc(self.node.importprivkey, self.key, self.label, self.rescan == Rescan.yes) + response = self.try_rpc(self.node.importprivkey, self.key, self.label, self.rescan == Rescan.yes) assert_equal(response, None) - assert_equal(error, {'message': 'Rescan is disabled in pruned mode', - 'code': -4} if self.expect_disabled else None) + elif self.call == Call.multi: response = self.node.importmulti([{ "scriptPubKey": { @@ -179,13 +183,5 @@ def run_test(self): else: variant.check() - -def try_rpc(func, *args, **kwargs): - try: - return func(*args, **kwargs), None - except JSONRPCException as e: - return None, e.error - - if __name__ == "__main__": ImportRescanTest().main() diff --git a/test/functional/importmulti.py b/test/functional/importmulti.py index 32f555c79..fcc38123a 100755 --- a/test/functional/importmulti.py +++ b/test/functional/importmulti.py @@ -423,11 +423,11 @@ def run_test (self): # Bad or missing timestamps self.log.info("Should throw on invalid or missing timestamp values") - assert_raises_message(JSONRPCException, 'Missing required timestamp field for key', + assert_raises_jsonrpc(-3, 'Missing required timestamp field for key', self.nodes[1].importmulti, [{ "scriptPubKey": address['scriptPubKey'], }]) - assert_raises_message(JSONRPCException, 'Expected number or "now" timestamp value for key. got type string', + assert_raises_jsonrpc(-3, 'Expected number or "now" timestamp value for key. got type string', self.nodes[1].importmulti, [{ "scriptPubKey": address['scriptPubKey'], "timestamp": "", diff --git a/test/functional/segwit.py b/test/functional/segwit.py index de5c8c6c8..3fcbaa4fa 100755 --- a/test/functional/segwit.py +++ b/test/functional/segwit.py @@ -452,11 +452,7 @@ def run_test(self): for i in importlist: # import all generated addresses. The wallet already has the private keys for some of these, so catch JSON RPC # exceptions and continue. - try: - self.nodes[0].importaddress(i,"",False,True) - except JSONRPCException as exp: - assert_equal(exp.error["message"], "The wallet already contains the private key for this address or script") - assert_equal(exp.error["code"], -4) + try_rpc(-4, "The wallet already contains the private key for this address or script", self.nodes[0].importaddress, i, "", False, True) self.nodes[0].importaddress(script_to_p2sh(op0)) # import OP_0 as address only self.nodes[0].importaddress(multisig_without_privkey_address) # Test multisig_without_privkey diff --git a/test/functional/signrawtransactions.py b/test/functional/signrawtransactions.py index b47ef9395..00a58e108 100755 --- a/test/functional/signrawtransactions.py +++ b/test/functional/signrawtransactions.py @@ -82,7 +82,7 @@ def script_verification_error_test(self): assert_equal(decodedRawTx["vin"][i]["vout"], inp["vout"]) # Make sure decoderawtransaction throws if there is extra data - assert_raises(JSONRPCException, self.nodes[0].decoderawtransaction, rawTx + "00") + assert_raises_jsonrpc(-22, "TX decode failed", self.nodes[0].decoderawtransaction, rawTx + "00") rawTxSigned = self.nodes[0].signrawtransaction(rawTx, scripts, privKeys) diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index 64966adb9..44d7e04a8 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -99,6 +99,13 @@ def assert_raises_jsonrpc(code, message, fun, *args, **kwds): args*: positional arguments for the function. kwds**: named arguments for the function. """ + assert try_rpc(code, message, fun, *args, **kwds), "No exception raised" + +def try_rpc(code, message, fun, *args, **kwds): + """Tries to run an rpc command. + + Test against error code and message if the rpc fails. + Returns whether a JSONRPCException was raised.""" try: fun(*args, **kwds) except JSONRPCException as e: @@ -107,10 +114,11 @@ def assert_raises_jsonrpc(code, message, fun, *args, **kwds): raise AssertionError("Unexpected JSONRPC error code %i" % e.error["code"]) if (message is not None) and (message not in e.error['message']): raise AssertionError("Expected substring not found:" + e.error['message']) + return True except Exception as e: raise AssertionError("Unexpected exception raised: " + type(e).__name__) else: - raise AssertionError("No exception raised") + return False def assert_is_hex_string(string): try: From 677d893ff758b61f66203730da224bd7ef8f0b43 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Wed, 12 Jul 2017 10:29:21 -0400 Subject: [PATCH 329/382] [tests] do not allow assert_raises_message to be called with JSONRPCException --- test/functional/test_framework/util.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index 44d7e04a8..ed35bf576 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -51,6 +51,8 @@ def assert_raises(exc, fun, *args, **kwds): def assert_raises_message(exc, message, fun, *args, **kwds): try: fun(*args, **kwds) + except JSONRPCException: + raise AssertionError("Use assert_raises_jsonrpc() to test RPC failures") except exc as e: if message is not None and message not in e.error['message']: raise AssertionError("Expected substring not found:" + e.error['message']) From 47ba8cf71e9a3a6f7452488fc9ab3b350f0fae36 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Wed, 12 Jul 2017 10:33:46 -0400 Subject: [PATCH 330/382] scripted-diff: rename assert_raises_jsonrpc to assert_raises_rpc error -BEGIN VERIFY SCRIPT- sed -i 's/assert_raises_jsonrpc/assert_raises_rpc_error/g' test/functional/*py test/functional/test_framework/*py -END VERIFY SCRIPT- --- test/functional/bip68-sequence.py | 10 +++---- test/functional/blockchain.py | 6 ++--- test/functional/bumpfee.py | 14 +++++----- test/functional/deprecated_rpc.py | 4 +-- test/functional/disablewallet.py | 4 +-- test/functional/disconnect_ban.py | 12 ++++----- test/functional/fundrawtransaction.py | 12 ++++----- test/functional/import-rescan.py | 4 +-- test/functional/importmulti.py | 4 +-- test/functional/importprunedfunds.py | 4 +-- test/functional/keypool.py | 8 +++--- test/functional/mempool_packages.py | 4 +-- test/functional/mempool_persist.py | 2 +- test/functional/mempool_reorg.py | 4 +-- test/functional/mempool_spendcoinbase.py | 2 +- test/functional/merkle_blocks.py | 8 +++--- test/functional/mining.py | 10 +++---- test/functional/multiwallet.py | 6 ++--- test/functional/net.py | 4 +-- test/functional/nulldummy.py | 6 ++--- test/functional/p2p-acceptblock.py | 2 +- test/functional/prioritise_transaction.py | 2 +- test/functional/pruning.py | 8 +++--- test/functional/rawtransactions.py | 12 ++++----- test/functional/replace-by-fee.py | 30 ++++++++++----------- test/functional/resendwallettransactions.py | 4 +-- test/functional/rpcbind_test.py | 2 +- test/functional/rpcnamedargs.py | 4 +-- test/functional/segwit.py | 8 +++--- test/functional/signrawtransactions.py | 2 +- test/functional/test_framework/util.py | 4 +-- test/functional/wallet-dump.py | 4 +-- test/functional/wallet-encryption.py | 12 ++++----- test/functional/wallet.py | 8 +++--- test/functional/zapwallettxes.py | 4 +-- 35 files changed, 117 insertions(+), 117 deletions(-) diff --git a/test/functional/bip68-sequence.py b/test/functional/bip68-sequence.py index 74f51d8cf..5f8f21701 100755 --- a/test/functional/bip68-sequence.py +++ b/test/functional/bip68-sequence.py @@ -83,7 +83,7 @@ def test_disable_flag(self): tx2.vout = [CTxOut(int(value-self.relayfee*COIN), CScript([b'a']))] tx2.rehash() - assert_raises_jsonrpc(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx2)) + assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx2)) # Setting the version back down to 1 should disable the sequence lock, # so this should be accepted. @@ -180,7 +180,7 @@ def test_sequence_lock_confirmed_inputs(self): if (using_sequence_locks and not should_pass): # This transaction should be rejected - assert_raises_jsonrpc(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, rawtx) + assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, rawtx) else: # This raw transaction should be accepted self.nodes[0].sendrawtransaction(rawtx) @@ -227,7 +227,7 @@ def test_nonzero_locks(orig_tx, node, relayfee, use_height_lock): if (orig_tx.hash in node.getrawmempool()): # sendrawtransaction should fail if the tx is in the mempool - assert_raises_jsonrpc(-26, NOT_FINAL_ERROR, node.sendrawtransaction, ToHex(tx)) + assert_raises_rpc_error(-26, NOT_FINAL_ERROR, node.sendrawtransaction, ToHex(tx)) else: # sendrawtransaction should succeed if the tx is not in the mempool node.sendrawtransaction(ToHex(tx)) @@ -280,7 +280,7 @@ def test_nonzero_locks(orig_tx, node, relayfee, use_height_lock): tx5.vout[0].nValue += int(utxos[0]["amount"]*COIN) raw_tx5 = self.nodes[0].signrawtransaction(ToHex(tx5))["hex"] - assert_raises_jsonrpc(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, raw_tx5) + assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, raw_tx5) # Test mempool-BIP68 consistency after reorg # @@ -353,7 +353,7 @@ def test_bip68_not_consensus(self): tx3.vout = [CTxOut(int(tx2.vout[0].nValue - self.relayfee*COIN), CScript([b'a']))] tx3.rehash() - assert_raises_jsonrpc(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx3)) + assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx3)) # make a block that violates bip68; ensure that the tip updates tip = int(self.nodes[0].getbestblockhash(), 16) diff --git a/test/functional/blockchain.py b/test/functional/blockchain.py index 5bd3bc8f8..d4851eea4 100755 --- a/test/functional/blockchain.py +++ b/test/functional/blockchain.py @@ -25,7 +25,7 @@ from test_framework.util import ( assert_equal, assert_raises, - assert_raises_jsonrpc, + assert_raises_rpc_error, assert_is_hex_string, assert_is_hash_string, ) @@ -101,7 +101,7 @@ def _test_getchaintxstats(self): assert('window_interval' not in chaintxstats) assert('txrate' not in chaintxstats) - assert_raises_jsonrpc(-8, "Invalid block count: should be between 0 and the block's height - 1", self.nodes[0].getchaintxstats, 201) + assert_raises_rpc_error(-8, "Invalid block count: should be between 0 and the block's height - 1", self.nodes[0].getchaintxstats, 201) def _test_gettxoutsetinfo(self): node = self.nodes[0] @@ -147,7 +147,7 @@ def _test_gettxoutsetinfo(self): def _test_getblockheader(self): node = self.nodes[0] - assert_raises_jsonrpc(-5, "Block not found", + assert_raises_rpc_error(-5, "Block not found", node.getblockheader, "nonsense") besthash = node.getbestblockhash() diff --git a/test/functional/bumpfee.py b/test/functional/bumpfee.py index dde87d5bd..008e83d5b 100755 --- a/test/functional/bumpfee.py +++ b/test/functional/bumpfee.py @@ -133,7 +133,7 @@ def test_segwit_bumpfee_succeeds(rbf_node, dest_address): def test_nonrbf_bumpfee_fails(peer_node, dest_address): # cannot replace a non RBF transaction (from node which did not enable RBF) not_rbfid = peer_node.sendtoaddress(dest_address, Decimal("0.00090000")) - assert_raises_jsonrpc(-4, "not BIP 125 replaceable", peer_node.bumpfee, not_rbfid) + assert_raises_rpc_error(-4, "not BIP 125 replaceable", peer_node.bumpfee, not_rbfid) def test_notmine_bumpfee_fails(rbf_node, peer_node, dest_address): @@ -153,7 +153,7 @@ def test_notmine_bumpfee_fails(rbf_node, peer_node, dest_address): signedtx = rbf_node.signrawtransaction(rawtx) signedtx = peer_node.signrawtransaction(signedtx["hex"]) rbfid = rbf_node.sendrawtransaction(signedtx["hex"]) - assert_raises_jsonrpc(-4, "Transaction contains inputs that don't belong to this wallet", + assert_raises_rpc_error(-4, "Transaction contains inputs that don't belong to this wallet", rbf_node.bumpfee, rbfid) @@ -164,7 +164,7 @@ def test_bumpfee_with_descendant_fails(rbf_node, rbf_node_address, dest_address) tx = rbf_node.createrawtransaction([{"txid": parent_id, "vout": 0}], {dest_address: 0.00020000}) tx = rbf_node.signrawtransaction(tx) rbf_node.sendrawtransaction(tx["hex"]) - assert_raises_jsonrpc(-8, "Transaction has descendants in the wallet", rbf_node.bumpfee, parent_id) + assert_raises_rpc_error(-8, "Transaction has descendants in the wallet", rbf_node.bumpfee, parent_id) def test_small_output_fails(rbf_node, dest_address): @@ -173,7 +173,7 @@ def test_small_output_fails(rbf_node, dest_address): rbf_node.bumpfee(rbfid, {"totalFee": 50000}) rbfid = spend_one_input(rbf_node, dest_address) - assert_raises_jsonrpc(-4, "Change output is too small", rbf_node.bumpfee, rbfid, {"totalFee": 50001}) + assert_raises_rpc_error(-4, "Change output is too small", rbf_node.bumpfee, rbfid, {"totalFee": 50001}) def test_dust_to_fee(rbf_node, dest_address): @@ -205,7 +205,7 @@ def test_rebumping(rbf_node, dest_address): # check that re-bumping the original tx fails, but bumping the bumper succeeds rbfid = spend_one_input(rbf_node, dest_address) bumped = rbf_node.bumpfee(rbfid, {"totalFee": 2000}) - assert_raises_jsonrpc(-4, "already bumped", rbf_node.bumpfee, rbfid, {"totalFee": 3000}) + assert_raises_rpc_error(-4, "already bumped", rbf_node.bumpfee, rbfid, {"totalFee": 3000}) rbf_node.bumpfee(bumped["txid"], {"totalFee": 3000}) @@ -213,7 +213,7 @@ def test_rebumping_not_replaceable(rbf_node, dest_address): # check that re-bumping a non-replaceable bump tx fails rbfid = spend_one_input(rbf_node, dest_address) bumped = rbf_node.bumpfee(rbfid, {"totalFee": 10000, "replaceable": False}) - assert_raises_jsonrpc(-4, "Transaction is not BIP 125 replaceable", rbf_node.bumpfee, bumped["txid"], + assert_raises_rpc_error(-4, "Transaction is not BIP 125 replaceable", rbf_node.bumpfee, bumped["txid"], {"totalFee": 20000}) @@ -264,7 +264,7 @@ def test_bumpfee_metadata(rbf_node, dest_address): def test_locked_wallet_fails(rbf_node, dest_address): rbfid = spend_one_input(rbf_node, dest_address) rbf_node.walletlock() - assert_raises_jsonrpc(-13, "Please enter the wallet passphrase with walletpassphrase first.", + assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first.", rbf_node.bumpfee, rbfid) diff --git a/test/functional/deprecated_rpc.py b/test/functional/deprecated_rpc.py index ec500ccbf..19fd24edb 100755 --- a/test/functional/deprecated_rpc.py +++ b/test/functional/deprecated_rpc.py @@ -4,7 +4,7 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test deprecation of RPC calls.""" from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_raises_jsonrpc +from test_framework.util import assert_raises_rpc_error class DeprecatedRpcTest(BitcoinTestFramework): def set_test_params(self): @@ -14,7 +14,7 @@ def set_test_params(self): def run_test(self): self.log.info("estimatefee: Shows deprecated message") - assert_raises_jsonrpc(-32, 'estimatefee is deprecated', self.nodes[0].estimatefee, 1) + assert_raises_rpc_error(-32, 'estimatefee is deprecated', self.nodes[0].estimatefee, 1) self.log.info("Using -deprecatedrpc=estimatefee bypasses the error") self.nodes[1].estimatefee(1) diff --git a/test/functional/disablewallet.py b/test/functional/disablewallet.py index c1d37963b..c75ef9b9f 100755 --- a/test/functional/disablewallet.py +++ b/test/functional/disablewallet.py @@ -19,7 +19,7 @@ def set_test_params(self): def run_test (self): # Make sure wallet is really disabled - assert_raises_jsonrpc(-32601, 'Method not found', self.nodes[0].getwalletinfo) + assert_raises_rpc_error(-32601, 'Method not found', self.nodes[0].getwalletinfo) x = self.nodes[0].validateaddress('3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy') assert(x['isvalid'] == False) x = self.nodes[0].validateaddress('mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ') @@ -28,7 +28,7 @@ def run_test (self): # Checking mining to an address without a wallet. Generating to a valid address should succeed # but generating to an invalid address will fail. self.nodes[0].generatetoaddress(1, 'mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ') - assert_raises_jsonrpc(-5, "Invalid address", self.nodes[0].generatetoaddress, 1, '3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy') + assert_raises_rpc_error(-5, "Invalid address", self.nodes[0].generatetoaddress, 1, '3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy') if __name__ == '__main__': DisableWalletTest ().main () diff --git a/test/functional/disconnect_ban.py b/test/functional/disconnect_ban.py index a6445b9b3..59655d37f 100755 --- a/test/functional/disconnect_ban.py +++ b/test/functional/disconnect_ban.py @@ -8,7 +8,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, - assert_raises_jsonrpc, + assert_raises_rpc_error, connect_nodes_bi, wait_until, ) @@ -34,14 +34,14 @@ def run_test(self): self.log.info("setban: fail to ban an already banned subnet") assert_equal(len(self.nodes[1].listbanned()), 1) - assert_raises_jsonrpc(-23, "IP/Subnet already banned", self.nodes[1].setban, "127.0.0.1", "add") + assert_raises_rpc_error(-23, "IP/Subnet already banned", self.nodes[1].setban, "127.0.0.1", "add") self.log.info("setban: fail to ban an invalid subnet") - assert_raises_jsonrpc(-30, "Error: Invalid IP/Subnet", self.nodes[1].setban, "127.0.0.1/42", "add") + assert_raises_rpc_error(-30, "Error: Invalid IP/Subnet", self.nodes[1].setban, "127.0.0.1/42", "add") assert_equal(len(self.nodes[1].listbanned()), 1) # still only one banned ip because 127.0.0.1 is within the range of 127.0.0.0/24 self.log.info("setban remove: fail to unban a non-banned subnet") - assert_raises_jsonrpc(-30, "Error: Unban failed", self.nodes[1].setban, "127.0.0.1", "remove") + assert_raises_rpc_error(-30, "Error: Unban failed", self.nodes[1].setban, "127.0.0.1", "remove") assert_equal(len(self.nodes[1].listbanned()), 1) self.log.info("setban remove: successfully unban subnet") @@ -81,10 +81,10 @@ def run_test(self): self.log.info("disconnectnode: fail to disconnect when calling with address and nodeid") address1 = self.nodes[0].getpeerinfo()[0]['addr'] node1 = self.nodes[0].getpeerinfo()[0]['addr'] - assert_raises_jsonrpc(-32602, "Only one of address and nodeid should be provided.", self.nodes[0].disconnectnode, address=address1, nodeid=node1) + assert_raises_rpc_error(-32602, "Only one of address and nodeid should be provided.", self.nodes[0].disconnectnode, address=address1, nodeid=node1) self.log.info("disconnectnode: fail to disconnect when calling with junk address") - assert_raises_jsonrpc(-29, "Node not found in connected nodes", self.nodes[0].disconnectnode, address="221B Baker Street") + assert_raises_rpc_error(-29, "Node not found in connected nodes", self.nodes[0].disconnectnode, address="221B Baker Street") self.log.info("disconnectnode: successfully disconnect node by address") address1 = self.nodes[0].getpeerinfo()[0]['addr'] diff --git a/test/functional/fundrawtransaction.py b/test/functional/fundrawtransaction.py index 71e88009b..d446f56d0 100755 --- a/test/functional/fundrawtransaction.py +++ b/test/functional/fundrawtransaction.py @@ -179,7 +179,7 @@ def run_test(self): dec_tx = self.nodes[2].decoderawtransaction(rawtx) assert_equal(utx['txid'], dec_tx['vin'][0]['txid']) - assert_raises_jsonrpc(-3, "Unexpected key foo", self.nodes[2].fundrawtransaction, rawtx, {'foo':'bar'}) + assert_raises_rpc_error(-3, "Unexpected key foo", self.nodes[2].fundrawtransaction, rawtx, {'foo':'bar'}) ############################################################ # test a fundrawtransaction with an invalid change address # @@ -192,7 +192,7 @@ def run_test(self): dec_tx = self.nodes[2].decoderawtransaction(rawtx) assert_equal(utx['txid'], dec_tx['vin'][0]['txid']) - assert_raises_jsonrpc(-5, "changeAddress must be a valid bitcoin address", self.nodes[2].fundrawtransaction, rawtx, {'changeAddress':'foobar'}) + assert_raises_rpc_error(-5, "changeAddress must be a valid bitcoin address", self.nodes[2].fundrawtransaction, rawtx, {'changeAddress':'foobar'}) ############################################################ # test a fundrawtransaction with a provided change address # @@ -206,7 +206,7 @@ def run_test(self): assert_equal(utx['txid'], dec_tx['vin'][0]['txid']) change = self.nodes[2].getnewaddress() - assert_raises_jsonrpc(-8, "changePosition out of bounds", self.nodes[2].fundrawtransaction, rawtx, {'changeAddress':change, 'changePosition':2}) + assert_raises_rpc_error(-8, "changePosition out of bounds", self.nodes[2].fundrawtransaction, rawtx, {'changeAddress':change, 'changePosition':2}) rawtxfund = self.nodes[2].fundrawtransaction(rawtx, {'changeAddress': change, 'changePosition': 0}) dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex']) out = dec_tx['vout'][0] @@ -314,7 +314,7 @@ def run_test(self): rawtx = self.nodes[2].createrawtransaction(inputs, outputs) dec_tx = self.nodes[2].decoderawtransaction(rawtx) - assert_raises_jsonrpc(-4, "Insufficient funds", self.nodes[2].fundrawtransaction, rawtx) + assert_raises_rpc_error(-4, "Insufficient funds", self.nodes[2].fundrawtransaction, rawtx) ############################################################ #compare fee of a standard pubkeyhash transaction @@ -469,14 +469,14 @@ def run_test(self): rawtx = self.nodes[1].createrawtransaction(inputs, outputs) # fund a transaction that requires a new key for the change output # creating the key must be impossible because the wallet is locked - assert_raises_jsonrpc(-4, "Keypool ran out, please call keypoolrefill first", self.nodes[1].fundrawtransaction, rawtx) + assert_raises_rpc_error(-4, "Keypool ran out, please call keypoolrefill first", self.nodes[1].fundrawtransaction, rawtx) #refill the keypool self.nodes[1].walletpassphrase("test", 100) self.nodes[1].keypoolrefill(8) #need to refill the keypool to get an internal change address self.nodes[1].walletlock() - assert_raises_jsonrpc(-13, "walletpassphrase", self.nodes[1].sendtoaddress, self.nodes[0].getnewaddress(), 1.2) + assert_raises_rpc_error(-13, "walletpassphrase", self.nodes[1].sendtoaddress, self.nodes[0].getnewaddress(), 1.2) oldBalance = self.nodes[0].getbalance() diff --git a/test/functional/import-rescan.py b/test/functional/import-rescan.py index aece60bc3..6807fa669 100755 --- a/test/functional/import-rescan.py +++ b/test/functional/import-rescan.py @@ -20,7 +20,7 @@ """ from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import (assert_raises_jsonrpc, connect_nodes, sync_blocks, assert_equal, set_node_times) +from test_framework.util import (assert_raises_rpc_error, connect_nodes, sync_blocks, assert_equal, set_node_times) import collections import enum @@ -36,7 +36,7 @@ class Variant(collections.namedtuple("Variant", "call data rescan prune")): def try_rpc(self, func, *args, **kwargs): if self.expect_disabled: - assert_raises_jsonrpc(-4, "Rescan is disabled in pruned mode", func, *args, **kwargs) + assert_raises_rpc_error(-4, "Rescan is disabled in pruned mode", func, *args, **kwargs) else: return func(*args, **kwargs) diff --git a/test/functional/importmulti.py b/test/functional/importmulti.py index fcc38123a..c1a42870e 100755 --- a/test/functional/importmulti.py +++ b/test/functional/importmulti.py @@ -423,11 +423,11 @@ def run_test (self): # Bad or missing timestamps self.log.info("Should throw on invalid or missing timestamp values") - assert_raises_jsonrpc(-3, 'Missing required timestamp field for key', + assert_raises_rpc_error(-3, 'Missing required timestamp field for key', self.nodes[1].importmulti, [{ "scriptPubKey": address['scriptPubKey'], }]) - assert_raises_jsonrpc(-3, 'Expected number or "now" timestamp value for key. got type string', + assert_raises_rpc_error(-3, 'Expected number or "now" timestamp value for key. got type string', self.nodes[1].importmulti, [{ "scriptPubKey": address['scriptPubKey'], "timestamp": "", diff --git a/test/functional/importprunedfunds.py b/test/functional/importprunedfunds.py index cb7001066..3236b3573 100755 --- a/test/functional/importprunedfunds.py +++ b/test/functional/importprunedfunds.py @@ -66,7 +66,7 @@ def run_test(self): self.sync_all() #Import with no affiliated address - assert_raises_jsonrpc(-5, "No addresses", self.nodes[1].importprunedfunds, rawtxn1, proof1) + assert_raises_rpc_error(-5, "No addresses", self.nodes[1].importprunedfunds, rawtxn1, proof1) balance1 = self.nodes[1].getbalance("", 0, True) assert_equal(balance1, Decimal(0)) @@ -97,7 +97,7 @@ def run_test(self): assert_equal(address_info['ismine'], True) #Remove transactions - assert_raises_jsonrpc(-8, "Transaction does not exist in wallet.", self.nodes[1].removeprunedfunds, txnid1) + assert_raises_rpc_error(-8, "Transaction does not exist in wallet.", self.nodes[1].removeprunedfunds, txnid1) balance1 = self.nodes[1].getbalance("*", 0, True) assert_equal(balance1, Decimal('0.075')) diff --git a/test/functional/keypool.py b/test/functional/keypool.py index b823ca63b..f2701c36b 100755 --- a/test/functional/keypool.py +++ b/test/functional/keypool.py @@ -28,7 +28,7 @@ def run_test(self): wallet_info = nodes[0].getwalletinfo() assert(addr_before_encrypting_data['hdmasterkeyid'] != wallet_info['hdmasterkeyid']) assert(addr_data['hdmasterkeyid'] == wallet_info['hdmasterkeyid']) - assert_raises_jsonrpc(-12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress) + assert_raises_rpc_error(-12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress) # put six (plus 2) new keys in the keypool (100% external-, +100% internal-keys, 1 in min) nodes[0].walletpassphrase('test', 12000) @@ -47,7 +47,7 @@ def run_test(self): nodes[0].getrawchangeaddress() addr = set() # the next one should fail - assert_raises_jsonrpc(-12, "Keypool ran out", nodes[0].getrawchangeaddress) + assert_raises_rpc_error(-12, "Keypool ran out", nodes[0].getrawchangeaddress) # drain the external keys addr.add(nodes[0].getnewaddress()) @@ -58,7 +58,7 @@ def run_test(self): addr.add(nodes[0].getnewaddress()) assert(len(addr) == 6) # the next one should fail - assert_raises_jsonrpc(-12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress) + assert_raises_rpc_error(-12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress) # refill keypool with three new addresses nodes[0].walletpassphrase('test', 1) @@ -72,7 +72,7 @@ def run_test(self): nodes[0].generate(1) nodes[0].generate(1) nodes[0].generate(1) - assert_raises_jsonrpc(-12, "Keypool ran out", nodes[0].generate, 1) + assert_raises_rpc_error(-12, "Keypool ran out", nodes[0].generate, 1) nodes[0].walletpassphrase('test', 100) nodes[0].keypoolrefill(100) diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py index 2b0988966..b845c7568 100755 --- a/test/functional/mempool_packages.py +++ b/test/functional/mempool_packages.py @@ -115,7 +115,7 @@ def run_test(self): assert_equal(mempool[x]['descendantfees'], descendant_fees * COIN + 1000) # Adding one more transaction on to the chain should fail. - assert_raises_jsonrpc(-26, "too-long-mempool-chain", self.chain_transaction, self.nodes[0], txid, vout, value, fee, 1) + assert_raises_rpc_error(-26, "too-long-mempool-chain", self.chain_transaction, self.nodes[0], txid, vout, value, fee, 1) # Check that prioritising a tx before it's added to the mempool works # First clear the mempool by mining a block. @@ -167,7 +167,7 @@ def run_test(self): # Sending one more chained transaction will fail utxo = transaction_package.pop(0) - assert_raises_jsonrpc(-26, "too-long-mempool-chain", self.chain_transaction, self.nodes[0], utxo['txid'], utxo['vout'], utxo['amount'], fee, 10) + assert_raises_rpc_error(-26, "too-long-mempool-chain", self.chain_transaction, self.nodes[0], utxo['txid'], utxo['vout'], utxo['amount'], fee, 10) # TODO: check that node1's mempool is as expected diff --git a/test/functional/mempool_persist.py b/test/functional/mempool_persist.py index 149a01870..92f66be2f 100755 --- a/test/functional/mempool_persist.py +++ b/test/functional/mempool_persist.py @@ -103,7 +103,7 @@ def run_test(self): mempooldotnew1 = mempooldat1 + '.new' with os.fdopen(os.open(mempooldotnew1, os.O_CREAT, 0o000), 'w'): pass - assert_raises_jsonrpc(-1, "Unable to dump mempool to disk", self.nodes[1].savemempool) + assert_raises_rpc_error(-1, "Unable to dump mempool to disk", self.nodes[1].savemempool) os.remove(mempooldotnew1) if __name__ == '__main__': diff --git a/test/functional/mempool_reorg.py b/test/functional/mempool_reorg.py index 7dfddd323..2803371f5 100755 --- a/test/functional/mempool_reorg.py +++ b/test/functional/mempool_reorg.py @@ -50,14 +50,14 @@ def run_test(self): timelock_tx = timelock_tx[:-8] + hex(self.nodes[0].getblockcount() + 2)[2:] + "000000" timelock_tx = self.nodes[0].signrawtransaction(timelock_tx)["hex"] # This will raise an exception because the timelock transaction is too immature to spend - assert_raises_jsonrpc(-26, "non-final", self.nodes[0].sendrawtransaction, timelock_tx) + assert_raises_rpc_error(-26, "non-final", self.nodes[0].sendrawtransaction, timelock_tx) # Broadcast and mine spend_102 and 103: spend_102_id = self.nodes[0].sendrawtransaction(spend_102_raw) spend_103_id = self.nodes[0].sendrawtransaction(spend_103_raw) self.nodes[0].generate(1) # Time-locked transaction is still too immature to spend - assert_raises_jsonrpc(-26,'non-final', self.nodes[0].sendrawtransaction, timelock_tx) + assert_raises_rpc_error(-26,'non-final', self.nodes[0].sendrawtransaction, timelock_tx) # Create 102_1 and 103_1: spend_102_1_raw = create_tx(self.nodes[0], spend_102_id, node1_address, 49.98) diff --git a/test/functional/mempool_spendcoinbase.py b/test/functional/mempool_spendcoinbase.py index 58ccd3e37..6e8a635a7 100755 --- a/test/functional/mempool_spendcoinbase.py +++ b/test/functional/mempool_spendcoinbase.py @@ -36,7 +36,7 @@ def run_test(self): spend_101_id = self.nodes[0].sendrawtransaction(spends_raw[0]) # coinbase at height 102 should be too immature to spend - assert_raises_jsonrpc(-26,"bad-txns-premature-spend-of-coinbase", self.nodes[0].sendrawtransaction, spends_raw[1]) + assert_raises_rpc_error(-26,"bad-txns-premature-spend-of-coinbase", self.nodes[0].sendrawtransaction, spends_raw[1]) # mempool should have just spend_101: assert_equal(self.nodes[0].getrawmempool(), [ spend_101_id ]) diff --git a/test/functional/merkle_blocks.py b/test/functional/merkle_blocks.py index a58334b2a..b3989a4c5 100755 --- a/test/functional/merkle_blocks.py +++ b/test/functional/merkle_blocks.py @@ -38,7 +38,7 @@ def run_test(self): tx2 = self.nodes[0].createrawtransaction([node0utxos.pop()], {self.nodes[1].getnewaddress(): 49.99}) txid2 = self.nodes[0].sendrawtransaction(self.nodes[0].signrawtransaction(tx2)["hex"]) # This will raise an exception because the transaction is not yet in a block - assert_raises_jsonrpc(-5, "Transaction not yet in block", self.nodes[0].gettxoutproof, [txid1]) + assert_raises_rpc_error(-5, "Transaction not yet in block", self.nodes[0].gettxoutproof, [txid1]) self.nodes[0].generate(1) blockhash = self.nodes[0].getblockhash(chain_height + 1) @@ -63,11 +63,11 @@ def run_test(self): txid_unspent = txid1 if txin_spent["txid"] != txid1 else txid2 # We can't find the block from a fully-spent tx - assert_raises_jsonrpc(-5, "Transaction not yet in block", self.nodes[2].gettxoutproof, [txid_spent]) + assert_raises_rpc_error(-5, "Transaction not yet in block", self.nodes[2].gettxoutproof, [txid_spent]) # We can get the proof if we specify the block assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid_spent], blockhash)), [txid_spent]) # We can't get the proof if we specify a non-existent block - assert_raises_jsonrpc(-5, "Block not found", self.nodes[2].gettxoutproof, [txid_spent], "00000000000000000000000000000000") + assert_raises_rpc_error(-5, "Block not found", self.nodes[2].gettxoutproof, [txid_spent], "00000000000000000000000000000000") # We can get the proof if the transaction is unspent assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid_unspent])), [txid_unspent]) # We can get the proof if we provide a list of transactions and one of them is unspent. The ordering of the list should not matter. @@ -76,7 +76,7 @@ def run_test(self): # We can always get a proof if we have a -txindex assert_equal(self.nodes[2].verifytxoutproof(self.nodes[3].gettxoutproof([txid_spent])), [txid_spent]) # We can't get a proof if we specify transactions from different blocks - assert_raises_jsonrpc(-5, "Not all transactions found in specified or retrieved block", self.nodes[2].gettxoutproof, [txid1, txid3]) + assert_raises_rpc_error(-5, "Not all transactions found in specified or retrieved block", self.nodes[2].gettxoutproof, [txid1, txid3]) if __name__ == '__main__': diff --git a/test/functional/mining.py b/test/functional/mining.py index c8fb1062b..9aee06864 100755 --- a/test/functional/mining.py +++ b/test/functional/mining.py @@ -15,7 +15,7 @@ from test_framework.blocktools import create_coinbase from test_framework.mininode import CBlock from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, assert_raises_jsonrpc +from test_framework.util import assert_equal, assert_raises_rpc_error def b2x(b): return b2a_hex(b).decode('ascii') @@ -68,7 +68,7 @@ def run_test(self): assert_template(node, block, None) self.log.info("submitblock: Test block decode failure") - assert_raises_jsonrpc(-22, "Block decode failed", node.submitblock, b2x(block.serialize()[:-15])) + assert_raises_rpc_error(-22, "Block decode failed", node.submitblock, b2x(block.serialize()[:-15])) self.log.info("getblocktemplate: Test bad input hash for coinbase transaction") bad_block = copy.deepcopy(block) @@ -77,10 +77,10 @@ def run_test(self): assert_template(node, bad_block, 'bad-cb-missing') self.log.info("submitblock: Test invalid coinbase transaction") - assert_raises_jsonrpc(-22, "Block does not start with a coinbase", node.submitblock, b2x(bad_block.serialize())) + assert_raises_rpc_error(-22, "Block does not start with a coinbase", node.submitblock, b2x(bad_block.serialize())) self.log.info("getblocktemplate: Test truncated final transaction") - assert_raises_jsonrpc(-22, "Block decode failed", node.getblocktemplate, {'data': b2x(block.serialize()[:-1]), 'mode': 'proposal'}) + assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {'data': b2x(block.serialize()[:-1]), 'mode': 'proposal'}) self.log.info("getblocktemplate: Test duplicate transaction") bad_block = copy.deepcopy(block) @@ -107,7 +107,7 @@ def run_test(self): bad_block_sn = bytearray(block.serialize()) assert_equal(bad_block_sn[TX_COUNT_OFFSET], 1) bad_block_sn[TX_COUNT_OFFSET] += 1 - assert_raises_jsonrpc(-22, "Block decode failed", node.getblocktemplate, {'data': b2x(bad_block_sn), 'mode': 'proposal'}) + assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {'data': b2x(bad_block_sn), 'mode': 'proposal'}) self.log.info("getblocktemplate: Test bad bits") bad_block = copy.deepcopy(block) diff --git a/test/functional/multiwallet.py b/test/functional/multiwallet.py index b4e15a332..6adcc1fd8 100755 --- a/test/functional/multiwallet.py +++ b/test/functional/multiwallet.py @@ -9,7 +9,7 @@ import os from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, assert_raises_jsonrpc +from test_framework.util import assert_equal, assert_raises_rpc_error class MultiWalletTest(BitcoinTestFramework): def set_test_params(self): @@ -43,10 +43,10 @@ def run_test(self): w1.generate(1) # accessing invalid wallet fails - assert_raises_jsonrpc(-18, "Requested wallet does not exist or is not loaded", wallet_bad.getwalletinfo) + assert_raises_rpc_error(-18, "Requested wallet does not exist or is not loaded", wallet_bad.getwalletinfo) # accessing wallet RPC without using wallet endpoint fails - assert_raises_jsonrpc(-19, "Wallet file not specified", self.nodes[0].getwalletinfo) + assert_raises_rpc_error(-19, "Wallet file not specified", self.nodes[0].getwalletinfo) # check w1 wallet balance w1_info = w1.getwalletinfo() diff --git a/test/functional/net.py b/test/functional/net.py index 830aeb45b..16e4f6adb 100755 --- a/test/functional/net.py +++ b/test/functional/net.py @@ -12,7 +12,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, - assert_raises_jsonrpc, + assert_raises_rpc_error, connect_nodes_bi, p2p_port, ) @@ -84,7 +84,7 @@ def _test_getaddednodeinfo(self): assert_equal(len(added_nodes), 1) assert_equal(added_nodes[0]['addednode'], ip_port) # check that a non-existent node returns an error - assert_raises_jsonrpc(-24, "Node has not been added", + assert_raises_rpc_error(-24, "Node has not been added", self.nodes[0].getaddednodeinfo, '1.1.1.1') def _test_getpeerinfo(self): diff --git a/test/functional/nulldummy.py b/test/functional/nulldummy.py index 60d0d876d..91c455065 100755 --- a/test/functional/nulldummy.py +++ b/test/functional/nulldummy.py @@ -71,7 +71,7 @@ def run_test(self): self.log.info("Test 2: Non-NULLDUMMY base multisig transaction should not be accepted to mempool before activation") test2tx = self.create_transaction(self.nodes[0], txid2, self.ms_address, 47) trueDummy(test2tx) - assert_raises_jsonrpc(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, bytes_to_hex_str(test2tx.serialize_with_witness()), True) + assert_raises_rpc_error(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, bytes_to_hex_str(test2tx.serialize_with_witness()), True) self.log.info("Test 3: Non-NULLDUMMY base transactions should be accepted in a block before activation [431]") self.block_submit(self.nodes[0], [test2tx], False, True) @@ -80,14 +80,14 @@ def run_test(self): test4tx = self.create_transaction(self.nodes[0], test2tx.hash, self.address, 46) test6txs=[CTransaction(test4tx)] trueDummy(test4tx) - assert_raises_jsonrpc(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, bytes_to_hex_str(test4tx.serialize_with_witness()), True) + assert_raises_rpc_error(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, bytes_to_hex_str(test4tx.serialize_with_witness()), True) self.block_submit(self.nodes[0], [test4tx]) self.log.info("Test 5: Non-NULLDUMMY P2WSH multisig transaction invalid after activation") test5tx = self.create_transaction(self.nodes[0], txid3, self.wit_address, 48) test6txs.append(CTransaction(test5tx)) test5tx.wit.vtxinwit[0].scriptWitness.stack[0] = b'\x01' - assert_raises_jsonrpc(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, bytes_to_hex_str(test5tx.serialize_with_witness()), True) + assert_raises_rpc_error(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, bytes_to_hex_str(test5tx.serialize_with_witness()), True) self.block_submit(self.nodes[0], [test5tx], True) self.log.info("Test 6: NULLDUMMY compliant base/witness transactions should be accepted to mempool and in block after activation [432]") diff --git a/test/functional/p2p-acceptblock.py b/test/functional/p2p-acceptblock.py index 293bc0553..5b6429b41 100755 --- a/test/functional/p2p-acceptblock.py +++ b/test/functional/p2p-acceptblock.py @@ -171,7 +171,7 @@ def run_test(self): # Blocks 1-287 should be accepted, block 288 should be ignored because it's too far ahead for x in all_blocks[:-1]: self.nodes[0].getblock(x.hash) - assert_raises_jsonrpc(-1, "Block not found on disk", self.nodes[0].getblock, all_blocks[-1].hash) + assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, all_blocks[-1].hash) headers_message.headers.pop() # Ensure the last block is unrequested white_node.send_message(headers_message) # Send headers leading to tip diff --git a/test/functional/prioritise_transaction.py b/test/functional/prioritise_transaction.py index 7ad368acd..bb56db9b4 100755 --- a/test/functional/prioritise_transaction.py +++ b/test/functional/prioritise_transaction.py @@ -101,7 +101,7 @@ def run_test(self): tx_id = self.nodes[0].decoderawtransaction(tx_hex)["txid"] # This will raise an exception due to min relay fee not being met - assert_raises_jsonrpc(-26, "66: min relay fee not met", self.nodes[0].sendrawtransaction, tx_hex) + assert_raises_rpc_error(-26, "66: min relay fee not met", self.nodes[0].sendrawtransaction, tx_hex) assert(tx_id not in self.nodes[0].getrawmempool()) # This is a less than 1000-byte transaction, so just set the fee diff --git a/test/functional/pruning.py b/test/functional/pruning.py index f53fe8288..0101f6118 100755 --- a/test/functional/pruning.py +++ b/test/functional/pruning.py @@ -185,7 +185,7 @@ def reorg_test(self): def reorg_back(self): # Verify that a block on the old main chain fork has been pruned away - assert_raises_jsonrpc(-1, "Block not available (pruned data)", self.nodes[2].getblock, self.forkhash) + assert_raises_rpc_error(-1, "Block not available (pruned data)", self.nodes[2].getblock, self.forkhash) self.log.info("Will need to redownload block %d" % self.forkheight) # Verify that we have enough history to reorg back to the fork point @@ -232,7 +232,7 @@ def manual_test(self, node_number, use_timestamp): self.start_node(node_number) node = self.nodes[node_number] assert_equal(node.getblockcount(), 995) - assert_raises_jsonrpc(-1, "not in prune mode", node.pruneblockchain, 500) + assert_raises_rpc_error(-1, "not in prune mode", node.pruneblockchain, 500) # now re-start in manual pruning mode self.stop_node(node_number) @@ -265,14 +265,14 @@ def has_block(index): return os.path.isfile(self.options.tmpdir + "/node{}/regtest/blocks/blk{:05}.dat".format(node_number, index)) # should not prune because chain tip of node 3 (995) < PruneAfterHeight (1000) - assert_raises_jsonrpc(-1, "Blockchain is too short for pruning", node.pruneblockchain, height(500)) + assert_raises_rpc_error(-1, "Blockchain is too short for pruning", node.pruneblockchain, height(500)) # mine 6 blocks so we are at height 1001 (i.e., above PruneAfterHeight) node.generate(6) assert_equal(node.getblockchaininfo()["blocks"], 1001) # negative heights should raise an exception - assert_raises_jsonrpc(-8, "Negative", node.pruneblockchain, -10) + assert_raises_rpc_error(-8, "Negative", node.pruneblockchain, -10) # height=100 too low to prune first block file so this is a no-op prune(100) diff --git a/test/functional/rawtransactions.py b/test/functional/rawtransactions.py index d7255daa0..2777cb969 100755 --- a/test/functional/rawtransactions.py +++ b/test/functional/rawtransactions.py @@ -48,7 +48,7 @@ def run_test(self): rawtx = self.nodes[2].signrawtransaction(rawtx) # This will raise an exception since there are missing inputs - assert_raises_jsonrpc(-25, "Missing inputs", self.nodes[2].sendrawtransaction, rawtx['hex']) + assert_raises_rpc_error(-25, "Missing inputs", self.nodes[2].sendrawtransaction, rawtx['hex']) ######################### # RAW TX MULTISIG TESTS # @@ -188,13 +188,13 @@ def run_test(self): assert_equal(self.nodes[0].getrawtransaction(txHash, True)["hex"], rawTxSigned['hex']) # 6. invalid parameters - supply txid and string "Flase" - assert_raises_jsonrpc(-3,"Invalid type", self.nodes[0].getrawtransaction, txHash, "Flase") + assert_raises_rpc_error(-3,"Invalid type", self.nodes[0].getrawtransaction, txHash, "Flase") # 7. invalid parameters - supply txid and empty array - assert_raises_jsonrpc(-3,"Invalid type", self.nodes[0].getrawtransaction, txHash, []) + assert_raises_rpc_error(-3,"Invalid type", self.nodes[0].getrawtransaction, txHash, []) # 8. invalid parameters - supply txid and empty dict - assert_raises_jsonrpc(-3,"Invalid type", self.nodes[0].getrawtransaction, txHash, {}) + assert_raises_rpc_error(-3,"Invalid type", self.nodes[0].getrawtransaction, txHash, {}) inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : 1000}] outputs = { self.nodes[0].getnewaddress() : 1 } @@ -205,12 +205,12 @@ def run_test(self): # 9. invalid parameters - sequence number out of range inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : -1}] outputs = { self.nodes[0].getnewaddress() : 1 } - assert_raises_jsonrpc(-8, 'Invalid parameter, sequence number is out of range', self.nodes[0].createrawtransaction, inputs, outputs) + assert_raises_rpc_error(-8, 'Invalid parameter, sequence number is out of range', self.nodes[0].createrawtransaction, inputs, outputs) # 10. invalid parameters - sequence number out of range inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : 4294967296}] outputs = { self.nodes[0].getnewaddress() : 1 } - assert_raises_jsonrpc(-8, 'Invalid parameter, sequence number is out of range', self.nodes[0].createrawtransaction, inputs, outputs) + assert_raises_rpc_error(-8, 'Invalid parameter, sequence number is out of range', self.nodes[0].createrawtransaction, inputs, outputs) inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : 4294967294}] outputs = { self.nodes[0].getnewaddress() : 1 } diff --git a/test/functional/replace-by-fee.py b/test/functional/replace-by-fee.py index 75f3d77c1..269d57775 100755 --- a/test/functional/replace-by-fee.py +++ b/test/functional/replace-by-fee.py @@ -125,9 +125,9 @@ def test_simple_doublespend(self): tx1b_hex = txToHex(tx1b) # This will raise an exception due to insufficient fee - assert_raises_jsonrpc(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, True) + assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, True) # This will raise an exception due to transaction replacement being disabled - assert_raises_jsonrpc(-26, "txn-mempool-conflict", self.nodes[1].sendrawtransaction, tx1b_hex, True) + assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[1].sendrawtransaction, tx1b_hex, True) # Extra 0.1 BTC fee tx1b = CTransaction() @@ -135,7 +135,7 @@ def test_simple_doublespend(self): tx1b.vout = [CTxOut(int(0.9*COIN), CScript([b'b']))] tx1b_hex = txToHex(tx1b) # Replacement still disabled even with "enough fee" - assert_raises_jsonrpc(-26, "txn-mempool-conflict", self.nodes[1].sendrawtransaction, tx1b_hex, True) + assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[1].sendrawtransaction, tx1b_hex, True) # Works when enabled tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True) @@ -178,7 +178,7 @@ def test_doublespend_chain(self): dbl_tx_hex = txToHex(dbl_tx) # This will raise an exception due to insufficient fee - assert_raises_jsonrpc(-26, "insufficient fee", self.nodes[0].sendrawtransaction, dbl_tx_hex, True) + assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, dbl_tx_hex, True) # Accepted with sufficient fee dbl_tx = CTransaction() @@ -239,7 +239,7 @@ def branch(prevout, initial_value, max_txs, tree_width=5, fee=0.0001*COIN, _tota dbl_tx.vout = [CTxOut(initial_nValue - fee*n, CScript([1]))] dbl_tx_hex = txToHex(dbl_tx) # This will raise an exception due to insufficient fee - assert_raises_jsonrpc(-26, "insufficient fee", self.nodes[0].sendrawtransaction, dbl_tx_hex, True) + assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, dbl_tx_hex, True) # 1 BTC fee is enough dbl_tx = CTransaction() @@ -267,7 +267,7 @@ def branch(prevout, initial_value, max_txs, tree_width=5, fee=0.0001*COIN, _tota dbl_tx.vout = [CTxOut(initial_nValue - 2*fee*n, CScript([1]))] dbl_tx_hex = txToHex(dbl_tx) # This will raise an exception - assert_raises_jsonrpc(-26, "too many potential replacements", self.nodes[0].sendrawtransaction, dbl_tx_hex, True) + assert_raises_rpc_error(-26, "too many potential replacements", self.nodes[0].sendrawtransaction, dbl_tx_hex, True) for tx in tree_txs: tx.rehash() @@ -291,7 +291,7 @@ def test_replacement_feeperkb(self): tx1b_hex = txToHex(tx1b) # This will raise an exception due to insufficient fee - assert_raises_jsonrpc(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, True) + assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, True) def test_spends_of_conflicting_outputs(self): """Replacements that spend conflicting tx outputs are rejected""" @@ -314,7 +314,7 @@ def test_spends_of_conflicting_outputs(self): tx2_hex = txToHex(tx2) # This will raise an exception - assert_raises_jsonrpc(-26, "bad-txns-spends-conflicting-tx", self.nodes[0].sendrawtransaction, tx2_hex, True) + assert_raises_rpc_error(-26, "bad-txns-spends-conflicting-tx", self.nodes[0].sendrawtransaction, tx2_hex, True) # Spend tx1a's output to test the indirect case. tx1b = CTransaction() @@ -331,7 +331,7 @@ def test_spends_of_conflicting_outputs(self): tx2_hex = txToHex(tx2) # This will raise an exception - assert_raises_jsonrpc(-26, "bad-txns-spends-conflicting-tx", self.nodes[0].sendrawtransaction, tx2_hex, True) + assert_raises_rpc_error(-26, "bad-txns-spends-conflicting-tx", self.nodes[0].sendrawtransaction, tx2_hex, True) def test_new_unconfirmed_inputs(self): """Replacements that add new unconfirmed inputs are rejected""" @@ -350,7 +350,7 @@ def test_new_unconfirmed_inputs(self): tx2_hex = txToHex(tx2) # This will raise an exception - assert_raises_jsonrpc(-26, "replacement-adds-unconfirmed", self.nodes[0].sendrawtransaction, tx2_hex, True) + assert_raises_rpc_error(-26, "replacement-adds-unconfirmed", self.nodes[0].sendrawtransaction, tx2_hex, True) def test_too_many_replacements(self): """Replacements that evict too many transactions are rejected""" @@ -396,7 +396,7 @@ def test_too_many_replacements(self): double_tx_hex = txToHex(double_tx) # This will raise an exception - assert_raises_jsonrpc(-26, "too many potential replacements", self.nodes[0].sendrawtransaction, double_tx_hex, True) + assert_raises_rpc_error(-26, "too many potential replacements", self.nodes[0].sendrawtransaction, double_tx_hex, True) # If we remove an input, it should pass double_tx = CTransaction() @@ -423,7 +423,7 @@ def test_opt_in(self): tx1b_hex = txToHex(tx1b) # This will raise an exception - assert_raises_jsonrpc(-26, "txn-mempool-conflict", self.nodes[0].sendrawtransaction, tx1b_hex, True) + assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[0].sendrawtransaction, tx1b_hex, True) tx1_outpoint = make_utxo(self.nodes[0], int(1.1*COIN)) @@ -441,7 +441,7 @@ def test_opt_in(self): tx2b_hex = txToHex(tx2b) # This will raise an exception - assert_raises_jsonrpc(-26, "txn-mempool-conflict", self.nodes[0].sendrawtransaction, tx2b_hex, True) + assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[0].sendrawtransaction, tx2b_hex, True) # Now create a new transaction that spends from tx1a and tx2a # opt-in on one of the inputs @@ -493,7 +493,7 @@ def test_prioritised_transactions(self): tx1b_hex = txToHex(tx1b) # Verify tx1b cannot replace tx1a. - assert_raises_jsonrpc(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, True) + assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, True) # Use prioritisetransaction to set tx1a's fee to 0. self.nodes[0].prioritisetransaction(txid=tx1a_txid, fee_delta=int(-0.1*COIN)) @@ -520,7 +520,7 @@ def test_prioritised_transactions(self): tx2b_hex = txToHex(tx2b) # Verify tx2b cannot replace tx2a. - assert_raises_jsonrpc(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx2b_hex, True) + assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx2b_hex, True) # Now prioritise tx2b to have a higher modified fee self.nodes[0].prioritisetransaction(txid=tx2b.hash, fee_delta=int(0.1*COIN)) diff --git a/test/functional/resendwallettransactions.py b/test/functional/resendwallettransactions.py index d6ba59139..d959bb4c3 100755 --- a/test/functional/resendwallettransactions.py +++ b/test/functional/resendwallettransactions.py @@ -5,7 +5,7 @@ """Test resendwallettransactions RPC.""" from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, assert_raises_jsonrpc +from test_framework.util import assert_equal, assert_raises_rpc_error class ResendWalletTransactionsTest(BitcoinTestFramework): def set_test_params(self): @@ -14,7 +14,7 @@ def set_test_params(self): def run_test(self): # Should raise RPC_WALLET_ERROR (-4) if walletbroadcast is disabled. - assert_raises_jsonrpc(-4, "Error: Wallet transaction broadcasting is disabled with -walletbroadcast", self.nodes[0].resendwallettransactions) + assert_raises_rpc_error(-4, "Error: Wallet transaction broadcasting is disabled with -walletbroadcast", self.nodes[0].resendwallettransactions) # Should return an empty array if there aren't unconfirmed wallet transactions. self.stop_node(0) diff --git a/test/functional/rpcbind_test.py b/test/functional/rpcbind_test.py index 0cf64beeb..0e8c3fa20 100755 --- a/test/functional/rpcbind_test.py +++ b/test/functional/rpcbind_test.py @@ -101,7 +101,7 @@ def run_test(self): # Check that with invalid rpcallowip, we are denied self.run_allowip_test([non_loopback_ip], non_loopback_ip, defaultport) - assert_raises_jsonrpc(-342, "non-JSON HTTP response with '403 Forbidden' from server", self.run_allowip_test, ['1.1.1.1'], non_loopback_ip, defaultport) + assert_raises_rpc_error(-342, "non-JSON HTTP response with '403 Forbidden' from server", self.run_allowip_test, ['1.1.1.1'], non_loopback_ip, defaultport) if __name__ == '__main__': RPCBindTest().main() diff --git a/test/functional/rpcnamedargs.py b/test/functional/rpcnamedargs.py index b3cc681da..c47212bdd 100755 --- a/test/functional/rpcnamedargs.py +++ b/test/functional/rpcnamedargs.py @@ -7,7 +7,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, - assert_raises_jsonrpc, + assert_raises_rpc_error, ) class NamedArgumentTest(BitcoinTestFramework): @@ -19,7 +19,7 @@ def run_test(self): h = node.help(command='getblockchaininfo') assert(h.startswith('getblockchaininfo\n')) - assert_raises_jsonrpc(-8, 'Unknown named parameter', node.help, random='getblockchaininfo') + assert_raises_rpc_error(-8, 'Unknown named parameter', node.help, random='getblockchaininfo') h = node.getblockhash(height=0) node.getblock(blockhash=h) diff --git a/test/functional/segwit.py b/test/functional/segwit.py index 3fcbaa4fa..6ecade7cb 100755 --- a/test/functional/segwit.py +++ b/test/functional/segwit.py @@ -99,11 +99,11 @@ def skip_mine(self, node, txid, sign, redeem_script=""): sync_blocks(self.nodes) def fail_accept(self, node, error_msg, txid, sign, redeem_script=""): - assert_raises_jsonrpc(-26, error_msg, send_to_witness, 1, node, getutxo(txid), self.pubkey[0], False, Decimal("49.998"), sign, redeem_script) + assert_raises_rpc_error(-26, error_msg, send_to_witness, 1, node, getutxo(txid), self.pubkey[0], False, Decimal("49.998"), sign, redeem_script) def fail_mine(self, node, txid, sign, redeem_script=""): send_to_witness(1, node, getutxo(txid), self.pubkey[0], False, Decimal("49.998"), sign, redeem_script) - assert_raises_jsonrpc(-1, "CreateNewBlock: TestBlockValidity failed", node.generate, 1) + assert_raises_rpc_error(-1, "CreateNewBlock: TestBlockValidity failed", node.generate, 1) sync_blocks(self.nodes) def run_test(self): @@ -465,7 +465,7 @@ def run_test(self): # addwitnessaddress should refuse to return a witness address if an uncompressed key is used # note that no witness address should be returned by unsolvable addresses for i in uncompressed_spendable_address + uncompressed_solvable_address + unknown_address + unsolvable_address: - assert_raises_jsonrpc(-4, "Public key or redeemscript not known to wallet, or the key is uncompressed", self.nodes[0].addwitnessaddress, i) + assert_raises_rpc_error(-4, "Public key or redeemscript not known to wallet, or the key is uncompressed", self.nodes[0].addwitnessaddress, i) # addwitnessaddress should return a witness addresses even if keys are not in the wallet self.nodes[0].addwitnessaddress(multisig_without_privkey_address) @@ -548,7 +548,7 @@ def run_test(self): # premature_witaddress are not accepted until the script is added with addwitnessaddress first for i in uncompressed_spendable_address + uncompressed_solvable_address + premature_witaddress: # This will raise an exception - assert_raises_jsonrpc(-4, "Public key or redeemscript not known to wallet, or the key is uncompressed", self.nodes[0].addwitnessaddress, i) + assert_raises_rpc_error(-4, "Public key or redeemscript not known to wallet, or the key is uncompressed", self.nodes[0].addwitnessaddress, i) # after importaddress it should pass addwitnessaddress v = self.nodes[0].validateaddress(compressed_solvable_address[1]) diff --git a/test/functional/signrawtransactions.py b/test/functional/signrawtransactions.py index 00a58e108..9a45d53cb 100755 --- a/test/functional/signrawtransactions.py +++ b/test/functional/signrawtransactions.py @@ -82,7 +82,7 @@ def script_verification_error_test(self): assert_equal(decodedRawTx["vin"][i]["vout"], inp["vout"]) # Make sure decoderawtransaction throws if there is extra data - assert_raises_jsonrpc(-22, "TX decode failed", self.nodes[0].decoderawtransaction, rawTx + "00") + assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].decoderawtransaction, rawTx + "00") rawTxSigned = self.nodes[0].signrawtransaction(rawTx, scripts, privKeys) diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index ed35bf576..102c90301 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -52,7 +52,7 @@ def assert_raises_message(exc, message, fun, *args, **kwds): try: fun(*args, **kwds) except JSONRPCException: - raise AssertionError("Use assert_raises_jsonrpc() to test RPC failures") + raise AssertionError("Use assert_raises_rpc_error() to test RPC failures") except exc as e: if message is not None and message not in e.error['message']: raise AssertionError("Expected substring not found:" + e.error['message']) @@ -85,7 +85,7 @@ def assert_raises_process_error(returncode, output, fun, *args, **kwds): else: raise AssertionError("No exception raised") -def assert_raises_jsonrpc(code, message, fun, *args, **kwds): +def assert_raises_rpc_error(code, message, fun, *args, **kwds): """Run an RPC and verify that a specific JSONRPC exception code and message is raised. Calls function `fun` with arguments `args` and `kwds`. Catches a JSONRPCException diff --git a/test/functional/wallet-dump.py b/test/functional/wallet-dump.py index 12db95e5d..47de8777a 100755 --- a/test/functional/wallet-dump.py +++ b/test/functional/wallet-dump.py @@ -7,7 +7,7 @@ import os from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import (assert_equal, assert_raises_jsonrpc) +from test_framework.util import (assert_equal, assert_raises_rpc_error) def read_dump(file_name, addrs, hd_master_addr_old): @@ -106,7 +106,7 @@ def run_test (self): assert_equal(found_addr_rsv, 90*2) # Overwriting should fail - assert_raises_jsonrpc(-8, "already exists", self.nodes[0].dumpwallet, tmpdir + "/node0/wallet.unencrypted.dump") + assert_raises_rpc_error(-8, "already exists", self.nodes[0].dumpwallet, tmpdir + "/node0/wallet.unencrypted.dump") if __name__ == '__main__': WalletDumpTest().main () diff --git a/test/functional/wallet-encryption.py b/test/functional/wallet-encryption.py index ce1e7744e..db62e1e30 100755 --- a/test/functional/wallet-encryption.py +++ b/test/functional/wallet-encryption.py @@ -9,7 +9,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, - assert_raises_jsonrpc, + assert_raises_rpc_error, ) class WalletEncryptionTest(BitcoinTestFramework): @@ -32,7 +32,7 @@ def run_test(self): self.start_node(0) # Test that the wallet is encrypted - assert_raises_jsonrpc(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].dumpprivkey, address) + assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].dumpprivkey, address) # Check that walletpassphrase works self.nodes[0].walletpassphrase(passphrase, 2) @@ -40,20 +40,20 @@ def run_test(self): # Check that the timeout is right time.sleep(2) - assert_raises_jsonrpc(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].dumpprivkey, address) + assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].dumpprivkey, address) # Test wrong passphrase - assert_raises_jsonrpc(-14, "wallet passphrase entered was incorrect", self.nodes[0].walletpassphrase, passphrase + "wrong", 10) + assert_raises_rpc_error(-14, "wallet passphrase entered was incorrect", self.nodes[0].walletpassphrase, passphrase + "wrong", 10) # Test walletlock self.nodes[0].walletpassphrase(passphrase, 84600) assert_equal(privkey, self.nodes[0].dumpprivkey(address)) self.nodes[0].walletlock() - assert_raises_jsonrpc(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].dumpprivkey, address) + assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].dumpprivkey, address) # Test passphrase changes self.nodes[0].walletpassphrasechange(passphrase, passphrase2) - assert_raises_jsonrpc(-14, "wallet passphrase entered was incorrect", self.nodes[0].walletpassphrase, passphrase, 10) + assert_raises_rpc_error(-14, "wallet passphrase entered was incorrect", self.nodes[0].walletpassphrase, passphrase, 10) self.nodes[0].walletpassphrase(passphrase2, 10) assert_equal(privkey, self.nodes[0].dumpprivkey(address)) diff --git a/test/functional/wallet.py b/test/functional/wallet.py index a643684c7..9d8ae5035 100755 --- a/test/functional/wallet.py +++ b/test/functional/wallet.py @@ -101,7 +101,7 @@ def run_test(self): unspent_0 = self.nodes[2].listunspent()[0] unspent_0 = {"txid": unspent_0["txid"], "vout": unspent_0["vout"]} self.nodes[2].lockunspent(False, [unspent_0]) - assert_raises_jsonrpc(-4, "Insufficient funds", self.nodes[2].sendtoaddress, self.nodes[2].getnewaddress(), 20) + assert_raises_rpc_error(-4, "Insufficient funds", self.nodes[2].sendtoaddress, self.nodes[2].getnewaddress(), 20) assert_equal([unspent_0], self.nodes[2].listlockunspent()) self.nodes[2].lockunspent(True, [unspent_0]) assert_equal(len(self.nodes[2].listlockunspent()), 0) @@ -286,10 +286,10 @@ def run_test(self): assert_equal(txObj['amount'], Decimal('-0.0001')) # This will raise an exception because the amount type is wrong - assert_raises_jsonrpc(-3, "Invalid amount", self.nodes[0].sendtoaddress, self.nodes[2].getnewaddress(), "1f-4") + assert_raises_rpc_error(-3, "Invalid amount", self.nodes[0].sendtoaddress, self.nodes[2].getnewaddress(), "1f-4") # This will raise an exception since generate does not accept a string - assert_raises_jsonrpc(-1, "not an integer", self.nodes[0].generate, "2") + assert_raises_rpc_error(-1, "not an integer", self.nodes[0].generate, "2") # Import address and private key to check correct behavior of spendable unspents # 1. Send some coins to generate new UTXO @@ -422,7 +422,7 @@ def run_test(self): node0_balance = self.nodes[0].getbalance() # With walletrejectlongchains we will not create the tx and store it in our wallet. - assert_raises_jsonrpc(-4, "Transaction has too long of a mempool chain", self.nodes[0].sendtoaddress, sending_addr, node0_balance - Decimal('0.01')) + assert_raises_rpc_error(-4, "Transaction has too long of a mempool chain", self.nodes[0].sendtoaddress, sending_addr, node0_balance - Decimal('0.01')) # Verify nothing new in wallet assert_equal(total_txs, len(self.nodes[0].listtransactions("*",99999))) diff --git a/test/functional/zapwallettxes.py b/test/functional/zapwallettxes.py index 83b11035c..8cd622dc8 100755 --- a/test/functional/zapwallettxes.py +++ b/test/functional/zapwallettxes.py @@ -17,7 +17,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, - assert_raises_jsonrpc, + assert_raises_rpc_error, wait_until, ) @@ -72,7 +72,7 @@ def run_test(self): assert_equal(self.nodes[0].gettransaction(txid1)['txid'], txid1) # This will raise an exception because the unconfirmed transaction has been zapped - assert_raises_jsonrpc(-5, 'Invalid or non-wallet transaction id', self.nodes[0].gettransaction, txid2) + assert_raises_rpc_error(-5, 'Invalid or non-wallet transaction id', self.nodes[0].gettransaction, txid2) if __name__ == '__main__': ZapWalletTXesTest().main() From 46b752ab5783a4689ed64b38b3a79429ad77217b Mon Sep 17 00:00:00 2001 From: fanquake Date: Sat, 7 Oct 2017 13:47:30 +0800 Subject: [PATCH 331/382] [depends] native_mac_alias 2.0.6 --- depends/packages/native_mac_alias.mk | 11 ++++---- .../patches/native_mac_alias/python3.patch | 28 ++++--------------- 2 files changed, 11 insertions(+), 28 deletions(-) diff --git a/depends/packages/native_mac_alias.mk b/depends/packages/native_mac_alias.mk index 85a8a402b..488ec8b59 100644 --- a/depends/packages/native_mac_alias.mk +++ b/depends/packages/native_mac_alias.mk @@ -1,14 +1,13 @@ package=native_mac_alias -$(package)_version=1.1.0 -$(package)_download_path=https://bitbucket.org/al45tair/mac_alias/get -$(package)_download_file=v$($(package)_version).tar.bz2 -$(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=87ad827e66790028361e43fc754f68ed041a9bdb214cca03c853f079b04fb120 +$(package)_version=2.0.6 +$(package)_download_path=https://github.com/al45tair/mac_alias/archive/ +$(package)_file_name=v$($(package)_version).tar.gz +$(package)_sha256_hash=78a3332d9a597eebf09ae652d38ad1e263b28db5c2e6dd725fad357b03b77371 $(package)_install_libdir=$(build_prefix)/lib/python/dist-packages $(package)_patches=python3.patch define $(package)_preprocess_cmds - patch -p1 < $($(package)_patch_dir)/python3.patch + patch -p1 < $($(package)_patch_dir)/python3.patch endef define $(package)_build_cmds diff --git a/depends/patches/native_mac_alias/python3.patch b/depends/patches/native_mac_alias/python3.patch index 1a32340be..6f2f5534a 100644 --- a/depends/patches/native_mac_alias/python3.patch +++ b/depends/patches/native_mac_alias/python3.patch @@ -1,7 +1,7 @@ diff -dur a/mac_alias/alias.py b/mac_alias/alias.py ---- a/mac_alias/alias.py 2015-10-19 12:12:48.000000000 +0200 -+++ b/mac_alias/alias.py 2016-04-03 12:13:12.037159417 +0200 -@@ -243,10 +243,10 @@ +--- a/mac_alias/alias.py ++++ b/mac_alias/alias.py +@@ -258,10 +258,10 @@ alias = Alias() alias.appinfo = appinfo @@ -14,7 +14,7 @@ diff -dur a/mac_alias/alias.py b/mac_alias/alias.py folder_cnid, cnid, crdate, creator_code, type_code) alias.target.levels_from = levels_from -@@ -261,9 +261,9 @@ +@@ -276,9 +276,9 @@ b.read(1) if tag == TAG_CARBON_FOLDER_NAME: @@ -26,7 +26,7 @@ diff -dur a/mac_alias/alias.py b/mac_alias/alias.py value) elif tag == TAG_CARBON_PATH: alias.target.carbon_path = value -@@ -298,9 +298,9 @@ +@@ -313,9 +313,9 @@ alias.target.creation_date \ = mac_epoch + datetime.timedelta(seconds=seconds) elif tag == TAG_POSIX_PATH: @@ -38,23 +38,7 @@ diff -dur a/mac_alias/alias.py b/mac_alias/alias.py elif tag == TAG_RECURSIVE_ALIAS_OF_DISK_IMAGE: alias.volume.disk_image_alias = Alias.from_bytes(value) elif tag == TAG_USER_HOME_LENGTH_PREFIX: -@@ -422,13 +422,13 @@ - # (so doing so is ridiculous, and nothing could rely on it). - b.write(struct.pack(b'>h28pI2shI64pII4s4shhI2s10s', - self.target.kind, -- carbon_volname, voldate, -+ carbon_volname, int(voldate), - self.volume.fs_type, - self.volume.disk_type, - self.target.folder_cnid, - carbon_filename, - self.target.cnid, -- crdate, -+ int(crdate), - self.target.creator_code, - self.target.type_code, - self.target.levels_from, -@@ -449,12 +449,12 @@ +@@ -467,12 +467,12 @@ b.write(struct.pack(b'>hhQhhQ', TAG_HIGH_RES_VOLUME_CREATION_DATE, From f617d1b06c17c7dd69d84efdfee41d49511ab7ae Mon Sep 17 00:00:00 2001 From: fanquake Date: Sat, 7 Oct 2017 13:54:45 +0800 Subject: [PATCH 332/382] [depends] native_ds_store 1.1.2 --- depends/packages/native_ds_store.mk | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/depends/packages/native_ds_store.mk b/depends/packages/native_ds_store.mk index 49f5829ac..116fa25d3 100644 --- a/depends/packages/native_ds_store.mk +++ b/depends/packages/native_ds_store.mk @@ -1,9 +1,8 @@ package=native_ds_store -$(package)_version=1.1.0 -$(package)_download_path=https://bitbucket.org/al45tair/ds_store/get -$(package)_download_file=v$($(package)_version).tar.bz2 -$(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=921596764d71d1bbd3297a90ef6d286f718794d667e4f81d91d14053525d64c1 +$(package)_version=1.1.2 +$(package)_download_path=https://github.com/al45tair/ds_store/archive/ +$(package)_file_name=v$($(package)_version).tar.gz +$(package)_sha256_hash=3b3ecb7bf0a5157f5b6010bc3af7c141fb0ad3527084e63336220d22744bc20c $(package)_install_libdir=$(build_prefix)/lib/python/dist-packages $(package)_dependencies=native_biplist From c6b07fddcfb664d7d1afbff460a68479fe1238d2 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Mon, 9 Oct 2017 10:37:40 +0200 Subject: [PATCH 333/382] Fix a vs. an typo --- src/netbase.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/netbase.cpp b/src/netbase.cpp index 5a560bc95..82040605c 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -291,7 +291,7 @@ struct ProxyCredentials std::string password; }; -/** Convert SOCKS5 reply to a an error message */ +/** Convert SOCKS5 reply to an error message */ std::string Socks5ErrorString(uint8_t err) { switch(err) { From aa57590d7c5eab1df5443e8ac9ed53ad0db46c02 Mon Sep 17 00:00:00 2001 From: Dusty Williams Date: Sun, 8 Oct 2017 23:18:48 -0400 Subject: [PATCH 334/382] Update importprivkey named args documentation Fixes #11462. Updated documentation for importprivkey function to use the correct name for the first argument. Also updates a call to importprivkey to use named args in functional test. --- src/wallet/rpcdump.cpp | 4 ++-- test/functional/importprunedfunds.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 1123fd6db..d6ea2a9db 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -80,10 +80,10 @@ UniValue importprivkey(const JSONRPCRequest& request) if (request.fHelp || request.params.size() < 1 || request.params.size() > 3) throw std::runtime_error( - "importprivkey \"bitcoinprivkey\" ( \"label\" ) ( rescan )\n" + "importprivkey \"privkey\" ( \"label\" ) ( rescan )\n" "\nAdds a private key (as returned by dumpprivkey) to your wallet.\n" "\nArguments:\n" - "1. \"bitcoinprivkey\" (string, required) The private key (see dumpprivkey)\n" + "1. \"privkey\" (string, required) The private key (see dumpprivkey)\n" "2. \"label\" (string, optional, default=\"\") An optional label\n" "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n" "\nNote: This call can take minutes to complete if rescan is true.\n" diff --git a/test/functional/importprunedfunds.py b/test/functional/importprunedfunds.py index cb7001066..9619d6795 100755 --- a/test/functional/importprunedfunds.py +++ b/test/functional/importprunedfunds.py @@ -78,7 +78,7 @@ def run_test(self): assert_equal(balance2, Decimal('0.05')) #Import with private key with no rescan - self.nodes[1].importprivkey(address3_privkey, "add3", False) + self.nodes[1].importprivkey(privkey=address3_privkey, label="add3", rescan=False) self.nodes[1].importprunedfunds(rawtxn3, proof3) balance3 = self.nodes[1].getbalance("add3", 0, False) assert_equal(balance3, Decimal('0.025')) From 68feb49105a9a473cf97dc2eb67e9370051d9ca6 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Mon, 9 Oct 2017 14:26:53 +0200 Subject: [PATCH 335/382] Use nullptr instead of NULL --- src/wallet/wallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 925b474d7..872b2d1e0 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3870,7 +3870,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) // Top up the keypool if (!walletInstance->TopUpKeyPool()) { InitError(_("Unable to generate initial keys") += "\n"); - return NULL; + return nullptr; } walletInstance->SetBestChain(chainActive.GetLocator()); From 0aacfa43c16af307ad154d9fbc5dade80f6f88d5 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Mon, 9 Oct 2017 14:36:01 +0200 Subject: [PATCH 336/382] Remove accidental stray semicolon --- contrib/verify-commits/verify-commits.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/verify-commits/verify-commits.sh b/contrib/verify-commits/verify-commits.sh index 74b7f3837..7194b040e 100755 --- a/contrib/verify-commits/verify-commits.sh +++ b/contrib/verify-commits/verify-commits.sh @@ -39,7 +39,7 @@ PREV_COMMIT="" while true; do if [ "$CURRENT_COMMIT" = $VERIFIED_ROOT ]; then echo "There is a valid path from "$CURRENT_COMMIT" to $VERIFIED_ROOT where all commits are signed!" - exit 0; + exit 0 fi if [ "$CURRENT_COMMIT" = $VERIFIED_SHA512_ROOT ]; then From f902e40c76d25eb9d32994f4844db6059f0f55ab Mon Sep 17 00:00:00 2001 From: Johannes Kanig Date: Mon, 9 Oct 2017 19:14:47 +0200 Subject: [PATCH 337/382] fix typo in comment of chain.cpp --- src/chain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chain.cpp b/src/chain.cpp index 47acde882..5e3dd9b31 100644 --- a/src/chain.cpp +++ b/src/chain.cpp @@ -128,7 +128,7 @@ arith_uint256 GetBlockProof(const CBlockIndex& block) // We need to compute 2**256 / (bnTarget+1), but we can't represent 2**256 // as it's too large for an arith_uint256. However, as 2**256 is at least as large // as bnTarget+1, it is equal to ((2**256 - bnTarget - 1) / (bnTarget+1)) + 1, - // or ~bnTarget / (nTarget+1) + 1. + // or ~bnTarget / (bnTarget+1) + 1. return (~bnTarget / (bnTarget + 1)) + 1; } From 9c72a464f87ad80d16ea6720dfc6a53578b94c92 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Wed, 4 Oct 2017 10:37:33 -0400 Subject: [PATCH 338/382] [tests] Tidy up forknotify.py --- test/functional/forknotify.py | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/test/functional/forknotify.py b/test/functional/forknotify.py index afcad1f9c..d74b3181d 100755 --- a/test/functional/forknotify.py +++ b/test/functional/forknotify.py @@ -4,9 +4,9 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test the -alertnotify option.""" import os -import time from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal, wait_until class ForkNotifyTest(BitcoinTestFramework): def set_test_params(self): @@ -14,46 +14,30 @@ def set_test_params(self): def setup_network(self): self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt") - with open(self.alert_filename, 'w', encoding='utf8'): - pass # Just open then close to create zero-length file - self.extra_args = [["-blockversion=2", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""], + self.extra_args = [["-alertnotify=echo %%s >> %s" % self.alert_filename], ["-blockversion=211"]] super().setup_network() def run_test(self): - # Mine 51 up-version blocks + # Mine 51 up-version blocks. -alertnotify should trigger on the 51st. self.nodes[1].generate(51) self.sync_all() - # -alertnotify should trigger on the 51'st, - # but mine and sync another to give - # -alertnotify time to write - self.nodes[1].generate(1) - self.sync_all() # Give bitcoind 10 seconds to write the alert notification - timeout = 10.0 - while timeout > 0: - if os.path.exists(self.alert_filename) and os.path.getsize(self.alert_filename): - break - time.sleep(0.1) - timeout -= 0.1 - else: - assert False, "-alertnotify did not warn of up-version blocks" + wait_until(lambda: os.path.isfile(self.alert_filename) and os.path.getsize(self.alert_filename), timeout=10) with open(self.alert_filename, 'r', encoding='utf8') as f: alert_text = f.read() # Mine more up-version blocks, should not get more alerts: - self.nodes[1].generate(1) - self.sync_all() - self.nodes[1].generate(1) + self.nodes[1].generate(2) self.sync_all() with open(self.alert_filename, 'r', encoding='utf8') as f: alert_text2 = f.read() - if alert_text != alert_text2: - raise AssertionError("-alertnotify excessive warning of up-version blocks") + self.log.info("-alertnotify should not continue notifying for more unknown version blocks") + assert_equal(alert_text, alert_text2) if __name__ == '__main__': ForkNotifyTest().main() From df18d29a02ae2804ae8490ea816fe0e88a971e8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= Date: Mon, 31 Jul 2017 22:13:27 +0100 Subject: [PATCH 339/382] [tests] Add -blocknotify functional test --- .../{forknotify.py => notifications.py} | 27 ++++++++++++++----- test/functional/test_runner.py | 2 +- 2 files changed, 22 insertions(+), 7 deletions(-) rename test/functional/{forknotify.py => notifications.py} (54%) diff --git a/test/functional/forknotify.py b/test/functional/notifications.py similarity index 54% rename from test/functional/forknotify.py rename to test/functional/notifications.py index d74b3181d..80a74c002 100755 --- a/test/functional/forknotify.py +++ b/test/functional/notifications.py @@ -2,25 +2,40 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -"""Test the -alertnotify option.""" +"""Test the -alertnotify and -blocknotify options.""" import os from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, wait_until -class ForkNotifyTest(BitcoinTestFramework): +class NotificationsTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 def setup_network(self): self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt") - self.extra_args = [["-alertnotify=echo %%s >> %s" % self.alert_filename], + self.block_filename = os.path.join(self.options.tmpdir, 'blocks.txt') + self.extra_args = [["-blockversion=2", + "-alertnotify=echo %%s >> %s" % self.alert_filename, + "-blocknotify=echo %%s >> %s" % self.block_filename], ["-blockversion=211"]] super().setup_network() def run_test(self): - # Mine 51 up-version blocks. -alertnotify should trigger on the 51st. - self.nodes[1].generate(51) + self.log.info("test -blocknotify") + block_count = 10 + blocks = self.nodes[1].generate(block_count) + + # wait at most 10 seconds for expected file size before reading the content + wait_until(lambda: os.path.isfile(self.block_filename) and os.stat(self.block_filename).st_size >= (block_count * 65), timeout=10) + + # file content should equal the generated blocks hashes + with open(self.block_filename, 'r') as f: + assert_equal(sorted(blocks), sorted(f.read().splitlines())) + + # Mine another 41 up-version blocks. -alertnotify should trigger on the 51st. + self.log.info("test -alertnotify") + self.nodes[1].generate(41) self.sync_all() # Give bitcoind 10 seconds to write the alert notification @@ -40,4 +55,4 @@ def run_test(self): assert_equal(alert_text, alert_text2) if __name__ == '__main__': - ForkNotifyTest().main() + NotificationsTest().main() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 5c8740d7c..43c3d0aa8 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -148,7 +148,7 @@ 'example_test.py', 'txn_doublespend.py', 'txn_clone.py --mineblock', - 'forknotify.py', + 'notifications.py', 'invalidateblock.py', 'p2p-acceptblock.py', 'replace-by-fee.py', From 857b32b4b280f13997cf2fa471802ad6a27075fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= Date: Mon, 31 Jul 2017 22:36:27 +0100 Subject: [PATCH 340/382] [tests] Add -walletnotify functional test --- test/functional/notifications.py | 36 ++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/test/functional/notifications.py b/test/functional/notifications.py index 80a74c002..c88972ab9 100755 --- a/test/functional/notifications.py +++ b/test/functional/notifications.py @@ -2,23 +2,29 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -"""Test the -alertnotify and -blocknotify options.""" +"""Test the -alertnotify, -blocknotify and -walletnotify options.""" import os from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, wait_until +from test_framework.util import assert_equal, wait_until, connect_nodes_bi class NotificationsTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 + self.setup_clean_chain = True def setup_network(self): self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt") - self.block_filename = os.path.join(self.options.tmpdir, 'blocks.txt') + self.block_filename = os.path.join(self.options.tmpdir, "blocks.txt") + self.tx_filename = os.path.join(self.options.tmpdir, "transactions.txt") + + # -alertnotify and -blocknotify on node0, walletnotify on node1 self.extra_args = [["-blockversion=2", "-alertnotify=echo %%s >> %s" % self.alert_filename, "-blocknotify=echo %%s >> %s" % self.block_filename], - ["-blockversion=211"]] + ["-blockversion=211", + "-rescan", + "-walletnotify=echo %%s >> %s" % self.tx_filename]] super().setup_network() def run_test(self): @@ -33,6 +39,28 @@ def run_test(self): with open(self.block_filename, 'r') as f: assert_equal(sorted(blocks), sorted(f.read().splitlines())) + self.log.info("test -walletnotify") + # wait at most 10 seconds for expected file size before reading the content + wait_until(lambda: os.path.isfile(self.tx_filename) and os.stat(self.tx_filename).st_size >= (block_count * 65), timeout=10) + + # file content should equal the generated transaction hashes + txids_rpc = list(map(lambda t: t['txid'], self.nodes[1].listtransactions("*", block_count))) + with open(self.tx_filename, 'r') as f: + assert_equal(sorted(txids_rpc), sorted(f.read().splitlines())) + os.remove(self.tx_filename) + + self.log.info("test -walletnotify after rescan") + # restart node to rescan to force wallet notifications + self.restart_node(1) + connect_nodes_bi(self.nodes, 0, 1) + + wait_until(lambda: os.path.isfile(self.tx_filename) and os.stat(self.tx_filename).st_size >= (block_count * 65), timeout=10) + + # file content should equal the generated transaction hashes + txids_rpc = list(map(lambda t: t['txid'], self.nodes[1].listtransactions("*", block_count))) + with open(self.tx_filename, 'r') as f: + assert_equal(sorted(txids_rpc), sorted(f.read().splitlines())) + # Mine another 41 up-version blocks. -alertnotify should trigger on the 51st. self.log.info("test -alertnotify") self.nodes[1].generate(41) From cc9ee809ad19a63ca284d2fbc327ac1cbcee31e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= Date: Wed, 4 Oct 2017 23:29:34 +0100 Subject: [PATCH 341/382] Improve ZMQ functional test --- test/functional/zmq_test.py | 172 +++++++++++++++--------------------- 1 file changed, 70 insertions(+), 102 deletions(-) diff --git a/test/functional/zmq_test.py b/test/functional/zmq_test.py index 382ef5bae..165f9192d 100755 --- a/test/functional/zmq_test.py +++ b/test/functional/zmq_test.py @@ -2,7 +2,7 @@ # Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -"""Test the ZMQ API.""" +"""Test the ZMQ notification interface.""" import configparser import os import struct @@ -13,6 +13,25 @@ hash256, ) +class ZMQSubscriber: + def __init__(self, socket, topic): + self.sequence = 0 + self.socket = socket + self.topic = topic + + import zmq + self.socket.setsockopt(zmq.SUBSCRIBE, self.topic) + + def receive(self): + topic, body, seq = self.socket.recv_multipart() + # Topic should match the subscriber topic. + assert_equal(topic, self.topic) + # Sequence should be incremental. + assert_equal(struct.unpack(' Date: Wed, 11 Oct 2017 11:12:59 +0100 Subject: [PATCH 342/382] Fix importmulti bug when importing an already imported key --- src/wallet/rpcdump.cpp | 2 +- test/functional/importmulti.py | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index d6ea2a9db..3ec4a5efb 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -961,7 +961,7 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6 pwallet->SetAddressBook(vchAddress, label, "receive"); if (pwallet->HaveKey(vchAddress)) { - return false; + throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script"); } pwallet->mapKeyMetadata[vchAddress].nCreateTime = timestamp; diff --git a/test/functional/importmulti.py b/test/functional/importmulti.py index c1a42870e..a691595f1 100755 --- a/test/functional/importmulti.py +++ b/test/functional/importmulti.py @@ -160,6 +160,18 @@ def run_test (self): assert_equal(address_assert['ismine'], True) assert_equal(address_assert['timestamp'], timestamp) + self.log.info("Should not import an address with private key if is already imported") + result = self.nodes[1].importmulti([{ + "scriptPubKey": { + "address": address['address'] + }, + "timestamp": "now", + "keys": [ self.nodes[0].dumpprivkey(address['address']) ] + }]) + assert_equal(result[0]['success'], False) + assert_equal(result[0]['error']['code'], -4) + assert_equal(result[0]['error']['message'], 'The wallet already contains the private key for this address or script') + # Address + Private key + watchonly self.log.info("Should not import an address with private key and with watchonly") address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) From c5dfa90aab53832c9ad339360d11d6635a26ba6b Mon Sep 17 00:00:00 2001 From: Cristian Mircea Messel Date: Wed, 11 Oct 2017 22:56:31 +0300 Subject: [PATCH 343/382] [tests] Add uacomment tests Checks for setting the value, max length and reserved characters --- test/functional/test_runner.py | 1 + test/functional/uacomment.py | 35 ++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100755 test/functional/uacomment.py diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 8c4651f6e..dd82f9513 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -124,6 +124,7 @@ 'resendwallettransactions.py', 'minchainwork.py', 'p2p-fingerprint.py', + 'uacomment.py', ] EXTENDED_SCRIPTS = [ diff --git a/test/functional/uacomment.py b/test/functional/uacomment.py new file mode 100755 index 000000000..0b2c64ab6 --- /dev/null +++ b/test/functional/uacomment.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test the -uacomment option.""" + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal + +class UacommentTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 1 + self.setup_clean_chain = True + + def run_test(self): + self.log.info("test multiple -uacomment") + test_uacomment = self.nodes[0].getnetworkinfo()["subversion"][-12:-1] + assert_equal(test_uacomment, "(testnode0)") + + self.restart_node(0, ["-uacomment=foo"]) + foo_uacomment = self.nodes[0].getnetworkinfo()["subversion"][-17:-1] + assert_equal(foo_uacomment, "(testnode0; foo)") + + self.log.info("test -uacomment max length") + self.stop_node(0) + expected = "Total length of network version string (286) exceeds maximum length (256). Reduce the number or size of uacomments." + self.assert_start_raises_init_error(0, ["-uacomment=" + 'a' * 256], expected) + + self.log.info("test -uacomment unsafe characters") + for unsafe_char in ['/', ':', '(', ')']: + expected = "User Agent comment (" + unsafe_char + ") contains unsafe characters" + self.assert_start_raises_init_error(0, ["-uacomment=" + unsafe_char], expected) + +if __name__ == '__main__': + UacommentTest().main() From c77170fbdbdfcd2ba830a1755450a2e5469f4e35 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Thu, 19 Nov 2015 16:05:37 +0100 Subject: [PATCH 344/382] [Wallet] add rescanblockchain RPC command --- src/qt/test/wallettests.cpp | 2 +- src/rpc/client.cpp | 2 + src/wallet/rpcwallet.cpp | 77 ++++++++++++++++++++++++++++++++ src/wallet/test/wallet_tests.cpp | 6 +-- src/wallet/wallet.cpp | 16 +++++-- src/wallet/wallet.h | 2 +- 6 files changed, 97 insertions(+), 8 deletions(-) diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index ff1eb59f1..ebd9102f8 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -157,7 +157,7 @@ void TestSendCoins() wallet.SetAddressBook(test.coinbaseKey.GetPubKey().GetID(), "", "receive"); wallet.AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey()); } - wallet.ScanForWalletTransactions(chainActive.Genesis(), true); + wallet.ScanForWalletTransactions(chainActive.Genesis(), nullptr, true); wallet.SetBroadcastTransactions(true); // Create widgets for sending coins and listing transactions. diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 417945378..d471cef07 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -140,6 +140,8 @@ static const CRPCConvertParam vRPCConvertParams[] = { "echojson", 7, "arg7" }, { "echojson", 8, "arg8" }, { "echojson", 9, "arg9" }, + { "rescanblockchain", 0, "start_height"}, + { "rescanblockchain", 1, "stop_height"}, }; class CRPCConvertTable diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index e3639e468..09075c6e9 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3169,6 +3169,81 @@ UniValue generate(const JSONRPCRequest& request) return generateBlocks(coinbase_script, num_generate, max_tries, true); } +UniValue rescanblockchain(const JSONRPCRequest& request) +{ + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { + return NullUniValue; + } + + if (request.fHelp || request.params.size() > 2) { + throw std::runtime_error( + "rescanblockchain (\"start_height\") (\"stop_height\")\n" + "\nRescan the local blockchain for wallet related transactions.\n" + "\nArguments:\n" + "1. \"start_height\" (numeric, optional) block height where the rescan should start\n" + "2. \"stop_height\" (numeric, optional) the last block height that should be scanned\n" + "\nResult:\n" + "{\n" + " \"start_height\" (numeric) The block height where the rescan has started. If omitted, rescan started from the genesis block.\n" + " \"stop_height\" (numeric) The height of the last rescanned block. If omitted, rescan stopped at the chain tip.\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("rescanblockchain", "100000 120000") + + HelpExampleRpc("rescanblockchain", "100000 120000") + ); + } + + LOCK2(cs_main, pwallet->cs_wallet); + + CBlockIndex *pindexStart = chainActive.Genesis(); + CBlockIndex *pindexStop = nullptr; + if (!request.params[0].isNull()) { + pindexStart = chainActive[request.params[0].get_int()]; + if (!pindexStart) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid start_height"); + } + } + + if (!request.params[1].isNull()) { + pindexStop = chainActive[request.params[1].get_int()]; + if (!pindexStop) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid stop_height"); + } + else if (pindexStop->nHeight < pindexStart->nHeight) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "stop_height must be greater then start_height"); + } + } + + // We can't rescan beyond non-pruned blocks, stop and throw an error + if (fPruneMode) { + CBlockIndex *block = pindexStop ? pindexStop : chainActive.Tip(); + while (block && block->nHeight >= pindexStart->nHeight) { + if (!(block->nStatus & BLOCK_HAVE_DATA)) { + throw JSONRPCError(RPC_MISC_ERROR, "Can't rescan beyond pruned data. Use RPC call getblockchaininfo to determine your pruned height."); + } + block = block->pprev; + } + } + + CBlockIndex *stopBlock = pwallet->ScanForWalletTransactions(pindexStart, pindexStop, true); + if (!stopBlock) { + if (pwallet->IsAbortingRescan()) { + throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted."); + } + // if we got a nullptr returned, ScanForWalletTransactions did rescan up to the requested stopindex + stopBlock = pindexStop ? pindexStop : chainActive.Tip(); + } + else { + throw JSONRPCError(RPC_MISC_ERROR, "Rescan failed. Potentially corrupted data files."); + } + + UniValue response(UniValue::VOBJ); + response.pushKV("start_height", pindexStart->nHeight); + response.pushKV("stop_height", stopBlock->nHeight); + return response; +} + extern UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp extern UniValue importprivkey(const JSONRPCRequest& request); @@ -3179,6 +3254,7 @@ extern UniValue importwallet(const JSONRPCRequest& request); extern UniValue importprunedfunds(const JSONRPCRequest& request); extern UniValue removeprunedfunds(const JSONRPCRequest& request); extern UniValue importmulti(const JSONRPCRequest& request); +extern UniValue rescanblockchain(const JSONRPCRequest& request); static const CRPCCommand commands[] = { // category name actor (function) argNames @@ -3233,6 +3309,7 @@ static const CRPCCommand commands[] = { "wallet", "walletpassphrasechange", &walletpassphrasechange, {"oldpassphrase","newpassphrase"} }, { "wallet", "walletpassphrase", &walletpassphrase, {"passphrase","timeout"} }, { "wallet", "removeprunedfunds", &removeprunedfunds, {"txid"} }, + { "wallet", "rescanblockchain", &rescanblockchain, {"start_height", "stop_height"} }, { "generating", "generate", &generate, {"nblocks","maxtries"} }, }; diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 5ebacd57d..2b12168c6 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -386,7 +386,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) { CWallet wallet; AddKey(wallet, coinbaseKey); - BOOST_CHECK_EQUAL(nullBlock, wallet.ScanForWalletTransactions(oldTip)); + BOOST_CHECK_EQUAL(nullBlock, wallet.ScanForWalletTransactions(oldTip, nullptr)); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 100 * COIN); } @@ -399,7 +399,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) { CWallet wallet; AddKey(wallet, coinbaseKey); - BOOST_CHECK_EQUAL(oldTip, wallet.ScanForWalletTransactions(oldTip)); + BOOST_CHECK_EQUAL(oldTip, wallet.ScanForWalletTransactions(oldTip, nullptr)); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN); } @@ -604,7 +604,7 @@ class ListCoinsTestingSetup : public TestChain100Setup bool firstRun; wallet->LoadWallet(firstRun); AddKey(*wallet, coinbaseKey); - wallet->ScanForWalletTransactions(chainActive.Genesis()); + wallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr); } ~ListCoinsTestingSetup() diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index d1d2060b0..efa93e638 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1539,7 +1539,7 @@ int64_t CWallet::RescanFromTime(int64_t startTime, bool update) LogPrintf("%s: Rescanning last %i blocks\n", __func__, startBlock ? chainActive.Height() - startBlock->nHeight + 1 : 0); if (startBlock) { - const CBlockIndex* const failedBlock = ScanForWalletTransactions(startBlock, update); + const CBlockIndex* const failedBlock = ScanForWalletTransactions(startBlock, nullptr, update); if (failedBlock) { return failedBlock->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1; } @@ -1555,12 +1555,19 @@ int64_t CWallet::RescanFromTime(int64_t startTime, bool update) * Returns null if scan was successful. Otherwise, if a complete rescan was not * possible (due to pruning or corruption), returns pointer to the most recent * block that could not be scanned. + * + * If pindexStop is not a nullptr, the scan will stop at the block-index + * defined by pindexStop */ -CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) +CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlockIndex* pindexStop, bool fUpdate) { int64_t nNow = GetTime(); const CChainParams& chainParams = Params(); + if (pindexStop) { + assert(pindexStop->nHeight >= pindexStart->nHeight); + } + CBlockIndex* pindex = pindexStart; CBlockIndex* ret = nullptr; { @@ -1588,6 +1595,9 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool f } else { ret = pindex; } + if (pindex == pindexStop) { + break; + } pindex = chainActive.Next(pindex); } if (pindex && fAbortRescan) { @@ -3903,7 +3913,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) } nStart = GetTimeMillis(); - walletInstance->ScanForWalletTransactions(pindexRescan, true); + walletInstance->ScanForWalletTransactions(pindexRescan, nullptr, true); LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart); walletInstance->SetBestChain(chainActive.GetLocator()); walletInstance->dbw->IncrementUpdateCounter(); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 73ad3bdec..3995f5563 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -919,7 +919,7 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface void BlockDisconnected(const std::shared_ptr& pblock) override; bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate); int64_t RescanFromTime(int64_t startTime, bool update); - CBlockIndex* ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); + CBlockIndex* ScanForWalletTransactions(CBlockIndex* pindexStart, CBlockIndex* pindexStop, bool fUpdate = false); void ReacceptWalletTransactions(); void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override; // ResendWalletTransactionsBefore may only be called if fBroadcastTransactions! From 7a91ceb5e0c9d29dddf7b6ae4cbba802b790924c Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Tue, 5 Sep 2017 17:37:36 -0700 Subject: [PATCH 345/382] [QA] Add RPC based rescan test --- test/functional/wallet-hd.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/test/functional/wallet-hd.py b/test/functional/wallet-hd.py index a6b96b745..fc5eb190a 100755 --- a/test/functional/wallet-hd.py +++ b/test/functional/wallet-hd.py @@ -10,6 +10,7 @@ connect_nodes_bi, ) import shutil +import os class WalletHDTest(BitcoinTestFramework): def set_test_params(self): @@ -70,9 +71,9 @@ def run_test (self): self.stop_node(1) # we need to delete the complete regtest directory # otherwise node1 would auto-recover all funds in flag the keypool keys as used - shutil.rmtree(tmpdir + "/node1/regtest/blocks") - shutil.rmtree(tmpdir + "/node1/regtest/chainstate") - shutil.copyfile(tmpdir + "/hd.bak", tmpdir + "/node1/regtest/wallet.dat") + shutil.rmtree(os.path.join(tmpdir, "node1/regtest/blocks")) + shutil.rmtree(os.path.join(tmpdir, "node1/regtest/chainstate")) + shutil.copyfile(os.path.join(tmpdir, "hd.bak"), os.path.join(tmpdir, "node1/regtest/wallet.dat")) self.start_node(1) # Assert that derivation is deterministic @@ -91,6 +92,22 @@ def run_test (self): self.start_node(1, extra_args=self.extra_args[1] + ['-rescan']) assert_equal(self.nodes[1].getbalance(), num_hd_adds + 1) + # Try a RPC based rescan + self.stop_node(1) + shutil.rmtree(os.path.join(tmpdir, "node1/regtest/blocks")) + shutil.rmtree(os.path.join(tmpdir, "node1/regtest/chainstate")) + shutil.copyfile(os.path.join(tmpdir, "hd.bak"), os.path.join(tmpdir, "node1/regtest/wallet.dat")) + self.start_node(1, extra_args=self.extra_args[1]) + connect_nodes_bi(self.nodes, 0, 1) + self.sync_all() + out = self.nodes[1].rescanblockchain(0, 1) + assert_equal(out['start_height'], 0) + assert_equal(out['stop_height'], 1) + out = self.nodes[1].rescanblockchain() + assert_equal(out['start_height'], 0) + assert_equal(out['stop_height'], self.nodes[1].getblockcount()) + assert_equal(self.nodes[1].getbalance(), num_hd_adds + 1) + # send a tx and make sure its using the internal chain for the changeoutput txid = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1) outs = self.nodes[1].decoderawtransaction(self.nodes[1].gettransaction(txid)['hex'])['vout'] From fe862c5ad4bdce6bcc3bf8712d9472561b270c02 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Mon, 26 Jun 2017 16:00:25 +0200 Subject: [PATCH 346/382] Avoid division by zero in the case of a corrupt estimates file --- src/policy/fees.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp index dcf49de5f..c7e57671c 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -180,6 +180,7 @@ TxConfirmStats::TxConfirmStats(const std::vector& defaultBuckets, : buckets(defaultBuckets), bucketMap(defaultBucketMap) { decay = _decay; + assert(_scale != 0 && "_scale must be non-zero"); scale = _scale; confAvg.resize(maxPeriods); for (unsigned int i = 0; i < maxPeriods; i++) { @@ -418,6 +419,9 @@ void TxConfirmStats::Read(CAutoFile& filein, int nFileVersion, size_t numBuckets throw std::runtime_error("Corrupt estimates file. Decay must be between 0 and 1 (non-inclusive)"); } filein >> scale; + if (scale == 0) { + throw std::runtime_error("Corrupt estimates file. Scale must be non-zero"); + } } filein >> avg; From fa9de370b17297d6dd542da627c8dd2b31aec340 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Tue, 10 Oct 2017 00:04:45 +0200 Subject: [PATCH 347/382] qa: Make tmpdir option an absolute path This should fix issues with the multiwallet test and symlinks when the tmpdir is a relative path. Rather than fixing os.symlink to work with paths relative to a directory descriptor, which does not work on Windows, normalize the path instead. --- test/functional/test_framework/test_framework.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 381513ab9..0e843ee67 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -102,8 +102,11 @@ def main(self): check_json_precision() + self.options.cachedir = os.path.abspath(self.options.cachedir) + # Set up temp directory and start logging if self.options.tmpdir: + self.options.tmpdir = os.path.abspath(self.options.tmpdir) os.makedirs(self.options.tmpdir, exist_ok=False) else: self.options.tmpdir = tempfile.mkdtemp(prefix="test") From fafa0039708e15d1067be091b2bfc10195afa480 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Wed, 11 Oct 2017 22:14:49 +0200 Subject: [PATCH 348/382] qa: Remove never used return value of sync_with_ping --- test/functional/p2p-acceptblock.py | 9 ++++++--- test/functional/test_framework/mininode.py | 1 - 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/test/functional/p2p-acceptblock.py b/test/functional/p2p-acceptblock.py index 293bc0553..9f61ce27d 100755 --- a/test/functional/p2p-acceptblock.py +++ b/test/functional/p2p-acceptblock.py @@ -103,7 +103,8 @@ def run_test(self): test_node.send_message(msg_block(blocks_h2[0])) white_node.send_message(msg_block(blocks_h2[1])) - [ x.sync_with_ping() for x in [test_node, white_node] ] + for x in [test_node, white_node]: + x.sync_with_ping() assert_equal(self.nodes[0].getblockcount(), 2) assert_equal(self.nodes[1].getblockcount(), 2) self.log.info("First height 2 block accepted by both nodes") @@ -116,7 +117,8 @@ def run_test(self): test_node.send_message(msg_block(blocks_h2f[0])) white_node.send_message(msg_block(blocks_h2f[1])) - [ x.sync_with_ping() for x in [test_node, white_node] ] + for x in [test_node, white_node]: + x.sync_with_ping() for x in self.nodes[0].getchaintips(): if x['hash'] == blocks_h2f[0].hash: assert_equal(x['status'], "headers-only") @@ -135,7 +137,8 @@ def run_test(self): test_node.send_message(msg_block(blocks_h3[0])) white_node.send_message(msg_block(blocks_h3[1])) - [ x.sync_with_ping() for x in [test_node, white_node] ] + for x in [test_node, white_node]: + x.sync_with_ping() # Since the earlier block was not processed by node0, the new block # can't be fully validated. for x in self.nodes[0].getchaintips(): diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py index d072969d7..453694c43 100755 --- a/test/functional/test_framework/mininode.py +++ b/test/functional/test_framework/mininode.py @@ -1615,7 +1615,6 @@ def sync_with_ping(self, timeout=60): test_function = lambda: self.last_message.get("pong") and self.last_message["pong"].nonce == self.ping_counter wait_until(test_function, timeout=timeout, lock=mininode_lock) self.ping_counter += 1 - return True # The actual NodeConn class # This class provides an interface for a p2p connection to a specified node From 44407100ff9b478d6131a1c38ee993b50b1830df Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Wed, 4 Oct 2017 17:59:30 -0400 Subject: [PATCH 349/382] Replace relevant services logic with a function suite. Adds HasAllRelevantServices and GetRelevantServices, which check for NETWORK|WITNESS. This changes the following: * Removes nRelevantServices from CConnman, disconnecting it a bit more from protocol-level logic. * Replaces our sometimes-connect-to-!WITNESS-nodes logic with simply always requiring WITNESS|NETWORK for outbound non-feeler connections (feelers still only require NETWORK). * This has the added benefit of removing nServicesExpected from CNode - instead letting net_processing's VERSION message handling simply check HasAllRelevantServices. * This implies we believe WITNESS nodes to continue to be a significant majority of nodes on the network, but also because we cannot sync properly from !WITNESS nodes, it is strange to continue using our valuable outbound slots on them. * In order to prevent this change from preventing connection to -connect= nodes which have !WITNESS, -connect nodes are now given the "addnode" flag. This also allows outbound connections to !NODE_NETWORK nodes for -connect nodes (which was already true of addnodes). * Has the (somewhat unintended) consequence of changing one of the eviction metrics from the same sometimes-connect-to-!WITNESS-nodes metric to requiring HasRelevantServices. This should make NODE_NETWORK_LIMITED much simpler to implement. --- src/init.cpp | 5 ----- src/net.cpp | 40 ++++++++++------------------------------ src/net.h | 8 -------- src/net_processing.cpp | 11 +++++++---- src/protocol.h | 37 +++++++++++++++++++++++++++++++++++++ src/rpc/net.cpp | 2 +- 6 files changed, 55 insertions(+), 48 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index b46b53ac1..951800f6c 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -815,7 +815,6 @@ void InitLogging() namespace { // Variables internal to initialization process only -ServiceFlags nRelevantServices = NODE_NETWORK; int nMaxConnections; int nUserMaxConnections; int nFD; @@ -1604,9 +1603,6 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) // Note that setting NODE_WITNESS is never required: the only downside from not // doing so is that after activation, no upgraded nodes will fetch from you. nLocalServices = ServiceFlags(nLocalServices | NODE_WITNESS); - // Only care about others providing witness capabilities if there is a softfork - // defined. - nRelevantServices = ServiceFlags(nRelevantServices | NODE_WITNESS); } // ********************************************************* Step 10: import blocks @@ -1656,7 +1652,6 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) CConnman::Options connOptions; connOptions.nLocalServices = nLocalServices; - connOptions.nRelevantServices = nRelevantServices; connOptions.nMaxConnections = nMaxConnections; connOptions.nMaxOutbound = std::min(MAX_OUTBOUND_CONNECTIONS, connOptions.nMaxConnections); connOptions.nMaxAddnode = MAX_ADDNODE_CONNECTIONS; diff --git a/src/net.cpp b/src/net.cpp index ea3840a70..581151b4b 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -444,7 +444,6 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize(); CAddress addr_bind = GetBindAddress(hSocket); CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", false); - pnode->nServicesExpected = ServiceFlags(addrConnect.nServices & nRelevantServices); pnode->AddRef(); return pnode; @@ -985,7 +984,7 @@ bool CConnman::AttemptToEvictConnection() continue; NodeEvictionCandidate candidate = {node->GetId(), node->nTimeConnected, node->nMinPingUsecTime, node->nLastBlockTime, node->nLastTXTime, - (node->nServices & nRelevantServices) == nRelevantServices, + HasAllDesirableServiceFlags(node->nServices), node->fRelayTxes, node->pfilter != nullptr, node->addr, node->nKeyedNetGroup}; vEvictionCandidates.push_back(candidate); } @@ -1602,7 +1601,7 @@ void CConnman::ThreadDNSAddressSeed() LOCK(cs_vNodes); int nRelevant = 0; for (auto pnode : vNodes) { - nRelevant += pnode->fSuccessfullyConnected && ((pnode->nServices & nRelevantServices) == nRelevantServices); + nRelevant += pnode->fSuccessfullyConnected && HasAllDesirableServiceFlags(pnode->nServices); } if (nRelevant >= 2) { LogPrintf("P2P peers available. Skipped DNS seeding.\n"); @@ -1624,7 +1623,7 @@ void CConnman::ThreadDNSAddressSeed() } else { std::vector vIPs; std::vector vAdd; - ServiceFlags requiredServiceBits = nRelevantServices; + ServiceFlags requiredServiceBits = GetDesirableServiceFlags(NODE_NONE); std::string host = GetDNSHost(seed, &requiredServiceBits); CNetAddr resolveSource; if (!resolveSource.SetInternal(host)) { @@ -1705,7 +1704,7 @@ void CConnman::ThreadOpenConnections(const std::vector connect) for (const std::string& strAddr : connect) { CAddress addr(CService(), NODE_NONE); - OpenNetworkConnection(addr, false, nullptr, strAddr.c_str()); + OpenNetworkConnection(addr, false, nullptr, strAddr.c_str(), false, false, true); for (int i = 0; i < 10 && i < nLoop; i++) { if (!interruptNet.sleep_for(std::chrono::milliseconds(500))) @@ -1753,17 +1752,11 @@ void CConnman::ThreadOpenConnections(const std::vector connect) // Only connect out to one peer per network group (/16 for IPv4). // Do this here so we don't have to critsect vNodes inside mapAddresses critsect. int nOutbound = 0; - int nOutboundRelevant = 0; std::set > setConnected; { LOCK(cs_vNodes); for (CNode* pnode : vNodes) { if (!pnode->fInbound && !pnode->fAddnode) { - - // Count the peers that have all relevant services - if (pnode->fSuccessfullyConnected && !pnode->fFeeler && ((pnode->nServices & nRelevantServices) == nRelevantServices)) { - nOutboundRelevant++; - } // Netgroups for inbound and addnode peers are not excluded because our goal here // is to not use multiple of our limited outbound slots on a single netgroup // but inbound and addnode peers do not use our outbound slots. Inbound peers @@ -1818,21 +1811,16 @@ void CConnman::ThreadOpenConnections(const std::vector connect) if (IsLimited(addr)) continue; - // only connect to full nodes - if ((addr.nServices & REQUIRED_SERVICES) != REQUIRED_SERVICES) - continue; - // only consider very recently tried nodes after 30 failed attempts if (nANow - addr.nLastTry < 600 && nTries < 30) continue; - // only consider nodes missing relevant services after 40 failed attempts and only if less than half the outbound are up. - ServiceFlags nRequiredServices = nRelevantServices; - if (nTries >= 40 && nOutbound < (nMaxOutbound >> 1)) { - nRequiredServices = REQUIRED_SERVICES; - } - - if ((addr.nServices & nRequiredServices) != nRequiredServices) { + // for non-feelers, require all the services we'll want, + // for feelers, only require they be a full node (only because most + // SPV clients don't have a good address DB available) + if (!fFeeler && !HasAllDesirableServiceFlags(addr.nServices)) { + continue; + } else if (fFeeler && !MayHaveUsefulAddressDB(addr.nServices)) { continue; } @@ -1841,13 +1829,6 @@ void CConnman::ThreadOpenConnections(const std::vector connect) continue; addrConnect = addr; - - // regardless of the services assumed to be available, only require the minimum if half or more outbound have relevant services - if (nOutboundRelevant >= (nMaxOutbound >> 1)) { - addrConnect.nServices = REQUIRED_SERVICES; - } else { - addrConnect.nServices = nRequiredServices; - } break; } @@ -2712,7 +2693,6 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn nSendVersion(0) { nServices = NODE_NONE; - nServicesExpected = NODE_NONE; hSocket = hSocketIn; nRecvVersion = INIT_PROTO_VERSION; nLastSend = 0; diff --git a/src/net.h b/src/net.h index 905d6eb95..b4873b1e4 100644 --- a/src/net.h +++ b/src/net.h @@ -84,8 +84,6 @@ static const bool DEFAULT_FORCEDNSSEED = false; static const size_t DEFAULT_MAXRECEIVEBUFFER = 5 * 1000; static const size_t DEFAULT_MAXSENDBUFFER = 1 * 1000; -static const ServiceFlags REQUIRED_SERVICES = NODE_NETWORK; - // NOTE: When adjusting this, update rpcnet:setban's help ("24h") static const unsigned int DEFAULT_MISBEHAVING_BANTIME = 60 * 60 * 24; // Default 24-hour ban @@ -130,7 +128,6 @@ class CConnman struct Options { ServiceFlags nLocalServices = NODE_NONE; - ServiceFlags nRelevantServices = NODE_NONE; int nMaxConnections = 0; int nMaxOutbound = 0; int nMaxAddnode = 0; @@ -152,7 +149,6 @@ class CConnman void Init(const Options& connOptions) { nLocalServices = connOptions.nLocalServices; - nRelevantServices = connOptions.nRelevantServices; nMaxConnections = connOptions.nMaxConnections; nMaxOutbound = std::min(connOptions.nMaxOutbound, connOptions.nMaxConnections); nMaxAddnode = connOptions.nMaxAddnode; @@ -390,9 +386,6 @@ class CConnman /** Services this instance offers */ ServiceFlags nLocalServices; - /** Services this instance cares about */ - ServiceFlags nRelevantServices; - CSemaphore *semOutbound; CSemaphore *semAddnode; int nMaxConnections; @@ -585,7 +578,6 @@ class CNode public: // socket std::atomic nServices; - ServiceFlags nServicesExpected; SOCKET hSocket; size_t nSendSize; // total size of all vSendMsg entries size_t nSendOffset; // offset inside the first vSendMsg already sent diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 7fced41d4..39cf989ee 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -1232,11 +1232,11 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr { connman->SetServices(pfrom->addr, nServices); } - if (pfrom->nServicesExpected & ~nServices) + if (!pfrom->fInbound && !pfrom->fFeeler && !pfrom->fAddnode && !HasAllDesirableServiceFlags(nServices)) { - LogPrint(BCLog::NET, "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->GetId(), nServices, pfrom->nServicesExpected); + LogPrint(BCLog::NET, "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->GetId(), nServices, GetDesirableServiceFlags(nServices)); connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD, - strprintf("Expected to offer services %08x", pfrom->nServicesExpected))); + strprintf("Expected to offer services %08x", GetDesirableServiceFlags(nServices)))); pfrom->fDisconnect = true; return false; } @@ -1455,7 +1455,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr if (interruptMsgProc) return true; - if ((addr.nServices & REQUIRED_SERVICES) != REQUIRED_SERVICES) + // We only bother storing full nodes, though this may include + // things which we would not make an outbound connection to, in + // part because we may make feeler connections to them. + if (!MayHaveUsefulAddressDB(addr.nServices)) continue; if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) diff --git a/src/protocol.h b/src/protocol.h index 67e01d960..56b59aed3 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -277,6 +277,43 @@ enum ServiceFlags : uint64_t { // BIP process. }; +/** + * Gets the set of service flags which are "desirable" for a given peer. + * + * These are the flags which are required for a peer to support for them + * to be "interesting" to us, ie for us to wish to use one of our few + * outbound connection slots for or for us to wish to prioritize keeping + * their connection around. + * + * Relevant service flags may be peer- and state-specific in that the + * version of the peer may determine which flags are required (eg in the + * case of NODE_NETWORK_LIMITED where we seek out NODE_NETWORK peers + * unless they set NODE_NETWORK_LIMITED and we are out of IBD, in which + * case NODE_NETWORK_LIMITED suffices). + * + * Thus, generally, avoid calling with peerServices == NODE_NONE. + */ +static ServiceFlags GetDesirableServiceFlags(ServiceFlags services) { + return ServiceFlags(NODE_NETWORK | NODE_WITNESS); +} + +/** + * A shortcut for (services & GetDesirableServiceFlags(services)) + * == GetDesirableServiceFlags(services), ie determines whether the given + * set of service flags are sufficient for a peer to be "relevant". + */ +static inline bool HasAllDesirableServiceFlags(ServiceFlags services) { + return !(GetDesirableServiceFlags(services) & (~services)); +} + +/** + * Checks if a peer with the given service flags may be capable of having a + * robust address-storage DB. Currently an alias for checking NODE_NETWORK. + */ +static inline bool MayHaveUsefulAddressDB(ServiceFlags services) { + return services & NODE_NETWORK; +} + /** A CService with information about it as peer */ class CAddress : public CService { diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index a3d3df26a..fa0d15e0c 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -217,7 +217,7 @@ UniValue addnode(const JSONRPCRequest& request) if (strCommand == "onetry") { CAddress addr; - g_connman->OpenNetworkConnection(addr, false, nullptr, strNode.c_str()); + g_connman->OpenNetworkConnection(addr, false, nullptr, strNode.c_str(), false, false, true); return NullUniValue; } From 57edc0b0c86549020a39cd65f96496e9771c4769 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Thu, 5 Oct 2017 11:49:16 -0400 Subject: [PATCH 350/382] Rename fAddnode to a more-descriptive "manual_connection" --- src/net.cpp | 12 ++++++------ src/net.h | 6 +++--- src/net_processing.cpp | 6 +++--- src/rpc/net.cpp | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/net.cpp b/src/net.cpp index 581151b4b..3e82969a1 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -684,7 +684,7 @@ void CNode::copyStats(CNodeStats &stats) X(cleanSubVer); } X(fInbound); - X(fAddnode); + X(m_manual_connection); X(nStartingHeight); { LOCK(cs_vSend); @@ -1756,7 +1756,7 @@ void CConnman::ThreadOpenConnections(const std::vector connect) { LOCK(cs_vNodes); for (CNode* pnode : vNodes) { - if (!pnode->fInbound && !pnode->fAddnode) { + if (!pnode->fInbound && !pnode->m_manual_connection) { // Netgroups for inbound and addnode peers are not excluded because our goal here // is to not use multiple of our limited outbound slots on a single netgroup // but inbound and addnode peers do not use our outbound slots. Inbound peers @@ -1927,7 +1927,7 @@ void CConnman::ThreadOpenAddedConnections() } // if successful, this moves the passed grant to the constructed node -bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler, bool fAddnode) +bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler, bool manual_connection) { // // Initiate outbound network connection @@ -1956,8 +1956,8 @@ bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai pnode->fOneShot = true; if (fFeeler) pnode->fFeeler = true; - if (fAddnode) - pnode->fAddnode = true; + if (manual_connection) + pnode->m_manual_connection = true; m_msgproc->InitializeNode(pnode); { @@ -2705,7 +2705,7 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn strSubVer = ""; fWhitelisted = false; fOneShot = false; - fAddnode = false; + m_manual_connection = false; fClient = false; // set by version message fFeeler = false; fSuccessfullyConnected = false; diff --git a/src/net.h b/src/net.h index b4873b1e4..f373ab0cf 100644 --- a/src/net.h +++ b/src/net.h @@ -171,7 +171,7 @@ class CConnman void Interrupt(); bool GetNetworkActive() const { return fNetworkActive; }; void SetNetworkActive(bool active); - bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = nullptr, const char *strDest = nullptr, bool fOneShot = false, bool fFeeler = false, bool fAddnode = false); + bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = nullptr, const char *strDest = nullptr, bool fOneShot = false, bool fFeeler = false, bool manual_connection = false); bool CheckIncomingNonce(uint64_t nonce); bool ForNode(NodeId id, std::function func); @@ -506,7 +506,7 @@ class CNodeStats int nVersion; std::string cleanSubVer; bool fInbound; - bool fAddnode; + bool m_manual_connection; int nStartingHeight; uint64_t nSendBytes; mapMsgCmdSize mapSendBytesPerMsgCmd; @@ -615,7 +615,7 @@ class CNode bool fWhitelisted; // This peer can bypass DoS banning. bool fFeeler; // If true this node is being used as a short lived feeler. bool fOneShot; - bool fAddnode; + bool m_manual_connection; bool fClient; const bool fInbound; std::atomic_bool fSuccessfullyConnected; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 39cf989ee..3b73533c0 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -1232,7 +1232,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr { connman->SetServices(pfrom->addr, nServices); } - if (!pfrom->fInbound && !pfrom->fFeeler && !pfrom->fAddnode && !HasAllDesirableServiceFlags(nServices)) + if (!pfrom->fInbound && !pfrom->fFeeler && !pfrom->m_manual_connection && !HasAllDesirableServiceFlags(nServices)) { LogPrint(BCLog::NET, "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->GetId(), nServices, GetDesirableServiceFlags(nServices)); connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD, @@ -2628,8 +2628,8 @@ static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman* connman) state.fShouldBan = false; if (pnode->fWhitelisted) LogPrintf("Warning: not punishing whitelisted peer %s!\n", pnode->addr.ToString()); - else if (pnode->fAddnode) - LogPrintf("Warning: not punishing addnoded peer %s!\n", pnode->addr.ToString()); + else if (pnode->m_manual_connection) + LogPrintf("Warning: not punishing manually-connected peer %s!\n", pnode->addr.ToString()); else { pnode->fDisconnect = true; if (pnode->addr.IsLocal()) diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index fa0d15e0c..076fe260b 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -92,7 +92,7 @@ UniValue getpeerinfo(const JSONRPCRequest& request) " \"version\": v, (numeric) The peer version, such as 7001\n" " \"subver\": \"/Satoshi:0.8.5/\", (string) The string version\n" " \"inbound\": true|false, (boolean) Inbound (true) or Outbound (false)\n" - " \"addnode\": true|false, (boolean) Whether connection was due to addnode and is using an addnode slot\n" + " \"addnode\": true|false, (boolean) Whether connection was due to addnode/-connect or if it was an automatic/inbound connection\n" " \"startingheight\": n, (numeric) The starting height (block) of the peer\n" " \"banscore\": n, (numeric) The ban score\n" " \"synced_headers\": n, (numeric) The last header we have in common with this peer\n" @@ -156,7 +156,7 @@ UniValue getpeerinfo(const JSONRPCRequest& request) // their ver message. obj.push_back(Pair("subver", stats.cleanSubVer)); obj.push_back(Pair("inbound", stats.fInbound)); - obj.push_back(Pair("addnode", stats.fAddnode)); + obj.push_back(Pair("addnode", stats.m_manual_connection)); obj.push_back(Pair("startingheight", stats.nStartingHeight)); if (fStateStats) { obj.push_back(Pair("banscore", statestats.nMisbehavior)); From 5ee88b4bdefecbf03b6883b2a6b56a44ec75167d Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Fri, 6 Oct 2017 17:27:04 -0400 Subject: [PATCH 351/382] Clarify docs for requirements/handling of addnode/connect nodes --- src/init.cpp | 4 ++-- src/rpc/net.cpp | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 951800f6c..8755ad420 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -369,11 +369,11 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-txindex", strprintf(_("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)"), DEFAULT_TXINDEX)); strUsage += HelpMessageGroup(_("Connection options:")); - strUsage += HelpMessageOpt("-addnode=", _("Add a node to connect to and attempt to keep the connection open")); + strUsage += HelpMessageOpt("-addnode=", _("Add a node to connect to and attempt to keep the connection open (see the `addnode` RPC command help for more info)")); strUsage += HelpMessageOpt("-banscore=", strprintf(_("Threshold for disconnecting misbehaving peers (default: %u)"), DEFAULT_BANSCORE_THRESHOLD)); strUsage += HelpMessageOpt("-bantime=", strprintf(_("Number of seconds to keep misbehaving peers from reconnecting (default: %u)"), DEFAULT_MISBEHAVING_BANTIME)); strUsage += HelpMessageOpt("-bind=", _("Bind to given address and always listen on it. Use [host]:port notation for IPv6")); - strUsage += HelpMessageOpt("-connect=", _("Connect only to the specified node(s); -connect=0 disables automatic connections")); + strUsage += HelpMessageOpt("-connect=", _("Connect only to the specified node(s); -connect=0 disables automatic connections (the rules for this peer are the same as for -addnode)")); strUsage += HelpMessageOpt("-discover", _("Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)")); strUsage += HelpMessageOpt("-dns", _("Allow DNS lookups for -addnode, -seednode and -connect") + " " + strprintf(_("(default: %u)"), DEFAULT_NAME_LOOKUP)); strUsage += HelpMessageOpt("-dnsseed", _("Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect used)")); diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 076fe260b..8fb8328c5 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -201,6 +201,8 @@ UniValue addnode(const JSONRPCRequest& request) "addnode \"node\" \"add|remove|onetry\"\n" "\nAttempts to add or remove a node from the addnode list.\n" "Or try a connection to a node once.\n" + "Nodes added using addnode (or -connect) are protected from DoS disconnection and are not required to be\n" + "full nodes/support SegWit as other outbound peers are (though such peers will not be synced from).\n" "\nArguments:\n" "1. \"node\" (string, required) The node (see getpeerinfo for nodes)\n" "2. \"command\" (string, required) 'add' to add a node to the list, 'remove' to remove a node from the list, 'onetry' to try a connection to the node once\n" From 15f5d3b17298be96c6c684c195c02ac249ffd392 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Fri, 6 Oct 2017 17:27:56 -0400 Subject: [PATCH 352/382] Switch DNSSeed-needed metric to any-automatic-nodes, not services --- src/net.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/net.cpp b/src/net.cpp index 3e82969a1..258599747 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1601,7 +1601,7 @@ void CConnman::ThreadDNSAddressSeed() LOCK(cs_vNodes); int nRelevant = 0; for (auto pnode : vNodes) { - nRelevant += pnode->fSuccessfullyConnected && HasAllDesirableServiceFlags(pnode->nServices); + nRelevant += pnode->fSuccessfullyConnected && !pnode->fFeeler && !pnode->fOneShot && !pnode->m_manual_connection && !pnode->fInbound; } if (nRelevant >= 2) { LogPrintf("P2P peers available. Skipped DNS seeding.\n"); From f4c4e388843a9cf5fd4a289b1d6bce8eab6b2ce6 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Fri, 13 Oct 2017 17:23:52 -0400 Subject: [PATCH 353/382] [trivial] Make namespace explicit for is_regular_file is_regular_file resolves using argument dependent lookup. Make the namespace explicit so it's obvious where the function is defined. --- src/init.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/init.cpp b/src/init.cpp index 539adc23d..9aac64ec0 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -588,7 +588,7 @@ void CleanupBlockRevFiles() LogPrintf("Removing unusable blk?????.dat and rev?????.dat files for -reindex with -prune\n"); fs::path blocksdir = GetDataDir() / "blocks"; for (fs::directory_iterator it(blocksdir); it != fs::directory_iterator(); it++) { - if (is_regular_file(*it) && + if (fs::is_regular_file(*it) && it->path().filename().string().length() == 12 && it->path().filename().string().substr(8,4) == ".dat") { From 43f76f6acdef3504c072ef7ff8cb92221a92b158 Mon Sep 17 00:00:00 2001 From: MeshCollider Date: Sat, 14 Oct 2017 12:34:04 +1300 Subject: [PATCH 354/382] Add missing comma from rescanblockchain --- src/wallet/rpcwallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index e23ef57db..d6989add8 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3233,7 +3233,7 @@ UniValue rescanblockchain(const JSONRPCRequest& request) "}\n" "\nExamples:\n" + HelpExampleCli("rescanblockchain", "100000 120000") - + HelpExampleRpc("rescanblockchain", "100000 120000") + + HelpExampleRpc("rescanblockchain", "100000, 120000") ); } From 7104de8b1f3a31d3a60009b5dc376adbedac6a9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= Date: Thu, 12 Oct 2017 23:14:46 +0100 Subject: [PATCH 355/382] [wallet] Fix leak in CDB constructor Now using a std::unique_ptr, the Db instance is correctly released when CDB initialization fails. The internal CDB state and mapFileUseCount are only mutated when the CDB initialization succeeds. --- src/wallet/db.cpp | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index d66ba4842..5e881d9ac 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -379,45 +379,43 @@ CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb if (!env->Open(GetDataDir())) throw std::runtime_error("CDB: Failed to open database environment."); - strFile = strFilename; - ++env->mapFileUseCount[strFile]; - pdb = env->mapDb[strFile]; + pdb = env->mapDb[strFilename]; if (pdb == nullptr) { int ret; - pdb = new Db(env->dbenv, 0); + std::unique_ptr pdb_temp(new Db(env->dbenv, 0)); bool fMockDb = env->IsMock(); if (fMockDb) { - DbMpoolFile* mpf = pdb->get_mpf(); + DbMpoolFile* mpf = pdb_temp->get_mpf(); ret = mpf->set_flags(DB_MPOOL_NOFILE, 1); - if (ret != 0) - throw std::runtime_error(strprintf("CDB: Failed to configure for no temp file backing for database %s", strFile)); + if (ret != 0) { + throw std::runtime_error(strprintf("CDB: Failed to configure for no temp file backing for database %s", strFilename)); + } } - ret = pdb->open(nullptr, // Txn pointer - fMockDb ? nullptr : strFile.c_str(), // Filename - fMockDb ? strFile.c_str() : "main", // Logical db name - DB_BTREE, // Database type - nFlags, // Flags + ret = pdb_temp->open(nullptr, // Txn pointer + fMockDb ? nullptr : strFilename.c_str(), // Filename + fMockDb ? strFilename.c_str() : "main", // Logical db name + DB_BTREE, // Database type + nFlags, // Flags 0); if (ret != 0) { - delete pdb; - pdb = nullptr; - --env->mapFileUseCount[strFile]; - strFile = ""; throw std::runtime_error(strprintf("CDB: Error %d, can't open database %s", ret, strFilename)); } + pdb = pdb_temp.release(); + env->mapDb[strFilename] = pdb; + if (fCreate && !Exists(std::string("version"))) { bool fTmp = fReadOnly; fReadOnly = false; WriteVersion(CLIENT_VERSION); fReadOnly = fTmp; } - - env->mapDb[strFile] = pdb; } + ++env->mapFileUseCount[strFilename]; + strFile = strFilename; } } From 207408b0889caf3f5986bde4b122dbece1ed0dac Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Sun, 15 Oct 2017 22:42:01 -0700 Subject: [PATCH 356/382] Fix crash via division by zero assertion --- src/qt/coincontroldialog.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 6952eb506..207e441b6 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -582,8 +582,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) QString toolTipDust = tr("This label turns red if any recipient receives an amount smaller than the current dust threshold."); // how many satoshis the estimated fee can vary per byte we guess wrong - assert(nBytes != 0); - double dFeeVary = (double)nPayFee / nBytes; + double dFeeVary = (nBytes != 0) ? (double)nPayFee / nBytes : 0; QString toolTip4 = tr("Can vary +/- %1 satoshi(s) per input.").arg(dFeeVary); From fc0176d01e57c0f512d6a8adf4d50df356121b02 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Mon, 17 Jul 2017 13:09:04 -0400 Subject: [PATCH 357/382] [tests] use python3 for authproxy.py --- test/functional/test_framework/authproxy.py | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/test/functional/test_framework/authproxy.py b/test/functional/test_framework/authproxy.py index 747bda309..8b2f31624 100644 --- a/test/functional/test_framework/authproxy.py +++ b/test/functional/test_framework/authproxy.py @@ -33,20 +33,14 @@ - uses standard Python json lib """ -try: - import http.client as httplib -except ImportError: - import httplib import base64 import decimal +import http.client import json import logging import socket import time -try: - import urllib.parse as urlparse -except ImportError: - import urlparse +import urllib.parse USER_AGENT = "AuthServiceProxy/0.1" @@ -60,7 +54,7 @@ def __init__(self, rpc_error): errmsg = '%(message)s (%(code)i)' % rpc_error except (KeyError, TypeError): errmsg = '' - Exception.__init__(self, errmsg) + super().__init__(errmsg) self.error = rpc_error @@ -77,7 +71,7 @@ def __init__(self, service_url, service_name=None, timeout=HTTP_TIMEOUT, connect self.__service_url = service_url self._service_name = service_name self.ensure_ascii = ensure_ascii # can be toggled on the fly by tests - self.__url = urlparse.urlparse(service_url) + self.__url = urllib.parse.urlparse(service_url) if self.__url.port is None: port = 80 else: @@ -98,10 +92,10 @@ def __init__(self, service_url, service_name=None, timeout=HTTP_TIMEOUT, connect # Callables re-use the connection of the original proxy self.__conn = connection elif self.__url.scheme == 'https': - self.__conn = httplib.HTTPSConnection(self.__url.hostname, port, + self.__conn = http.client.HTTPSConnection(self.__url.hostname, port, timeout=timeout) else: - self.__conn = httplib.HTTPConnection(self.__url.hostname, port, + self.__conn = http.client.HTTPConnection(self.__url.hostname, port, timeout=timeout) def __getattr__(self, name): @@ -124,7 +118,7 @@ def _request(self, method, path, postdata): try: self.__conn.request(method, path, postdata, headers) return self._get_response() - except httplib.BadStatusLine as e: + except http.client.BadStatusLine as e: if e.line == "''": # if connection was closed, try again self.__conn.close() self.__conn.request(method, path, postdata, headers) From 323d8f61e99cd867fee653694b42e10776324a5b Mon Sep 17 00:00:00 2001 From: John Newbery Date: Mon, 17 Jul 2017 13:16:57 -0400 Subject: [PATCH 358/382] [tests] fix flake8 warnings in authproxy.py --- test/functional/test_framework/authproxy.py | 25 +++++++++------------ 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/test/functional/test_framework/authproxy.py b/test/functional/test_framework/authproxy.py index 8b2f31624..e3ae0f793 100644 --- a/test/functional/test_framework/authproxy.py +++ b/test/functional/test_framework/authproxy.py @@ -42,9 +42,8 @@ import time import urllib.parse -USER_AGENT = "AuthServiceProxy/0.1" - HTTP_TIMEOUT = 30 +USER_AGENT = "AuthServiceProxy/0.1" log = logging.getLogger("BitcoinRPC") @@ -70,7 +69,7 @@ class AuthServiceProxy(object): def __init__(self, service_url, service_name=None, timeout=HTTP_TIMEOUT, connection=None, ensure_ascii=True): self.__service_url = service_url self._service_name = service_name - self.ensure_ascii = ensure_ascii # can be toggled on the fly by tests + self.ensure_ascii = ensure_ascii # can be toggled on the fly by tests self.__url = urllib.parse.urlparse(service_url) if self.__url.port is None: port = 80 @@ -92,11 +91,9 @@ def __init__(self, service_url, service_name=None, timeout=HTTP_TIMEOUT, connect # Callables re-use the connection of the original proxy self.__conn = connection elif self.__url.scheme == 'https': - self.__conn = http.client.HTTPSConnection(self.__url.hostname, port, - timeout=timeout) + self.__conn = http.client.HTTPSConnection(self.__url.hostname, port, timeout=timeout) else: - self.__conn = http.client.HTTPConnection(self.__url.hostname, port, - timeout=timeout) + self.__conn = http.client.HTTPConnection(self.__url.hostname, port, timeout=timeout) def __getattr__(self, name): if name.startswith('__') and name.endswith('__'): @@ -119,13 +116,13 @@ def _request(self, method, path, postdata): self.__conn.request(method, path, postdata, headers) return self._get_response() except http.client.BadStatusLine as e: - if e.line == "''": # if connection was closed, try again + if e.line == "''": # if connection was closed, try again self.__conn.close() self.__conn.request(method, path, postdata, headers) return self._get_response() else: raise - except (BrokenPipeError,ConnectionResetError): + except (BrokenPipeError, ConnectionResetError): # Python 3.5+ raises BrokenPipeError instead of BadStatusLine when the connection was reset # ConnectionResetError happens on FreeBSD with Python 3.4 self.__conn.close() @@ -135,8 +132,8 @@ def _request(self, method, path, postdata): def get_request(self, *args, **argsn): AuthServiceProxy.__id_count += 1 - log.debug("-%s-> %s %s"%(AuthServiceProxy.__id_count, self._service_name, - json.dumps(args, default=EncodeDecimal, ensure_ascii=self.ensure_ascii))) + log.debug("-%s-> %s %s" % (AuthServiceProxy.__id_count, self._service_name, + json.dumps(args, default=EncodeDecimal, ensure_ascii=self.ensure_ascii))) if args and argsn: raise ValueError('Cannot handle both named and positional arguments') return {'version': '1.1', @@ -157,7 +154,7 @@ def __call__(self, *args, **argsn): def batch(self, rpc_call_list): postdata = json.dumps(list(rpc_call_list), default=EncodeDecimal, ensure_ascii=self.ensure_ascii) - log.debug("--> "+postdata) + log.debug("--> " + postdata) return self._request('POST', self.__url.path, postdata.encode('utf-8')) def _get_response(self): @@ -184,9 +181,9 @@ def _get_response(self): response = json.loads(responsedata, parse_float=decimal.Decimal) elapsed = time.time() - req_start_time if "error" in response and response["error"] is None: - log.debug("<-%s- [%.6f] %s"%(response["id"], elapsed, json.dumps(response["result"], default=EncodeDecimal, ensure_ascii=self.ensure_ascii))) + log.debug("<-%s- [%.6f] %s" % (response["id"], elapsed, json.dumps(response["result"], default=EncodeDecimal, ensure_ascii=self.ensure_ascii))) else: - log.debug("<-- [%.6f] %s"%(elapsed,responsedata)) + log.debug("<-- [%.6f] %s" % (elapsed, responsedata)) return response def __truediv__(self, relative_uri): From 8f9e3627ef054c9732f4e529c6ed429ed8dc7183 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Tue, 10 Oct 2017 10:49:28 -0400 Subject: [PATCH 359/382] [tests] authproxy.py: tidy up __init__() --- test/functional/test_framework/authproxy.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/test/functional/test_framework/authproxy.py b/test/functional/test_framework/authproxy.py index e3ae0f793..1a3acf48a 100644 --- a/test/functional/test_framework/authproxy.py +++ b/test/functional/test_framework/authproxy.py @@ -71,19 +71,9 @@ def __init__(self, service_url, service_name=None, timeout=HTTP_TIMEOUT, connect self._service_name = service_name self.ensure_ascii = ensure_ascii # can be toggled on the fly by tests self.__url = urllib.parse.urlparse(service_url) - if self.__url.port is None: - port = 80 - else: - port = self.__url.port - (user, passwd) = (self.__url.username, self.__url.password) - try: - user = user.encode('utf8') - except AttributeError: - pass - try: - passwd = passwd.encode('utf8') - except AttributeError: - pass + port = 80 if self.__url.port is None else self.__url.port + user = None if self.__url.username is None else self.__url.username.encode('utf8') + passwd = None if self.__url.password is None else self.__url.password.encode('utf8') authpair = user + b':' + passwd self.__auth_header = b'Basic ' + base64.b64encode(authpair) From f893085325b57dd07bde78a01e5dcb7d303154de Mon Sep 17 00:00:00 2001 From: John Newbery Date: Mon, 16 Oct 2017 21:46:23 -0400 Subject: [PATCH 360/382] [tests] Don't subclass from object for Python 3 --- test/functional/p2p-fullblocktest.py | 2 +- test/functional/p2p-segwit.py | 2 +- test/functional/test_framework/authproxy.py | 2 +- test/functional/test_framework/blockstore.py | 4 +- test/functional/test_framework/comptool.py | 6 +- test/functional/test_framework/coverage.py | 2 +- test/functional/test_framework/key.py | 2 +- test/functional/test_framework/mininode.py | 86 +++++++++---------- test/functional/test_framework/script.py | 2 +- test/functional/test_framework/socks5.py | 8 +- .../test_framework/test_framework.py | 2 +- test/functional/test_runner.py | 2 +- 12 files changed, 60 insertions(+), 60 deletions(-) diff --git a/test/functional/p2p-fullblocktest.py b/test/functional/p2p-fullblocktest.py index 1d969fc7c..f19b845a3 100755 --- a/test/functional/p2p-fullblocktest.py +++ b/test/functional/p2p-fullblocktest.py @@ -20,7 +20,7 @@ from test_framework.script import * import struct -class PreviousSpendableOutput(object): +class PreviousSpendableOutput(): def __init__(self, tx = CTransaction(), n = -1): self.tx = tx self.n = n # the output we're spending diff --git a/test/functional/p2p-segwit.py b/test/functional/p2p-segwit.py index a9ef47559..f80336766 100755 --- a/test/functional/p2p-segwit.py +++ b/test/functional/p2p-segwit.py @@ -89,7 +89,7 @@ def test_witness_block(self, block, accepted, with_witness=True): assert_equal(self.connection.rpc.getbestblockhash() == block.hash, accepted) # Used to keep track of anyone-can-spend outputs that we can use in the tests -class UTXO(object): +class UTXO(): def __init__(self, sha256, n, nValue): self.sha256 = sha256 self.n = n diff --git a/test/functional/test_framework/authproxy.py b/test/functional/test_framework/authproxy.py index 1a3acf48a..bd3a3b3fa 100644 --- a/test/functional/test_framework/authproxy.py +++ b/test/functional/test_framework/authproxy.py @@ -62,7 +62,7 @@ def EncodeDecimal(o): return str(o) raise TypeError(repr(o) + " is not JSON serializable") -class AuthServiceProxy(object): +class AuthServiceProxy(): __id_count = 0 # ensure_ascii: escape unicode as \uXXXX, passed to json.dumps diff --git a/test/functional/test_framework/blockstore.py b/test/functional/test_framework/blockstore.py index 4b2170a03..ad0472248 100644 --- a/test/functional/test_framework/blockstore.py +++ b/test/functional/test_framework/blockstore.py @@ -10,7 +10,7 @@ logger = logging.getLogger("TestFramework.blockstore") -class BlockStore(object): +class BlockStore(): """BlockStore helper class. BlockStore keeps a map of blocks and implements helper functions for @@ -127,7 +127,7 @@ def get_locator(self, current_tip=None): locator.vHave = r return locator -class TxStore(object): +class TxStore(): def __init__(self, datadir): self.txDB = dbmd.open(datadir + "/transactions", 'c') diff --git a/test/functional/test_framework/comptool.py b/test/functional/test_framework/comptool.py index bfbc0c3b0..b0417e02d 100755 --- a/test/functional/test_framework/comptool.py +++ b/test/functional/test_framework/comptool.py @@ -27,7 +27,7 @@ global mininode_lock -class RejectResult(object): +class RejectResult(): """Outcome that expects rejection of a transaction or block.""" def __init__(self, code, reason=b''): self.code = code @@ -156,13 +156,13 @@ def send_mempool(self): # across all connections. (If outcome of final tx is specified as true # or false, then only the last tx is tested against outcome.) -class TestInstance(object): +class TestInstance(): def __init__(self, objects=None, sync_every_block=True, sync_every_tx=False): self.blocks_and_transactions = objects if objects else [] self.sync_every_block = sync_every_block self.sync_every_tx = sync_every_tx -class TestManager(object): +class TestManager(): def __init__(self, testgen, datadir): self.test_generator = testgen diff --git a/test/functional/test_framework/coverage.py b/test/functional/test_framework/coverage.py index 84049e76b..ddc3c515b 100644 --- a/test/functional/test_framework/coverage.py +++ b/test/functional/test_framework/coverage.py @@ -14,7 +14,7 @@ REFERENCE_FILENAME = 'rpc_interface.txt' -class AuthServiceProxyWrapper(object): +class AuthServiceProxyWrapper(): """ An object that wraps AuthServiceProxy to record specific RPC calls. diff --git a/test/functional/test_framework/key.py b/test/functional/test_framework/key.py index 85a6158a2..aa91fb5b0 100644 --- a/test/functional/test_framework/key.py +++ b/test/functional/test_framework/key.py @@ -84,7 +84,7 @@ def _check_result(val, func, args): ssl.EC_KEY_new_by_curve_name.restype = ctypes.c_void_p ssl.EC_KEY_new_by_curve_name.errcheck = _check_result -class CECKey(object): +class CECKey(): """Wrapper around OpenSSL's EC_KEY""" POINT_CONVERSION_COMPRESSED = 2 diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py index c6f596156..339efb72c 100755 --- a/test/functional/test_framework/mininode.py +++ b/test/functional/test_framework/mininode.py @@ -219,7 +219,7 @@ def ToHex(obj): # Objects that map to bitcoind objects, which can be serialized/deserialized -class CAddress(object): +class CAddress(): def __init__(self): self.nServices = 1 self.pchReserved = b"\x00" * 10 + b"\xff" * 2 @@ -246,7 +246,7 @@ def __repr__(self): MSG_WITNESS_FLAG = 1<<30 -class CInv(object): +class CInv(): typemap = { 0: "Error", 1: "TX", @@ -275,7 +275,7 @@ def __repr__(self): % (self.typemap[self.type], self.hash) -class CBlockLocator(object): +class CBlockLocator(): def __init__(self): self.nVersion = MY_VERSION self.vHave = [] @@ -295,7 +295,7 @@ def __repr__(self): % (self.nVersion, repr(self.vHave)) -class COutPoint(object): +class COutPoint(): def __init__(self, hash=0, n=0): self.hash = hash self.n = n @@ -314,7 +314,7 @@ def __repr__(self): return "COutPoint(hash=%064x n=%i)" % (self.hash, self.n) -class CTxIn(object): +class CTxIn(): def __init__(self, outpoint=None, scriptSig=b"", nSequence=0): if outpoint is None: self.prevout = COutPoint() @@ -342,7 +342,7 @@ def __repr__(self): self.nSequence) -class CTxOut(object): +class CTxOut(): def __init__(self, nValue=0, scriptPubKey=b""): self.nValue = nValue self.scriptPubKey = scriptPubKey @@ -363,7 +363,7 @@ def __repr__(self): bytes_to_hex_str(self.scriptPubKey)) -class CScriptWitness(object): +class CScriptWitness(): def __init__(self): # stack is a vector of strings self.stack = [] @@ -378,7 +378,7 @@ def is_null(self): return True -class CTxInWitness(object): +class CTxInWitness(): def __init__(self): self.scriptWitness = CScriptWitness() @@ -395,7 +395,7 @@ def is_null(self): return self.scriptWitness.is_null() -class CTxWitness(object): +class CTxWitness(): def __init__(self): self.vtxinwit = [] @@ -423,7 +423,7 @@ def is_null(self): return True -class CTransaction(object): +class CTransaction(): def __init__(self, tx=None): if tx is None: self.nVersion = 1 @@ -526,7 +526,7 @@ def __repr__(self): % (self.nVersion, repr(self.vin), repr(self.vout), repr(self.wit), self.nLockTime) -class CBlockHeader(object): +class CBlockHeader(): def __init__(self, header=None): if header is None: self.set_null() @@ -666,7 +666,7 @@ def __repr__(self): time.ctime(self.nTime), self.nBits, self.nNonce, repr(self.vtx)) -class CUnsignedAlert(object): +class CUnsignedAlert(): def __init__(self): self.nVersion = 1 self.nRelayUntil = 0 @@ -721,7 +721,7 @@ def __repr__(self): self.strComment, self.strStatusBar, self.strReserved) -class CAlert(object): +class CAlert(): def __init__(self): self.vchMsg = b"" self.vchSig = b"" @@ -741,7 +741,7 @@ def __repr__(self): % (len(self.vchMsg), len(self.vchSig)) -class PrefilledTransaction(object): +class PrefilledTransaction(): def __init__(self, index=0, tx = None): self.index = index self.tx = tx @@ -767,7 +767,7 @@ def __repr__(self): return "PrefilledTransaction(index=%d, tx=%s)" % (self.index, repr(self.tx)) # This is what we send on the wire, in a cmpctblock message. -class P2PHeaderAndShortIDs(object): +class P2PHeaderAndShortIDs(): def __init__(self): self.header = CBlockHeader() self.nonce = 0 @@ -819,7 +819,7 @@ def calculate_shortid(k0, k1, tx_hash): # This version gets rid of the array lengths, and reinterprets the differential # encoding into indices that can be used for lookup. -class HeaderAndShortIDs(object): +class HeaderAndShortIDs(): def __init__(self, p2pheaders_and_shortids = None): self.header = CBlockHeader() self.nonce = 0 @@ -880,7 +880,7 @@ def __repr__(self): return "HeaderAndShortIDs(header=%s, nonce=%d, shortids=%s, prefilledtxn=%s" % (repr(self.header), self.nonce, repr(self.shortids), repr(self.prefilled_txn)) -class BlockTransactionsRequest(object): +class BlockTransactionsRequest(): def __init__(self, blockhash=0, indexes = None): self.blockhash = blockhash @@ -920,7 +920,7 @@ def __repr__(self): return "BlockTransactionsRequest(hash=%064x indexes=%s)" % (self.blockhash, repr(self.indexes)) -class BlockTransactions(object): +class BlockTransactions(): def __init__(self, blockhash=0, transactions = None): self.blockhash = blockhash @@ -944,7 +944,7 @@ def __repr__(self): # Objects that correspond to messages on the wire -class msg_version(object): +class msg_version(): command = b"version" def __init__(self): @@ -1012,7 +1012,7 @@ def __repr__(self): self.strSubVer, self.nStartingHeight, self.nRelay) -class msg_verack(object): +class msg_verack(): command = b"verack" def __init__(self): @@ -1028,7 +1028,7 @@ def __repr__(self): return "msg_verack()" -class msg_addr(object): +class msg_addr(): command = b"addr" def __init__(self): @@ -1044,7 +1044,7 @@ def __repr__(self): return "msg_addr(addrs=%s)" % (repr(self.addrs)) -class msg_alert(object): +class msg_alert(): command = b"alert" def __init__(self): @@ -1063,7 +1063,7 @@ def __repr__(self): return "msg_alert(alert=%s)" % (repr(self.alert), ) -class msg_inv(object): +class msg_inv(): command = b"inv" def __init__(self, inv=None): @@ -1082,7 +1082,7 @@ def __repr__(self): return "msg_inv(inv=%s)" % (repr(self.inv)) -class msg_getdata(object): +class msg_getdata(): command = b"getdata" def __init__(self, inv=None): @@ -1098,7 +1098,7 @@ def __repr__(self): return "msg_getdata(inv=%s)" % (repr(self.inv)) -class msg_getblocks(object): +class msg_getblocks(): command = b"getblocks" def __init__(self): @@ -1121,7 +1121,7 @@ def __repr__(self): % (repr(self.locator), self.hashstop) -class msg_tx(object): +class msg_tx(): command = b"tx" def __init__(self, tx=CTransaction()): @@ -1142,7 +1142,7 @@ def serialize(self): return self.tx.serialize_with_witness() -class msg_block(object): +class msg_block(): command = b"block" def __init__(self, block=None): @@ -1162,7 +1162,7 @@ def __repr__(self): # for cases where a user needs tighter control over what is sent over the wire # note that the user must supply the name of the command, and the data -class msg_generic(object): +class msg_generic(): def __init__(self, command, data=None): self.command = command self.data = data @@ -1179,7 +1179,7 @@ def serialize(self): r = self.block.serialize(with_witness=True) return r -class msg_getaddr(object): +class msg_getaddr(): command = b"getaddr" def __init__(self): @@ -1195,7 +1195,7 @@ def __repr__(self): return "msg_getaddr()" -class msg_ping_prebip31(object): +class msg_ping_prebip31(): command = b"ping" def __init__(self): @@ -1211,7 +1211,7 @@ def __repr__(self): return "msg_ping() (pre-bip31)" -class msg_ping(object): +class msg_ping(): command = b"ping" def __init__(self, nonce=0): @@ -1229,7 +1229,7 @@ def __repr__(self): return "msg_ping(nonce=%08x)" % self.nonce -class msg_pong(object): +class msg_pong(): command = b"pong" def __init__(self, nonce=0): @@ -1247,7 +1247,7 @@ def __repr__(self): return "msg_pong(nonce=%08x)" % self.nonce -class msg_mempool(object): +class msg_mempool(): command = b"mempool" def __init__(self): @@ -1262,7 +1262,7 @@ def serialize(self): def __repr__(self): return "msg_mempool()" -class msg_sendheaders(object): +class msg_sendheaders(): command = b"sendheaders" def __init__(self): @@ -1282,7 +1282,7 @@ def __repr__(self): # number of entries # vector of hashes # hash_stop (hash of last desired block header, 0 to get as many as possible) -class msg_getheaders(object): +class msg_getheaders(): command = b"getheaders" def __init__(self): @@ -1307,7 +1307,7 @@ def __repr__(self): # headers message has # -class msg_headers(object): +class msg_headers(): command = b"headers" def __init__(self, headers=None): @@ -1327,7 +1327,7 @@ def __repr__(self): return "msg_headers(headers=%s)" % repr(self.headers) -class msg_reject(object): +class msg_reject(): command = b"reject" REJECT_MALFORMED = 1 @@ -1358,7 +1358,7 @@ def __repr__(self): return "msg_reject: %s %d %s [%064x]" \ % (self.message, self.code, self.reason, self.data) -class msg_feefilter(object): +class msg_feefilter(): command = b"feefilter" def __init__(self, feerate=0): @@ -1375,7 +1375,7 @@ def serialize(self): def __repr__(self): return "msg_feefilter(feerate=%08x)" % self.feerate -class msg_sendcmpct(object): +class msg_sendcmpct(): command = b"sendcmpct" def __init__(self): @@ -1395,7 +1395,7 @@ def serialize(self): def __repr__(self): return "msg_sendcmpct(announce=%s, version=%lu)" % (self.announce, self.version) -class msg_cmpctblock(object): +class msg_cmpctblock(): command = b"cmpctblock" def __init__(self, header_and_shortids = None): @@ -1413,7 +1413,7 @@ def serialize(self): def __repr__(self): return "msg_cmpctblock(HeaderAndShortIDs=%s)" % repr(self.header_and_shortids) -class msg_getblocktxn(object): +class msg_getblocktxn(): command = b"getblocktxn" def __init__(self): @@ -1431,7 +1431,7 @@ def serialize(self): def __repr__(self): return "msg_getblocktxn(block_txn_request=%s)" % (repr(self.block_txn_request)) -class msg_blocktxn(object): +class msg_blocktxn(): command = b"blocktxn" def __init__(self): @@ -1454,7 +1454,7 @@ def serialize(self): r += self.block_transactions.serialize(with_witness=True) return r -class NodeConnCB(object): +class NodeConnCB(): """Callback and helper functions for P2P connection to a bitcoind node. Individual testcases should subclass this and override the on_* methods diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py index 8f5339a02..a4c046bd3 100644 --- a/test/functional/test_framework/script.py +++ b/test/functional/test_framework/script.py @@ -370,7 +370,7 @@ def __init__(self, msg, data): super(CScriptTruncatedPushDataError, self).__init__(msg) # This is used, eg, for blockchain heights in coinbase scripts (bip34) -class CScriptNum(object): +class CScriptNum(): def __init__(self, d=0): self.value = d diff --git a/test/functional/test_framework/socks5.py b/test/functional/test_framework/socks5.py index 007084416..7b40c47fb 100644 --- a/test/functional/test_framework/socks5.py +++ b/test/functional/test_framework/socks5.py @@ -31,7 +31,7 @@ def recvall(s, n): return rv ### Implementation classes -class Socks5Configuration(object): +class Socks5Configuration(): """Proxy configuration.""" def __init__(self): self.addr = None # Bind address (must be set) @@ -39,7 +39,7 @@ def __init__(self): self.unauth = False # Support unauthenticated self.auth = False # Support authentication -class Socks5Command(object): +class Socks5Command(): """Information about an incoming socks5 command.""" def __init__(self, cmd, atyp, addr, port, username, password): self.cmd = cmd # Command (one of Command.*) @@ -51,7 +51,7 @@ def __init__(self, cmd, atyp, addr, port, username, password): def __repr__(self): return 'Socks5Command(%s,%s,%s,%s,%s,%s)' % (self.cmd, self.atyp, self.addr, self.port, self.username, self.password) -class Socks5Connection(object): +class Socks5Connection(): def __init__(self, serv, conn, peer): self.serv = serv self.conn = conn @@ -122,7 +122,7 @@ def handle(self): finally: self.conn.close() -class Socks5Server(object): +class Socks5Server(): def __init__(self, conf): self.conf = conf self.s = socket.socket(conf.af) diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 381513ab9..d31e9a285 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -43,7 +43,7 @@ class TestStatus(Enum): TEST_EXIT_FAILED = 1 TEST_EXIT_SKIPPED = 77 -class BitcoinTestFramework(object): +class BitcoinTestFramework(): """Base class for a bitcoin test script. Individual bitcoin test scripts should subclass this class and override the set_test_params() and run_test() methods. diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 8c4651f6e..5411dab3f 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -459,7 +459,7 @@ def check_script_list(src_dir): # On travis this warning is an error to prevent merging incomplete commits into master sys.exit(1) -class RPCCoverage(object): +class RPCCoverage(): """ Coverage reporting utilities for test_runner. From 8e4aa35ffbcaa0e0a3eceaa500193b0f97d957ab Mon Sep 17 00:00:00 2001 From: Aaron Golliver Date: Sat, 14 Oct 2017 16:06:21 -0700 Subject: [PATCH 361/382] move human-readable byte formatting to guiutil --- src/qt/guiutil.cpp | 12 ++++++++++++ src/qt/guiutil.h | 2 ++ src/qt/rpcconsole.cpp | 20 ++++---------------- src/qt/rpcconsole.h | 1 - 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index d520d7d4b..4bd63f464 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -984,6 +984,18 @@ QString formatNiceTimeOffset(qint64 secs) return timeBehindText; } +QString formatBytes(uint64_t bytes) +{ + if(bytes < 1024) + return QString(QObject::tr("%1 B")).arg(bytes); + if(bytes < 1024 * 1024) + return QString(QObject::tr("%1 KB")).arg(bytes / 1024); + if(bytes < 1024 * 1024 * 1024) + return QString(QObject::tr("%1 MB")).arg(bytes / 1024 / 1024); + + return QString(QObject::tr("%1 GB")).arg(bytes / 1024 / 1024 / 1024); +} + void ClickableLabel::mouseReleaseEvent(QMouseEvent *event) { Q_EMIT clicked(event->pos()); diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index d10818d0c..7622816f7 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -199,6 +199,8 @@ namespace GUIUtil QString formatNiceTimeOffset(qint64 secs); + QString formatBytes(uint64_t bytes); + class ClickableLabel : public QLabel { Q_OBJECT diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index d895fc166..068c40e1e 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -935,18 +935,6 @@ void RPCConsole::on_sldGraphRange_valueChanged(int value) setTrafficGraphRange(mins); } -QString RPCConsole::FormatBytes(quint64 bytes) -{ - if(bytes < 1024) - return QString(tr("%1 B")).arg(bytes); - if(bytes < 1024 * 1024) - return QString(tr("%1 KB")).arg(bytes / 1024); - if(bytes < 1024 * 1024 * 1024) - return QString(tr("%1 MB")).arg(bytes / 1024 / 1024); - - return QString(tr("%1 GB")).arg(bytes / 1024 / 1024 / 1024); -} - void RPCConsole::setTrafficGraphRange(int mins) { ui->trafficGraph->setGraphRangeMins(mins); @@ -955,8 +943,8 @@ void RPCConsole::setTrafficGraphRange(int mins) void RPCConsole::updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut) { - ui->lblBytesIn->setText(FormatBytes(totalBytesIn)); - ui->lblBytesOut->setText(FormatBytes(totalBytesOut)); + ui->lblBytesIn->setText(GUIUtil::formatBytes(totalBytesIn)); + ui->lblBytesOut->setText(GUIUtil::formatBytes(totalBytesOut)); } void RPCConsole::peerSelected(const QItemSelection &selected, const QItemSelection &deselected) @@ -1050,8 +1038,8 @@ void RPCConsole::updateNodeDetail(const CNodeCombinedStats *stats) ui->peerServices->setText(GUIUtil::formatServicesStr(stats->nodeStats.nServices)); 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->peerBytesSent->setText(GUIUtil::formatBytes(stats->nodeStats.nSendBytes)); + ui->peerBytesRecv->setText(GUIUtil::formatBytes(stats->nodeStats.nRecvBytes)); 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)); diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index da06818f8..ad6e84a44 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -123,7 +123,6 @@ public Q_SLOTS: void cmdRequest(const QString &command); private: - static QString FormatBytes(quint64 bytes); void startExecutor(); void setTrafficGraphRange(int mins); /** show detailed information on ui about selected node */ From 6b1891e2c04410ebaa1d6399aab56b6c7495d0ff Mon Sep 17 00:00:00 2001 From: Aaron Golliver Date: Sat, 14 Oct 2017 16:06:54 -0700 Subject: [PATCH 362/382] Add Sent and Received information to the debug menu peer list --- src/qt/peertablemodel.cpp | 20 +++++++++++++++++--- src/qt/peertablemodel.h | 6 ++++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp index 42934f805..8b2a7e704 100644 --- a/src/qt/peertablemodel.cpp +++ b/src/qt/peertablemodel.cpp @@ -33,6 +33,10 @@ bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombine return pLeft->cleanSubVer.compare(pRight->cleanSubVer) < 0; case PeerTableModel::Ping: return pLeft->dMinPing < pRight->dMinPing; + case PeerTableModel::Sent: + return pLeft->nSendBytes < pRight->nSendBytes; + case PeerTableModel::Received: + return pLeft->nRecvBytes < pRight->nRecvBytes; } return false; @@ -114,7 +118,7 @@ PeerTableModel::PeerTableModel(ClientModel *parent) : clientModel(parent), timer(0) { - columns << tr("NodeId") << tr("Node/Service") << tr("User Agent") << tr("Ping"); + columns << tr("NodeId") << tr("Node/Service") << tr("Ping") << tr("Sent") << tr("Received") << tr("User Agent"); priv.reset(new PeerTablePriv()); // default to unsorted priv->sortColumn = -1; @@ -173,10 +177,20 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const return QString::fromStdString(rec->nodeStats.cleanSubVer); case Ping: return GUIUtil::formatPingTime(rec->nodeStats.dMinPing); + case Sent: + return GUIUtil::formatBytes(rec->nodeStats.nSendBytes); + case Received: + return GUIUtil::formatBytes(rec->nodeStats.nRecvBytes); } } else if (role == Qt::TextAlignmentRole) { - if (index.column() == Ping) - return (QVariant)(Qt::AlignRight | Qt::AlignVCenter); + switch (index.column()) { + case Ping: + case Sent: + case Received: + return QVariant(Qt::AlignRight | Qt::AlignVCenter); + default: + return QVariant(); + } } return QVariant(); diff --git a/src/qt/peertablemodel.h b/src/qt/peertablemodel.h index cc47b67ec..ec91d0712 100644 --- a/src/qt/peertablemodel.h +++ b/src/qt/peertablemodel.h @@ -55,8 +55,10 @@ class PeerTableModel : public QAbstractTableModel enum ColumnIndex { NetNodeId = 0, Address = 1, - Subversion = 2, - Ping = 3 + Ping = 2, + Sent = 3, + Received = 4, + Subversion = 5 }; /** @name Methods overridden from QAbstractTableModel From a86e81b78fc2a2ecc827a7890cba21ed229957f2 Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Wed, 18 Oct 2017 14:24:21 -0400 Subject: [PATCH 363/382] travis: move back to the minimal image The most recent update replaced the minimal image with a large one for the 'generic' image. Switching back to 'minimal' should reduce dependencies and maybe speed us up some. It should also eliminiate the need for aa2e0f09e. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0de7ca6f7..8e0499db0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ sudo: required dist: trusty os: linux -language: generic +language: minimal cache: directories: - depends/built From 3d1c31126b8ac9fe18d151dc4e9cf8598dd77362 Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Wed, 18 Oct 2017 14:42:08 -0400 Subject: [PATCH 364/382] Revert "travis: filter out pyenv" This reverts commit aa2e0f09ec94dd0908f792ebc2249859ad174586. --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8e0499db0..4bb0b1f9f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,7 +40,6 @@ env: before_install: - export PATH=$(echo $PATH | tr ':' "\n" | sed '/\/opt\/python/d' | tr "\n" ":" | sed "s|::|:|g") - - export PATH=$(echo $PATH | tr ':' "\n" | sed '/\/opt\/pyenv/d' | tr "\n" ":" | sed "s|::|:|g") install: - if [ -n "$DPKG_ADD_ARCH" ]; then sudo dpkg --add-architecture "$DPKG_ADD_ARCH" ; fi - if [ -n "$PACKAGES" ]; then travis_retry sudo apt-get update; fi From 132d3225f325b84afc282638c9e99623d249a52c Mon Sep 17 00:00:00 2001 From: Andreas Schildbach Date: Thu, 19 Oct 2017 12:45:11 +0200 Subject: [PATCH 365/382] Remove my testnet DNS seed as I currently don't have the capacity to keep it up to date. --- src/chainparams.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 85c9cd693..afdac16da 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -230,7 +230,6 @@ class CTestNetParams : public CChainParams { vSeeds.emplace_back("testnet-seed.bitcoin.jonasschnelli.ch", true); vSeeds.emplace_back("seed.tbtc.petertodd.org", true); vSeeds.emplace_back("testnet-seed.bluematt.me", false); - vSeeds.emplace_back("testnet-seed.bitcoin.schildbach.de", false); base58Prefixes[PUBKEY_ADDRESS] = std::vector(1,111); base58Prefixes[SCRIPT_ADDRESS] = std::vector(1,196); From 478a89c1ef79a75275d1b508122c06eee9386b2d Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Tue, 10 Oct 2017 15:27:26 -0400 Subject: [PATCH 366/382] Avoid opening copied wallet databases simultaneously Make sure wallet databases have unique fileids. If they don't, throw an error. BDB caches do not work properly when more than one open database has the same fileid, because values written to one database may show up in reads to other databases. Bitcoin will never create different databases with the same fileid, but users can create them by manually copying database files. BDB caching bug was reported by Chris Moore https://github.com/bitcoin/bitcoin/issues/11429 Fixes #11429 --- src/wallet/db.cpp | 35 ++++++++++++++++++++++++++++++++++ test/functional/multiwallet.py | 6 ++++++ 2 files changed, 41 insertions(+) diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 5e881d9ac..459d289a4 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -20,6 +20,40 @@ #include +namespace { +//! Make sure database has a unique fileid within the environment. If it +//! doesn't, throw an error. BDB caches do not work properly when more than one +//! open database has the same fileid (values written to one database may show +//! up in reads to other databases). +//! +//! BerkeleyDB generates unique fileids by default +//! (https://docs.oracle.com/cd/E17275_01/html/programmer_reference/program_copy.html), +//! so bitcoin should never create different databases with the same fileid, but +//! this error can be triggered if users manually copy database files. +void CheckUniqueFileid(const CDBEnv& env, const std::string& filename, Db& db) +{ + if (env.IsMock()) return; + + u_int8_t fileid[DB_FILE_ID_LEN]; + int ret = db.get_mpf()->get_fileid(fileid); + if (ret != 0) { + throw std::runtime_error(strprintf("CDB: Can't open database %s (get_fileid failed with %d)", filename, ret)); + } + + for (const auto& item : env.mapDb) { + u_int8_t item_fileid[DB_FILE_ID_LEN]; + if (item.second && item.second->get_mpf()->get_fileid(item_fileid) == 0 && + memcmp(fileid, item_fileid, sizeof(fileid)) == 0) { + const char* item_filename = nullptr; + item.second->get_dbname(&item_filename, nullptr); + throw std::runtime_error(strprintf("CDB: Can't open database %s (duplicates fileid %s from %s)", filename, + HexStr(std::begin(item_fileid), std::end(item_fileid)), + item_filename ? item_filename : "(unknown database)")); + } + } +} +} // namespace + // // CDB // @@ -403,6 +437,7 @@ CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb if (ret != 0) { throw std::runtime_error(strprintf("CDB: Error %d, can't open database %s", ret, strFilename)); } + CheckUniqueFileid(*env, strFilename, *pdb_temp); pdb = pdb_temp.release(); env->mapDb[strFilename] = pdb; diff --git a/test/functional/multiwallet.py b/test/functional/multiwallet.py index f55da7681..7a0fbce47 100755 --- a/test/functional/multiwallet.py +++ b/test/functional/multiwallet.py @@ -7,6 +7,7 @@ Verify that a bitcoind node can load multiple wallet files """ import os +import shutil from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_raises_rpc_error @@ -29,6 +30,11 @@ def run_test(self): os.mkdir(os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w11')) self.assert_start_raises_init_error(0, ['-wallet=w11'], 'Error loading wallet w11. -wallet filename must be a regular file.') + # should not initialize if one wallet is a copy of another + shutil.copyfile(os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w2'), + os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w22')) + self.assert_start_raises_init_error(0, ['-wallet=w2', '-wallet=w22'], 'duplicates fileid') + # should not initialize if wallet file is a symlink os.symlink(os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w1'), os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w12')) self.assert_start_raises_init_error(0, ['-wallet=w12'], 'Error loading wallet w12. -wallet filename must be a regular file.') From 7a5f9303a9ed8e9efa25f8264ed491644e451645 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= Date: Thu, 19 Oct 2017 15:49:45 +0100 Subject: [PATCH 367/382] Avoid slow transaction search with txindex enabled --- src/validation.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/validation.cpp b/src/validation.cpp index 1a1c1941e..d19521bd3 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -939,6 +939,9 @@ bool GetTransaction(const uint256 &hash, CTransactionRef &txOut, const Consensus return error("%s: txid mismatch", __func__); return true; } + + // transaction not found in index, nothing more can be done + return false; } if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it From fa81534d068cb5479684ed9fb073dc51532b91ca Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Thu, 19 Oct 2017 22:19:57 +0200 Subject: [PATCH 368/382] Add share/rpcuser to dist. source code archive --- Makefile.am | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index 8216b7d60..bbfaf9d34 100644 --- a/Makefile.am +++ b/Makefile.am @@ -44,6 +44,9 @@ DIST_CONTRIB = $(top_srcdir)/contrib/bitcoin-cli.bash-completion \ $(top_srcdir)/contrib/bitcoind.bash-completion \ $(top_srcdir)/contrib/init \ $(top_srcdir)/contrib/rpm +DIST_SHARE = \ + $(top_srcdir)/share/genbuild.sh \ + $(top_srcdir)/share/rpcuser BIN_CHECKS=$(top_srcdir)/contrib/devtools/symbol-check.py \ $(top_srcdir)/contrib/devtools/security-check.py @@ -213,7 +216,7 @@ endif dist_noinst_SCRIPTS = autogen.sh -EXTRA_DIST = $(top_srcdir)/share/genbuild.sh test/functional/test_runner.py test/functional $(DIST_CONTRIB) $(DIST_DOCS) $(WINDOWS_PACKAGING) $(OSX_PACKAGING) $(BIN_CHECKS) +EXTRA_DIST = $(DIST_SHARE) test/functional/test_runner.py test/functional $(DIST_CONTRIB) $(DIST_DOCS) $(WINDOWS_PACKAGING) $(OSX_PACKAGING) $(BIN_CHECKS) EXTRA_DIST += \ test/util/bitcoin-util-test.py \ From ce8cd7a7da9174ab151172fc0ce97b5164637cf3 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Fri, 6 Oct 2017 14:11:43 -0400 Subject: [PATCH 369/382] Don't process unrequested, low-work blocks A peer could try to waste our resources by sending us unrequested blocks with low work, eg to fill up our disk. Since e2652002b6011f793185d473f87f1730c625593b we no longer request blocks until we know we're on a chain with more than nMinimumChainWork (our anti-DoS threshold), but we would still process unrequested blocks that had more work than our tip. This commit fixes that behavior. --- src/validation.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/validation.cpp b/src/validation.cpp index d19521bd3..866e0c9fb 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -3135,6 +3135,12 @@ static bool AcceptBlock(const std::shared_ptr& pblock, CValidation if (pindex->nTx != 0) return true; // This is a previously-processed block that was pruned if (!fHasMoreWork) return true; // Don't process less-work chains if (fTooFarAhead) return true; // Block height is too high + + // Protect against DoS attacks from low-work chains. + // If our tip is behind, a peer could try to send us + // low-work blocks on a fake chain that we would never + // request; don't process these. + if (pindex->nChainWork < nMinimumChainWork) return true; } if (fNewBlock) *fNewBlock = true; From 08fd822771bf8dae1c21698811f57aa691b2f25d Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Fri, 6 Oct 2017 14:32:07 -0400 Subject: [PATCH 370/382] qa: add test for minchainwork use in acceptblock --- test/functional/p2p-acceptblock.py | 44 ++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/test/functional/p2p-acceptblock.py b/test/functional/p2p-acceptblock.py index 6c7e6a22e..27ae0c27e 100755 --- a/test/functional/p2p-acceptblock.py +++ b/test/functional/p2p-acceptblock.py @@ -8,17 +8,22 @@ versus non-whitelisted peers, this tests the behavior of both (effectively two separate tests running in parallel). -Setup: two nodes, node0 and node1, not connected to each other. Node0 does not +Setup: three nodes, node0+node1+node2, not connected to each other. Node0 does not whitelist localhost, but node1 does. They will each be on their own chain for -this test. +this test. Node2 will have nMinimumChainWork set to 0x10, so it won't process +low-work unrequested blocks. -We have one NodeConn connection to each, test_node and white_node respectively. +We have one NodeConn connection to each, test_node, white_node, and min_work_node, +respectively. The test: 1. Generate one block on each node, to leave IBD. 2. Mine a new block on each tip, and deliver to each node from node's peer. - The tip should advance. + The tip should advance for node0 and node1, but node2 should skip processing + due to nMinimumChainWork. + +Node2 is unused in tests 3-7: 3. Mine a block that forks the previous block, and deliver to each node from corresponding peer. @@ -46,6 +51,10 @@ 7. Send Node0 the missing block again. Node0 should process and the tip should advance. + +8. Test Node2 is able to sync when connected to node0 (which should have sufficient +work on its chain). + """ from test_framework.mininode import * @@ -62,52 +71,60 @@ def add_options(self, parser): def set_test_params(self): self.setup_clean_chain = True - self.num_nodes = 2 - self.extra_args = [[], ["-whitelist=127.0.0.1"]] + self.num_nodes = 3 + self.extra_args = [[], ["-whitelist=127.0.0.1"], ["-minimumchainwork=0x10"]] def setup_network(self): # Node0 will be used to test behavior of processing unrequested blocks # from peers which are not whitelisted, while Node1 will be used for # the whitelisted case. + # Node2 will be used for non-whitelisted peers to test the interaction + # with nMinimumChainWork. self.setup_nodes() def run_test(self): # Setup the p2p connections and start up the network thread. test_node = NodeConnCB() # connects to node0 (not whitelisted) white_node = NodeConnCB() # connects to node1 (whitelisted) + min_work_node = NodeConnCB() # connects to node2 (not whitelisted) connections = [] connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node)) connections.append(NodeConn('127.0.0.1', p2p_port(1), self.nodes[1], white_node)) + connections.append(NodeConn('127.0.0.1', p2p_port(2), self.nodes[2], min_work_node)) test_node.add_connection(connections[0]) white_node.add_connection(connections[1]) + min_work_node.add_connection(connections[2]) NetworkThread().start() # Start up network handling in another thread # Test logic begins here test_node.wait_for_verack() white_node.wait_for_verack() + min_work_node.wait_for_verack() - # 1. Have both nodes mine a block (leave IBD) + # 1. Have nodes mine a block (nodes1/2 leave IBD) [ n.generate(1) for n in self.nodes ] tips = [ int("0x" + n.getbestblockhash(), 0) for n in self.nodes ] # 2. Send one block that builds on each tip. - # This should be accepted. + # This should be accepted by nodes 1/2 blocks_h2 = [] # the height 2 blocks on each node's chain block_time = int(time.time()) + 1 - for i in range(2): + for i in range(3): blocks_h2.append(create_block(tips[i], create_coinbase(2), block_time)) blocks_h2[i].solve() block_time += 1 test_node.send_message(msg_block(blocks_h2[0])) white_node.send_message(msg_block(blocks_h2[1])) + min_work_node.send_message(msg_block(blocks_h2[2])) - for x in [test_node, white_node]: + for x in [test_node, white_node, min_work_node]: x.sync_with_ping() assert_equal(self.nodes[0].getblockcount(), 2) assert_equal(self.nodes[1].getblockcount(), 2) - self.log.info("First height 2 block accepted by both nodes") + assert_equal(self.nodes[2].getblockcount(), 1) + self.log.info("First height 2 block accepted by node0/node1; correctly rejected by node2") # 3. Send another block that builds on the original tip. blocks_h2f = [] # Blocks at height 2 that fork off the main chain @@ -220,6 +237,11 @@ def run_test(self): assert_equal(self.nodes[0].getblockcount(), 290) self.log.info("Successfully reorged to longer chain from non-whitelisted peer") + # 8. Connect node2 to node0 and ensure it is able to sync + connect_nodes(self.nodes[0], 2) + sync_blocks([self.nodes[0], self.nodes[2]]) + self.log.info("Successfully synced nodes 2 and 0") + [ c.disconnect_node() for c in connections ] if __name__ == '__main__': From 01b52cedd42f50a93b40981c91af7c12de6e45ce Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Thu, 19 Oct 2017 20:52:30 -0400 Subject: [PATCH 371/382] Add comment explaining forced processing of compact blocks --- src/net_processing.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 6c26cd4ce..61f98ca74 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -2146,7 +2146,16 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr mapBlockSource.emplace(pblock->GetHash(), std::make_pair(pfrom->GetId(), false)); } bool fNewBlock = false; - ProcessNewBlock(chainparams, pblock, true, &fNewBlock); + // Setting fForceProcessing to true means that we bypass some of + // our anti-DoS protections in AcceptBlock, which filters + // unrequested blocks that might be trying to waste our resources + // (eg disk space). Because we only try to reconstruct blocks when + // we're close to caught up (via the CanDirectFetch() requirement + // above, combined with the behavior of not requesting blocks until + // we have a chain with at least nMinimumChainWork), and we ignore + // compact blocks with less work than our tip, it is safe to treat + // reconstructed compact blocks as having been requested. + ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock); if (fNewBlock) { pfrom->nLastBlockTime = GetTime(); } else { @@ -2226,7 +2235,11 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr bool fNewBlock = false; // Since we requested this block (it was in mapBlocksInFlight), force it to be processed, // even if it would not be a candidate for new tip (missing previous block, chain not long enough, etc) - ProcessNewBlock(chainparams, pblock, true, &fNewBlock); + // This bypasses some anti-DoS logic in AcceptBlock (eg to prevent + // disk-space attacks), but this should be safe due to the + // protections in the compact block handler -- see related comment + // in compact block optimistic reconstruction handling. + ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock); if (fNewBlock) { pfrom->nLastBlockTime = GetTime(); } else { From c96b2e4f096780c75e3fa8acba496d71322583a1 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Fri, 20 Oct 2017 16:10:08 -0400 Subject: [PATCH 372/382] qa: Fix replace-by-fee race condition failures --- test/functional/replace-by-fee.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/test/functional/replace-by-fee.py b/test/functional/replace-by-fee.py index 269d57775..17667b0bc 100755 --- a/test/functional/replace-by-fee.py +++ b/test/functional/replace-by-fee.py @@ -72,6 +72,10 @@ def set_test_params(self): ["-mempoolreplacement=0"]] def run_test(self): + # Leave IBD and ensure nodes are synced + self.nodes[0].generate(1) + self.sync_all() + make_utxo(self.nodes[0], 1*COIN) self.log.info("Running test simple doublespend...") @@ -110,13 +114,18 @@ def test_simple_doublespend(self): """Simple doublespend""" tx0_outpoint = make_utxo(self.nodes[0], int(1.1*COIN)) + # make_utxo may have generated a bunch of blocks, so we need to sync + # before we can spend the coins generated, or else the resulting + # transactions might not be accepted by our peers. + self.sync_all() + tx1a = CTransaction() tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)] tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))] tx1a_hex = txToHex(tx1a) tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) - self.sync_all([self.nodes]) + self.sync_all() # Should fail because we haven't changed the fee tx1b = CTransaction() From d23be309c2c45f655d5f5405e031833fb4b6bbb4 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Fri, 20 Oct 2017 16:35:16 -0400 Subject: [PATCH 373/382] [verify-commits] Allow revoked keys to expire --- contrib/verify-commits/gpg.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/contrib/verify-commits/gpg.sh b/contrib/verify-commits/gpg.sh index b01e2a6d3..abd8f5fd9 100755 --- a/contrib/verify-commits/gpg.sh +++ b/contrib/verify-commits/gpg.sh @@ -46,6 +46,11 @@ for LINE in $(echo "$GPG_RES"); do REVSIG=true GOODREVSIG="[GNUPG:] GOODSIG ${LINE#* * *}" ;; + "[GNUPG:] EXPKEYSIG "*) + [ "$BITCOIN_VERIFY_COMMITS_ALLOW_REVSIG" != 1 ] && exit 1 + REVSIG=true + GOODREVSIG="[GNUPG:] GOODSIG ${LINE#* * *}" + ;; esac done if ! $VALID; then From f8c66972ddc2a70f5015497436870e2af6833ecc Mon Sep 17 00:00:00 2001 From: Evan Klitzke Date: Tue, 8 Aug 2017 18:56:18 -0700 Subject: [PATCH 374/382] Fix automake warnings when running autogen.sh --- Makefile.am | 1 - src/Makefile.am | 4 ---- 2 files changed, 5 deletions(-) diff --git a/Makefile.am b/Makefile.am index 3b62a1060..8b3973d0e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -9,7 +9,6 @@ SUBDIRS += doc/man endif .PHONY: deploy FORCE -GZIP_ENV="-9n" export PYTHONPATH if BUILD_BITCOIN_LIBS diff --git a/src/Makefile.am b/src/Makefile.am index 90deff48b..3e4307687 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -500,10 +500,6 @@ clean-local: ## FIXME: How to get the appropriate modulename_CPPFLAGS in here? $(AM_V_GEN) $(WINDRES) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(CPPFLAGS) -DWINDRES_PREPROC -i $< -o $@ -.mm.o: - $(AM_V_CXX) $(OBJCXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ - $(CPPFLAGS) $(AM_CXXFLAGS) $(QT_INCLUDES) $(AM_CXXFLAGS) $(PIE_FLAGS) $(CXXFLAGS) -c -o $@ $< - check-symbols: $(bin_PROGRAMS) if GLIBC_BACK_COMPAT @echo "Checking glibc back compat..." From cc5c39ddca4cf5e01c45fdf69462f50ac984e264 Mon Sep 17 00:00:00 2001 From: fanquake Date: Sat, 21 Oct 2017 12:13:25 +0800 Subject: [PATCH 375/382] [Build] Add AM_OBJCXXFLAGS and QT_PIE_FLAGS to OBJCXXFLAGS to future-proof darwin targets --- src/Makefile.qt.include | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index e4b64c1ca..0767ee130 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -368,6 +368,7 @@ BITCOIN_QT_INCLUDES = -I$(builddir)/qt -I$(srcdir)/qt -I$(srcdir)/qt/forms \ qt_libbitcoinqt_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_QT_INCLUDES) \ $(QT_INCLUDES) $(QT_DBUS_INCLUDES) $(PROTOBUF_CFLAGS) $(QR_CFLAGS) qt_libbitcoinqt_a_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS) +qt_libbitcoinqt_a_OBJCXXFLAGS = $(AM_OBJCXXFLAGS) $(QT_PIE_FLAGS) qt_libbitcoinqt_a_SOURCES = $(BITCOIN_QT_CPP) $(BITCOIN_QT_H) $(QT_FORMS_UI) \ $(QT_QRC) $(QT_QRC_LOCALE) $(QT_TS) $(PROTOBUF_PROTO) $(RES_ICONS) $(RES_IMAGES) $(RES_MOVIES) From 6d51eaefe924bfaf2b0f4928dd6020023733480f Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Fri, 20 Oct 2017 16:24:10 -0400 Subject: [PATCH 376/382] qa: Fix race condition in sendheaders.py --- test/functional/replace-by-fee.py | 6 ++++-- test/functional/sendheaders.py | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/test/functional/replace-by-fee.py b/test/functional/replace-by-fee.py index 17667b0bc..815e96484 100755 --- a/test/functional/replace-by-fee.py +++ b/test/functional/replace-by-fee.py @@ -72,12 +72,14 @@ def set_test_params(self): ["-mempoolreplacement=0"]] def run_test(self): - # Leave IBD and ensure nodes are synced + # Leave IBD self.nodes[0].generate(1) - self.sync_all() make_utxo(self.nodes[0], 1*COIN) + # Ensure nodes are synced + self.sync_all() + self.log.info("Running test simple doublespend...") self.test_simple_doublespend() diff --git a/test/functional/sendheaders.py b/test/functional/sendheaders.py index fe577dc20..60d107b24 100755 --- a/test/functional/sendheaders.py +++ b/test/functional/sendheaders.py @@ -225,6 +225,10 @@ def run_test(self): inv_node.wait_for_verack() test_node.wait_for_verack() + # Ensure verack's have been processed by our peer + inv_node.sync_with_ping() + test_node.sync_with_ping() + tip = int(self.nodes[0].getbestblockhash(), 16) # PART 1 From fd3a2f3130ebd1d1001c5dff80c1ff026654b00d Mon Sep 17 00:00:00 2001 From: practicalswift Date: Wed, 25 Oct 2017 22:08:10 +0200 Subject: [PATCH 377/382] [tests] Add fuzz testing for BlockTransactions and BlockTransactionsRequest --- src/test/test_bitcoin_fuzzy.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/test/test_bitcoin_fuzzy.cpp b/src/test/test_bitcoin_fuzzy.cpp index 581ad2ffa..6694c5caa 100644 --- a/src/test/test_bitcoin_fuzzy.cpp +++ b/src/test/test_bitcoin_fuzzy.cpp @@ -19,6 +19,7 @@ #include "undo.h" #include "version.h" #include "pubkey.h" +#include "blockencodings.h" #include #include @@ -45,6 +46,8 @@ enum TEST_ID { CBLOOMFILTER_DESERIALIZE, CDISKBLOCKINDEX_DESERIALIZE, CTXOUTCOMPRESSOR_DESERIALIZE, + BLOCKTRANSACTIONS_DESERIALIZE, + BLOCKTRANSACTIONSREQUEST_DESERIALIZE, TEST_ID_END }; @@ -245,6 +248,26 @@ int test_one_input(std::vector buffer) { break; } + case BLOCKTRANSACTIONS_DESERIALIZE: + { + try + { + BlockTransactions bt; + ds >> bt; + } catch (const std::ios_base::failure& e) {return 0;} + + break; + } + case BLOCKTRANSACTIONSREQUEST_DESERIALIZE: + { + try + { + BlockTransactionsRequest btr; + ds >> btr; + } catch (const std::ios_base::failure& e) {return 0;} + + break; + } default: return 0; } From c60fd71a65e841efe187992f46c583a704cc37f5 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Wed, 11 Oct 2017 08:55:14 -0400 Subject: [PATCH 378/382] Disconnecting from bad outbound peers in IBD When in IBD, we'd like to use all our outbound peers to help us sync the chain. Disconnect any outbound peers whose headers have insufficient work. --- src/net_processing.cpp | 18 ++++++++++++++++++ test/functional/minchainwork.py | 8 ++++++++ 2 files changed, 26 insertions(+) diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 6c26cd4ce..8551e8455 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -2383,6 +2383,24 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr } } } + // If we're in IBD, we want outbound peers that will serve us a useful + // chain. Disconnect peers that are on chains with insufficient work. + if (IsInitialBlockDownload() && nCount != MAX_HEADERS_RESULTS) { + // When nCount < MAX_HEADERS_RESULTS, we know we have no more + // headers to fetch from this peer. + if (nodestate->pindexBestKnownBlock && nodestate->pindexBestKnownBlock->nChainWork < nMinimumChainWork) { + // This peer has too little work on their headers chain to help + // us sync -- disconnect if using an outbound slot (unless + // whitelisted or addnode). + // Note: We compare their tip to nMinimumChainWork (rather than + // chainActive.Tip()) because we won't start block download + // until we have a headers chain that has at least + // nMinimumChainWork, even if a peer has a chain past our tip, + if (!(pfrom->fInbound || pfrom->fWhitelisted || pfrom->m_manual_connection)) { + pfrom->fDisconnect = true; + } + } + } } } diff --git a/test/functional/minchainwork.py b/test/functional/minchainwork.py index c7579d254..35cd7ad14 100755 --- a/test/functional/minchainwork.py +++ b/test/functional/minchainwork.py @@ -27,6 +27,7 @@ class MinimumChainWorkTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 + self.extra_args = [[], ["-minimumchainwork=0x65"], ["-minimumchainwork=0x65"]] self.node_min_work = [0, 101, 101] @@ -74,6 +75,13 @@ def run_test(self): self.nodes[0].generate(1) self.log.info("Verifying nodes are all synced") + + # Because nodes in regtest are all manual connections (eg using + # addnode), node1 should not have disconnected node0. If not for that, + # we'd expect node1 to have disconnected node0 for serving an + # insufficient work chain, in which case we'd need to reconnect them to + # continue the test. + self.sync_all() self.log.info("Blockcounts: %s", [n.getblockcount() for n in self.nodes]) From 5a6d00c6defc587e22c93e63029fdd538ce8858d Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Thu, 12 Oct 2017 13:55:43 -0400 Subject: [PATCH 379/382] Permit disconnection of outbound peers on bad/slow chains Currently we have no rotation of outbound peers. If an outbound peer stops serving us blocks, or is on a consensus-incompatible chain with less work than our tip (but otherwise valid headers), then we will never disconnect that peer, even though that peer is using one of our 8 outbound connection slots. Because we rely on our outbound peers to find an honest node in order to reach consensus, allowing an incompatible peer to occupy one of those slots is undesirable, particularly if it is possible for all such slots to be occupied by such peers. Protect against this by always checking to see if a peer's best known block has less work than our tip, and if so, set a 20 minute timeout -- if the peer is still not known to have caught up to a chain with as much work as ours after 20 minutes, then send a single getheaders message, wait 2 more minutes, and if a better header hasn't been received by then, disconnect that peer. Note: - we do not require that our peer sync to the same tip as ours, just an equal or greater work tip. (Doing otherwise would risk partitioning the network in the event of a chain split, and is also unnecessary.) - we pick 4 of our outbound peers and do not subject them to this logic, to be more conservative. We don't wish to permit temporary network issues (or an attacker) to excessively disrupt network topology. --- src/net_processing.cpp | 113 ++++++++++++++++++++++++++++++++++++++++- src/net_processing.h | 8 +++ 2 files changed, 120 insertions(+), 1 deletion(-) diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 8551e8455..f83a20102 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -124,6 +124,9 @@ namespace { /** Number of peers from which we're downloading blocks. */ int nPeersWithValidatedDownloads = 0; + /** Number of outbound peers with m_chain_sync.m_protect. */ + int g_outbound_peers_with_protect_from_disconnect = 0; + /** Relay map, protected by cs_main. */ typedef std::map MapRelay; MapRelay mapRelay; @@ -201,6 +204,33 @@ struct CNodeState { */ bool fSupportsDesiredCmpctVersion; + /** State used to enforce CHAIN_SYNC_TIMEOUT + * Only in effect for outbound, non-manual connections, with + * m_protect == false + * Algorithm: if a peer's best known block has less work than our tip, + * set a timeout CHAIN_SYNC_TIMEOUT seconds in the future: + * - If at timeout their best known block now has more work than our tip + * when the timeout was set, then either reset the timeout or clear it + * (after comparing against our current tip's work) + * - If at timeout their best known block still has less work than our + * tip did when the timeout was set, then send a getheaders message, + * and set a shorter timeout, HEADERS_RESPONSE_TIME seconds in future. + * If their best known block is still behind when that new timeout is + * reached, disconnect. + */ + struct ChainSyncTimeoutState { + //! A timeout used for checking whether our peer has sufficiently synced + int64_t m_timeout; + //! A header with the work we require on our peer's chain + const CBlockIndex * m_work_header; + //! After timeout is reached, set to true after sending getheaders + bool m_sent_getheaders; + //! Whether this peer is protected from disconnection due to a bad/slow chain + bool m_protect; + }; + + ChainSyncTimeoutState m_chain_sync; + CNodeState(CAddress addrIn, std::string addrNameIn) : address(addrIn), name(addrNameIn) { fCurrentlyConnected = false; nMisbehavior = 0; @@ -223,6 +253,7 @@ struct CNodeState { fHaveWitness = false; fWantsCmpctWitness = false; fSupportsDesiredCmpctVersion = false; + m_chain_sync = { 0, nullptr, false, false }; } }; @@ -502,6 +533,13 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vectorfInbound || node->m_manual_connection || node->fFeeler || node->fOneShot); +} + void PeerLogicValidation::InitializeNode(CNode *pnode) { CAddress addr = pnode->addr; std::string addrName = pnode->GetAddrName(); @@ -534,6 +572,8 @@ void PeerLogicValidation::FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTim nPreferredDownload -= state->fPreferredDownload; nPeersWithValidatedDownloads -= (state->nBlocksInFlightValidHeaders != 0); assert(nPeersWithValidatedDownloads >= 0); + g_outbound_peers_with_protect_from_disconnect -= state->m_chain_sync.m_protect; + assert(g_outbound_peers_with_protect_from_disconnect >= 0); mapNodeState.erase(nodeid); @@ -542,6 +582,7 @@ void PeerLogicValidation::FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTim assert(mapBlocksInFlight.empty()); assert(nPreferredDownload == 0); assert(nPeersWithValidatedDownloads == 0); + assert(g_outbound_peers_with_protect_from_disconnect == 0); } LogPrint(BCLog::NET, "Cleared nodestate for peer=%d\n", nodeid); } @@ -2324,6 +2365,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr assert(pindexLast); UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash()); + // From here, pindexBestKnownBlock should be guaranteed to be non-null, + // because it is set in UpdateBlockAvailability. Some nullptr checks + // are still present, however, as belt-and-suspenders. + if (nCount == MAX_HEADERS_RESULTS) { // Headers message had its maximum size; the peer may have more headers. // TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue @@ -2396,11 +2441,22 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // chainActive.Tip()) because we won't start block download // until we have a headers chain that has at least // nMinimumChainWork, even if a peer has a chain past our tip, - if (!(pfrom->fInbound || pfrom->fWhitelisted || pfrom->m_manual_connection)) { + // as an anti-DoS measure. + if (IsOutboundDisconnectionCandidate(pfrom)) { + LogPrintf("Disconnecting outbound peer %d -- headers chain has insufficient work\n", pfrom->GetId()); pfrom->fDisconnect = true; } } } + + if (!pfrom->fDisconnect && IsOutboundDisconnectionCandidate(pfrom) && nodestate->pindexBestKnownBlock != nullptr) { + // If this is an outbound peer, check to see if we should protect + // it from the bad/lagging chain logic. + if (g_outbound_peers_with_protect_from_disconnect < MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && nodestate->pindexBestKnownBlock->nChainWork >= chainActive.Tip()->nChainWork && !nodestate->m_chain_sync.m_protect) { + nodestate->m_chain_sync.m_protect = true; + ++g_outbound_peers_with_protect_from_disconnect; + } + } } } @@ -2799,6 +2855,58 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic& inter return fMoreWork; } +void PeerLogicValidation::ConsiderEviction(CNode *pto, int64_t time_in_seconds) +{ + AssertLockHeld(cs_main); + + CNodeState &state = *State(pto->GetId()); + const CNetMsgMaker msgMaker(pto->GetSendVersion()); + + if (!state.m_chain_sync.m_protect && IsOutboundDisconnectionCandidate(pto) && state.fSyncStarted) { + // This is an outbound peer subject to disconnection if they don't + // announce a block with as much work as the current tip within + // CHAIN_SYNC_TIMEOUT + HEADERS_RESPONSE_TIME seconds (note: if + // their chain has more work than ours, we should sync to it, + // unless it's invalid, in which case we should find that out and + // disconnect from them elsewhere). + if (state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainWork >= chainActive.Tip()->nChainWork) { + if (state.m_chain_sync.m_timeout != 0) { + state.m_chain_sync.m_timeout = 0; + state.m_chain_sync.m_work_header = nullptr; + state.m_chain_sync.m_sent_getheaders = false; + } + } else if (state.m_chain_sync.m_timeout == 0 || (state.m_chain_sync.m_work_header != nullptr && state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainWork >= state.m_chain_sync.m_work_header->nChainWork)) { + // Our best block known by this peer is behind our tip, and we're either noticing + // that for the first time, OR this peer was able to catch up to some earlier point + // where we checked against our tip. + // Either way, set a new timeout based on current tip. + state.m_chain_sync.m_timeout = time_in_seconds + CHAIN_SYNC_TIMEOUT; + state.m_chain_sync.m_work_header = chainActive.Tip(); + state.m_chain_sync.m_sent_getheaders = false; + } else if (state.m_chain_sync.m_timeout > 0 && time_in_seconds > state.m_chain_sync.m_timeout) { + // No evidence yet that our peer has synced to a chain with work equal to that + // of our tip, when we first detected it was behind. Send a single getheaders + // message to give the peer a chance to update us. + if (state.m_chain_sync.m_sent_getheaders) { + // They've run out of time to catch up! + LogPrintf("Disconnecting outbound peer %d for old chain, best known block = %s\n", pto->GetId(), state.pindexBestKnownBlock != nullptr ? state.pindexBestKnownBlock->GetBlockHash().ToString() : ""); + pto->fDisconnect = true; + } else { + LogPrint(BCLog::NET, "sending getheaders to outbound peer=%d to verify chain work (current best known block:%s, benchmark blockhash: %s)\n", pto->GetId(), state.pindexBestKnownBlock != nullptr ? state.pindexBestKnownBlock->GetBlockHash().ToString() : "", state.m_chain_sync.m_work_header->GetBlockHash().ToString()); + connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(state.m_chain_sync.m_work_header->pprev), uint256())); + state.m_chain_sync.m_sent_getheaders = true; + constexpr int64_t HEADERS_RESPONSE_TIME = 120; // 2 minutes + // Bump the timeout to allow a response, which could clear the timeout + // (if the response shows the peer has synced), reset the timeout (if + // the peer syncs to the required work but not to our tip), or result + // in disconnect (if we advance to the timeout and pindexBestKnownBlock + // has not sufficiently progressed) + state.m_chain_sync.m_timeout = time_in_seconds + HEADERS_RESPONSE_TIME; + } + } + } +} + class CompareInvMempoolOrder { CTxMemPool *mp; @@ -3265,6 +3373,9 @@ bool PeerLogicValidation::SendMessages(CNode* pto, std::atomic& interruptM } } + // Check that outbound peers have reasonable chains + // GetTime() is used by this anti-DoS logic so we can test this using mocktime + ConsiderEviction(pto, GetTime()); // // Message: getdata (blocks) diff --git a/src/net_processing.h b/src/net_processing.h index 79745cdd4..656324bba 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -21,6 +21,12 @@ static const unsigned int DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN = 100; * Timeout = base + per_header * (expected number of headers) */ static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_BASE = 15 * 60 * 1000000; // 15 minutes static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER = 1000; // 1ms/header +/** Protect at least this many outbound peers from disconnection due to slow/ + * behind headers chain. + */ +static constexpr int32_t MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT = 4; +/** Timeout for (unprotected) outbound peers to sync to our chainwork, in seconds */ +static constexpr int64_t CHAIN_SYNC_TIMEOUT = 20 * 60; // 20 minutes class PeerLogicValidation : public CValidationInterface, public NetEventsInterface { private: @@ -47,6 +53,8 @@ class PeerLogicValidation : public CValidationInterface, public NetEventsInterfa * @return True if there is more work to be done */ bool SendMessages(CNode* pto, std::atomic& interrupt) override; + + void ConsiderEviction(CNode *pto, int64_t time_in_seconds); }; struct CNodeStateStats { From e065249c014a070a8799b2ff947af5b8f012c5c1 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Thu, 19 Oct 2017 11:42:47 -0400 Subject: [PATCH 380/382] Add unit test for outbound peer eviction --- src/test/DoS_tests.cpp | 55 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp index b88ad5ed1..7bcf30483 100644 --- a/src/test/DoS_tests.cpp +++ b/src/test/DoS_tests.cpp @@ -42,6 +42,51 @@ static NodeId id = 0; BOOST_FIXTURE_TEST_SUITE(DoS_tests, TestingSetup) +// Test eviction of an outbound peer whose chain never advances +// Mock a node connection, and use mocktime to simulate a peer +// which never sends any headers messages. PeerLogic should +// decide to evict that outbound peer, after the appropriate timeouts. +// Note that we protect 4 outbound nodes from being subject to +// this logic; this test takes advantage of that protection only +// being applied to nodes which send headers with sufficient +// work. +BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction) +{ + std::atomic interruptDummy(false); + + // Mock an outbound peer + CAddress addr1(ip(0xa0b0c001), NODE_NONE); + CNode dummyNode1(id++, ServiceFlags(NODE_NETWORK|NODE_WITNESS), 0, INVALID_SOCKET, addr1, 0, 0, CAddress(), "", /*fInboundIn=*/ false); + dummyNode1.SetSendVersion(PROTOCOL_VERSION); + + peerLogic->InitializeNode(&dummyNode1); + dummyNode1.nVersion = 1; + dummyNode1.fSuccessfullyConnected = true; + + // This test requires that we have a chain with non-zero work. + BOOST_CHECK(chainActive.Tip() != nullptr); + BOOST_CHECK(chainActive.Tip()->nChainWork > 0); + + // Test starts here + peerLogic->SendMessages(&dummyNode1, interruptDummy); // should result in getheaders + BOOST_CHECK(dummyNode1.vSendMsg.size() > 0); + dummyNode1.vSendMsg.clear(); + + int64_t nStartTime = GetTime(); + // Wait 21 minutes + SetMockTime(nStartTime+21*60); + peerLogic->SendMessages(&dummyNode1, interruptDummy); // should result in getheaders + BOOST_CHECK(dummyNode1.vSendMsg.size() > 0); + // Wait 3 more minutes + SetMockTime(nStartTime+24*60); + peerLogic->SendMessages(&dummyNode1, interruptDummy); // should result in disconnect + BOOST_CHECK(dummyNode1.fDisconnect == true); + SetMockTime(0); + + bool dummy; + peerLogic->FinalizeNode(dummyNode1.GetId(), dummy); +} + BOOST_AUTO_TEST_CASE(DoS_banning) { std::atomic interruptDummy(false); @@ -71,6 +116,10 @@ BOOST_AUTO_TEST_CASE(DoS_banning) Misbehaving(dummyNode2.GetId(), 50); peerLogic->SendMessages(&dummyNode2, interruptDummy); BOOST_CHECK(connman->IsBanned(addr2)); + + bool dummy; + peerLogic->FinalizeNode(dummyNode1.GetId(), dummy); + peerLogic->FinalizeNode(dummyNode2.GetId(), dummy); } BOOST_AUTO_TEST_CASE(DoS_banscore) @@ -95,6 +144,9 @@ BOOST_AUTO_TEST_CASE(DoS_banscore) peerLogic->SendMessages(&dummyNode1, interruptDummy); BOOST_CHECK(connman->IsBanned(addr1)); gArgs.ForceSetArg("-banscore", std::to_string(DEFAULT_BANSCORE_THRESHOLD)); + + bool dummy; + peerLogic->FinalizeNode(dummyNode1.GetId(), dummy); } BOOST_AUTO_TEST_CASE(DoS_bantime) @@ -121,6 +173,9 @@ BOOST_AUTO_TEST_CASE(DoS_bantime) SetMockTime(nStartTime+60*60*24+1); BOOST_CHECK(!connman->IsBanned(addr)); + + bool dummy; + peerLogic->FinalizeNode(dummyNode.GetId(), dummy); } CTransactionRef RandomOrphan() From 4637f18522429473e68f6f512a03040e121a446d Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Thu, 26 Oct 2017 14:46:17 -0400 Subject: [PATCH 381/382] moveonly: factor out headers processing into separate function ProcessMessages will now return earlier when processing headers messages, rather than continuing on (and do nothing). --- src/net_processing.cpp | 336 +++++++++++++++++++++-------------------- 1 file changed, 173 insertions(+), 163 deletions(-) diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 50ac76924..78dc1a752 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -1205,6 +1205,178 @@ inline void static SendBlockTransactions(const CBlock& block, const BlockTransac connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp)); } +bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::vector& headers, const CChainParams& chainparams) +{ + const CNetMsgMaker msgMaker(pfrom->GetSendVersion()); + size_t nCount = headers.size(); + + if (nCount == 0) { + // Nothing interesting. Stop asking this peers for more headers. + return true; + } + + const CBlockIndex *pindexLast = nullptr; + { + LOCK(cs_main); + CNodeState *nodestate = State(pfrom->GetId()); + + // If this looks like it could be a block announcement (nCount < + // MAX_BLOCKS_TO_ANNOUNCE), use special logic for handling headers that + // don't connect: + // - Send a getheaders message in response to try to connect the chain. + // - The peer can send up to MAX_UNCONNECTING_HEADERS in a row that + // don't connect before giving DoS points + // - Once a headers message is received that is valid and does connect, + // nUnconnectingHeaders gets reset back to 0. + if (mapBlockIndex.find(headers[0].hashPrevBlock) == mapBlockIndex.end() && nCount < MAX_BLOCKS_TO_ANNOUNCE) { + nodestate->nUnconnectingHeaders++; + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256())); + LogPrint(BCLog::NET, "received header %s: missing prev block %s, sending getheaders (%d) to end (peer=%d, nUnconnectingHeaders=%d)\n", + headers[0].GetHash().ToString(), + headers[0].hashPrevBlock.ToString(), + pindexBestHeader->nHeight, + pfrom->GetId(), nodestate->nUnconnectingHeaders); + // Set hashLastUnknownBlock for this peer, so that if we + // eventually get the headers - even from a different peer - + // we can use this peer to download. + UpdateBlockAvailability(pfrom->GetId(), headers.back().GetHash()); + + if (nodestate->nUnconnectingHeaders % MAX_UNCONNECTING_HEADERS == 0) { + Misbehaving(pfrom->GetId(), 20); + } + return true; + } + + uint256 hashLastBlock; + for (const CBlockHeader& header : headers) { + if (!hashLastBlock.IsNull() && header.hashPrevBlock != hashLastBlock) { + Misbehaving(pfrom->GetId(), 20); + return error("non-continuous headers sequence"); + } + hashLastBlock = header.GetHash(); + } + } + + CValidationState state; + if (!ProcessNewBlockHeaders(headers, state, chainparams, &pindexLast)) { + int nDoS; + if (state.IsInvalid(nDoS)) { + if (nDoS > 0) { + LOCK(cs_main); + Misbehaving(pfrom->GetId(), nDoS); + } + return error("invalid header received"); + } + } + + { + LOCK(cs_main); + CNodeState *nodestate = State(pfrom->GetId()); + if (nodestate->nUnconnectingHeaders > 0) { + LogPrint(BCLog::NET, "peer=%d: resetting nUnconnectingHeaders (%d -> 0)\n", pfrom->GetId(), nodestate->nUnconnectingHeaders); + } + nodestate->nUnconnectingHeaders = 0; + + assert(pindexLast); + UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash()); + + // From here, pindexBestKnownBlock should be guaranteed to be non-null, + // because it is set in UpdateBlockAvailability. Some nullptr checks + // are still present, however, as belt-and-suspenders. + + if (nCount == MAX_HEADERS_RESULTS) { + // Headers message had its maximum size; the peer may have more headers. + // TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue + // from there instead. + LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->GetId(), pfrom->nStartingHeight); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexLast), uint256())); + } + + bool fCanDirectFetch = CanDirectFetch(chainparams.GetConsensus()); + // 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) { + std::vector 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) && + !mapBlocksInFlight.count(pindexWalk->GetBlockHash()) && + (!IsWitnessEnabled(pindexWalk->pprev, chainparams.GetConsensus()) || State(pfrom->GetId())->fHaveWitness)) { + // We don't have this block, and it's not yet in flight. + vToFetch.push_back(pindexWalk); + } + pindexWalk = pindexWalk->pprev; + } + // If pindexWalk still isn't on our main chain, we're looking at a + // very large reorg at a time we think we're close to caught up to + // the main chain -- this shouldn't really happen. Bail out on the + // direct fetch and rely on parallel download instead. + if (!chainActive.Contains(pindexWalk)) { + LogPrint(BCLog::NET, "Large reorg, won't direct fetch to %s (%d)\n", + pindexLast->GetBlockHash().ToString(), + pindexLast->nHeight); + } else { + std::vector vGetData; + // Download as much as possible, from earliest to latest. + for (const CBlockIndex *pindex : reverse_iterate(vToFetch)) { + if (nodestate->nBlocksInFlight >= MAX_BLOCKS_IN_TRANSIT_PER_PEER) { + // Can't download any more from this peer + break; + } + uint32_t nFetchFlags = GetFetchFlags(pfrom); + vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash())); + MarkBlockAsInFlight(pfrom->GetId(), pindex->GetBlockHash(), pindex); + LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n", + pindex->GetBlockHash().ToString(), pfrom->GetId()); + } + if (vGetData.size() > 1) { + LogPrint(BCLog::NET, "Downloading blocks toward %s (%d) via headers direct fetch\n", + pindexLast->GetBlockHash().ToString(), pindexLast->nHeight); + } + if (vGetData.size() > 0) { + if (nodestate->fSupportsDesiredCmpctVersion && vGetData.size() == 1 && mapBlocksInFlight.size() == 1 && pindexLast->pprev->IsValid(BLOCK_VALID_CHAIN)) { + // In any case, we want to download using a compact block, not a regular one + vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash); + } + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData)); + } + } + } + // If we're in IBD, we want outbound peers that will serve us a useful + // chain. Disconnect peers that are on chains with insufficient work. + if (IsInitialBlockDownload() && nCount != MAX_HEADERS_RESULTS) { + // When nCount < MAX_HEADERS_RESULTS, we know we have no more + // headers to fetch from this peer. + if (nodestate->pindexBestKnownBlock && nodestate->pindexBestKnownBlock->nChainWork < nMinimumChainWork) { + // This peer has too little work on their headers chain to help + // us sync -- disconnect if using an outbound slot (unless + // whitelisted or addnode). + // Note: We compare their tip to nMinimumChainWork (rather than + // chainActive.Tip()) because we won't start block download + // until we have a headers chain that has at least + // nMinimumChainWork, even if a peer has a chain past our tip, + // as an anti-DoS measure. + if (IsOutboundDisconnectionCandidate(pfrom)) { + LogPrintf("Disconnecting outbound peer %d -- headers chain has insufficient work\n", pfrom->GetId()); + pfrom->fDisconnect = true; + } + } + } + + if (!pfrom->fDisconnect && IsOutboundDisconnectionCandidate(pfrom) && nodestate->pindexBestKnownBlock != nullptr) { + // If this is an outbound peer, check to see if we should protect + // it from the bad/lagging chain logic. + if (g_outbound_peers_with_protect_from_disconnect < MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && nodestate->pindexBestKnownBlock->nChainWork >= chainActive.Tip()->nChainWork && !nodestate->m_chain_sync.m_protect) { + nodestate->m_chain_sync.m_protect = true; + ++g_outbound_peers_with_protect_from_disconnect; + } + } + } + + return true; +} + bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman* connman, const std::atomic& interruptMsgProc) { LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->GetId()); @@ -2308,169 +2480,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr ReadCompactSize(vRecv); // ignore tx count; assume it is 0. } - if (nCount == 0) { - // Nothing interesting. Stop asking this peers for more headers. - return true; - } - - const CBlockIndex *pindexLast = nullptr; - { - LOCK(cs_main); - CNodeState *nodestate = State(pfrom->GetId()); - - // If this looks like it could be a block announcement (nCount < - // MAX_BLOCKS_TO_ANNOUNCE), use special logic for handling headers that - // don't connect: - // - Send a getheaders message in response to try to connect the chain. - // - The peer can send up to MAX_UNCONNECTING_HEADERS in a row that - // don't connect before giving DoS points - // - Once a headers message is received that is valid and does connect, - // nUnconnectingHeaders gets reset back to 0. - if (mapBlockIndex.find(headers[0].hashPrevBlock) == mapBlockIndex.end() && nCount < MAX_BLOCKS_TO_ANNOUNCE) { - nodestate->nUnconnectingHeaders++; - connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256())); - LogPrint(BCLog::NET, "received header %s: missing prev block %s, sending getheaders (%d) to end (peer=%d, nUnconnectingHeaders=%d)\n", - headers[0].GetHash().ToString(), - headers[0].hashPrevBlock.ToString(), - pindexBestHeader->nHeight, - pfrom->GetId(), nodestate->nUnconnectingHeaders); - // Set hashLastUnknownBlock for this peer, so that if we - // eventually get the headers - even from a different peer - - // we can use this peer to download. - UpdateBlockAvailability(pfrom->GetId(), headers.back().GetHash()); - - if (nodestate->nUnconnectingHeaders % MAX_UNCONNECTING_HEADERS == 0) { - Misbehaving(pfrom->GetId(), 20); - } - return true; - } - - uint256 hashLastBlock; - for (const CBlockHeader& header : headers) { - if (!hashLastBlock.IsNull() && header.hashPrevBlock != hashLastBlock) { - Misbehaving(pfrom->GetId(), 20); - return error("non-continuous headers sequence"); - } - hashLastBlock = header.GetHash(); - } - } - - CValidationState state; - if (!ProcessNewBlockHeaders(headers, state, chainparams, &pindexLast)) { - int nDoS; - if (state.IsInvalid(nDoS)) { - if (nDoS > 0) { - LOCK(cs_main); - Misbehaving(pfrom->GetId(), nDoS); - } - return error("invalid header received"); - } - } - - { - LOCK(cs_main); - CNodeState *nodestate = State(pfrom->GetId()); - if (nodestate->nUnconnectingHeaders > 0) { - LogPrint(BCLog::NET, "peer=%d: resetting nUnconnectingHeaders (%d -> 0)\n", pfrom->GetId(), nodestate->nUnconnectingHeaders); - } - nodestate->nUnconnectingHeaders = 0; - - assert(pindexLast); - UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash()); - - // From here, pindexBestKnownBlock should be guaranteed to be non-null, - // because it is set in UpdateBlockAvailability. Some nullptr checks - // are still present, however, as belt-and-suspenders. - - if (nCount == MAX_HEADERS_RESULTS) { - // Headers message had its maximum size; the peer may have more headers. - // TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue - // from there instead. - LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->GetId(), pfrom->nStartingHeight); - connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexLast), uint256())); - } - - bool fCanDirectFetch = CanDirectFetch(chainparams.GetConsensus()); - // 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) { - std::vector 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) && - !mapBlocksInFlight.count(pindexWalk->GetBlockHash()) && - (!IsWitnessEnabled(pindexWalk->pprev, chainparams.GetConsensus()) || State(pfrom->GetId())->fHaveWitness)) { - // We don't have this block, and it's not yet in flight. - vToFetch.push_back(pindexWalk); - } - pindexWalk = pindexWalk->pprev; - } - // If pindexWalk still isn't on our main chain, we're looking at a - // very large reorg at a time we think we're close to caught up to - // the main chain -- this shouldn't really happen. Bail out on the - // direct fetch and rely on parallel download instead. - if (!chainActive.Contains(pindexWalk)) { - LogPrint(BCLog::NET, "Large reorg, won't direct fetch to %s (%d)\n", - pindexLast->GetBlockHash().ToString(), - pindexLast->nHeight); - } else { - std::vector vGetData; - // Download as much as possible, from earliest to latest. - for (const CBlockIndex *pindex : reverse_iterate(vToFetch)) { - if (nodestate->nBlocksInFlight >= MAX_BLOCKS_IN_TRANSIT_PER_PEER) { - // Can't download any more from this peer - break; - } - uint32_t nFetchFlags = GetFetchFlags(pfrom); - vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash())); - MarkBlockAsInFlight(pfrom->GetId(), pindex->GetBlockHash(), pindex); - LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n", - pindex->GetBlockHash().ToString(), pfrom->GetId()); - } - if (vGetData.size() > 1) { - LogPrint(BCLog::NET, "Downloading blocks toward %s (%d) via headers direct fetch\n", - pindexLast->GetBlockHash().ToString(), pindexLast->nHeight); - } - if (vGetData.size() > 0) { - if (nodestate->fSupportsDesiredCmpctVersion && vGetData.size() == 1 && mapBlocksInFlight.size() == 1 && pindexLast->pprev->IsValid(BLOCK_VALID_CHAIN)) { - // In any case, we want to download using a compact block, not a regular one - vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash); - } - connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData)); - } - } - } - // If we're in IBD, we want outbound peers that will serve us a useful - // chain. Disconnect peers that are on chains with insufficient work. - if (IsInitialBlockDownload() && nCount != MAX_HEADERS_RESULTS) { - // When nCount < MAX_HEADERS_RESULTS, we know we have no more - // headers to fetch from this peer. - if (nodestate->pindexBestKnownBlock && nodestate->pindexBestKnownBlock->nChainWork < nMinimumChainWork) { - // This peer has too little work on their headers chain to help - // us sync -- disconnect if using an outbound slot (unless - // whitelisted or addnode). - // Note: We compare their tip to nMinimumChainWork (rather than - // chainActive.Tip()) because we won't start block download - // until we have a headers chain that has at least - // nMinimumChainWork, even if a peer has a chain past our tip, - // as an anti-DoS measure. - if (IsOutboundDisconnectionCandidate(pfrom)) { - LogPrintf("Disconnecting outbound peer %d -- headers chain has insufficient work\n", pfrom->GetId()); - pfrom->fDisconnect = true; - } - } - } - - if (!pfrom->fDisconnect && IsOutboundDisconnectionCandidate(pfrom) && nodestate->pindexBestKnownBlock != nullptr) { - // If this is an outbound peer, check to see if we should protect - // it from the bad/lagging chain logic. - if (g_outbound_peers_with_protect_from_disconnect < MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && nodestate->pindexBestKnownBlock->nChainWork >= chainActive.Tip()->nChainWork && !nodestate->m_chain_sync.m_protect) { - nodestate->m_chain_sync.m_protect = true; - ++g_outbound_peers_with_protect_from_disconnect; - } - } - } + return ProcessHeadersMessage(pfrom, connman, headers, chainparams); } else if (strCommand == NetMsgType::BLOCK && !fImporting && !fReindex) // Ignore blocks received while importing From 37886d5e2f9992678dea4b1bd893f4f10d61d3ad Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Thu, 26 Oct 2017 14:54:33 -0400 Subject: [PATCH 382/382] Disconnect outbound peers relaying invalid headers --- src/net_processing.cpp | 61 +++++++++++++++++++++++++++++++++++------- src/validation.cpp | 4 ++- src/validation.h | 3 ++- 3 files changed, 56 insertions(+), 12 deletions(-) diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 78dc1a752..4c3aacaf5 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -1205,7 +1205,7 @@ inline void static SendBlockTransactions(const CBlock& block, const BlockTransac connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp)); } -bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::vector& headers, const CChainParams& chainparams) +bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::vector& headers, const CChainParams& chainparams, bool punish_duplicate_invalid) { const CNetMsgMaker msgMaker(pfrom->GetSendVersion()); size_t nCount = headers.size(); @@ -1258,13 +1258,48 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve } CValidationState state; - if (!ProcessNewBlockHeaders(headers, state, chainparams, &pindexLast)) { + CBlockHeader first_invalid_header; + if (!ProcessNewBlockHeaders(headers, state, chainparams, &pindexLast, &first_invalid_header)) { int nDoS; if (state.IsInvalid(nDoS)) { if (nDoS > 0) { LOCK(cs_main); Misbehaving(pfrom->GetId(), nDoS); } + if (punish_duplicate_invalid && mapBlockIndex.find(first_invalid_header.GetHash()) != mapBlockIndex.end()) { + // Goal: don't allow outbound peers to use up our outbound + // connection slots if they are on incompatible chains. + // + // We ask the caller to set punish_invalid appropriately based + // on the peer and the method of header delivery (compact + // blocks are allowed to be invalid in some circumstances, + // under BIP 152). + // Here, we try to detect the narrow situation that we have a + // valid block header (ie it was valid at the time the header + // was received, and hence stored in mapBlockIndex) but know the + // block is invalid, and that a peer has announced that same + // block as being on its active chain. + // Disconnect the peer in such a situation. + // + // Note: if the header that is invalid was not accepted to our + // mapBlockIndex at all, that may also be grounds for + // disconnecting the peer, as the chain they are on is likely + // to be incompatible. However, there is a circumstance where + // that does not hold: if the header's timestamp is more than + // 2 hours ahead of our current time. In that case, the header + // may become valid in the future, and we don't want to + // disconnect a peer merely for serving us one too-far-ahead + // block header, to prevent an attacker from splitting the + // network by mining a block right at the 2 hour boundary. + // + // TODO: update the DoS logic (or, rather, rewrite the + // DoS-interface between validation and net_processing) so that + // the interface is cleaner, and so that we disconnect on all the + // reasons that a peer's headers chain is incompatible + // with ours (eg block->nVersion softforks, MTP violations, + // etc), and not just the duplicate-invalid case. + pfrom->fDisconnect = true; + } return error("invalid header received"); } } @@ -2219,7 +2254,6 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // If we end up treating this as a plain headers message, call that as well // without cs_main. bool fRevertToHeaderProcessing = false; - CDataStream vHeadersMsg(SER_NETWORK, PROTOCOL_VERSION); // Keep a CBlock for "optimistic" compactblock reconstructions (see // below) @@ -2336,10 +2370,6 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr return true; } else { // If this was an announce-cmpctblock, we want the same treatment as a header message - // Dirty hack to process as if it were just a headers message (TODO: move message handling into their own functions) - std::vector headers; - headers.push_back(cmpctblock.header); - vHeadersMsg << headers; fRevertToHeaderProcessing = true; } } @@ -2348,8 +2378,14 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr if (fProcessBLOCKTXN) return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, nTimeReceived, chainparams, connman, interruptMsgProc); - if (fRevertToHeaderProcessing) - return ProcessMessage(pfrom, NetMsgType::HEADERS, vHeadersMsg, nTimeReceived, chainparams, connman, interruptMsgProc); + if (fRevertToHeaderProcessing) { + // Headers received from HB compact block peers are permitted to be + // relayed before full validation (see BIP 152), so we don't want to disconnect + // the peer if the header turns out to be for an invalid block. + // Note that if a peer tries to build on an invalid chain, that + // will be detected and the peer will be banned. + return ProcessHeadersMessage(pfrom, connman, {cmpctblock.header}, chainparams, /*punish_duplicate_invalid=*/false); + } if (fBlockReconstructed) { // If we got here, we were able to optimistically reconstruct a @@ -2480,7 +2516,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr ReadCompactSize(vRecv); // ignore tx count; assume it is 0. } - return ProcessHeadersMessage(pfrom, connman, headers, chainparams); + // Headers received via a HEADERS message should be valid, and reflect + // the chain the peer is on. If we receive a known-invalid header, + // disconnect the peer if it is using one of our outbound connection + // slots. + bool should_punish = !pfrom->fInbound && !pfrom->m_manual_connection; + return ProcessHeadersMessage(pfrom, connman, headers, chainparams, should_punish); } else if (strCommand == NetMsgType::BLOCK && !fImporting && !fReindex) // Ignore blocks received while importing diff --git a/src/validation.cpp b/src/validation.cpp index 866e0c9fb..78eb6d730 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -3079,13 +3079,15 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state } // Exposed wrapper for AcceptBlockHeader -bool ProcessNewBlockHeaders(const std::vector& headers, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex) +bool ProcessNewBlockHeaders(const std::vector& headers, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex, CBlockHeader *first_invalid) { + if (first_invalid != nullptr) first_invalid->SetNull(); { LOCK(cs_main); for (const CBlockHeader& header : headers) { CBlockIndex *pindex = nullptr; // Use a temp pindex instead of ppindex to avoid a const_cast if (!AcceptBlockHeader(header, state, chainparams, &pindex)) { + if (first_invalid) *first_invalid = header; return false; } if (ppindex) { diff --git a/src/validation.h b/src/validation.h index 6bc52753c..93669de6c 100644 --- a/src/validation.h +++ b/src/validation.h @@ -247,8 +247,9 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr& block, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex=nullptr); +bool ProcessNewBlockHeaders(const std::vector& block, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex=nullptr, CBlockHeader *first_invalid=nullptr); /** Check whether enough disk space is available for an incoming block */ bool CheckDiskSpace(uint64_t nAdditionalBytes = 0);