diff options
39 files changed, 247 insertions, 162 deletions
diff --git a/.travis.yml b/.travis.yml index c281d3ed70..adf2140642 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,8 +31,8 @@ language: minimal cache: ccache: true directories: - - depends/built - - depends/sdk-sources + - $TRAVIS_BUILD_DIR/depends/built + - $TRAVIS_BUILD_DIR/depends/sdk-sources - $HOME/.ccache stages: - lint diff --git a/.travis/lint_04_install.sh b/.travis/lint_04_install.sh index 9a22773e57..62174620f2 100755 --- a/.travis/lint_04_install.sh +++ b/.travis/lint_04_install.sh @@ -6,7 +6,7 @@ export LC_ALL=C -travis_retry pip install codespell==1.13.0 +travis_retry pip install codespell==1.15.0 travis_retry pip install flake8==3.5.0 travis_retry pip install vulture==0.29 diff --git a/.travis/test_06_script_b.sh b/.travis/test_06_script_b.sh index 0420acb993..e40055a6ee 100755 --- a/.travis/test_06_script_b.sh +++ b/.travis/test_06_script_b.sh @@ -25,3 +25,5 @@ if [ "$RUN_FUZZ_TESTS" = "true" ]; then DOCKER_EXEC test/fuzz/test_runner.py -l DEBUG ${DIR_FUZZ_IN} END_FOLD fi + +cd ${TRAVIS_BUILD_DIR} || (echo "could not enter travis build dir $TRAVIS_BUILD_DIR"; exit 1) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5df99adba8..a2456f5b8c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -237,24 +237,35 @@ request. Typically reviewers will review the code for obvious errors, as well as test out the patch set and opine on the technical merits of the patch. Project maintainers take into account the peer review when determining if there is consensus to merge a pull request (remember that discussions may have been -spread out over GitHub, mailing list and IRC discussions). The following +spread out over GitHub, mailing list and IRC discussions). + +#### Conceptual Review + +A review can be a conceptual review, where the reviewer leaves a comment + * `Concept (N)ACK`, meaning "I do (not) agree in the general goal of this pull + request", + * `Approach (N)ACK`, meaning `Concept ACK`, but "I do (not) agree with the + approach of this change". + +A `NACK` needs to include a rationale why the change is not worthwhile. +NACKs without accompanying reasoning may be disregarded. + +#### Code Review + +After conceptual agreement on the change, code review can be provided. It is +starting with `ACK BRANCH_COMMIT`, where `BRANCH_COMMIT` is the top of the +topic branch. The review is followed by a description of how the reviewer did +the review. The following language is used within pull-request comments: - - (t)ACK means "I have tested the code and I agree it should be merged", involving + - "I have tested the code", involving change-specific manual testing in addition to running the unit and functional tests, and in case it is not obvious how the manual testing was done, it should be described; - - NACK means "I disagree this should be merged", and must be accompanied by - sound technical justification (or in certain cases of copyright/patent/licensing - issues, legal justification). NACKs without accompanying reasoning may be - disregarded; - - utACK means "I have not tested the code, but I have reviewed it and it looks + - "I have not tested the code, but I have reviewed it and it looks OK, I agree it can be merged"; - - Concept ACK means "I agree in the general principle of this pull request"; - Nit refers to trivial, often non-blocking issues. -Reviewers should include the commit hash which they reviewed in their comments. - Project maintainers reserve the right to weigh the opinions of peer reviewers using common sense judgement and also may weight based on meritocracy: Those that have demonstrated a deeper commitment and understanding towards the project diff --git a/SECURITY.md b/SECURITY.md index 4044722374..7ed96c7cea 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,15 +2,8 @@ ## Supported Versions -Versions of Bitcoin Core that are currently supported with security updates: - -| Version | Supported | -| ------- | ------------------ | -| 0.18 | :white_check_mark: | -| 0.17 | :white_check_mark: | -| 0.16 | :white_check_mark: | -| 0.15 | :white_check_mark: | -| < 0.15 | :x: | +See our website for versions of Bitcoin Core that are currently supported with +security updates: https://bitcoincore.org/en/lifecycle/#schedule ## Reporting a Vulnerability diff --git a/contrib/devtools/README.md b/contrib/devtools/README.md index 0c8c396503..4994d7f0a5 100644 --- a/contrib/devtools/README.md +++ b/contrib/devtools/README.md @@ -144,7 +144,7 @@ Then do: Create and verify timestamps of merge commits --------------------------------------------- To create or verify timestamps on the merge commits, install the OpenTimestamps -client via `pip3 install opentimestamps-client`. Then, dowload the gpg wrapper +client via `pip3 install opentimestamps-client`. Then, download the gpg wrapper `ots-git-gpg-wrapper.sh` and set it as git's `gpg.program`. See [the ots git integration documentation](https://github.com/opentimestamps/opentimestamps-client/blob/master/doc/git-integration.md#usage) for further details. diff --git a/contrib/devtools/github-merge.py b/contrib/devtools/github-merge.py index df1f3c8024..03179cec24 100755 --- a/contrib/devtools/github-merge.py +++ b/contrib/devtools/github-merge.py @@ -32,11 +32,11 @@ BASH = os.getenv('BASH','bash') # OS specific configuration for terminal attributes ATTR_RESET = '' ATTR_PR = '' -COMMIT_FORMAT = '%h %s (%an)%d' +COMMIT_FORMAT = '%H %s (%an)%d' if os.name == 'posix': # if posix, assume we can use basic terminal escapes ATTR_RESET = '\033[0m' ATTR_PR = '\033[1;36m' - COMMIT_FORMAT = '%C(bold blue)%h%Creset %s %C(cyan)(%an)%Creset%C(green)%d%Creset' + COMMIT_FORMAT = '%C(bold blue)%H%Creset %s %C(cyan)(%an)%Creset%C(green)%d%Creset' def git_config_get(option, default=None): ''' @@ -279,7 +279,7 @@ def main(): else: firstline = 'Merge #%s' % (pull,) message = firstline + '\n\n' - message += subprocess.check_output([GIT,'log','--no-merges','--topo-order','--pretty=format:%h %s (%an)',base_branch+'..'+head_branch]).decode('utf-8') + message += subprocess.check_output([GIT,'log','--no-merges','--topo-order','--pretty=format:%H %s (%an)',base_branch+'..'+head_branch]).decode('utf-8') message += '\n\nPull request description:\n\n ' + body.replace('\n', '\n ') + '\n' message += get_acks_from_comments(head_commit=subprocess.check_output([GIT,'log','-1','--pretty=format:%H',head_branch]).decode('utf-8')[:6], comments=comments) try: diff --git a/contrib/devtools/test_deterministic_coverage.sh b/contrib/devtools/test_deterministic_coverage.sh index 16d03e1fff..88ac850021 100755 --- a/contrib/devtools/test_deterministic_coverage.sh +++ b/contrib/devtools/test_deterministic_coverage.sh @@ -14,6 +14,7 @@ GCOV_EXECUTABLE="gcov" # Disable tests known to cause non-deterministic behaviour and document the source or point of non-determinism. NON_DETERMINISTIC_TESTS=( + "blockfilter_index_tests/blockfilter_index_initial_sync" # src/checkqueue.h: In CCheckQueue::Loop(): while (queue.empty()) { ... } "coinselector_tests/knapsack_solver_test" # coinselector_tests.cpp: if (equal_sets(setCoinsRet, setCoinsRet2)) "denialofservice_tests/DoS_mapOrphans" # denialofservice_tests.cpp: it = mapOrphanTransactions.lower_bound(InsecureRand256()); "fs_tests/fsbridge_fstream" # deterministic test failure? diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml index 4f2238abdb..c50750ed44 100644 --- a/contrib/gitian-descriptors/gitian-linux.yml +++ b/contrib/gitian-descriptors/gitian-linux.yml @@ -149,7 +149,7 @@ script: | mkdir -p temp pushd temp tar -xf ../$SOURCEDIST - find bitcoin-* | sort | tar --mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME" --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ../$SOURCEDIST + find bitcoin-* | sort | tar --mtime="$REFERENCE_DATETIME" --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) @@ -182,8 +182,8 @@ script: | find ${DISTNAME}/bin -type f -executable -print0 | xargs -0 -n1 -I{} ../contrib/devtools/split-debug.sh {} {} {}.dbg find ${DISTNAME}/lib -type f -print0 | xargs -0 -n1 -I{} ../contrib/devtools/split-debug.sh {} {} {}.dbg cp ../doc/README.md ${DISTNAME}/ - find ${DISTNAME} -not -name "*.dbg" | sort | tar --mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME" --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-${i}.tar.gz - find ${DISTNAME} -name "*.dbg" | sort | tar --mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME" --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-${i}-debug.tar.gz + find ${DISTNAME} -not -name "*.dbg" | sort | tar --mtime="$REFERENCE_DATETIME" --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-${i}.tar.gz + find ${DISTNAME} -name "*.dbg" | sort | tar --mtime="$REFERENCE_DATETIME" --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-${i}-debug.tar.gz cd ../../ rm -rf distsrc-${i} done diff --git a/contrib/gitian-descriptors/gitian-osx.yml b/contrib/gitian-descriptors/gitian-osx.yml index 866d233f0f..8c51ce7159 100644 --- a/contrib/gitian-descriptors/gitian-osx.yml +++ b/contrib/gitian-descriptors/gitian-osx.yml @@ -113,7 +113,7 @@ script: | mkdir -p temp pushd temp tar -xf ../$SOURCEDIST - find bitcoin-* | sort | tar --mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME" --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ../$SOURCEDIST + find bitcoin-* | sort | tar --mtime="$REFERENCE_DATETIME" --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) @@ -150,7 +150,7 @@ script: | cp ${BASEPREFIX}/${i}/native/bin/${i}-pagestuff unsigned-app-${i}/pagestuff mv dist unsigned-app-${i} pushd unsigned-app-${i} - find . | sort | tar --mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME" --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-osx-unsigned.tar.gz + find . | sort | tar --mtime="$REFERENCE_DATETIME" --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-osx-unsigned.tar.gz popd make deploy @@ -160,7 +160,7 @@ script: | find . -name "lib*.la" -delete find . -name "lib*.a" -delete rm -rf ${DISTNAME}/lib/pkgconfig - find ${DISTNAME} | sort | tar --mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME" --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-${i}.tar.gz + find ${DISTNAME} | sort | tar --mtime="$REFERENCE_DATETIME" --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-${i}.tar.gz cd ../../ done mkdir -p $OUTDIR/src diff --git a/contrib/gitian-descriptors/gitian-win.yml b/contrib/gitian-descriptors/gitian-win.yml index 58ba1ddad5..f227c6ad92 100644 --- a/contrib/gitian-descriptors/gitian-win.yml +++ b/contrib/gitian-descriptors/gitian-win.yml @@ -129,7 +129,7 @@ script: | mkdir -p temp pushd temp tar -xf ../$SOURCEDIST - find bitcoin-* | sort | tar --mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME" --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ../$SOURCEDIST + find bitcoin-* | sort | tar --mtime="$REFERENCE_DATETIME" --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ../$SOURCEDIST mkdir -p $OUTDIR/src cp ../$SOURCEDIST $OUTDIR/src popd @@ -175,6 +175,6 @@ script: | cd $BUILD_DIR/windeploy mkdir unsigned cp $OUTDIR/bitcoin-*setup-unsigned.exe unsigned/ - find . | sort | tar --mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME" --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-win-unsigned.tar.gz + find . | sort | tar --mtime="$REFERENCE_DATETIME" --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-win-unsigned.tar.gz mv ${OUTDIR}/${DISTNAME}-x86_64-*-debug.zip ${OUTDIR}/${DISTNAME}-win64-debug.zip mv ${OUTDIR}/${DISTNAME}-x86_64-*.zip ${OUTDIR}/${DISTNAME}-win64.zip diff --git a/doc/developer-notes.md b/doc/developer-notes.md index 45e55b7c40..f4fc55427d 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -280,7 +280,7 @@ thread](https://askubuntu.com/questions/50145/how-to-install-perf-monitoring-too for specific instructions. Certain kernel parameters may need to be set for perf to be able to inspect the -running process' stack. +running process's stack. ```sh $ sudo sysctl -w kernel.perf_event_paranoid=-1 diff --git a/doc/release-notes.md b/doc/release-notes.md index 4a86469ccf..9efb6cbabb 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -90,7 +90,7 @@ Updated RPCs Note: some low-level RPC changes mainly useful for testing are described in the Low-level Changes section below. -* The `sendmany` RPC had an argument `minconf` that was not well specified and +- The `sendmany` RPC had an argument `minconf` that was not well specified and would lead to RPC errors even when the wallet's coin selection would succeed. The `sendtoaddress` RPC never had this check, so to normalize the behavior, `minconf` is now ignored in `sendmany`. If the coin selection does not @@ -106,11 +106,22 @@ Low-level changes Configuration ------------ -* An error is issued where previously a warning was issued when a setting in +- An error is issued where previously a warning was issued when a setting in the config file was specified in the default section, but not overridden for the selected network. This change takes only effect if the selected network is not mainnet. +Network +------- + +- When fetching a transaction announced by multiple peers, previous versions of + Bitcoin Core would sequentially attempt to download the transaction from each + announcing peer until the transaction is received, in the order that those + peers' announcements were received. In this release, the download logic has + changed to randomize the fetch order across peers and to prefer sending + download requests to outbound peers over inbound peers. This fixes an issue + where inbound peers can prevent a node from getting a transaction. + Wallet ------ diff --git a/doc/release-notes/release-notes-0.12.0.md b/doc/release-notes/release-notes-0.12.0.md index cf74a17975..bc0d5ea3b0 100644 --- a/doc/release-notes/release-notes-0.12.0.md +++ b/doc/release-notes/release-notes-0.12.0.md @@ -127,7 +127,7 @@ minimum relay feerate. The initial minimum relay feerate is set to Bitcoin Core 0.12 also introduces new default policy limits on the length and size of unconfirmed transaction chains that are allowed in the mempool (generally limiting the length of unconfirmed chains to 25 transactions, with a -total size of 101 KB). These limits can be overriden using command line +total size of 101 KB). These limits can be overridden using command line arguments; see the extended help (`--help -help-debug`) for more information. Opt-in Replace-by-fee transactions diff --git a/doc/release-process.md b/doc/release-process.md index 8f1222cb88..480b09ee11 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -41,12 +41,10 @@ Release Process #### After branch-off (on master) - Update the version of `contrib/gitian-descriptors/*.yml`. -- Update the versions in `SECURITY.md` as per the software lifecycle [maintenance policy](https://bitcoincore.org/en/lifecycle/#maintenance-period), generally bumping all up one major version. #### After branch-off (on the major release branch) - Update the versions and the link to the release notes draft in `doc/release-notes.md`. -- Delete `SECURITY.md`. #### Before final release @@ -325,6 +323,9 @@ bitcoin.org (see below for bitcoin.org update instructions). - bitcoincore.org blog post + - bitcoincore.org maintained versions update: + [table](https://github.com/bitcoin-core/bitcoincore.org/commits/master/_includes/posts/maintenance-table.md) + - bitcoincore.org RPC documentation update - Update packaging repo diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp index a788b23823..8eea96d930 100644 --- a/src/bench/bench_bitcoin.cpp +++ b/src/bench/bench_bitcoin.cpp @@ -36,7 +36,7 @@ int main(int argc, char** argv) SetupBenchArgs(); std::string error; if (!gArgs.ParseParameters(argc, argv, error)) { - fprintf(stderr, "Error parsing command line arguments: %s\n", error.c_str()); + tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error.c_str()); return EXIT_FAILURE; } @@ -53,7 +53,7 @@ int main(int argc, char** argv) double scaling_factor; if (!ParseDouble(scaling_str, &scaling_factor)) { - fprintf(stderr, "Error parsing scaling factor as double: %s\n", scaling_str.c_str()); + tfm::format(std::cerr, "Error parsing scaling factor as double: %s\n", scaling_str.c_str()); return EXIT_FAILURE; } diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 1009a771f8..38010c461e 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -101,7 +101,7 @@ static int AppInitRPC(int argc, char* argv[]) SetupCliArgs(); std::string error; if (!gArgs.ParseParameters(argc, argv, error)) { - fprintf(stderr, "Error parsing command line arguments: %s\n", error.c_str()); + tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error.c_str()); return EXIT_FAILURE; } if (argc < 2 || HelpRequested(gArgs) || gArgs.IsArgSet("-version")) { @@ -115,26 +115,26 @@ static int AppInitRPC(int argc, char* argv[]) strUsage += "\n" + gArgs.GetHelpMessage(); } - fprintf(stdout, "%s", strUsage.c_str()); + tfm::format(std::cout, "%s", strUsage.c_str()); if (argc < 2) { - fprintf(stderr, "Error: too few parameters\n"); + tfm::format(std::cerr, "Error: too few parameters\n"); return EXIT_FAILURE; } return EXIT_SUCCESS; } if (!fs::is_directory(GetDataDir(false))) { - fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str()); + tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str()); return EXIT_FAILURE; } if (!gArgs.ReadConfigFiles(error, true)) { - fprintf(stderr, "Error reading configuration file: %s\n", error.c_str()); + tfm::format(std::cerr, "Error reading configuration file: %s\n", error.c_str()); return EXIT_FAILURE; } // Check for -testnet or -regtest parameter (BaseParams() calls are only valid after this clause) try { SelectBaseParams(gArgs.GetChainName()); } catch (const std::exception& e) { - fprintf(stderr, "Error: %s\n", e.what()); + tfm::format(std::cerr, "Error: %s\n", e.what()); return EXIT_FAILURE; } return CONTINUE_EXECUTION; @@ -495,7 +495,7 @@ static int CommandLineRPC(int argc, char *argv[]) } if (strPrint != "") { - fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str()); + tfm::format(nRet == 0 ? std::cout : std::cerr, "%s\n", strPrint.c_str()); } return nRet; } @@ -508,7 +508,7 @@ int main(int argc, char* argv[]) #endif SetupEnvironment(); if (!SetupNetworking()) { - fprintf(stderr, "Error: Initializing networking failed\n"); + tfm::format(std::cerr, "Error: Initializing networking failed\n"); return EXIT_FAILURE; } event_set_log_callback(&libevent_log_cb); diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index ac1d62a8f4..933b34744d 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -82,7 +82,7 @@ static int AppInitRawTx(int argc, char* argv[]) SetupBitcoinTxArgs(); std::string error; if (!gArgs.ParseParameters(argc, argv, error)) { - fprintf(stderr, "Error parsing command line arguments: %s\n", error.c_str()); + tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error.c_str()); return EXIT_FAILURE; } @@ -90,7 +90,7 @@ static int AppInitRawTx(int argc, char* argv[]) try { SelectParams(gArgs.GetChainName()); } catch (const std::exception& e) { - fprintf(stderr, "Error: %s\n", e.what()); + tfm::format(std::cerr, "Error: %s\n", e.what()); return EXIT_FAILURE; } @@ -104,10 +104,10 @@ static int AppInitRawTx(int argc, char* argv[]) "\n"; strUsage += gArgs.GetHelpMessage(); - fprintf(stdout, "%s", strUsage.c_str()); + tfm::format(std::cout, "%s", strUsage.c_str()); if (argc < 2) { - fprintf(stderr, "Error: too few parameters\n"); + tfm::format(std::cerr, "Error: too few parameters\n"); return EXIT_FAILURE; } return EXIT_SUCCESS; @@ -723,21 +723,21 @@ static void OutputTxJSON(const CTransaction& tx) TxToUniv(tx, uint256(), entry); std::string jsonOutput = entry.write(4); - fprintf(stdout, "%s\n", jsonOutput.c_str()); + tfm::format(std::cout, "%s\n", jsonOutput.c_str()); } static void OutputTxHash(const CTransaction& tx) { std::string strHexHash = tx.GetHash().GetHex(); // the hex-encoded transaction hash (aka the transaction id) - fprintf(stdout, "%s\n", strHexHash.c_str()); + tfm::format(std::cout, "%s\n", strHexHash.c_str()); } static void OutputTxHex(const CTransaction& tx) { std::string strHex = EncodeHexTx(tx); - fprintf(stdout, "%s\n", strHex.c_str()); + tfm::format(std::cout, "%s\n", strHex.c_str()); } static void OutputTx(const CTransaction& tx) @@ -828,7 +828,7 @@ static int CommandLineRawTx(int argc, char* argv[]) } if (strPrint != "") { - fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str()); + tfm::format(nRet == 0 ? std::cout : std::cerr, "%s\n", strPrint.c_str()); } return nRet; } diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp index ad31f0be4a..cbb4ea750c 100644 --- a/src/bitcoin-wallet.cpp +++ b/src/bitcoin-wallet.cpp @@ -36,7 +36,7 @@ static bool WalletAppInit(int argc, char* argv[]) SetupWalletToolArgs(); std::string error_message; if (!gArgs.ParseParameters(argc, argv, error_message)) { - fprintf(stderr, "Error parsing command line arguments: %s\n", error_message.c_str()); + tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error_message.c_str()); return false; } if (argc < 2 || HelpRequested(gArgs)) { @@ -48,7 +48,7 @@ static bool WalletAppInit(int argc, char* argv[]) " bitcoin-wallet [options] <command>\n\n" + gArgs.GetHelpMessage(); - fprintf(stdout, "%s", usage.c_str()); + tfm::format(std::cout, "%s", usage.c_str()); return false; } @@ -56,7 +56,7 @@ static bool WalletAppInit(int argc, char* argv[]) LogInstance().m_print_to_console = gArgs.GetBoolArg("-printtoconsole", gArgs.GetBoolArg("-debug", false)); if (!fs::is_directory(GetDataDir(false))) { - fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str()); + tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str()); return false; } // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause) @@ -87,7 +87,7 @@ int main(int argc, char* argv[]) for(int i = 1; i < argc; ++i) { if (!IsSwitchChar(argv[i][0])) { if (!method.empty()) { - fprintf(stderr, "Error: two methods provided (%s and %s). Only one method should be provided.\n", method.c_str(), argv[i]); + tfm::format(std::cerr, "Error: two methods provided (%s and %s). Only one method should be provided.\n", method.c_str(), argv[i]); return EXIT_FAILURE; } method = argv[i]; @@ -95,13 +95,13 @@ int main(int argc, char* argv[]) } if (method.empty()) { - fprintf(stderr, "No method provided. Run `bitcoin-wallet -help` for valid methods.\n"); + tfm::format(std::cerr, "No method provided. Run `bitcoin-wallet -help` for valid methods.\n"); return EXIT_FAILURE; } // A name must be provided when creating a file if (method == "create" && !gArgs.IsArgSet("-wallet")) { - fprintf(stderr, "Wallet name must be provided when creating a new wallet.\n"); + tfm::format(std::cerr, "Wallet name must be provided when creating a new wallet.\n"); return EXIT_FAILURE; } diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 094820293e..ba6de702e0 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -70,7 +70,7 @@ static bool AppInit(int argc, char* argv[]) SetupServerArgs(); std::string error; if (!gArgs.ParseParameters(argc, argv, error)) { - fprintf(stderr, "Error parsing command line arguments: %s\n", error.c_str()); + tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error.c_str()); return false; } @@ -88,7 +88,7 @@ static bool AppInit(int argc, char* argv[]) strUsage += "\n" + gArgs.GetHelpMessage(); } - fprintf(stdout, "%s", strUsage.c_str()); + tfm::format(std::cout, "%s", strUsage.c_str()); return true; } @@ -96,25 +96,25 @@ static bool AppInit(int argc, char* argv[]) { if (!fs::is_directory(GetDataDir(false))) { - fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str()); + tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str()); return false; } if (!gArgs.ReadConfigFiles(error, true)) { - fprintf(stderr, "Error reading configuration file: %s\n", error.c_str()); + tfm::format(std::cerr, "Error reading configuration file: %s\n", error.c_str()); return false; } // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause) try { SelectParams(gArgs.GetChainName()); } catch (const std::exception& e) { - fprintf(stderr, "Error: %s\n", e.what()); + tfm::format(std::cerr, "Error: %s\n", e.what()); return false; } // Error out when loose non-argument tokens are encountered on command line for (int i = 1; i < argc; i++) { if (!IsSwitchChar(argv[i][0])) { - fprintf(stderr, "Error: Command line contains unexpected token '%s', see bitcoind -h for a list of options.\n", argv[i]); + tfm::format(std::cerr, "Error: Command line contains unexpected token '%s', see bitcoind -h for a list of options.\n", argv[i]); return false; } } @@ -146,18 +146,18 @@ static bool AppInit(int argc, char* argv[]) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif - fprintf(stdout, "Bitcoin server starting\n"); + tfm::format(std::cout, "Bitcoin server starting\n"); // Daemonize if (daemon(1, 0)) { // don't chdir (1), do close FDs (0) - fprintf(stderr, "Error: daemon() failed: %s\n", strerror(errno)); + tfm::format(std::cerr, "Error: daemon() failed: %s\n", strerror(errno)); return false; } #if defined(MAC_OSX) #pragma GCC diagnostic pop #endif #else - fprintf(stderr, "Error: -daemon is not supported on this operating system\n"); + tfm::format(std::cerr, "Error: -daemon is not supported on this operating system\n"); return false; #endif // HAVE_DECL_DAEMON } diff --git a/src/init.cpp b/src/init.cpp index 6912b49717..93aa6f21b5 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -108,14 +108,13 @@ static fs::path GetPidFile() NODISCARD static bool CreatePidFile() { - FILE* file = fsbridge::fopen(GetPidFile(), "w"); + fsbridge::ofstream file{GetPidFile()}; if (file) { #ifdef WIN32 - fprintf(file, "%d\n", GetCurrentProcessId()); + tfm::format(file, "%d\n", GetCurrentProcessId()); #else - fprintf(file, "%d\n", getpid()); + tfm::format(file, "%d\n", getpid()); #endif - fclose(file); return true; } else { return InitError(strprintf(_("Unable to create the PID file '%s': %s"), GetPidFile().string(), std::strerror(errno))); diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 0d9e314afd..e57706980a 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -68,11 +68,13 @@ static constexpr int32_t MAX_PEER_TX_IN_FLIGHT = 100; /** Maximum number of announced transactions from a peer */ static constexpr int32_t MAX_PEER_TX_ANNOUNCEMENTS = 2 * MAX_INV_SZ; /** How many microseconds to delay requesting transactions from inbound peers */ -static constexpr int64_t INBOUND_PEER_TX_DELAY = 2 * 1000000; +static constexpr int64_t INBOUND_PEER_TX_DELAY = 2 * 1000000; // 2 seconds /** How long to wait (in microseconds) before downloading a transaction from an additional peer */ -static constexpr int64_t GETDATA_TX_INTERVAL = 60 * 1000000; +static constexpr int64_t GETDATA_TX_INTERVAL = 60 * 1000000; // 1 minute /** Maximum delay (in microseconds) for transaction requests to avoid biasing some peers over others. */ -static constexpr int64_t MAX_GETDATA_RANDOM_DELAY = 2 * 1000000; +static constexpr int64_t MAX_GETDATA_RANDOM_DELAY = 2 * 1000000; // 2 seconds +/** How long to wait (in microseconds) before expiring an in-flight getdata request to a peer */ +static constexpr int64_t TX_EXPIRY_INTERVAL = 10 * GETDATA_TX_INTERVAL; static_assert(INBOUND_PEER_TX_DELAY >= MAX_GETDATA_RANDOM_DELAY, "To preserve security, MAX_GETDATA_RANDOM_DELAY should not exceed INBOUND_PEER_DELAY"); /** Limit to avoid sending big packets. Not used in processing incoming GETDATA for compatibility */ @@ -343,8 +345,11 @@ struct CNodeState { //! Store all the transactions a peer has recently announced std::set<uint256> m_tx_announced; - //! Store transactions which were requested by us - std::set<uint256> m_tx_in_flight; + //! Store transactions which were requested by us, with timestamp + std::map<uint256, int64_t> m_tx_in_flight; + + //! Periodically check for stuck getdata requests + int64_t m_check_expiry_timer{0}; }; TxDownloadState m_tx_download; @@ -702,30 +707,40 @@ void UpdateTxRequestTime(const uint256& txid, int64_t request_time) EXCLUSIVE_LO } } - -void RequestTx(CNodeState* state, const uint256& txid, int64_t nNow) EXCLUSIVE_LOCKS_REQUIRED(cs_main) +int64_t CalculateTxGetDataTime(const uint256& txid, int64_t current_time, bool use_inbound_delay) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { - CNodeState::TxDownloadState& peer_download_state = state->m_tx_download; - if (peer_download_state.m_tx_announced.size() >= MAX_PEER_TX_ANNOUNCEMENTS || peer_download_state.m_tx_announced.count(txid)) { - // Too many queued announcements from this peer, or we already have - // this announcement - return; - } - peer_download_state.m_tx_announced.insert(txid); - int64_t process_time; int64_t last_request_time = GetTxRequestTime(txid); // First time requesting this tx if (last_request_time == 0) { - process_time = nNow; + process_time = current_time; } else { // Randomize the delay to avoid biasing some peers over others (such as due to // fixed ordering of peer processing in ThreadMessageHandler) process_time = last_request_time + GETDATA_TX_INTERVAL + GetRand(MAX_GETDATA_RANDOM_DELAY); } - // We delay processing announcements from non-preferred (eg inbound) peers - if (!state->fPreferredDownload) process_time += INBOUND_PEER_TX_DELAY; + // We delay processing announcements from inbound peers + if (use_inbound_delay) process_time += INBOUND_PEER_TX_DELAY; + + return process_time; +} + +void RequestTx(CNodeState* state, const uint256& txid, int64_t nNow) EXCLUSIVE_LOCKS_REQUIRED(cs_main) +{ + CNodeState::TxDownloadState& peer_download_state = state->m_tx_download; + if (peer_download_state.m_tx_announced.size() >= MAX_PEER_TX_ANNOUNCEMENTS || + peer_download_state.m_tx_process_time.size() >= MAX_PEER_TX_ANNOUNCEMENTS || + peer_download_state.m_tx_announced.count(txid)) { + // Too many queued announcements from this peer, or we already have + // this announcement + return; + } + peer_download_state.m_tx_announced.insert(txid); + + // Calculate the time to try requesting this transaction. Use + // fPreferredDownload as a proxy for outbound peers. + int64_t process_time = CalculateTxGetDataTime(txid, nNow, !state->fPreferredDownload); peer_download_state.m_tx_process_time.emplace(process_time, txid); } @@ -1544,12 +1559,19 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm if (!vNotFound.empty()) { // Let the peer know that we didn't find what it asked for, so it doesn't - // have to wait around forever. Currently only SPV clients actually care - // about this message: it's needed when they are recursively walking the - // dependencies of relevant unconfirmed transactions. SPV clients want to - // 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. + // have to wait around forever. + // SPV clients care about this message: it's needed when they are + // recursively walking the dependencies of relevant unconfirmed + // transactions. SPV clients want to 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. + // Also, other nodes can use these messages to automatically request a + // transaction from some other peer that annnounced it, and stop + // waiting for us to respond. + // In normal operation, we often send NOTFOUND messages for parents of + // transactions that we relay; if a peer is missing a parent, they may + // assume we have them and request the parents from us. connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::NOTFOUND, vNotFound)); } } @@ -3146,8 +3168,27 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr } if (strCommand == NetMsgType::NOTFOUND) { - // We do not care about the NOTFOUND message, but logging an Unknown Command - // message would be undesirable as we transmit it ourselves. + // Remove the NOTFOUND transactions from the peer + LOCK(cs_main); + CNodeState *state = State(pfrom->GetId()); + std::vector<CInv> vInv; + vRecv >> vInv; + if (vInv.size() <= MAX_PEER_TX_IN_FLIGHT + MAX_BLOCKS_IN_TRANSIT_PER_PEER) { + for (CInv &inv : vInv) { + if (inv.type == MSG_TX || inv.type == MSG_WITNESS_TX) { + // If we receive a NOTFOUND message for a txid we requested, erase + // it from our data structures for this peer. + auto in_flight_it = state->m_tx_download.m_tx_in_flight.find(inv.hash); + if (in_flight_it == state->m_tx_download.m_tx_in_flight.end()) { + // Skip any further work if this is a spurious NOTFOUND + // message. + continue; + } + state->m_tx_download.m_tx_in_flight.erase(in_flight_it); + state->m_tx_download.m_tx_announced.erase(inv.hash); + } + } + } return true; } @@ -3945,9 +3986,33 @@ bool PeerLogicValidation::SendMessages(CNode* pto) // // Message: getdata (non-blocks) // + + // For robustness, expire old requests after a long timeout, so that + // we can resume downloading transactions from a peer even if they + // were unresponsive in the past. + // Eventually we should consider disconnecting peers, but this is + // conservative. + if (state.m_tx_download.m_check_expiry_timer <= nNow) { + for (auto it=state.m_tx_download.m_tx_in_flight.begin(); it != state.m_tx_download.m_tx_in_flight.end();) { + if (it->second <= nNow - TX_EXPIRY_INTERVAL) { + LogPrint(BCLog::NET, "timeout of inflight tx %s from peer=%d\n", it->first.ToString(), pto->GetId()); + state.m_tx_download.m_tx_announced.erase(it->first); + state.m_tx_download.m_tx_in_flight.erase(it++); + } else { + ++it; + } + } + // On average, we do this check every TX_EXPIRY_INTERVAL. Randomize + // so that we're not doing this for all peers at the same time. + state.m_tx_download.m_check_expiry_timer = nNow + TX_EXPIRY_INTERVAL/2 + GetRand(TX_EXPIRY_INTERVAL); + } + auto& tx_process_time = state.m_tx_download.m_tx_process_time; while (!tx_process_time.empty() && tx_process_time.begin()->first <= nNow && state.m_tx_download.m_tx_in_flight.size() < MAX_PEER_TX_IN_FLIGHT) { - const uint256& txid = tx_process_time.begin()->second; + const uint256 txid = tx_process_time.begin()->second; + // Erase this entry from tx_process_time (it may be added back for + // processing at a later time, see below) + tx_process_time.erase(tx_process_time.begin()); CInv inv(MSG_TX | GetFetchFlags(pto), txid); if (!AlreadyHave(inv)) { // If this transaction was last requested more than 1 minute ago, @@ -3961,20 +4026,20 @@ bool PeerLogicValidation::SendMessages(CNode* pto) vGetData.clear(); } UpdateTxRequestTime(inv.hash, nNow); - state.m_tx_download.m_tx_in_flight.insert(inv.hash); + state.m_tx_download.m_tx_in_flight.emplace(inv.hash, nNow); } else { // This transaction is in flight from someone else; queue // up processing to happen after the download times out // (with a slight delay for inbound peers, to prefer // requests to outbound peers). - RequestTx(&state, txid, nNow); + int64_t next_process_time = CalculateTxGetDataTime(txid, nNow, !state.fPreferredDownload); + tx_process_time.emplace(next_process_time, txid); } } else { // We have already seen this transaction, no need to download. state.m_tx_download.m_tx_announced.erase(inv.hash); state.m_tx_download.m_tx_in_flight.erase(inv.hash); } - tx_process_time.erase(tx_process_time.begin()); } diff --git a/src/netaddress.cpp b/src/netaddress.cpp index db6c46d12a..4fbfa2b5c8 100644 --- a/src/netaddress.cpp +++ b/src/netaddress.cpp @@ -239,7 +239,7 @@ bool CNetAddr::IsLocal() const * be used to refer to an actual host. * * @note A valid address may or may not be publicly routable on the global - * internet. As in, the set of valid addreses is a superset of the set of + * internet. As in, the set of valid addresses is a superset of the set of * publicly routable addresses. * * @see CNetAddr::IsRoutable() @@ -287,7 +287,7 @@ bool CNetAddr::IsValid() const * @returns Whether or not this network address is publicly routable on the * global internet. * - * @note A routable address is always valid. As in, the set of routable addreses + * @note A routable address is always valid. As in, the set of routable addresses * is a subset of the set of valid addresses. * * @see CNetAddr::IsValid() diff --git a/src/noui.cpp b/src/noui.cpp index c7d8fee0ba..0c18b0e231 100644 --- a/src/noui.cpp +++ b/src/noui.cpp @@ -37,7 +37,7 @@ bool noui_ThreadSafeMessageBox(const std::string& message, const std::string& ca if (!fSecure) LogPrintf("%s: %s\n", strCaption, message); - fprintf(stderr, "%s: %s\n", strCaption.c_str(), message.c_str()); + tfm::format(std::cerr, "%s: %s\n", strCaption.c_str(), message.c_str()); return false; } diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp index 7e93a746ca..5d538606c2 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -47,7 +47,7 @@ private: std::vector<double> txCtAvg; // Count the total # of txs confirmed within Y blocks in each bucket - // Track the historical moving average of theses totals over blocks + // Track the historical moving average of these totals over blocks std::vector<std::vector<double>> confAvg; // confAvg[Y][X] // Track moving avg of txs which have been evicted from the mempool diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index b5c92e10a2..1444dddeb1 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -335,7 +335,7 @@ void BitcoinGUI::createActions() openAction->setStatusTip(tr("Open a bitcoin: URI or payment request")); m_open_wallet_action = new QAction(tr("Open Wallet"), this); - m_open_wallet_action->setMenu(new QMenu(this)); + m_open_wallet_action->setEnabled(false); m_open_wallet_action->setStatusTip(tr("Open a wallet")); m_close_wallet_action = new QAction(tr("Close Wallet..."), this); @@ -633,6 +633,9 @@ void BitcoinGUI::setWalletController(WalletController* wallet_controller) m_wallet_controller = wallet_controller; + m_open_wallet_action->setEnabled(true); + m_open_wallet_action->setMenu(new QMenu(this)); + connect(wallet_controller, &WalletController::walletAdded, this, &BitcoinGUI::addWallet); connect(wallet_controller, &WalletController::walletRemoved, this, &BitcoinGUI::removeWallet); diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp index 3a4eebd94a..aa810690c9 100644 --- a/src/qt/utilitydialog.cpp +++ b/src/qt/utilitydialog.cpp @@ -124,7 +124,7 @@ HelpMessageDialog::~HelpMessageDialog() void HelpMessageDialog::printToConsole() { // On other operating systems, the expected action is to print the message to the console. - fprintf(stdout, "%s\n", qPrintable(text)); + tfm::format(std::cout, "%s\n", qPrintable(text)); } void HelpMessageDialog::showOrPrint() diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 49d7ae2016..50c4589d9f 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1061,7 +1061,12 @@ static UniValue pruneblockchain(const JSONRPCRequest& request) } PruneBlockFilesManual(height); - return uint64_t(height); + const CBlockIndex* block = ::ChainActive().Tip(); + assert(block); + while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) { + block = block->pprev; + } + return uint64_t(block->nHeight); } static UniValue gettxoutsetinfo(const JSONRPCRequest& request) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index df6e9f029d..196ca0f17b 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -39,7 +39,7 @@ /** High fee for sendrawtransaction and testmempoolaccept. * By default, transaction with a fee higher than this will be rejected by the - * RPCs. This can be overriden with the maxfeerate argument. + * RPCs. This can be overridden with the maxfeerate argument. */ constexpr static CAmount DEFAULT_MAX_RAW_TX_FEE{COIN / 10}; diff --git a/src/sync.cpp b/src/sync.cpp index c2767b200a..20258d8e9a 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -57,7 +57,7 @@ struct CLockLocation { std::string ToString() const { - return tfm::format( + return strprintf( "%s %s:%s%s (in thread %s)", mutexName, sourceFile, itostr(sourceLine), (fTry ? " (TRY)" : ""), m_thread_name); } @@ -118,7 +118,7 @@ static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch, LogPrintf(" %s\n", i.second.ToString()); } if (g_debug_lockorder_abort) { - fprintf(stderr, "Assertion failed: detected inconsistent lock order at %s:%i, details in debug log.\n", __FILE__, __LINE__); + tfm::format(std::cerr, "Assertion failed: detected inconsistent lock order at %s:%i, details in debug log.\n", __FILE__, __LINE__); abort(); } throw std::logic_error("potential deadlock detected"); @@ -175,7 +175,7 @@ void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, for (const std::pair<void*, CLockLocation>& i : g_lockstack) if (i.first == cs) return; - fprintf(stderr, "Assertion failed: lock %s not held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld().c_str()); + tfm::format(std::cerr, "Assertion failed: lock %s not held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld().c_str()); abort(); } @@ -183,7 +183,7 @@ void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLi { for (const std::pair<void*, CLockLocation>& i : g_lockstack) { if (i.first == cs) { - fprintf(stderr, "Assertion failed: lock %s held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld().c_str()); + tfm::format(std::cerr, "Assertion failed: lock %s held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld().c_str()); abort(); } } diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index 35911e507f..4e2acca4c3 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -567,7 +567,7 @@ BOOST_AUTO_TEST_CASE(poly1305_testvector) BOOST_AUTO_TEST_CASE(hkdf_hmac_sha256_l32_tests) { - // Use rfc5869 test vectors but trucated to 32 bytes (our implementation only support length 32) + // Use rfc5869 test vectors but truncated to 32 bytes (our implementation only support length 32) TestHKDF_SHA256_32( /* IKM */ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", /* salt */ "000102030405060708090a0b0c", diff --git a/src/tinyformat.h b/src/tinyformat.h index 14b7cd3026..182f518a0b 100644 --- a/src/tinyformat.h +++ b/src/tinyformat.h @@ -1063,6 +1063,7 @@ std::string format(const std::string &fmt, const Args&... args) } // namespace tinyformat +/** Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for details) */ #define strprintf tfm::format #endif // TINYFORMAT_H_INCLUDED diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index 550e23b222..84f54da515 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -412,7 +412,7 @@ public: TorController(struct event_base* base, const std::string& target); ~TorController(); - /** Get name fo file to store private key in */ + /** Get name of file to store private key in */ fs::path GetPrivateKeyFile(); /** Reconnect, after getting disconnected */ diff --git a/src/util/system.cpp b/src/util/system.cpp index 6a6b82c967..fca29a9f31 100644 --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -673,7 +673,7 @@ void PrintExceptionContinue(const std::exception* pex, const char* pszThread) { std::string message = FormatException(pex, pszThread); LogPrintf("\n\n************************\n%s\n", message); - fprintf(stderr, "\n\n************************\n%s\n", message.c_str()); + tfm::format(std::cerr, "\n\n************************\n%s\n", message.c_str()); } fs::path GetDefaultDataDir() @@ -933,7 +933,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys) } } for (const std::string& to_include : includeconf) { - fprintf(stderr, "warning: -includeconf cannot be used from included files; ignoring -includeconf=%s\n", to_include.c_str()); + tfm::format(std::cerr, "warning: -includeconf cannot be used from included files; ignoring -includeconf=%s\n", to_include.c_str()); } } } diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp index 440c32c367..0843194511 100644 --- a/src/wallet/wallettool.cpp +++ b/src/wallet/wallettool.cpp @@ -22,7 +22,7 @@ static void WalletToolReleaseWallet(CWallet* wallet) static std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs::path& path) { if (fs::exists(path)) { - fprintf(stderr, "Error: File exists already\n"); + tfm::format(std::cerr, "Error: File exists already\n"); return nullptr; } // dummy chain interface @@ -30,7 +30,7 @@ static std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs:: bool first_run = true; DBErrors load_wallet_ret = wallet_instance->LoadWallet(first_run); if (load_wallet_ret != DBErrors::LOAD_OK) { - fprintf(stderr, "Error creating %s", name.c_str()); + tfm::format(std::cerr, "Error creating %s", name.c_str()); return nullptr; } @@ -40,7 +40,7 @@ static std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs:: CPubKey seed = wallet_instance->GenerateNewSeed(); wallet_instance->SetHDSeed(seed); - fprintf(stdout, "Topping up keypool...\n"); + tfm::format(std::cout, "Topping up keypool...\n"); wallet_instance->TopUpKeyPool(); return wallet_instance; } @@ -48,7 +48,7 @@ static std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs:: static std::shared_ptr<CWallet> LoadWallet(const std::string& name, const fs::path& path) { if (!fs::exists(path)) { - fprintf(stderr, "Error: Wallet files does not exist\n"); + tfm::format(std::cerr, "Error: Wallet files does not exist\n"); return nullptr; } @@ -59,28 +59,28 @@ static std::shared_ptr<CWallet> LoadWallet(const std::string& name, const fs::pa bool first_run; load_wallet_ret = wallet_instance->LoadWallet(first_run); } catch (const std::runtime_error&) { - fprintf(stderr, "Error loading %s. Is wallet being used by another process?\n", name.c_str()); + tfm::format(std::cerr, "Error loading %s. Is wallet being used by another process?\n", name.c_str()); return nullptr; } if (load_wallet_ret != DBErrors::LOAD_OK) { wallet_instance = nullptr; if (load_wallet_ret == DBErrors::CORRUPT) { - fprintf(stderr, "Error loading %s: Wallet corrupted", name.c_str()); + tfm::format(std::cerr, "Error loading %s: Wallet corrupted", name.c_str()); return nullptr; } else if (load_wallet_ret == DBErrors::NONCRITICAL_ERROR) { - fprintf(stderr, "Error reading %s! All keys read correctly, but transaction data" + tfm::format(std::cerr, "Error reading %s! All keys read correctly, but transaction data" " or address book entries might be missing or incorrect.", name.c_str()); } else if (load_wallet_ret == DBErrors::TOO_NEW) { - fprintf(stderr, "Error loading %s: Wallet requires newer version of %s", + tfm::format(std::cerr, "Error loading %s: Wallet requires newer version of %s", name.c_str(), PACKAGE_NAME); return nullptr; } else if (load_wallet_ret == DBErrors::NEED_REWRITE) { - fprintf(stderr, "Wallet needed to be rewritten: restart %s to complete", PACKAGE_NAME); + tfm::format(std::cerr, "Wallet needed to be rewritten: restart %s to complete", PACKAGE_NAME); return nullptr; } else { - fprintf(stderr, "Error loading %s", name.c_str()); + tfm::format(std::cerr, "Error loading %s", name.c_str()); return nullptr; } } @@ -92,12 +92,12 @@ static void WalletShowInfo(CWallet* wallet_instance) { LOCK(wallet_instance->cs_wallet); - fprintf(stdout, "Wallet info\n===========\n"); - fprintf(stdout, "Encrypted: %s\n", wallet_instance->IsCrypted() ? "yes" : "no"); - fprintf(stdout, "HD (hd seed available): %s\n", wallet_instance->GetHDChain().seed_id.IsNull() ? "no" : "yes"); - fprintf(stdout, "Keypool Size: %u\n", wallet_instance->GetKeyPoolSize()); - fprintf(stdout, "Transactions: %zu\n", wallet_instance->mapWallet.size()); - fprintf(stdout, "Address Book: %zu\n", wallet_instance->mapAddressBook.size()); + tfm::format(std::cout, "Wallet info\n===========\n"); + tfm::format(std::cout, "Encrypted: %s\n", wallet_instance->IsCrypted() ? "yes" : "no"); + tfm::format(std::cout, "HD (hd seed available): %s\n", wallet_instance->GetHDChain().seed_id.IsNull() ? "no" : "yes"); + tfm::format(std::cout, "Keypool Size: %u\n", wallet_instance->GetKeyPoolSize()); + tfm::format(std::cout, "Transactions: %zu\n", wallet_instance->mapWallet.size()); + tfm::format(std::cout, "Address Book: %zu\n", wallet_instance->mapAddressBook.size()); } bool ExecuteWalletToolFunc(const std::string& command, const std::string& name) @@ -112,12 +112,12 @@ bool ExecuteWalletToolFunc(const std::string& command, const std::string& name) } } else if (command == "info") { if (!fs::exists(path)) { - fprintf(stderr, "Error: no wallet file at %s\n", name.c_str()); + tfm::format(std::cerr, "Error: no wallet file at %s\n", name.c_str()); return false; } std::string error; if (!WalletBatch::VerifyEnvironment(path, error)) { - fprintf(stderr, "Error loading %s. Is wallet being used by other process?\n", name.c_str()); + tfm::format(std::cerr, "Error loading %s. Is wallet being used by other process?\n", name.c_str()); return false; } std::shared_ptr<CWallet> wallet_instance = LoadWallet(name, path); @@ -125,7 +125,7 @@ bool ExecuteWalletToolFunc(const std::string& command, const std::string& name) WalletShowInfo(wallet_instance.get()); wallet_instance->Flush(true); } else { - fprintf(stderr, "Invalid command: %s\n", command.c_str()); + tfm::format(std::cerr, "Invalid command: %s\n", command.c_str()); return false; } diff --git a/test/functional/feature_pruning.py b/test/functional/feature_pruning.py index e2b3b2d544..66c395d7a2 100755 --- a/test/functional/feature_pruning.py +++ b/test/functional/feature_pruning.py @@ -23,8 +23,6 @@ from test_framework.util import ( wait_until, ) -MIN_BLOCKS_TO_KEEP = 288 - # Rescans start at the earliest block up to 2 hours before a key timestamp, so # the manual prune RPC avoids pruning blocks in the same window to be # compatible with pruning based on key creation time. @@ -273,20 +271,9 @@ class PruneTest(BitcoinTestFramework): else: return index - def prune(index, expected_ret=None): + def prune(index): ret = node.pruneblockchain(height=height(index)) - # Check the return value. When use_timestamp is True, just check - # that the return value is less than or equal to the expected - # value, because when more than one block is generated per second, - # a timestamp will not be granular enough to uniquely identify an - # individual block. - if expected_ret is None: - expected_ret = index - if use_timestamp: - assert_greater_than(ret, 0) - assert_greater_than(expected_ret + 1, ret) - else: - assert_equal(ret, expected_ret) + assert_equal(ret, node.getblockchaininfo()['pruneheight']) def has_block(index): return os.path.isfile(os.path.join(self.nodes[node_number].datadir, "regtest", "blocks", "blk{:05}.dat".format(index))) @@ -326,7 +313,7 @@ class PruneTest(BitcoinTestFramework): assert not has_block(1), "blk00001.dat is still there, should be pruned by now" # height=1000 should not prune anything more, because tip-288 is in blk00002.dat. - prune(1000, 1001 - MIN_BLOCKS_TO_KEEP) + prune(1000) assert has_block(2), "blk00002.dat is still there, should be pruned by now" # advance the tip so blk00002.dat and blk00003.dat can be pruned (the last 288 blocks should now be in blk00004.dat) diff --git a/test/lint/lint-format-strings.sh b/test/lint/lint-format-strings.sh index c994ae3f4d..cb630c78ad 100755 --- a/test/lint/lint-format-strings.sh +++ b/test/lint/lint-format-strings.sh @@ -13,6 +13,7 @@ export LC_ALL=C FUNCTION_NAMES_AND_NUMBER_OF_LEADING_ARGUMENTS=( "FatalError,0" "fprintf,1" + "tfm::format,1" # Assuming tfm::::format(std::ostream&, ... "LogConnectFailure,1" "LogPrint,1" "LogPrintf,0" diff --git a/test/lint/lint-locale-dependence.sh b/test/lint/lint-locale-dependence.sh index 2b6c78c2c8..9a1aa766f7 100755 --- a/test/lint/lint-locale-dependence.sh +++ b/test/lint/lint-locale-dependence.sh @@ -8,7 +8,6 @@ KNOWN_VIOLATIONS=( "src/dbwrapper.cpp:.*vsnprintf" "src/httprpc.cpp.*trim" "src/init.cpp:.*atoi" - "src/init.cpp:.*fprintf" "src/qt/rpcconsole.cpp:.*atoi" "src/rest.cpp:.*strtol" "src/test/dbwrapper_tests.cpp:.*snprintf" @@ -85,7 +84,7 @@ LOCALE_DEPENDENT_FUNCTIONS=( mbtowc # LC_CTYPE mktime normalize # boost::locale::normalize -# printf # LC_NUMERIC + printf # LC_NUMERIC putwc putwchar scanf # LC_NUMERIC @@ -189,8 +188,7 @@ GIT_GREP_OUTPUT=$(git grep -E "[^a-zA-Z0-9_\`'\"<>](${REGEXP_LOCALE_DEPENDENT_FU EXIT_CODE=0 for LOCALE_DEPENDENT_FUNCTION in "${LOCALE_DEPENDENT_FUNCTIONS[@]}"; do MATCHES=$(grep -E "[^a-zA-Z0-9_\`'\"<>]${LOCALE_DEPENDENT_FUNCTION}(_r|_s)?[^a-zA-Z0-9_\`'\"<>]" <<< "${GIT_GREP_OUTPUT}" | \ - grep -vE "\.(c|cpp|h):\s*(//|\*|/\*|\").*${LOCALE_DEPENDENT_FUNCTION}" | \ - grep -vE 'fprintf\(.*(stdout|stderr)') + grep -vE "\.(c|cpp|h):\s*(//|\*|/\*|\").*${LOCALE_DEPENDENT_FUNCTION}") if [[ ${REGEXP_IGNORE_EXTERNAL_DEPENDENCIES} != "" ]]; then MATCHES=$(grep -vE "${REGEXP_IGNORE_EXTERNAL_DEPENDENCIES}" <<< "${MATCHES}") fi diff --git a/test/lint/lint-spelling.ignore-words.txt b/test/lint/lint-spelling.ignore-words.txt index f0415443db..a25de2435b 100644 --- a/test/lint/lint-spelling.ignore-words.txt +++ b/test/lint/lint-spelling.ignore-words.txt @@ -5,3 +5,10 @@ mut objext unselect useable +wit +unparseable +copyable +cachable +errorstring +keyserver +homogenous |