diff options
58 files changed, 919 insertions, 482 deletions
diff --git a/.appveyor.yml b/.appveyor.yml index 57319d24f6..ff84c01a39 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -6,12 +6,11 @@ platform: x64 environment: APPVEYOR_SAVE_CACHE_ON_ERROR: true CLCACHE_SERVER: 1 - PACKAGES: boost-filesystem boost-signals2 boost-interprocess boost-test libevent openssl zeromq berkeleydb secp256k1 leveldb + PACKAGES: boost-filesystem boost-signals2 boost-test libevent openssl zeromq berkeleydb secp256k1 leveldb PYTHONIOENCODING: utf-8 cache: - C:\tools\vcpkg\installed - C:\Users\appveyor\clcache -- build_msvc\cache init: - cmd: set PATH=C:\Python36-x64;C:\Python36-x64\Scripts;%PATH% install: @@ -23,32 +22,39 @@ install: $env:ALL_PACKAGES += $packages[$i] + ":" + $env:PLATFORM + "-windows-static " } - cmd: git -C C:\Tools\vcpkg pull # This is a temporary fix, can be removed after appveyor update its image to include Microsoft/vcpkg#4046 +- cmd: C:\Tools\vcpkg\bootstrap-vcpkg.bat +- cmd: vcpkg remove --recurse --outdated - cmd: vcpkg install %ALL_PACKAGES% -- cmd: vcpkg upgrade --no-dry-run - cmd: del /s /q C:\Tools\vcpkg\installed\%PLATFORM%-windows-static\debug # Remove unused debug library before_build: -- cmd: if not exist build_msvc\cache\ (del build_msvc\cache & mkdir build_msvc\cache) -- cmd: if not exist build_msvc\%PLATFORM%\%CONFIGURATION%\ (mkdir build_msvc\%PLATFORM%\%CONFIGURATION%) -- cmd: if exist build_msvc\cache\*.iobj (move build_msvc\cache\* build_msvc\%PLATFORM%\%CONFIGURATION%\) -- cmd: clcache -M 2147483648 +- ps: clcache -M 536870912 - cmd: python build_msvc\msvc-autogen.py - ps: $files = (Get-ChildItem -Recurse | where {$_.extension -eq ".vcxproj"}).FullName -- ps: for ($i = 0; $i -lt $files.length; $i++) { - (Get-Content $files[$i]).Replace("</RuntimeLibrary>", "</RuntimeLibrary><DebugInformationFormat>None</DebugInformationFormat>").Replace("NDEBUG;", "") | Set-Content $files[$i] +- ps: for (${i} = 0; ${i} -lt ${files}.length; ${i}++) { + ${content} = (Get-Content ${files}[${i}]); + ${content} = ${content}.Replace("</RuntimeLibrary>", "</RuntimeLibrary><DebugInformationFormat>None</DebugInformationFormat>"); + ${content} = ${content}.Replace("<WholeProgramOptimization>true", "<WholeProgramOptimization>false"); + ${content} = ${content}.Replace("NDEBUG;", ""); + Set-Content ${files}[${i}] ${content}; } - ps: Start-Process clcache-server +- ps: fsutil behavior set disablelastaccess 0 # Enable Access time feature on Windows (for clcache) build_script: - cmd: msbuild /p:TrackFileAccess=false /p:CLToolExe=clcache.exe build_msvc\bitcoin.sln /m /v:q /nowarn:C4244;C4267;C4715 /nologo after_build: -- cmd: move build_msvc\%PLATFORM%\%CONFIGURATION%\*.iobj build_msvc\cache\ -- cmd: move build_msvc\%PLATFORM%\%CONFIGURATION%\*.ipdb build_msvc\cache\ -- cmd: del C:\Users\appveyor\clcache\stats.txt +- ps: fsutil behavior set disablelastaccess 0 # Disable Access time feature on Windows (better performance) +- ps: clcache -z before_test: - ps: ${conf_ini} = (Get-Content([IO.Path]::Combine(${env:APPVEYOR_BUILD_FOLDER}, "test", "config.ini.in"))) -- ps: ${conf_ini} = $conf_ini.Replace("@abs_top_srcdir@", ${env:APPVEYOR_BUILD_FOLDER}).Replace("@abs_top_builddir@", ${env:APPVEYOR_BUILD_FOLDER}).Replace("@EXEEXT@", ".exe") -- ps: ${conf_ini} = $conf_ini.Replace("@ENABLE_WALLET_TRUE@", "").Replace("@BUILD_BITCOIN_CLI_TRUE@", "").Replace("@BUILD_BITCOIND_TRUE@", "").Replace("@ENABLE_ZMQ_TRUE@", "") +- ps: ${conf_ini} = ${conf_ini}.Replace("@abs_top_srcdir@", ${env:APPVEYOR_BUILD_FOLDER}) +- ps: ${conf_ini} = ${conf_ini}.Replace("@abs_top_builddir@", ${env:APPVEYOR_BUILD_FOLDER}) +- ps: ${conf_ini} = ${conf_ini}.Replace("@EXEEXT@", ".exe") +- ps: ${conf_ini} = ${conf_ini}.Replace("@ENABLE_WALLET_TRUE@", "") +- ps: ${conf_ini} = ${conf_ini}.Replace("@BUILD_BITCOIN_CLI_TRUE@", "") +- ps: ${conf_ini} = ${conf_ini}.Replace("@BUILD_BITCOIND_TRUE@", "") +- ps: ${conf_ini} = ${conf_ini}.Replace("@ENABLE_ZMQ_TRUE@", "") - ps: ${utf8} = New-Object System.Text.UTF8Encoding ${false} -- ps: '[IO.File]::WriteAllLines([IO.Path]::Combine(${env:APPVEYOR_BUILD_FOLDER}, "test", "config.ini"), $conf_ini, ${utf8})' +- ps: '[IO.File]::WriteAllLines([IO.Path]::Combine(${env:APPVEYOR_BUILD_FOLDER}, "test", "config.ini"), ${conf_ini}, ${utf8})' - ps: move "build_msvc\${env:PLATFORM}\${env:CONFIGURATION}\*.exe" src test_script: - cmd: src\test_bitcoin.exe diff --git a/.travis.yml b/.travis.yml index 8819d38914..647d117733 100644 --- a/.travis.yml +++ b/.travis.yml @@ -121,7 +121,7 @@ jobs: - stage: test env: >- HOST=x86_64-unknown-linux-gnu - PACKAGES="python3" + PACKAGES="python3-zmq" DEP_OPTS="NO_WALLET=1" GOAL="install" BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports" diff --git a/.travis/lint_04_install.sh b/.travis/lint_04_install.sh index 7925189021..8c3a9124b9 100755 --- a/.travis/lint_04_install.sh +++ b/.travis/lint_04_install.sh @@ -7,4 +7,4 @@ export LC_ALL=C travis_retry pip install codespell==1.13.0 -travis_retry pip install flake8 +travis_retry pip install flake8==3.5.0 diff --git a/Makefile.am b/Makefile.am index eec9f72215..7eb4ea2f52 100644 --- a/Makefile.am +++ b/Makefile.am @@ -294,5 +294,5 @@ clean-docs: clean-local: clean-docs rm -rf coverage_percent.txt test_bitcoin.coverage/ total.coverage/ test/tmp/ cache/ $(OSX_APP) - rm -rf test/functional/__pycache__ test/functional/test_framework/__pycache__ test/cache + rm -rf test/functional/__pycache__ test/functional/test_framework/__pycache__ test/cache share/rpcauth/__pycache__ diff --git a/build_msvc/common.vcxproj b/build_msvc/common.vcxproj index 3a53f3bad3..5c87026efe 100644 --- a/build_msvc/common.vcxproj +++ b/build_msvc/common.vcxproj @@ -12,4 +12,9 @@ Outputs="$(MSBuildThisFileDirectory)..\src\config\bitcoin-config.h"> <Copy SourceFiles="$(MSBuildThisFileDirectory)bitcoin_config.h" DestinationFiles="$(MSBuildThisFileDirectory)..\src\config\bitcoin-config.h" /> </Target> + <ItemDefinitionGroup> + <ClCompile> + <AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions> + </ClCompile> + </ItemDefinitionGroup> </Project>
\ No newline at end of file diff --git a/build_msvc/test_bitcoin/test_bitcoin.vcxproj b/build_msvc/test_bitcoin/test_bitcoin.vcxproj index 444a2ed725..2316e473aa 100644 --- a/build_msvc/test_bitcoin/test_bitcoin.vcxproj +++ b/build_msvc/test_bitcoin/test_bitcoin.vcxproj @@ -24,7 +24,7 @@ <ClCompile Include="..\..\src\wallet\test\*_tests.cpp" /> <ClCompile Include="..\..\src\test\test_bitcoin.cpp" /> <ClCompile Include="..\..\src\test\test_bitcoin_main.cpp" /> - <ClCompile Include="..\..\src\wallet\test\wallet_test_fixture.cpp" /> + <ClCompile Include="..\..\src\wallet\test\*_fixture.cpp" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\libbitcoinconsensus\libbitcoinconsensus.vcxproj"> diff --git a/depends/packages/freetype.mk b/depends/packages/freetype.mk index 76b025c463..41e02e2030 100644 --- a/depends/packages/freetype.mk +++ b/depends/packages/freetype.mk @@ -5,7 +5,7 @@ $(package)_file_name=$(package)-$($(package)_version).tar.bz2 $(package)_sha256_hash=3a3bb2c4e15ffb433f2032f50a5b5a92558206822e22bfe8cbe339af4aa82f88 define $(package)_set_vars - $(package)_config_opts=--without-zlib --without-png --disable-static + $(package)_config_opts=--without-zlib --without-png --without-harfbuzz --without-bzip2 --disable-static $(package)_config_opts_linux=--with-pic endef diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk index d15f147cd7..dc1d17cd57 100644 --- a/depends/packages/qt.mk +++ b/depends/packages/qt.mk @@ -64,6 +64,7 @@ $(package)_config_opts += -prefix $(host_prefix) $(package)_config_opts += -qt-libpng $(package)_config_opts += -qt-libjpeg $(package)_config_opts += -qt-pcre +$(package)_config_opts += -qt-harfbuzz $(package)_config_opts += -system-zlib $(package)_config_opts += -static $(package)_config_opts += -silent diff --git a/doc/build-unix.md b/doc/build-unix.md index 9162098967..87dade42a3 100644 --- a/doc/build-unix.md +++ b/doc/build-unix.md @@ -74,7 +74,7 @@ Build requirements: Now, you can either build from self-compiled [depends](/depends/README.md) or install the required dependencies: - sudo apt-get libssl-dev libevent-dev libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev + sudo apt-get install libssl-dev libevent-dev libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev BerkeleyDB is required for the wallet. diff --git a/doc/build-windows.md b/doc/build-windows.md index 12adadacdc..8c4b79bebc 100644 --- a/doc/build-windows.md +++ b/doc/build-windows.md @@ -133,7 +133,7 @@ Installation ------------- After building using the Windows subsystem it can be useful to copy the compiled -executables to a directory on the windows drive in the same directory structure +executables to a directory on the Windows drive in the same directory structure as they appear in the release `.zip` archive. This can be done in the following way. This will install to `c:\workspace\bitcoin`, for example: diff --git a/doc/developer-notes.md b/doc/developer-notes.md index 1d103d481b..c86648c5b8 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -705,10 +705,10 @@ Current subtrees include: - Upstream at https://github.com/google/leveldb ; Maintained by Google, but open important PRs to Core to avoid delay. - **Note**: Follow the instructions in [Upgrading LevelDB](#upgrading-leveldb) when - merging upstream changes to the leveldb subtree. + merging upstream changes to the LevelDB subtree. - src/libsecp256k1 - - Upstream at https://github.com/bitcoin-core/secp256k1/ ; actively maintaned by Core contributors. + - Upstream at https://github.com/bitcoin-core/secp256k1/ ; actively maintained by Core contributors. - src/crypto/ctaes - Upstream at https://github.com/bitcoin-core/ctaes ; actively maintained by Core contributors. diff --git a/doc/init.md b/doc/init.md index 239b74e4e1..5778b09d05 100644 --- a/doc/init.md +++ b/doc/init.md @@ -22,7 +22,7 @@ Configuration At a bare minimum, bitcoind requires that the rpcpassword setting be set when running as a daemon. If the configuration file does not exist or this -setting is not set, bitcoind will shutdown promptly after startup. +setting is not set, bitcoind will shut down promptly after startup. This password does not have to be remembered or typed as it is mostly used as a fixed token that bitcoind and client programs read from the configuration diff --git a/doc/release-notes/release-notes-0.17.0.md b/doc/release-notes/release-notes-0.17.0.md index ce7a1f6ac1..418d7ba5f9 100644 --- a/doc/release-notes/release-notes-0.17.0.md +++ b/doc/release-notes/release-notes-0.17.0.md @@ -270,7 +270,7 @@ Low-level RPC changes - The new RPC `scantxoutset` can be used to scan the UTXO set for entries that match certain output descriptors. Refer to the [output descriptors - reference documentation](doc/descriptors.md) for more details. This call + reference documentation](/doc/descriptors.md) for more details. This call is similar to `listunspent` but does not use a wallet, meaning that the wallet can be disabled at compile or run time. This call is experimental, as such, is subject to changes or removal in future releases. diff --git a/doc/release-process.md b/doc/release-process.md index 3ba622ee6d..f2fe44c8bf 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -296,6 +296,8 @@ bitcoin.org (see below for bitcoin.org update instructions). - bitcoincore.org blog post + - bitcoincore.org RPC documentation update + - Update title of #bitcoin on Freenode IRC - Optionally twitter, reddit /r/Bitcoin, ... but this will usually sort out itself diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 8a537ed4f6..4506d5dd6a 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -51,6 +51,7 @@ BITCOIN_TESTS =\ test/cuckoocache_tests.cpp \ test/denialofservice_tests.cpp \ test/descriptor_tests.cpp \ + test/fs_tests.cpp \ test/getarg_tests.cpp \ test/hash_tests.cpp \ test/key_io_tests.cpp \ @@ -110,11 +111,14 @@ BITCOIN_TESTS += \ wallet/test/psbt_wallet_tests.cpp \ wallet/test/wallet_tests.cpp \ wallet/test/wallet_crypto_tests.cpp \ - wallet/test/coinselector_tests.cpp + wallet/test/coinselector_tests.cpp \ + wallet/test/init_tests.cpp BITCOIN_TEST_SUITE += \ wallet/test/wallet_test_fixture.cpp \ - wallet/test/wallet_test_fixture.h + wallet/test/wallet_test_fixture.h \ + wallet/test/init_test_fixture.cpp \ + wallet/test/init_test_fixture.h endif test_test_bitcoin_SOURCES = $(BITCOIN_TEST_SUITE) $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES) @@ -161,7 +165,7 @@ nodist_test_test_bitcoin_SOURCES = $(GENERATED_TEST_FILES) $(BITCOIN_TESTS): $(GENERATED_TEST_FILES) -CLEAN_BITCOIN_TEST = test/*.gcda test/*.gcno $(GENERATED_TEST_FILES) +CLEAN_BITCOIN_TEST = test/*.gcda test/*.gcno $(GENERATED_TEST_FILES) $(BITCOIN_TESTS:=.log) CLEANFILES += $(CLEAN_BITCOIN_TEST) diff --git a/src/addrman.h b/src/addrman.h index cf1949c28c..6d5780afa8 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -187,36 +187,37 @@ public: */ class CAddrMan { -private: +protected: //! critical section to protect the inner data structures mutable CCriticalSection cs; +private: //! last used nId - int nIdCount; + int nIdCount GUARDED_BY(cs); //! table with information about all nIds - std::map<int, CAddrInfo> mapInfo; + std::map<int, CAddrInfo> mapInfo GUARDED_BY(cs); //! find an nId based on its network address - std::map<CNetAddr, int> mapAddr; + std::map<CNetAddr, int> mapAddr GUARDED_BY(cs); //! randomly-ordered vector of all nIds - std::vector<int> vRandom; + std::vector<int> vRandom GUARDED_BY(cs); // number of "tried" entries - int nTried; + int nTried GUARDED_BY(cs); //! list of "tried" buckets - int vvTried[ADDRMAN_TRIED_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE]; + int vvTried[ADDRMAN_TRIED_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE] GUARDED_BY(cs); //! number of (unique) "new" entries - int nNew; + int nNew GUARDED_BY(cs); //! list of "new" buckets - int vvNew[ADDRMAN_NEW_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE]; + int vvNew[ADDRMAN_NEW_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE] GUARDED_BY(cs); //! last time Good was called (memory only) - int64_t nLastGood; + int64_t nLastGood GUARDED_BY(cs); //! Holds addrs inserted into tried table that collide with existing entries. Test-before-evict discipline used to resolve these collisions. std::set<int> m_tried_collisions; @@ -229,58 +230,58 @@ protected: FastRandomContext insecure_rand; //! Find an entry. - CAddrInfo* Find(const CNetAddr& addr, int *pnId = nullptr); + CAddrInfo* Find(const CNetAddr& addr, int *pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs); //! 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 = nullptr); + CAddrInfo* Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs); //! Swap two elements in vRandom. - void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2); + void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2) EXCLUSIVE_LOCKS_REQUIRED(cs); //! Move an entry from the "new" table(s) to the "tried" table - void MakeTried(CAddrInfo& info, int nId); + void MakeTried(CAddrInfo& info, int nId) EXCLUSIVE_LOCKS_REQUIRED(cs); //! Delete an entry. It must not be in tried, and have refcount 0. - void Delete(int nId); + void Delete(int nId) EXCLUSIVE_LOCKS_REQUIRED(cs); //! Clear a position in a "new" table. This is the only place where entries are actually deleted. - void ClearNew(int nUBucket, int nUBucketPos); + void ClearNew(int nUBucket, int nUBucketPos) EXCLUSIVE_LOCKS_REQUIRED(cs); //! Mark an entry "good", possibly moving it from "new" to "tried". - void Good_(const CService &addr, bool test_before_evict, int64_t time); + void Good_(const CService &addr, bool test_before_evict, int64_t time) EXCLUSIVE_LOCKS_REQUIRED(cs); //! Add an entry to the "new" table. - bool Add_(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty); + bool Add_(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty) EXCLUSIVE_LOCKS_REQUIRED(cs); //! Mark an entry as attempted to connect. - void Attempt_(const CService &addr, bool fCountFailure, int64_t nTime); + void Attempt_(const CService &addr, bool fCountFailure, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs); //! Select an address to connect to, if newOnly is set to true, only the new table is selected from. - CAddrInfo Select_(bool newOnly); + CAddrInfo Select_(bool newOnly) EXCLUSIVE_LOCKS_REQUIRED(cs); //! See if any to-be-evicted tried table entries have been tested and if so resolve the collisions. - void ResolveCollisions_(); + void ResolveCollisions_() EXCLUSIVE_LOCKS_REQUIRED(cs); //! Return a random to-be-evicted tried table address. - CAddrInfo SelectTriedCollision_(); + CAddrInfo SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs); //! Wraps GetRandInt to allow tests to override RandomInt and make it determinismistic. virtual int RandomInt(int nMax); #ifdef DEBUG_ADDRMAN //! Perform consistency check. Returns an error code or zero. - int Check_(); + int Check_() EXCLUSIVE_LOCKS_REQUIRED(cs); #endif //! Select several addresses at once. - void GetAddr_(std::vector<CAddress> &vAddr); + void GetAddr_(std::vector<CAddress> &vAddr) EXCLUSIVE_LOCKS_REQUIRED(cs); //! Mark an entry as currently-connected-to. - void Connected_(const CService &addr, int64_t nTime); + void Connected_(const CService &addr, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs); //! Update an entry's service bits. - void SetServices_(const CService &addr, ServiceFlags nServices); + void SetServices_(const CService &addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs); public: /** diff --git a/src/bench/checkblock.cpp b/src/bench/checkblock.cpp index 6f03581c4b..e325333c01 100644 --- a/src/bench/checkblock.cpp +++ b/src/bench/checkblock.cpp @@ -20,7 +20,7 @@ namespace block_bench { static void DeserializeBlockTest(benchmark::State& state) { CDataStream stream((const char*)block_bench::block413567, - (const char*)&block_bench::block413567[sizeof(block_bench::block413567)], + (const char*)block_bench::block413567 + sizeof(block_bench::block413567), SER_NETWORK, PROTOCOL_VERSION); char a = '\0'; stream.write(&a, 1); // Prevent compaction @@ -36,7 +36,7 @@ static void DeserializeBlockTest(benchmark::State& state) static void DeserializeAndCheckBlockTest(benchmark::State& state) { CDataStream stream((const char*)block_bench::block413567, - (const char*)&block_bench::block413567[sizeof(block_bench::block413567)], + (const char*)block_bench::block413567 + sizeof(block_bench::block413567), SER_NETWORK, PROTOCOL_VERSION); char a = '\0'; stream.write(&a, 1); // Prevent compaction diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 09507fd249..f466505114 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -17,6 +17,7 @@ #include <memory> #include <stdio.h> +#include <tuple> #include <event2/buffer.h> #include <event2/keyvalq_struct.h> @@ -511,6 +512,10 @@ static int CommandLineRPC(int argc, char *argv[]) int main(int argc, char* argv[]) { +#ifdef WIN32 + util::WinCmdLineArgs winArgs; + std::tie(argc, argv) = winArgs.get(); +#endif SetupEnvironment(); if (!SetupNetworking()) { fprintf(stderr, "Error: Initializing networking failed\n"); diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index bf04d95b50..18fcd9bc2a 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -185,6 +185,10 @@ static bool AppInit(int argc, char* argv[]) int main(int argc, char* argv[]) { +#ifdef WIN32 + util::WinCmdLineArgs winArgs; + std::tie(argc, argv) = winArgs.get(); +#endif SetupEnvironment(); // Connect bitcoind signal handlers diff --git a/src/fs.cpp b/src/fs.cpp index df79b5e3df..a146107c4c 100644 --- a/src/fs.cpp +++ b/src/fs.cpp @@ -113,4 +113,106 @@ std::string get_filesystem_error_message(const fs::filesystem_error& e) #endif } +#ifdef WIN32 +#ifdef __GLIBCXX__ + +// reference: https://github.com/gcc-mirror/gcc/blob/gcc-7_3_0-release/libstdc%2B%2B-v3/include/std/fstream#L270 + +static std::string openmodeToStr(std::ios_base::openmode mode) +{ + switch (mode & ~std::ios_base::ate) { + case std::ios_base::out: + case std::ios_base::out | std::ios_base::trunc: + return "w"; + case std::ios_base::out | std::ios_base::app: + case std::ios_base::app: + return "a"; + case std::ios_base::in: + return "r"; + case std::ios_base::in | std::ios_base::out: + return "r+"; + case std::ios_base::in | std::ios_base::out | std::ios_base::trunc: + return "w+"; + case std::ios_base::in | std::ios_base::out | std::ios_base::app: + case std::ios_base::in | std::ios_base::app: + return "a+"; + case std::ios_base::out | std::ios_base::binary: + case std::ios_base::out | std::ios_base::trunc | std::ios_base::binary: + return "wb"; + case std::ios_base::out | std::ios_base::app | std::ios_base::binary: + case std::ios_base::app | std::ios_base::binary: + return "ab"; + case std::ios_base::in | std::ios_base::binary: + return "rb"; + case std::ios_base::in | std::ios_base::out | std::ios_base::binary: + return "r+b"; + case std::ios_base::in | std::ios_base::out | std::ios_base::trunc | std::ios_base::binary: + return "w+b"; + case std::ios_base::in | std::ios_base::out | std::ios_base::app | std::ios_base::binary: + case std::ios_base::in | std::ios_base::app | std::ios_base::binary: + return "a+b"; + default: + return std::string(); + } +} + +void ifstream::open(const fs::path& p, std::ios_base::openmode mode) +{ + close(); + m_file = fsbridge::fopen(p, openmodeToStr(mode).c_str()); + if (m_file == nullptr) { + return; + } + m_filebuf = __gnu_cxx::stdio_filebuf<char>(m_file, mode); + rdbuf(&m_filebuf); + if (mode & std::ios_base::ate) { + seekg(0, std::ios_base::end); + } +} + +void ifstream::close() +{ + if (m_file != nullptr) { + m_filebuf.close(); + fclose(m_file); + } + m_file = nullptr; +} + +void ofstream::open(const fs::path& p, std::ios_base::openmode mode) +{ + close(); + m_file = fsbridge::fopen(p, openmodeToStr(mode).c_str()); + if (m_file == nullptr) { + return; + } + m_filebuf = __gnu_cxx::stdio_filebuf<char>(m_file, mode); + rdbuf(&m_filebuf); + if (mode & std::ios_base::ate) { + seekp(0, std::ios_base::end); + } +} + +void ofstream::close() +{ + if (m_file != nullptr) { + m_filebuf.close(); + fclose(m_file); + } + m_file = nullptr; +} +#else // __GLIBCXX__ + +static_assert(sizeof(*fs::path().BOOST_FILESYSTEM_C_STR) == sizeof(wchar_t), + "Warning: This build is using boost::filesystem ofstream and ifstream " + "implementations which will fail to open paths containing multibyte " + "characters. You should delete this static_assert to ignore this warning, " + "or switch to a different C++ standard library like the Microsoft C++ " + "Standard Library (where boost uses non-standard extensions to construct " + "stream objects with wide filenames), or the GNU libstdc++ library (where " + "a more complicated workaround has been implemented above)."); + +#endif // __GLIBCXX__ +#endif // WIN32 + } // fsbridge @@ -7,6 +7,9 @@ #include <stdio.h> #include <string> +#if defined WIN32 && defined __GLIBCXX__ +#include <ext/stdio_filebuf.h> +#endif #include <boost/filesystem.hpp> #include <boost/filesystem/fstream.hpp> @@ -39,6 +42,54 @@ namespace fsbridge { }; std::string get_filesystem_error_message(const fs::filesystem_error& e); + + // GNU libstdc++ specific workaround for opening UTF-8 paths on Windows. + // + // On Windows, it is only possible to reliably access multibyte file paths through + // `wchar_t` APIs, not `char` APIs. But because the C++ standard doesn't + // require ifstream/ofstream `wchar_t` constructors, and the GNU library doesn't + // provide them (in contrast to the Microsoft C++ library, see + // https://stackoverflow.com/questions/821873/how-to-open-an-stdfstream-ofstream-or-ifstream-with-a-unicode-filename/822032#822032), + // Boost is forced to fall back to `char` constructors which may not work properly. + // + // Work around this issue by creating stream objects with `_wfopen` in + // combination with `__gnu_cxx::stdio_filebuf`. This workaround can be removed + // with an upgrade to C++17, where streams can be constructed directly from + // `std::filesystem::path` objects. + +#if defined WIN32 && defined __GLIBCXX__ + class ifstream : public std::istream + { + public: + ifstream() = default; + explicit ifstream(const fs::path& p, std::ios_base::openmode mode = std::ios_base::in) { open(p, mode); } + ~ifstream() { close(); } + void open(const fs::path& p, std::ios_base::openmode mode = std::ios_base::in); + bool is_open() { return m_filebuf.is_open(); } + void close(); + + private: + __gnu_cxx::stdio_filebuf<char> m_filebuf; + FILE* m_file = nullptr; + }; + class ofstream : public std::ostream + { + public: + ofstream() = default; + explicit ofstream(const fs::path& p, std::ios_base::openmode mode = std::ios_base::out) { open(p, mode); } + ~ofstream() { close(); } + void open(const fs::path& p, std::ios_base::openmode mode = std::ios_base::out); + bool is_open() { return m_filebuf.is_open(); } + void close(); + + private: + __gnu_cxx::stdio_filebuf<char> m_filebuf; + FILE* m_file = nullptr; + }; +#else // !(WIN32 && __GLIBCXX__) + typedef fs::ifstream ifstream; + typedef fs::ofstream ofstream; +#endif // WIN32 && __GLIBCXX__ }; #endif // BITCOIN_FS_H diff --git a/src/net.cpp b/src/net.cpp index f83f39a67d..c8d3efceed 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1153,310 +1153,322 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) { } } -void CConnman::ThreadSocketHandler() +void CConnman::DisconnectNodes() { - unsigned int nPrevNodeCount = 0; - while (!interruptNet) { - // - // Disconnect nodes - // - { - LOCK(cs_vNodes); + LOCK(cs_vNodes); - if (!fNetworkActive) { - // Disconnect any connected nodes - for (CNode* pnode : vNodes) { - if (!pnode->fDisconnect) { - LogPrint(BCLog::NET, "Network not active, dropping peer=%d\n", pnode->GetId()); - pnode->fDisconnect = true; - } + if (!fNetworkActive) { + // Disconnect any connected nodes + for (CNode* pnode : vNodes) { + if (!pnode->fDisconnect) { + LogPrint(BCLog::NET, "Network not active, dropping peer=%d\n", pnode->GetId()); + pnode->fDisconnect = true; } } + } - // Disconnect unused nodes - std::vector<CNode*> vNodesCopy = vNodes; - for (CNode* pnode : vNodesCopy) + // Disconnect unused nodes + std::vector<CNode*> vNodesCopy = vNodes; + for (CNode* pnode : vNodesCopy) + { + if (pnode->fDisconnect) { - if (pnode->fDisconnect) - { - // remove from vNodes - vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end()); + // remove from vNodes + vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end()); - // release outbound grant (if any) - pnode->grantOutbound.Release(); + // release outbound grant (if any) + pnode->grantOutbound.Release(); - // close socket and cleanup - pnode->CloseSocketDisconnect(); + // close socket and cleanup + pnode->CloseSocketDisconnect(); - // hold in disconnected pool until all refs are released - pnode->Release(); - vNodesDisconnected.push_back(pnode); - } + // hold in disconnected pool until all refs are released + pnode->Release(); + vNodesDisconnected.push_back(pnode); } } + } + { + // Delete disconnected nodes + std::list<CNode*> vNodesDisconnectedCopy = vNodesDisconnected; + for (CNode* pnode : vNodesDisconnectedCopy) { - // Delete disconnected nodes - std::list<CNode*> vNodesDisconnectedCopy = vNodesDisconnected; - for (CNode* pnode : vNodesDisconnectedCopy) - { - // wait until threads are done using it - if (pnode->GetRefCount() <= 0) { - bool fDelete = false; - { - TRY_LOCK(pnode->cs_inventory, lockInv); - if (lockInv) { - TRY_LOCK(pnode->cs_vSend, lockSend); - if (lockSend) { - fDelete = true; - } + // wait until threads are done using it + if (pnode->GetRefCount() <= 0) { + bool fDelete = false; + { + TRY_LOCK(pnode->cs_inventory, lockInv); + if (lockInv) { + TRY_LOCK(pnode->cs_vSend, lockSend); + if (lockSend) { + fDelete = true; } } - if (fDelete) { - vNodesDisconnected.remove(pnode); - DeleteNode(pnode); - } + } + if (fDelete) { + vNodesDisconnected.remove(pnode); + DeleteNode(pnode); } } } - size_t vNodesSize; + } +} + +void CConnman::NotifyNumConnectionsChanged() +{ + size_t vNodesSize; + { + LOCK(cs_vNodes); + vNodesSize = vNodes.size(); + } + if(vNodesSize != nPrevNodeCount) { + nPrevNodeCount = vNodesSize; + if(clientInterface) + clientInterface->NotifyNumConnectionsChanged(vNodesSize); + } +} + +void CConnman::InactivityCheck(CNode *pnode) +{ + int64_t nTime = GetSystemTimeInSeconds(); + if (nTime - pnode->nTimeConnected > 60) + { + if (pnode->nLastRecv == 0 || pnode->nLastSend == 0) + { + LogPrint(BCLog::NET, "socket no message in first 60 seconds, %d %d from %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0, pnode->GetId()); + pnode->fDisconnect = true; + } + else if (nTime - pnode->nLastSend > TIMEOUT_INTERVAL) { - LOCK(cs_vNodes); - vNodesSize = vNodes.size(); + LogPrintf("socket sending timeout: %is\n", nTime - pnode->nLastSend); + pnode->fDisconnect = true; + } + else if (nTime - pnode->nLastRecv > (pnode->nVersion > BIP0031_VERSION ? TIMEOUT_INTERVAL : 90*60)) + { + LogPrintf("socket receive timeout: %is\n", nTime - pnode->nLastRecv); + pnode->fDisconnect = true; + } + else if (pnode->nPingNonceSent && pnode->nPingUsecStart + TIMEOUT_INTERVAL * 1000000 < GetTimeMicros()) + { + LogPrintf("ping timeout: %fs\n", 0.000001 * (GetTimeMicros() - pnode->nPingUsecStart)); + pnode->fDisconnect = true; } - if(vNodesSize != nPrevNodeCount) { - nPrevNodeCount = vNodesSize; - if(clientInterface) - clientInterface->NotifyNumConnectionsChanged(vNodesSize); + else if (!pnode->fSuccessfullyConnected) + { + LogPrint(BCLog::NET, "version handshake timeout from %d\n", pnode->GetId()); + pnode->fDisconnect = true; } + } +} - // - // Find which sockets have data to receive - // - struct timeval timeout; - timeout.tv_sec = 0; - timeout.tv_usec = 50000; // frequency to poll pnode->vSend - - fd_set fdsetRecv; - fd_set fdsetSend; - fd_set fdsetError; - FD_ZERO(&fdsetRecv); - FD_ZERO(&fdsetSend); - FD_ZERO(&fdsetError); - SOCKET hSocketMax = 0; - bool have_fds = false; +void CConnman::SocketHandler() +{ + // + // Find which sockets have data to receive + // + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 50000; // frequency to poll pnode->vSend - for (const ListenSocket& hListenSocket : vhListenSocket) { - FD_SET(hListenSocket.socket, &fdsetRecv); - hSocketMax = std::max(hSocketMax, hListenSocket.socket); - have_fds = true; - } + fd_set fdsetRecv; + fd_set fdsetSend; + fd_set fdsetError; + FD_ZERO(&fdsetRecv); + FD_ZERO(&fdsetSend); + FD_ZERO(&fdsetError); + SOCKET hSocketMax = 0; + bool have_fds = false; + for (const ListenSocket& hListenSocket : vhListenSocket) { + FD_SET(hListenSocket.socket, &fdsetRecv); + hSocketMax = std::max(hSocketMax, hListenSocket.socket); + have_fds = true; + } + + { + LOCK(cs_vNodes); + for (CNode* pnode : vNodes) { - LOCK(cs_vNodes); - for (CNode* pnode : vNodes) + // Implement the following logic: + // * If there is data to send, select() for sending data. As this only + // happens when optimistic write failed, we choose to first drain the + // write buffer in this case before receiving more. This avoids + // needlessly queueing received data, if the remote peer is not themselves + // receiving data. This means properly utilizing TCP flow control signalling. + // * Otherwise, if there is space left in the receive buffer, select() for + // receiving data. + // * Hand off all complete messages to the processor, to be handled without + // blocking here. + + bool select_recv = !pnode->fPauseRecv; + bool select_send; { - // Implement the following logic: - // * If there is data to send, select() for sending data. As this only - // happens when optimistic write failed, we choose to first drain the - // write buffer in this case before receiving more. This avoids - // needlessly queueing received data, if the remote peer is not themselves - // receiving data. This means properly utilizing TCP flow control signalling. - // * Otherwise, if there is space left in the receive buffer, select() for - // receiving data. - // * Hand off all complete messages to the processor, to be handled without - // blocking here. - - bool select_recv = !pnode->fPauseRecv; - bool select_send; - { - LOCK(pnode->cs_vSend); - select_send = !pnode->vSendMsg.empty(); - } + LOCK(pnode->cs_vSend); + select_send = !pnode->vSendMsg.empty(); + } - LOCK(pnode->cs_hSocket); - if (pnode->hSocket == INVALID_SOCKET) - continue; + LOCK(pnode->cs_hSocket); + if (pnode->hSocket == INVALID_SOCKET) + continue; - FD_SET(pnode->hSocket, &fdsetError); - hSocketMax = std::max(hSocketMax, pnode->hSocket); - have_fds = true; + FD_SET(pnode->hSocket, &fdsetError); + hSocketMax = std::max(hSocketMax, pnode->hSocket); + have_fds = true; - if (select_send) { - FD_SET(pnode->hSocket, &fdsetSend); - continue; - } - if (select_recv) { - FD_SET(pnode->hSocket, &fdsetRecv); - } + if (select_send) { + FD_SET(pnode->hSocket, &fdsetSend); + continue; + } + if (select_recv) { + FD_SET(pnode->hSocket, &fdsetRecv); } } + } - int nSelect = select(have_fds ? hSocketMax + 1 : 0, - &fdsetRecv, &fdsetSend, &fdsetError, &timeout); - if (interruptNet) - return; + int nSelect = select(have_fds ? hSocketMax + 1 : 0, + &fdsetRecv, &fdsetSend, &fdsetError, &timeout); + if (interruptNet) + return; - if (nSelect == SOCKET_ERROR) + if (nSelect == SOCKET_ERROR) + { + if (have_fds) { - if (have_fds) - { - int nErr = WSAGetLastError(); - LogPrintf("socket select error %s\n", NetworkErrorString(nErr)); - for (unsigned int i = 0; i <= hSocketMax; i++) - FD_SET(i, &fdsetRecv); - } - FD_ZERO(&fdsetSend); - FD_ZERO(&fdsetError); - if (!interruptNet.sleep_for(std::chrono::milliseconds(timeout.tv_usec/1000))) - return; + int nErr = WSAGetLastError(); + LogPrintf("socket select error %s\n", NetworkErrorString(nErr)); + for (unsigned int i = 0; i <= hSocketMax; i++) + FD_SET(i, &fdsetRecv); } + FD_ZERO(&fdsetSend); + FD_ZERO(&fdsetError); + if (!interruptNet.sleep_for(std::chrono::milliseconds(timeout.tv_usec/1000))) + return; + } - // - // Accept new connections - // - for (const ListenSocket& hListenSocket : vhListenSocket) + // + // Accept new connections + // + for (const ListenSocket& hListenSocket : vhListenSocket) + { + if (hListenSocket.socket != INVALID_SOCKET && FD_ISSET(hListenSocket.socket, &fdsetRecv)) { - if (hListenSocket.socket != INVALID_SOCKET && FD_ISSET(hListenSocket.socket, &fdsetRecv)) - { - AcceptConnection(hListenSocket); - } + AcceptConnection(hListenSocket); } + } + + // + // Service each socket + // + std::vector<CNode*> vNodesCopy; + { + LOCK(cs_vNodes); + vNodesCopy = vNodes; + for (CNode* pnode : vNodesCopy) + pnode->AddRef(); + } + for (CNode* pnode : vNodesCopy) + { + if (interruptNet) + return; // - // Service each socket + // Receive // - std::vector<CNode*> vNodesCopy; + bool recvSet = false; + bool sendSet = false; + bool errorSet = false; { - LOCK(cs_vNodes); - vNodesCopy = vNodes; - for (CNode* pnode : vNodesCopy) - pnode->AddRef(); + LOCK(pnode->cs_hSocket); + if (pnode->hSocket == INVALID_SOCKET) + continue; + recvSet = FD_ISSET(pnode->hSocket, &fdsetRecv); + sendSet = FD_ISSET(pnode->hSocket, &fdsetSend); + errorSet = FD_ISSET(pnode->hSocket, &fdsetError); } - for (CNode* pnode : vNodesCopy) + if (recvSet || errorSet) { - if (interruptNet) - return; - - // - // Receive - // - bool recvSet = false; - bool sendSet = false; - bool errorSet = false; + // typical socket buffer is 8K-64K + char pchBuf[0x10000]; + int nBytes = 0; { LOCK(pnode->cs_hSocket); if (pnode->hSocket == INVALID_SOCKET) continue; - recvSet = FD_ISSET(pnode->hSocket, &fdsetRecv); - sendSet = FD_ISSET(pnode->hSocket, &fdsetSend); - errorSet = FD_ISSET(pnode->hSocket, &fdsetError); + nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT); } - if (recvSet || errorSet) + if (nBytes > 0) { - // typical socket buffer is 8K-64K - char pchBuf[0x10000]; - int nBytes = 0; - { - LOCK(pnode->cs_hSocket); - if (pnode->hSocket == INVALID_SOCKET) - continue; - nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT); - } - if (nBytes > 0) - { - bool notify = false; - if (!pnode->ReceiveMsgBytes(pchBuf, nBytes, notify)) - pnode->CloseSocketDisconnect(); - RecordBytesRecv(nBytes); - if (notify) { - size_t nSizeAdded = 0; - auto it(pnode->vRecvMsg.begin()); - for (; it != pnode->vRecvMsg.end(); ++it) { - if (!it->complete()) - break; - nSizeAdded += it->vRecv.size() + CMessageHeader::HEADER_SIZE; - } - { - LOCK(pnode->cs_vProcessMsg); - pnode->vProcessMsg.splice(pnode->vProcessMsg.end(), pnode->vRecvMsg, pnode->vRecvMsg.begin(), it); - pnode->nProcessQueueSize += nSizeAdded; - pnode->fPauseRecv = pnode->nProcessQueueSize > nReceiveFloodSize; - } - WakeMessageHandler(); - } - } - else if (nBytes == 0) - { - // socket closed gracefully - if (!pnode->fDisconnect) { - LogPrint(BCLog::NET, "socket closed\n"); - } + bool notify = false; + if (!pnode->ReceiveMsgBytes(pchBuf, nBytes, notify)) pnode->CloseSocketDisconnect(); - } - else if (nBytes < 0) - { - // error - int nErr = WSAGetLastError(); - if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) + RecordBytesRecv(nBytes); + if (notify) { + size_t nSizeAdded = 0; + auto it(pnode->vRecvMsg.begin()); + for (; it != pnode->vRecvMsg.end(); ++it) { + if (!it->complete()) + break; + nSizeAdded += it->vRecv.size() + CMessageHeader::HEADER_SIZE; + } { - if (!pnode->fDisconnect) - LogPrintf("socket recv error %s\n", NetworkErrorString(nErr)); - pnode->CloseSocketDisconnect(); + LOCK(pnode->cs_vProcessMsg); + pnode->vProcessMsg.splice(pnode->vProcessMsg.end(), pnode->vRecvMsg, pnode->vRecvMsg.begin(), it); + pnode->nProcessQueueSize += nSizeAdded; + pnode->fPauseRecv = pnode->nProcessQueueSize > nReceiveFloodSize; } + WakeMessageHandler(); } } - - // - // Send - // - if (sendSet) + else if (nBytes == 0) { - LOCK(pnode->cs_vSend); - size_t nBytes = SocketSendData(pnode); - if (nBytes) { - RecordBytesSent(nBytes); + // socket closed gracefully + if (!pnode->fDisconnect) { + LogPrint(BCLog::NET, "socket closed\n"); } + pnode->CloseSocketDisconnect(); } - - // - // Inactivity checking - // - int64_t nTime = GetSystemTimeInSeconds(); - if (nTime - pnode->nTimeConnected > 60) + else if (nBytes < 0) { - if (pnode->nLastRecv == 0 || pnode->nLastSend == 0) - { - LogPrint(BCLog::NET, "socket no message in first 60 seconds, %d %d from %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0, pnode->GetId()); - pnode->fDisconnect = true; - } - else if (nTime - pnode->nLastSend > TIMEOUT_INTERVAL) - { - LogPrintf("socket sending timeout: %is\n", nTime - pnode->nLastSend); - pnode->fDisconnect = true; - } - else if (nTime - pnode->nLastRecv > (pnode->nVersion > BIP0031_VERSION ? TIMEOUT_INTERVAL : 90*60)) - { - LogPrintf("socket receive timeout: %is\n", nTime - pnode->nLastRecv); - pnode->fDisconnect = true; - } - else if (pnode->nPingNonceSent && pnode->nPingUsecStart + TIMEOUT_INTERVAL * 1000000 < GetTimeMicros()) - { - LogPrintf("ping timeout: %fs\n", 0.000001 * (GetTimeMicros() - pnode->nPingUsecStart)); - pnode->fDisconnect = true; - } - else if (!pnode->fSuccessfullyConnected) + // error + int nErr = WSAGetLastError(); + if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) { - LogPrint(BCLog::NET, "version handshake timeout from %d\n", pnode->GetId()); - pnode->fDisconnect = true; + if (!pnode->fDisconnect) + LogPrintf("socket recv error %s\n", NetworkErrorString(nErr)); + pnode->CloseSocketDisconnect(); } } } + + // + // Send + // + if (sendSet) { - LOCK(cs_vNodes); - for (CNode* pnode : vNodesCopy) - pnode->Release(); + LOCK(pnode->cs_vSend); + size_t nBytes = SocketSendData(pnode); + if (nBytes) { + RecordBytesSent(nBytes); + } } + + InactivityCheck(pnode); + } + { + LOCK(cs_vNodes); + for (CNode* pnode : vNodesCopy) + pnode->Release(); + } +} + +void CConnman::ThreadSocketHandler() +{ + while (!interruptNet) + { + DisconnectNodes(); + NotifyNumConnectionsChanged(); + SocketHandler(); } } @@ -2217,6 +2229,7 @@ CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In) : nSeed0(nSeed0In), nSe setBannedIsDirty = false; fAddressesInitialized = false; nLastNodeId = 0; + nPrevNodeCount = 0; nSendBufferMaxSize = 0; nReceiveFloodSize = 0; flagInterruptMsgProc = false; @@ -338,6 +338,10 @@ private: void ThreadOpenConnections(std::vector<std::string> connect); void ThreadMessageHandler(); void AcceptConnection(const ListenSocket& hListenSocket); + void DisconnectNodes(); + void NotifyNumConnectionsChanged(); + void InactivityCheck(CNode *pnode); + void SocketHandler(); void ThreadSocketHandler(); void ThreadDNSAddressSeed(); @@ -408,6 +412,7 @@ private: std::list<CNode*> vNodesDisconnected; mutable CCriticalSection cs_vNodes; std::atomic<NodeId> nLastNodeId; + unsigned int nPrevNodeCount; /** Services this instance offers */ ServiceFlags nLocalServices; diff --git a/src/qt/README.md b/src/qt/README.md index 3ec538b4f4..0eb18f7cd5 100644 --- a/src/qt/README.md +++ b/src/qt/README.md @@ -64,8 +64,8 @@ Represents the view to a single wallet. * `callback.h` * `guiconstants.h`: UI colors, app name, etc * `guiutil.h`: several helper functions -* `macdockiconhandler.(h/cpp)` -* `macdockiconhandler.(h/cpp)`: display notifications in macOS +* `macdockiconhandler.(h/mm)`: macOS dock icon handler +* `macnotificationhandler.(h/mm)`: display notifications in macOS ## Contribute diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 1e950e2686..a014ad4b28 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -51,7 +51,6 @@ #include <QThread> #include <QTimer> #include <QTranslator> -#include <QSslConfiguration> #if defined(QT_STATICPLUGIN) #include <QtPlugin> @@ -552,6 +551,10 @@ static void SetupUIArgs() #ifndef BITCOIN_QT_TEST int main(int argc, char *argv[]) { +#ifdef WIN32 + util::WinCmdLineArgs winArgs; + std::tie(argc, argv) = winArgs.get(); +#endif SetupEnvironment(); std::unique_ptr<interfaces::Node> node = interfaces::MakeNode(); @@ -573,13 +576,6 @@ int main(int argc, char *argv[]) #ifdef Q_OS_MAC QApplication::setAttribute(Qt::AA_DontShowIconsInMenus); #endif -#if QT_VERSION >= 0x050500 - // Because of the POODLE attack it is recommended to disable SSLv3 (https://disablessl3.com/), - // so set SSL protocols to TLS1.0+. - QSslConfiguration sslconf = QSslConfiguration::defaultConfiguration(); - sslconf.setProtocol(QSsl::TlsV1_0OrLater); - QSslConfiguration::setDefaultConfiguration(sslconf); -#endif // Register meta types used for QMetaObject::invokeMethod qRegisterMetaType< bool* >(); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 51aff08c42..311841017f 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -620,14 +620,16 @@ void BitcoinGUI::createTrayIconMenu() trayIconMenu->addAction(toggleHideAction); trayIconMenu->addSeparator(); #endif - trayIconMenu->addAction(sendCoinsMenuAction); - trayIconMenu->addAction(receiveCoinsMenuAction); - trayIconMenu->addSeparator(); - trayIconMenu->addAction(signMessageAction); - trayIconMenu->addAction(verifyMessageAction); - trayIconMenu->addSeparator(); + if (enableWallet) { + trayIconMenu->addAction(sendCoinsMenuAction); + trayIconMenu->addAction(receiveCoinsMenuAction); + trayIconMenu->addSeparator(); + trayIconMenu->addAction(signMessageAction); + trayIconMenu->addAction(verifyMessageAction); + trayIconMenu->addSeparator(); + trayIconMenu->addAction(openRPCConsoleAction); + } trayIconMenu->addAction(optionsAction); - trayIconMenu->addAction(openRPCConsoleAction); #ifndef Q_OS_MAC // This is built-in on Mac trayIconMenu->addSeparator(); trayIconMenu->addAction(quitAction); diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index b894fc8166..5f6af61a70 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -367,7 +367,7 @@ bool openBitcoinConf() fs::path pathConfig = GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)); /* Create the file */ - fs::ofstream configFile(pathConfig, std::ios_base::app); + fsbridge::ofstream configFile(pathConfig, std::ios_base::app); if (!configFile.good()) return false; @@ -611,7 +611,7 @@ fs::path static GetAutostartFilePath() bool GetStartOnSystemStartup() { - fs::ifstream optionFile(GetAutostartFilePath()); + fsbridge::ifstream optionFile(GetAutostartFilePath()); if (!optionFile.good()) return false; // Scan through file for "Hidden=true": @@ -642,7 +642,7 @@ bool SetStartOnSystemStartup(bool fAutoStart) fs::create_directories(GetAutostartDir()); - fs::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out|std::ios_base::trunc); + fsbridge::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out | std::ios_base::trunc); if (!optionFile.good()) return false; std::string chain = gArgs.GetChainName(); diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 7ec4feabfb..c004c783f2 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -539,6 +539,7 @@ bool RPCConsole::eventFilter(QObject* obj, QEvent *event) // forward these events to lineEdit if(obj == autoCompleter->popup()) { QApplication::postEvent(ui->lineEdit, new QKeyEvent(*keyevt)); + autoCompleter->popup()->hide(); return true; } break; diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 6d08a3b0fb..68410c8bd6 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -106,7 +106,11 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa } else { amountWidget->setFixedWidth(100); } - amountWidget->setValidator(new QDoubleValidator(0, 1e20, 8, this)); + QDoubleValidator *amountValidator = new QDoubleValidator(0, 1e20, 8, this); + QLocale amountLocale(QLocale::C); + amountLocale.setNumberOptions(QLocale::RejectGroupSeparator); + amountValidator->setLocale(amountLocale); + amountWidget->setValidator(amountValidator); hlayout->addWidget(amountWidget); // Delay before filtering transactions in ms diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 1eca0277b0..ff71b19250 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -182,7 +182,7 @@ static UniValue getbestblockhash(const JSONRPCRequest& request) "getbestblockhash\n" "\nReturns the hash of the best (tip) block in the longest blockchain.\n" "\nResult:\n" - "\"hex\" (string) the block hash hex encoded\n" + "\"hex\" (string) the block hash, hex-encoded\n" "\nExamples:\n" + HelpExampleCli("getbestblockhash", "") + HelpExampleRpc("getbestblockhash", "") @@ -509,17 +509,17 @@ static UniValue getmempoolancestors(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( - "getmempoolancestors txid (verbose)\n" + "getmempoolancestors txid ( verbose )\n" "\nIf txid is in the mempool, returns all in-mempool ancestors.\n" "\nArguments:\n" "1. \"txid\" (string, required) The transaction id (must be in mempool)\n" "2. verbose (boolean, optional, default=false) True for a json object, false for array of transaction ids\n" - "\nResult (for verbose=false):\n" + "\nResult (for verbose = false):\n" "[ (json array of strings)\n" " \"transactionid\" (string) The transaction id of an in-mempool ancestor transaction\n" " ,...\n" "]\n" - "\nResult (for verbose=true):\n" + "\nResult (for verbose = true):\n" "{ (json object)\n" " \"transactionid\" : { (json object)\n" + EntryDescriptionString() @@ -573,17 +573,17 @@ static UniValue getmempooldescendants(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( - "getmempooldescendants txid (verbose)\n" + "getmempooldescendants txid ( verbose )\n" "\nIf txid is in the mempool, returns all in-mempool descendants.\n" "\nArguments:\n" "1. \"txid\" (string, required) The transaction id (must be in mempool)\n" "2. verbose (boolean, optional, default=false) True for a json object, false for array of transaction ids\n" - "\nResult (for verbose=false):\n" + "\nResult (for verbose = false):\n" "[ (json array of strings)\n" " \"transactionid\" (string) The transaction id of an in-mempool descendant transaction\n" " ,...\n" "]\n" - "\nResult (for verbose=true):\n" + "\nResult (for verbose = true):\n" "{ (json object)\n" " \"transactionid\" : { (json object)\n" + EntryDescriptionString() @@ -700,7 +700,7 @@ static UniValue getblockheader(const JSONRPCRequest& request) "If verbose is true, returns an Object with information about blockheader <hash>.\n" "\nArguments:\n" "1. \"hash\" (string, required) The block hash\n" - "2. verbose (boolean, optional, default=true) true for a json object, false for the hex encoded data\n" + "2. verbose (boolean, optional, default=true) true for a json object, false for the hex-encoded data\n" "\nResult (for verbose = true):\n" "{\n" " \"hash\" : \"hash\", (string) the block hash (same as provided)\n" @@ -779,7 +779,7 @@ static UniValue getblock(const JSONRPCRequest& request) "If verbosity is 2, returns an Object with information about block <hash> and information about each transaction. \n" "\nArguments:\n" "1. \"blockhash\" (string, required) The block hash\n" - "2. verbosity (numeric, optional, default=1) 0 for hex encoded data, 1 for a json object, and 2 for json object with transaction data\n" + "2. verbosity (numeric, optional, default=1) 0 for hex-encoded data, 1 for a json object, and 2 for json object with transaction data\n" "\nResult (for verbosity = 0):\n" "\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n" "\nResult (for verbosity = 1):\n" @@ -1046,7 +1046,7 @@ UniValue gettxout(const JSONRPCRequest& request) + HelpExampleCli("listunspent", "") + "\nView the details\n" + HelpExampleCli("gettxout", "\"txid\" 1") + - "\nAs a json rpc call\n" + "\nAs a JSON-RPC call\n" + HelpExampleRpc("gettxout", "\"txid\", 1") ); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 1b2fc2c156..c565b9b4f9 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -362,8 +362,8 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) "}\n" "\nExamples:\n" - + HelpExampleCli("getblocktemplate", "") - + HelpExampleRpc("getblocktemplate", "") + + HelpExampleCli("getblocktemplate", "{\"rules\": [\"segwit\"]}") + + HelpExampleRpc("getblocktemplate", "{\"rules\": [\"segwit\"]}") ); LOCK(cs_main); @@ -813,7 +813,7 @@ static UniValue estimatesmartfee(const JSONRPCRequest& request) " higher feerate and is more likely to be sufficient for the desired\n" " target, but is not as responsive to short term drops in the\n" " prevailing fee market. Must be one of:\n" - " \"UNSET\" (defaults to CONSERVATIVE)\n" + " \"UNSET\"\n" " \"ECONOMICAL\"\n" " \"CONSERVATIVE\"\n" "\nResult:\n" diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 0f3b601414..6a66998d37 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -44,7 +44,7 @@ static UniValue validateaddress(const JSONRPCRequest& request) "{\n" " \"isvalid\" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.\n" " \"address\" : \"address\", (string) The bitcoin address validated\n" - " \"scriptPubKey\" : \"hex\", (string) The hex encoded scriptPubKey generated by the address\n" + " \"scriptPubKey\" : \"hex\", (string) The hex-encoded scriptPubKey generated by the address\n" " \"isscript\" : true|false, (boolean) If the key is a script\n" " \"iswitness\" : true|false, (boolean) If the address is a witness address\n" " \"witness_version\" : version (numeric, optional) The version number of the witness program\n" @@ -99,7 +99,7 @@ static UniValue createmultisig(const JSONRPCRequest& request) "\nExamples:\n" "\nCreate a multisig address from 2 public keys\n" + HelpExampleCli("createmultisig", "2 \"[\\\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd342cf11ae157a7ace5fd\\\",\\\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e17e107ef3f6aa5a61626\\\"]\"") + - "\nAs a json rpc call\n" + "\nAs a JSON-RPC call\n" + HelpExampleRpc("createmultisig", "2, \"[\\\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd342cf11ae157a7ace5fd\\\",\\\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e17e107ef3f6aa5a61626\\\"]\"") ; throw std::runtime_error(msg); @@ -157,7 +157,7 @@ static UniValue verifymessage(const JSONRPCRequest& request) + HelpExampleCli("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"my message\"") + "\nVerify the signature\n" + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"signature\" \"my message\"") + - "\nAs json rpc\n" + "\nAs a JSON-RPC call\n" + HelpExampleRpc("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"signature\", \"my message\"") ); @@ -210,7 +210,7 @@ static UniValue signmessagewithprivkey(const JSONRPCRequest& request) + HelpExampleCli("signmessagewithprivkey", "\"privkey\" \"my message\"") + "\nVerify the signature\n" + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"signature\" \"my message\"") + - "\nAs json rpc\n" + "\nAs a JSON-RPC call\n" + HelpExampleRpc("signmessagewithprivkey", "\"privkey\", \"my message\"") ); diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp index 55bebb5662..ee178f34ce 100644 --- a/src/rpc/protocol.cpp +++ b/src/rpc/protocol.cpp @@ -12,8 +12,6 @@ #include <utiltime.h> #include <version.h> -#include <fstream> - /** * 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 @@ -85,9 +83,9 @@ bool GenerateAuthCookie(std::string *cookie_out) /** the umask determines what permissions are used to create this file - * these are set to 077 in init.cpp unless overridden with -sysperms. */ - std::ofstream file; + fsbridge::ofstream file; fs::path filepath_tmp = GetAuthCookieFile(true); - file.open(filepath_tmp.string().c_str()); + file.open(filepath_tmp); if (!file.is_open()) { LogPrintf("Unable to open cookie authentication file %s for writing\n", filepath_tmp.string()); return false; @@ -109,10 +107,10 @@ bool GenerateAuthCookie(std::string *cookie_out) bool GetAuthCookie(std::string *cookie_out) { - std::ifstream file; + fsbridge::ifstream file; std::string cookie; fs::path filepath = GetAuthCookieFile(); - file.open(filepath.string().c_str()); + file.open(filepath); if (!file.is_open()) return false; std::getline(file, cookie); diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 7397216506..a2d990b51d 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -466,13 +466,13 @@ static UniValue createrawtransaction(const JSONRPCRequest& request) " \"address\": x.xxx, (obj, optional) A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + "\n" " },\n" " {\n" - " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex encoded data\n" + " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex-encoded data\n" " }\n" " ,... More key-value pairs of the above form. For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n" " accepted as second parameter.\n" " ]\n" "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n" - "4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125 replaceable.\n" + "4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125-replaceable.\n" " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\n" "\nResult:\n" "\"transaction\" (string) hex string of the transaction\n" @@ -581,11 +581,11 @@ static UniValue decodescript(const JSONRPCRequest& request) "decodescript \"hexstring\"\n" "\nDecode a hex-encoded script.\n" "\nArguments:\n" - "1. \"hexstring\" (string) the hex encoded script\n" + "1. \"hexstring\" (string) the hex-encoded script\n" "\nResult:\n" "{\n" " \"asm\":\"asm\", (string) Script public key\n" - " \"hex\":\"hex\", (string) hex encoded public key\n" + " \"hex\":\"hex\", (string) hex-encoded public key\n" " \"type\":\"type\", (string) The output type\n" " \"reqSigs\": n, (numeric) The required signatures\n" " \"addresses\": [ (json array of string)\n" @@ -922,7 +922,7 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request) " }\n" " ,...\n" " ]\n" - "4. \"sighashtype\" (string, optional, default=ALL) The signature hash type. Must be one of\n" + "4. \"sighashtype\" (string, optional, default=ALL) The signature hash type. Must be one of:\n" " \"ALL\"\n" " \"NONE\"\n" " \"SINGLE\"\n" @@ -999,7 +999,7 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request) + HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") + "\nSend the transaction (signed hex)\n" + HelpExampleCli("sendrawtransaction", "\"signedhex\"") + - "\nAs a json rpc call\n" + "\nAs a JSON-RPC call\n" + HelpExampleRpc("sendrawtransaction", "\"signedhex\"") ); @@ -1104,7 +1104,7 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request) + HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") + "\nTest acceptance of the transaction (signed hex)\n" + HelpExampleCli("testmempoolaccept", "\"signedhex\"") + - "\nAs a json rpc call\n" + "\nAs a JSON-RPC call\n" + HelpExampleRpc("testmempoolaccept", "[\"signedhex\"]") // clang-format on ); @@ -1587,7 +1587,7 @@ UniValue createpsbt(const JSONRPCRequest& request) " \"address\": x.xxx, (obj, optional) A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + "\n" " },\n" " {\n" - " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex encoded data\n" + " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex-encoded data\n" " }\n" " ,... More key-value pairs of the above form. For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n" " accepted as second parameter.\n" diff --git a/src/script/sign.cpp b/src/script/sign.cpp index d779910425..0042f35e2e 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -73,15 +73,18 @@ static bool GetPubKey(const SigningProvider& provider, SignatureData& sigdata, c return false; } -static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdata, const SigningProvider& provider, std::vector<unsigned char>& sig_out, const CKeyID& keyid, const CScript& scriptcode, SigVersion sigversion) +static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdata, const SigningProvider& provider, std::vector<unsigned char>& sig_out, const CPubKey& pubkey, const CScript& scriptcode, SigVersion sigversion) { + CKeyID keyid = pubkey.GetID(); const auto it = sigdata.signatures.find(keyid); if (it != sigdata.signatures.end()) { sig_out = it->second.second; return true; } - CPubKey pubkey; - GetPubKey(provider, sigdata, keyid, pubkey); + KeyOriginInfo info; + if (provider.GetKeyOrigin(keyid, info)) { + sigdata.misc_pubkeys.emplace(keyid, std::make_pair(pubkey, std::move(info))); + } if (creator.CreateSig(provider, sig_out, keyid, scriptcode, sigversion)) { auto i = sigdata.signatures.emplace(keyid, SigPair(pubkey, sig_out)); assert(i.second); @@ -114,15 +117,15 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator case TX_WITNESS_UNKNOWN: return false; case TX_PUBKEY: - if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]).GetID(), scriptPubKey, sigversion)) return false; + if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]), scriptPubKey, sigversion)) return false; ret.push_back(std::move(sig)); return true; case TX_PUBKEYHASH: { CKeyID keyID = CKeyID(uint160(vSolutions[0])); - if (!CreateSig(creator, sigdata, provider, sig, keyID, scriptPubKey, sigversion)) return false; - ret.push_back(std::move(sig)); CPubKey pubkey; GetPubKey(provider, sigdata, keyID, pubkey); + if (!CreateSig(creator, sigdata, provider, sig, pubkey, scriptPubKey, sigversion)) return false; + ret.push_back(std::move(sig)); ret.push_back(ToByteVector(pubkey)); return true; } @@ -138,7 +141,7 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator ret.push_back(valtype()); // workaround CHECKMULTISIG bug for (size_t i = 1; i < vSolutions.size() - 1; ++i) { CPubKey pubkey = CPubKey(vSolutions[i]); - if (ret.size() < required + 1 && CreateSig(creator, sigdata, provider, sig, pubkey.GetID(), scriptPubKey, sigversion)) { + if (ret.size() < required + 1 && CreateSig(creator, sigdata, provider, sig, pubkey, scriptPubKey, sigversion)) { ret.push_back(std::move(sig)); } } diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp index f57d0c6d79..8c2873d916 100644 --- a/src/test/addrman_tests.cpp +++ b/src/test/addrman_tests.cpp @@ -40,22 +40,26 @@ public: CAddrInfo* Find(const CNetAddr& addr, int* pnId = nullptr) { + LOCK(cs); return CAddrMan::Find(addr, pnId); } CAddrInfo* Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId = nullptr) { + LOCK(cs); return CAddrMan::Create(addr, addrSource, pnId); } void Delete(int nId) { + LOCK(cs); CAddrMan::Delete(nId); } // Simulates connection failure so that we can test eviction of offline nodes void SimConnFail(CService& addr) { + LOCK(cs); int64_t nLastSuccess = 1; Good_(addr, true, nLastSuccess); // Set last good connection in the deep past. diff --git a/src/test/fs_tests.cpp b/src/test/fs_tests.cpp new file mode 100644 index 0000000000..93aee10bb7 --- /dev/null +++ b/src/test/fs_tests.cpp @@ -0,0 +1,56 @@ +// Copyright (c) 2011-2018 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 <fs.h> +#include <test/test_bitcoin.h> + +#include <boost/test/unit_test.hpp> + +BOOST_FIXTURE_TEST_SUITE(fs_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(fsbridge_fstream) +{ + fs::path tmpfolder = SetDataDir("fsbridge_fstream"); + // tmpfile1 should be the same as tmpfile2 + fs::path tmpfile1 = tmpfolder / "fs_tests_₿_🏃"; + fs::path tmpfile2 = tmpfolder / L"fs_tests_₿_🏃"; + { + fsbridge::ofstream file(tmpfile1); + file << "bitcoin"; + } + { + fsbridge::ifstream file(tmpfile2); + std::string input_buffer; + file >> input_buffer; + BOOST_CHECK_EQUAL(input_buffer, "bitcoin"); + } + { + fsbridge::ifstream file(tmpfile1, std::ios_base::in | std::ios_base::ate); + std::string input_buffer; + file >> input_buffer; + BOOST_CHECK_EQUAL(input_buffer, ""); + } + { + fsbridge::ofstream file(tmpfile2, std::ios_base::out | std::ios_base::app); + file << "tests"; + } + { + fsbridge::ifstream file(tmpfile1); + std::string input_buffer; + file >> input_buffer; + BOOST_CHECK_EQUAL(input_buffer, "bitcointests"); + } + { + fsbridge::ofstream file(tmpfile2, std::ios_base::out | std::ios_base::trunc); + file << "bitcoin"; + } + { + fsbridge::ifstream file(tmpfile1); + std::string input_buffer; + file >> input_buffer; + BOOST_CHECK_EQUAL(input_buffer, "bitcoin"); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 67c377778f..7fbf37e7fb 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -1013,21 +1013,21 @@ BOOST_AUTO_TEST_CASE(script_PushData) ScriptError err; std::vector<std::vector<unsigned char> > directStack; - BOOST_CHECK(EvalScript(directStack, CScript(&direct[0], &direct[sizeof(direct)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err)); + BOOST_CHECK(EvalScript(directStack, CScript(direct, direct + sizeof(direct)), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); std::vector<std::vector<unsigned char> > pushdata1Stack; - BOOST_CHECK(EvalScript(pushdata1Stack, CScript(&pushdata1[0], &pushdata1[sizeof(pushdata1)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err)); + BOOST_CHECK(EvalScript(pushdata1Stack, CScript(pushdata1, pushdata1 + sizeof(pushdata1)), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err)); BOOST_CHECK(pushdata1Stack == directStack); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); std::vector<std::vector<unsigned char> > pushdata2Stack; - BOOST_CHECK(EvalScript(pushdata2Stack, CScript(&pushdata2[0], &pushdata2[sizeof(pushdata2)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err)); + BOOST_CHECK(EvalScript(pushdata2Stack, CScript(pushdata2, pushdata2 + sizeof(pushdata2)), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err)); BOOST_CHECK(pushdata2Stack == directStack); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); std::vector<std::vector<unsigned char> > pushdata4Stack; - BOOST_CHECK(EvalScript(pushdata4Stack, CScript(&pushdata4[0], &pushdata4[sizeof(pushdata4)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err)); + BOOST_CHECK(EvalScript(pushdata4Stack, CScript(pushdata4, pushdata4 + sizeof(pushdata4)), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err)); BOOST_CHECK(pushdata4Stack == directStack); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); } diff --git a/src/util.cpp b/src/util.cpp index fa624aee90..6479b9b9ce 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -61,6 +61,7 @@ #include <codecvt> #include <io.h> /* for _commit */ +#include <shellapi.h> #include <shlobj.h> #endif @@ -891,7 +892,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys) } const std::string confPath = GetArg("-conf", BITCOIN_CONF_FILENAME); - fs::ifstream stream(GetConfigFile(confPath)); + fsbridge::ifstream stream(GetConfigFile(confPath)); // ok to not have a config file if (stream.good()) { @@ -924,7 +925,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys) } for (const std::string& to_include : includeconf) { - fs::ifstream include_config(GetConfigFile(to_include)); + fsbridge::ifstream include_config(GetConfigFile(to_include)); if (include_config.good()) { if (!ReadConfigStream(include_config, error, ignore_invalid_keys)) { return false; @@ -1200,6 +1201,10 @@ void SetupEnvironment() } catch (const std::runtime_error&) { setenv("LC_ALL", "C", 1); } +#elif defined(WIN32) + // Set the default input/output charset is utf-8 + SetConsoleCP(CP_UTF8); + SetConsoleOutputCP(CP_UTF8); #endif // The path locale is lazy initialized and to avoid deinitialization errors // in multithreading environments, it is set explicitly by the main thread. @@ -1265,3 +1270,30 @@ int ScheduleBatchPriority() return 1; #endif } + +namespace util { +#ifdef WIN32 +WinCmdLineArgs::WinCmdLineArgs() +{ + wchar_t** wargv = CommandLineToArgvW(GetCommandLineW(), &argc); + std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> utf8_cvt; + argv = new char*[argc]; + args.resize(argc); + for (int i = 0; i < argc; i++) { + args[i] = utf8_cvt.to_bytes(wargv[i]); + argv[i] = &*args[i].begin(); + } + LocalFree(wargv); +} + +WinCmdLineArgs::~WinCmdLineArgs() +{ + delete[] argv; +} + +std::pair<int, char**> WinCmdLineArgs::get() +{ + return std::make_pair(argc, argv); +} +#endif +} // namespace util diff --git a/src/util.h b/src/util.h index f119385e48..fa6d2cd489 100644 --- a/src/util.h +++ b/src/util.h @@ -29,6 +29,7 @@ #include <stdint.h> #include <string> #include <unordered_set> +#include <utility> #include <vector> #include <boost/thread/condition_variable.hpp> // for boost::thread_interrupted @@ -361,6 +362,21 @@ inline void insert(std::set<TsetT>& dst, const Tsrc& src) { dst.insert(src.begin(), src.end()); } +#ifdef WIN32 +class WinCmdLineArgs +{ +public: + WinCmdLineArgs(); + ~WinCmdLineArgs(); + std::pair<int, char**> get(); + +private: + int argc; + char** argv; + std::vector<std::string> args; +}; +#endif + } // namespace util #endif // BITCOIN_UTIL_H diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index a299a4ee44..46983642f0 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -182,13 +182,18 @@ bool WalletInit::Verify() const if (gArgs.IsArgSet("-walletdir")) { fs::path wallet_dir = gArgs.GetArg("-walletdir", ""); - if (!fs::exists(wallet_dir)) { + boost::system::error_code error; + // The canonical path cleans the path, preventing >1 Berkeley environment instances for the same directory + fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error); + if (error || !fs::exists(wallet_dir)) { return InitError(strprintf(_("Specified -walletdir \"%s\" does not exist"), wallet_dir.string())); } else if (!fs::is_directory(wallet_dir)) { return InitError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), wallet_dir.string())); + // The canonical path transforms relative paths into absolute ones, so we check the non-canonical version } else if (!wallet_dir.is_absolute()) { return InitError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), wallet_dir.string())); } + gArgs.ForceSetArg("-walletdir", canonical_wallet_dir.string()); } LogPrintf("Using wallet directory %s\n", GetWalletDir().string()); diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index c97bc38e6f..92457c4644 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -17,7 +17,6 @@ #include <wallet/rpcwallet.h> -#include <fstream> #include <stdint.h> #include <boost/algorithm/string.hpp> @@ -540,8 +539,8 @@ UniValue importwallet(const JSONRPCRequest& request) EnsureWalletIsUnlocked(pwallet); - std::ifstream file; - file.open(request.params[0].get_str().c_str(), std::ios::in | std::ios::ate); + fsbridge::ifstream file; + file.open(request.params[0].get_str(), std::ios::in | std::ios::ate); if (!file.is_open()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); } @@ -717,8 +716,8 @@ UniValue dumpwallet(const JSONRPCRequest& request) 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()); + fsbridge::ofstream file; + file.open(filepath); if (!file.is_open()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 7d0219201e..df10fb48cc 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -504,7 +504,7 @@ static UniValue signmessage(const JSONRPCRequest& request) + HelpExampleCli("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"my message\"") + "\nVerify the signature\n" + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"signature\" \"my message\"") + - "\nAs json rpc\n" + "\nAs a JSON-RPC call\n" + HelpExampleRpc("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"my message\"") ); @@ -566,7 +566,7 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request) + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 0") + "\nThe amount with at least 6 confirmations\n" + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 6") + - "\nAs a json rpc call\n" + "\nAs a JSON-RPC call\n" + HelpExampleRpc("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", 6") ); @@ -633,7 +633,7 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request) + HelpExampleCli("getreceivedbylabel", "\"tabby\" 0") + "\nThe amount with at least 6 confirmations\n" + HelpExampleCli("getreceivedbylabel", "\"tabby\" 6") + - "\nAs a json rpc call\n" + "\nAs a JSON-RPC call\n" + HelpExampleRpc("getreceivedbylabel", "\"tabby\", 6") ); @@ -699,7 +699,7 @@ static UniValue getbalance(const JSONRPCRequest& request) + HelpExampleCli("getbalance", "") + "\nThe total amount in the wallet at least 6 blocks confirmed\n" + HelpExampleCli("getbalance", "\"*\" 6") + - "\nAs a json rpc call\n" + "\nAs a JSON-RPC call\n" + HelpExampleRpc("getbalance", "\"*\", 6") ); @@ -798,7 +798,7 @@ static UniValue sendmany(const JSONRPCRequest& request) + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 6 \"testing\"") + "\nSend two amounts to two different addresses, subtract fee from amount:\n" + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 1 \"\" \"[\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\",\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\"]\"") + - "\nAs a json rpc call\n" + "\nAs a JSON-RPC call\n" + HelpExampleRpc("sendmany", "\"\", {\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\":0.01,\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\":0.02}, 6, \"testing\"") ); @@ -939,7 +939,7 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request) "\nExamples:\n" "\nAdd a multisig address from 2 addresses\n" + HelpExampleCli("addmultisigaddress", "2 \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") + - "\nAs json rpc call\n" + "\nAs a JSON-RPC call\n" + HelpExampleRpc("addmultisigaddress", "2, \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") ; throw std::runtime_error(msg); @@ -1500,7 +1500,7 @@ UniValue listtransactions(const JSONRPCRequest& request) + HelpExampleCli("listtransactions", "") + "\nList transactions 100 to 120\n" + HelpExampleCli("listtransactions", "\"*\" 20 100") + - "\nAs a json rpc call\n" + "\nAs a JSON-RPC call\n" + HelpExampleRpc("listtransactions", "\"*\", 20, 100") ); @@ -1960,7 +1960,7 @@ static UniValue walletpassphrase(const JSONRPCRequest& request) + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 60") + "\nLock the wallet again (before 60 seconds)\n" + HelpExampleCli("walletlock", "") + - "\nAs json rpc call\n" + "\nAs a JSON-RPC call\n" + HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60") ); } @@ -2083,7 +2083,7 @@ static UniValue walletlock(const JSONRPCRequest& request) + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 1.0") + "\nClear the passphrase since we are done before 2 minutes is up\n" + HelpExampleCli("walletlock", "") + - "\nAs json rpc call\n" + "\nAs a JSON-RPC call\n" + HelpExampleRpc("walletlock", "") ); } @@ -2129,7 +2129,7 @@ static UniValue encryptwallet(const JSONRPCRequest& request) + HelpExampleCli("signmessage", "\"address\" \"test message\"") + "\nNow lock the wallet again by removing the passphrase\n" + HelpExampleCli("walletlock", "") + - "\nAs a json rpc call\n" + "\nAs a JSON-RPC call\n" + HelpExampleRpc("encryptwallet", "\"my pass phrase\"") ); } @@ -2200,7 +2200,7 @@ static UniValue lockunspent(const JSONRPCRequest& request) + HelpExampleCli("listlockunspent", "") + "\nUnlock the transaction again\n" + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + - "\nAs a json rpc call\n" + "\nAs a JSON-RPC call\n" + HelpExampleRpc("lockunspent", "false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") ); @@ -2314,7 +2314,7 @@ static UniValue listlockunspent(const JSONRPCRequest& request) + HelpExampleCli("listlockunspent", "") + "\nUnlock the transaction again\n" + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + - "\nAs a json rpc call\n" + "\nAs a JSON-RPC call\n" + HelpExampleRpc("listlockunspent", "") ); @@ -3536,7 +3536,7 @@ UniValue getaddressinfo(const JSONRPCRequest& request) "\nResult:\n" "{\n" " \"address\" : \"address\", (string) The bitcoin address validated\n" - " \"scriptPubKey\" : \"hex\", (string) The hex encoded scriptPubKey generated by the address\n" + " \"scriptPubKey\" : \"hex\", (string) The hex-encoded scriptPubKey generated by the address\n" " \"ismine\" : true|false, (boolean) If the address is yours or not\n" " \"iswatchonly\" : true|false, (boolean) If the address is watchonly\n" " \"isscript\" : true|false, (boolean) If the key is a script\n" @@ -3705,7 +3705,7 @@ static UniValue listlabels(const JSONRPCRequest& request) + HelpExampleCli("listlabels", "receive") + "\nList labels that have sending addresses\n" + HelpExampleCli("listlabels", "send") + - "\nAs json rpc call\n" + "\nAs a JSON-RPC call\n" + HelpExampleRpc("listlabels", "receive") ); @@ -3959,7 +3959,7 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request) " \"address\": x.xxx, (obj, optional) A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + "\n" " },\n" " {\n" - " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex encoded data\n" + " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex-encoded data\n" " }\n" " ,... More key-value pairs of the above form. For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n" " accepted as second parameter.\n" diff --git a/src/wallet/test/init_test_fixture.cpp b/src/wallet/test/init_test_fixture.cpp new file mode 100644 index 0000000000..1453029c9c --- /dev/null +++ b/src/wallet/test/init_test_fixture.cpp @@ -0,0 +1,42 @@ +// Copyright (c) 2018 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 <fs.h> + +#include <wallet/test/init_test_fixture.h> + +InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainName): BasicTestingSetup(chainName) +{ + std::string sep; + sep += fs::path::preferred_separator; + + m_datadir = SetDataDir("tempdir"); + m_cwd = fs::current_path(); + + m_walletdir_path_cases["default"] = m_datadir / "wallets"; + m_walletdir_path_cases["custom"] = m_datadir / "my_wallets"; + m_walletdir_path_cases["nonexistent"] = m_datadir / "path_does_not_exist"; + m_walletdir_path_cases["file"] = m_datadir / "not_a_directory.dat"; + m_walletdir_path_cases["trailing"] = m_datadir / "wallets" / sep; + m_walletdir_path_cases["trailing2"] = m_datadir / "wallets" / sep / sep; + + fs::current_path(m_datadir); + m_walletdir_path_cases["relative"] = "wallets"; + + fs::create_directories(m_walletdir_path_cases["default"]); + fs::create_directories(m_walletdir_path_cases["custom"]); + fs::create_directories(m_walletdir_path_cases["relative"]); + std::ofstream f(m_walletdir_path_cases["file"].BOOST_FILESYSTEM_C_STR); + f.close(); +} + +InitWalletDirTestingSetup::~InitWalletDirTestingSetup() +{ + fs::current_path(m_cwd); +} + +void InitWalletDirTestingSetup::SetWalletDir(const fs::path& walletdir_path) +{ + gArgs.ForceSetArg("-walletdir", walletdir_path.string()); +}
\ No newline at end of file diff --git a/src/wallet/test/init_test_fixture.h b/src/wallet/test/init_test_fixture.h new file mode 100644 index 0000000000..5684adbece --- /dev/null +++ b/src/wallet/test/init_test_fixture.h @@ -0,0 +1,21 @@ +// Copyright (c) 2018 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_TEST_INIT_TEST_FIXTURE_H +#define BITCOIN_WALLET_TEST_INIT_TEST_FIXTURE_H + +#include <test/test_bitcoin.h> + + +struct InitWalletDirTestingSetup: public BasicTestingSetup { + explicit InitWalletDirTestingSetup(const std::string& chainName = CBaseChainParams::MAIN); + ~InitWalletDirTestingSetup(); + void SetWalletDir(const fs::path& walletdir_path); + + fs::path m_datadir; + fs::path m_cwd; + std::map<std::string, fs::path> m_walletdir_path_cases; +}; + +#endif // BITCOIN_WALLET_TEST_INIT_TEST_FIXTURE_H diff --git a/src/wallet/test/init_tests.cpp b/src/wallet/test/init_tests.cpp new file mode 100644 index 0000000000..7048547b2b --- /dev/null +++ b/src/wallet/test/init_tests.cpp @@ -0,0 +1,78 @@ +// Copyright (c) 2018 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 <boost/test/unit_test.hpp> + +#include <test/test_bitcoin.h> +#include <wallet/test/init_test_fixture.h> + +#include <init.h> +#include <walletinitinterface.h> +#include <wallet/wallet.h> + + +BOOST_FIXTURE_TEST_SUITE(init_tests, InitWalletDirTestingSetup) + +BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_default) +{ + SetWalletDir(m_walletdir_path_cases["default"]); + bool result = g_wallet_init_interface.Verify(); + BOOST_CHECK(result == true); + fs::path walletdir = gArgs.GetArg("-walletdir", ""); + fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]); + BOOST_CHECK(walletdir == expected_path); +} + +BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_custom) +{ + SetWalletDir(m_walletdir_path_cases["custom"]); + bool result = g_wallet_init_interface.Verify(); + BOOST_CHECK(result == true); + fs::path walletdir = gArgs.GetArg("-walletdir", ""); + fs::path expected_path = fs::canonical(m_walletdir_path_cases["custom"]); + BOOST_CHECK(walletdir == expected_path); +} + +BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_does_not_exist) +{ + SetWalletDir(m_walletdir_path_cases["nonexistent"]); + bool result = g_wallet_init_interface.Verify(); + BOOST_CHECK(result == false); +} + +BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_is_not_directory) +{ + SetWalletDir(m_walletdir_path_cases["file"]); + bool result = g_wallet_init_interface.Verify(); + BOOST_CHECK(result == false); +} + +BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_is_not_relative) +{ + SetWalletDir(m_walletdir_path_cases["relative"]); + bool result = g_wallet_init_interface.Verify(); + BOOST_CHECK(result == false); +} + +BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing) +{ + SetWalletDir(m_walletdir_path_cases["trailing"]); + bool result = g_wallet_init_interface.Verify(); + BOOST_CHECK(result == true); + fs::path walletdir = gArgs.GetArg("-walletdir", ""); + fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]); + BOOST_CHECK(walletdir == expected_path); +} + +BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing2) +{ + SetWalletDir(m_walletdir_path_cases["trailing2"]); + bool result = g_wallet_init_interface.Verify(); + BOOST_CHECK(result == true); + fs::path walletdir = gArgs.GetArg("-walletdir", ""); + fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]); + BOOST_CHECK(walletdir == expected_path); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py index 1124119e2b..492772d5e3 100755 --- a/test/functional/feature_config_args.py +++ b/test/functional/feature_config_args.py @@ -14,9 +14,6 @@ class ConfArgsTest(BitcoinTestFramework): self.setup_clean_chain = True self.num_nodes = 1 - def skip_test_if_missing_module(self): - self.skip_if_no_wallet() - def test_config_file_parser(self): # Assume node is stopped @@ -68,13 +65,18 @@ class ConfArgsTest(BitcoinTestFramework): # Temporarily disabled, because this test would access the user's home dir (~/.bitcoin) #self.start_node(0, ['-conf='+conf_file, '-wallet=w1']) #self.stop_node(0) + #assert os.path.exists(os.path.join(new_data_dir, 'regtest', 'blocks')) + #if self.is_wallet_compiled(): #assert os.path.exists(os.path.join(new_data_dir, 'regtest', 'wallets', 'w1')) # Ensure command line argument overrides datadir in conf os.mkdir(new_data_dir_2) self.nodes[0].datadir = new_data_dir_2 self.start_node(0, ['-datadir='+new_data_dir_2, '-conf='+conf_file, '-wallet=w2']) - assert os.path.exists(os.path.join(new_data_dir_2, 'regtest', 'wallets', 'w2')) + assert os.path.exists(os.path.join(new_data_dir_2, 'regtest', 'blocks')) + if self.is_wallet_compiled(): + assert os.path.exists(os.path.join(new_data_dir_2, 'regtest', 'wallets', 'w2')) + if __name__ == '__main__': ConfArgsTest().main() diff --git a/test/functional/feature_notifications.py b/test/functional/feature_notifications.py index a93443f2db..90dc4c8e2b 100755 --- a/test/functional/feature_notifications.py +++ b/test/functional/feature_notifications.py @@ -5,17 +5,16 @@ """Test the -alertnotify, -blocknotify and -walletnotify options.""" import os +from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE from test_framework.test_framework import BitcoinTestFramework 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 skip_test_if_missing_module(self): - self.skip_if_no_wallet() - def setup_network(self): self.alertnotify_dir = os.path.join(self.options.tmpdir, "alertnotify") self.blocknotify_dir = os.path.join(self.options.tmpdir, "blocknotify") @@ -25,7 +24,7 @@ class NotificationsTest(BitcoinTestFramework): os.mkdir(self.walletnotify_dir) # -alertnotify and -blocknotify on node0, walletnotify on node1 - self.extra_args = [["-blockversion=2", + self.extra_args = [[ "-alertnotify=echo > {}".format(os.path.join(self.alertnotify_dir, '%s')), "-blocknotify=echo > {}".format(os.path.join(self.blocknotify_dir, '%s'))], ["-blockversion=211", @@ -36,7 +35,7 @@ class NotificationsTest(BitcoinTestFramework): def run_test(self): self.log.info("test -blocknotify") block_count = 10 - blocks = self.nodes[1].generate(block_count) + blocks = self.nodes[1].generatetoaddress(block_count, self.nodes[1].getnewaddress() if self.is_wallet_compiled() else ADDRESS_BCRT1_UNSPENDABLE) # wait at most 10 seconds for expected number of files before reading the content wait_until(lambda: len(os.listdir(self.blocknotify_dir)) == block_count, timeout=10) @@ -44,30 +43,31 @@ class NotificationsTest(BitcoinTestFramework): # directory content should equal the generated blocks hashes assert_equal(sorted(blocks), sorted(os.listdir(self.blocknotify_dir))) - self.log.info("test -walletnotify") - # wait at most 10 seconds for expected number of files before reading the content - wait_until(lambda: len(os.listdir(self.walletnotify_dir)) == block_count, timeout=10) + if self.is_wallet_compiled(): + self.log.info("test -walletnotify") + # wait at most 10 seconds for expected number of files before reading the content + wait_until(lambda: len(os.listdir(self.walletnotify_dir)) == block_count, timeout=10) - # directory content should equal the generated transaction hashes - txids_rpc = list(map(lambda t: t['txid'], self.nodes[1].listtransactions("*", block_count))) - assert_equal(sorted(txids_rpc), sorted(os.listdir(self.walletnotify_dir))) - for tx_file in os.listdir(self.walletnotify_dir): - os.remove(os.path.join(self.walletnotify_dir, tx_file)) + # directory content should equal the generated transaction hashes + txids_rpc = list(map(lambda t: t['txid'], self.nodes[1].listtransactions("*", block_count))) + assert_equal(sorted(txids_rpc), sorted(os.listdir(self.walletnotify_dir))) + for tx_file in os.listdir(self.walletnotify_dir): + os.remove(os.path.join(self.walletnotify_dir, tx_file)) - 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) + 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: len(os.listdir(self.walletnotify_dir)) == block_count, timeout=10) + wait_until(lambda: len(os.listdir(self.walletnotify_dir)) == block_count, timeout=10) - # directory content should equal the generated transaction hashes - txids_rpc = list(map(lambda t: t['txid'], self.nodes[1].listtransactions("*", block_count))) - assert_equal(sorted(txids_rpc), sorted(os.listdir(self.walletnotify_dir))) + # directory content should equal the generated transaction hashes + txids_rpc = list(map(lambda t: t['txid'], self.nodes[1].listtransactions("*", block_count))) + assert_equal(sorted(txids_rpc), sorted(os.listdir(self.walletnotify_dir))) # Mine another 41 up-version blocks. -alertnotify should trigger on the 51st. self.log.info("test -alertnotify") - self.nodes[1].generate(41) + self.nodes[1].generatetoaddress(41, ADDRESS_BCRT1_UNSPENDABLE) self.sync_all() # Give bitcoind 10 seconds to write the alert notification @@ -77,7 +77,7 @@ class NotificationsTest(BitcoinTestFramework): os.remove(os.path.join(self.alertnotify_dir, notify_file)) # Mine more up-version blocks, should not get more alerts: - self.nodes[1].generate(2) + self.nodes[1].generatetoaddress(2, ADDRESS_BCRT1_UNSPENDABLE) self.sync_all() self.log.info("-alertnotify should not continue notifying for more unknown version blocks") diff --git a/test/functional/feature_uacomment.py b/test/functional/feature_uacomment.py index 691a39b825..fb4ad21359 100755 --- a/test/functional/feature_uacomment.py +++ b/test/functional/feature_uacomment.py @@ -31,7 +31,7 @@ class UacommentTest(BitcoinTestFramework): self.nodes[0].assert_start_raises_init_error(["-uacomment=" + 'a' * 256], expected, match=ErrorMatch.FULL_REGEX) self.log.info("test -uacomment unsafe characters") - for unsafe_char in ['/', ':', '(', ')']: + for unsafe_char in ['/', ':', '(', ')', '₿', '🏃']: expected = "Error: User Agent comment \(" + re.escape(unsafe_char) + "\) contains unsafe characters." self.nodes[0].assert_start_raises_init_error(["-uacomment=" + unsafe_char], expected, match=ErrorMatch.FULL_REGEX) diff --git a/test/functional/interface_bitcoin_cli.py b/test/functional/interface_bitcoin_cli.py index f311858bee..58cdaf861f 100755 --- a/test/functional/interface_bitcoin_cli.py +++ b/test/functional/interface_bitcoin_cli.py @@ -12,9 +12,6 @@ class TestBitcoinCli(BitcoinTestFramework): self.setup_clean_chain = True self.num_nodes = 1 - def skip_test_if_missing_module(self): - self.skip_if_no_wallet() - def run_test(self): """Main test logic""" @@ -22,9 +19,10 @@ class TestBitcoinCli(BitcoinTestFramework): assert("Bitcoin Core RPC client version" in cli_response) self.log.info("Compare responses from gewalletinfo RPC and `bitcoin-cli getwalletinfo`") - cli_response = self.nodes[0].cli.getwalletinfo() - rpc_response = self.nodes[0].getwalletinfo() - assert_equal(cli_response, rpc_response) + if self.is_wallet_compiled(): + 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_response = self.nodes[0].cli.getblockchaininfo() @@ -52,26 +50,30 @@ class TestBitcoinCli(BitcoinTestFramework): self.log.info("Compare responses from `bitcoin-cli -getinfo` and the RPCs data is retrieved from.") cli_get_info = self.nodes[0].cli('-getinfo').send_cli() - wallet_info = self.nodes[0].getwalletinfo() + if self.is_wallet_compiled(): + 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']) + if self.is_wallet_compiled(): + 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 self.is_wallet_compiled(): + 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() diff --git a/test/functional/interface_zmq.py b/test/functional/interface_zmq.py index 1c518eab75..48136a0108 100755 --- a/test/functional/interface_zmq.py +++ b/test/functional/interface_zmq.py @@ -5,6 +5,7 @@ """Test the ZMQ notification interface.""" import struct +from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE from test_framework.test_framework import BitcoinTestFramework from test_framework.messages import CTransaction from test_framework.util import ( @@ -14,6 +15,7 @@ from test_framework.util import ( ) from io import BytesIO +ADDRESS = "tcp://127.0.0.1:28332" class ZMQSubscriber: def __init__(self, socket, topic): @@ -41,7 +43,6 @@ class ZMQTest (BitcoinTestFramework): def skip_test_if_missing_module(self): self.skip_if_no_py3_zmq() self.skip_if_no_bitcoind_zmq() - self.skip_if_no_wallet() def setup_nodes(self): import zmq @@ -51,11 +52,10 @@ class ZMQTest (BitcoinTestFramework): # that this test fails if the publishing order changes. # Note that the publishing order is not defined in the documentation and # is subject to change. - address = "tcp://127.0.0.1:28332" self.zmq_context = zmq.Context() socket = self.zmq_context.socket(zmq.SUB) socket.set(zmq.RCVTIMEO, 60000) - socket.connect(address) + socket.connect(ADDRESS) # Subscribe to all available topics. self.hashblock = ZMQSubscriber(socket, b"hashblock") @@ -64,7 +64,7 @@ class ZMQTest (BitcoinTestFramework): self.rawtx = ZMQSubscriber(socket, b"rawtx") self.extra_args = [ - ["-zmqpub%s=%s" % (sub.topic.decode(), address) for sub in [self.hashblock, self.hashtx, self.rawblock, self.rawtx]], + ["-zmqpub%s=%s" % (sub.topic.decode(), ADDRESS) for sub in [self.hashblock, self.hashtx, self.rawblock, self.rawtx]], [], ] self.add_nodes(self.num_nodes, self.extra_args) @@ -81,7 +81,7 @@ class ZMQTest (BitcoinTestFramework): def _zmq_test(self): num_blocks = 5 self.log.info("Generate %(n)d blocks (and %(n)d coinbase txes)" % {"n": num_blocks}) - genhashes = self.nodes[0].generate(num_blocks) + genhashes = self.nodes[0].generatetoaddress(num_blocks, ADDRESS_BCRT1_UNSPENDABLE) self.sync_all() for x in range(num_blocks): @@ -105,17 +105,29 @@ class ZMQTest (BitcoinTestFramework): block = self.rawblock.receive() assert_equal(genhashes[x], bytes_to_hex_str(hash256(block[:80]))) - self.log.info("Wait for tx from second node") - payment_txid = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1.0) - self.sync_all() + if self.is_wallet_compiled(): + self.log.info("Wait for tx from second node") + payment_txid = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1.0) + self.sync_all() + + # Should receive the broadcasted txid. + txid = self.hashtx.receive() + assert_equal(payment_txid, bytes_to_hex_str(txid)) + + # Should receive the broadcasted raw transaction. + hex = self.rawtx.receive() + assert_equal(payment_txid, bytes_to_hex_str(hash256(hex))) + - # Should receive the broadcasted txid. - txid = self.hashtx.receive() - assert_equal(payment_txid, bytes_to_hex_str(txid)) + self.log.info("Test the getzmqnotifications RPC") + assert_equal(self.nodes[0].getzmqnotifications(), [ + {"type": "pubhashblock", "address": ADDRESS}, + {"type": "pubhashtx", "address": ADDRESS}, + {"type": "pubrawblock", "address": ADDRESS}, + {"type": "pubrawtx", "address": ADDRESS}, + ]) - # Should receive the broadcasted raw transaction. - hex = self.rawtx.receive() - assert_equal(payment_txid, bytes_to_hex_str(hash256(hex))) + assert_equal(self.nodes[1].getzmqnotifications(), []) if __name__ == '__main__': ZMQTest().main() diff --git a/test/functional/rpc_zmq.py b/test/functional/rpc_zmq.py deleted file mode 100755 index bfa6b06f67..0000000000 --- a/test/functional/rpc_zmq.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) 2018 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 for the ZMQ RPC methods.""" - -from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal - - -class RPCZMQTest(BitcoinTestFramework): - - address = "tcp://127.0.0.1:28332" - - def set_test_params(self): - self.num_nodes = 1 - self.setup_clean_chain = True - - def skip_test_if_missing_module(self): - self.skip_if_no_py3_zmq() - self.skip_if_no_bitcoind_zmq() - - def run_test(self): - self._test_getzmqnotifications() - - def _test_getzmqnotifications(self): - self.restart_node(0, extra_args=[]) - assert_equal(self.nodes[0].getzmqnotifications(), []) - - self.restart_node(0, extra_args=["-zmqpubhashtx=%s" % self.address]) - assert_equal(self.nodes[0].getzmqnotifications(), [ - {"type": "pubhashtx", "address": self.address}, - ]) - - -if __name__ == '__main__': - RPCZMQTest().main() diff --git a/test/functional/test_framework/address.py b/test/functional/test_framework/address.py index d1fb97b024..456d43aa2e 100644 --- a/test/functional/test_framework/address.py +++ b/test/functional/test_framework/address.py @@ -9,8 +9,11 @@ from .util import bytes_to_hex_str, hex_str_to_bytes from . import segwit_addr +ADDRESS_BCRT1_UNSPENDABLE = 'bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj' + chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' + def byte_to_base58(b, version): result = '' str = bytes_to_hex_str(b) diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 7ab7fcfcb4..c05988c661 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -187,7 +187,9 @@ class TestNode(): 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? + # -28 RPC in warmup + # -342 Service unavailable, RPC server started but is shutting down due to error + if e.error['code'] != -28 and e.error['code'] != -342: 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): diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index d9960460d9..c6d1574201 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -152,7 +152,6 @@ BASE_SCRIPTS = [ 'feature_versionbits_warning.py', 'rpc_preciousblock.py', 'wallet_importprunedfunds.py', - 'rpc_zmq.py', 'rpc_signmessage.py', 'feature_nulldummy.py', 'mempool_accept.py', diff --git a/test/lint/lint-python.sh b/test/lint/lint-python.sh index 4f4542ff0c..d44a585294 100755 --- a/test/lint/lint-python.sh +++ b/test/lint/lint-python.sh @@ -87,4 +87,4 @@ elif PYTHONWARNINGS="ignore" flake8 --version | grep -q "Python 2"; then exit 0 fi -PYTHONWARNINGS="ignore" flake8 --ignore=B,C,E,F,I,N,W --select=E101,E112,E113,E115,E116,E125,E129,E131,E133,E223,E224,E242,E266,E271,E272,E273,E274,E275,E304,E306,E401,E402,E502,E701,E702,E703,E714,E721,E741,E742,E743,E901,E902,F401,F402,F403,F404,F405,F406,F407,F601,F602,F621,F622,F631,F701,F702,F703,F704,F705,F706,F707,F811,F812,F821,F822,F823,F831,F841,W191,W291,W292,W293,W504,W601,W602,W603,W604,W605,W606 . +PYTHONWARNINGS="ignore" flake8 --ignore=B,C,E,F,I,N,W --select=E101,E112,E113,E115,E116,E125,E129,E131,E133,E223,E224,E242,E266,E271,E272,E273,E274,E275,E304,E306,E401,E402,E502,E701,E702,E703,E714,E721,E741,E742,E743,E901,E902,F401,F402,F403,F404,F405,F406,F407,F601,F602,F621,F622,F631,F701,F702,F703,F704,F705,F706,F707,F811,F812,F821,F822,F823,F831,F841,W191,W291,W292,W293,W504,W601,W602,W603,W604,W605,W606 "${@:-.}" diff --git a/test/lint/lint-spelling.ignore-words.txt b/test/lint/lint-spelling.ignore-words.txt index 9a49f32271..f0415443db 100644 --- a/test/lint/lint-spelling.ignore-words.txt +++ b/test/lint/lint-spelling.ignore-words.txt @@ -1,6 +1,7 @@ cas hights mor +mut objext unselect useable diff --git a/test/lint/lint-spelling.sh b/test/lint/lint-spelling.sh index ebeafd7d58..5d672698a7 100755 --- a/test/lint/lint-spelling.sh +++ b/test/lint/lint-spelling.sh @@ -9,10 +9,7 @@ export LC_ALL=C -EXIT_CODE=0 IGNORE_WORDS_FILE=test/lint/lint-spelling.ignore-words.txt if ! codespell --check-filenames --disable-colors --quiet-level=7 --ignore-words=${IGNORE_WORDS_FILE} $(git ls-files -- ":(exclude)build-aux/m4/" ":(exclude)contrib/seeds/*.txt" ":(exclude)depends/" ":(exclude)doc/release-notes/" ":(exclude)src/leveldb/" ":(exclude)src/qt/locale/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/"); then echo "^ Warning: codespell identified likely spelling errors. Any false positives? Add them to the list of ignored words in ${IGNORE_WORDS_FILE}" - EXIT_CODE=1 fi -exit ${EXIT_CODE} |