diff options
37 files changed, 695 insertions, 296 deletions
diff --git a/Makefile.am b/Makefile.am index bd41c5ae27..5428ba93ee 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -# Copyright (c) 2013-2016 The Bitcoin Core developers +# Copyright (c) 2013-2020 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -43,16 +43,7 @@ OSX_INSTALLER_ICONS=$(top_srcdir)/src/qt/res/icons/bitcoin.icns OSX_PLIST=$(top_builddir)/share/qt/Info.plist #not installed OSX_QT_TRANSLATIONS = da,de,es,hu,ru,uk,zh_CN,zh_TW -DIST_DOCS = \ - README.md \ - $(wildcard doc/*.md) \ - $(wildcard doc/release-notes/*.md) -DIST_CONTRIB = $(top_srcdir)/contrib/bitcoin-cli.bash-completion \ - $(top_srcdir)/contrib/bitcoin-tx.bash-completion \ - $(top_srcdir)/contrib/bitcoind.bash-completion \ - $(top_srcdir)/contrib/debian/copyright \ - $(top_srcdir)/contrib/init \ - $(top_srcdir)/contrib/install_db4.sh \ +DIST_CONTRIB = \ $(top_srcdir)/contrib/linearize/linearize-data.py \ $(top_srcdir)/contrib/linearize/linearize-hashes.py @@ -246,7 +237,7 @@ endif dist_noinst_SCRIPTS = autogen.sh -EXTRA_DIST = $(DIST_SHARE) $(DIST_CONTRIB) $(DIST_DOCS) $(WINDOWS_PACKAGING) $(OSX_PACKAGING) $(BIN_CHECKS) +EXTRA_DIST = $(DIST_SHARE) $(DIST_CONTRIB) $(WINDOWS_PACKAGING) $(OSX_PACKAGING) $(BIN_CHECKS) EXTRA_DIST += \ test/functional \ diff --git a/ci/test/00_setup_env_native_valgrind.sh b/ci/test/00_setup_env_native_valgrind.sh index f112aa87f9..2a7b32cefc 100644 --- a/ci/test/00_setup_env_native_valgrind.sh +++ b/ci/test/00_setup_env_native_valgrind.sh @@ -10,7 +10,6 @@ export CONTAINER_NAME=ci_native_valgrind export PACKAGES="valgrind clang llvm python3-zmq libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev" export USE_VALGRIND=1 export NO_DEPENDS=1 -export TEST_RUNNER_EXTRA="--exclude feature_abortnode,feature_block,rpc_bind" # Excluded for now -export RUN_FUNCTIONAL_TESTS=true +export TEST_RUNNER_EXTRA="--exclude rpc_bind" # Excluded for now, see https://github.com/bitcoin/bitcoin/issues/17765#issuecomment-602068547 export GOAL="install" export BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --with-gui=no CC=clang CXX=clang++" # TODO enable GUI diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml index 4a8b125ae7..a13a42d391 100644 --- a/contrib/gitian-descriptors/gitian-linux.yml +++ b/contrib/gitian-descriptors/gitian-linux.yml @@ -147,13 +147,6 @@ script: | SOURCEDIST=$(echo bitcoin-*.tar.gz) DISTNAME=${SOURCEDIST/%.tar.gz} - # Correct tar file order - mkdir -p temp - pushd temp - tar -xf ../$SOURCEDIST - find bitcoin-* | sort | tar --mtime="$REFERENCE_DATETIME" --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ../$SOURCEDIST - popd - # Workaround for tarball not building with the bare tag version (prep) make -C src obj/build.h @@ -190,11 +183,12 @@ script: | rm -rf ${DISTNAME}/lib/pkgconfig find ${DISTNAME}/bin -type f -executable -print0 | xargs -0 -n1 -I{} ../contrib/devtools/split-debug.sh {} {} {}.dbg find ${DISTNAME}/lib -type f -print0 | xargs -0 -n1 -I{} ../contrib/devtools/split-debug.sh {} {} {}.dbg - cp ../README.md ${DISTNAME}/ + cp ../../README.md ${DISTNAME}/ find ${DISTNAME} -not -name "*.dbg" | sort | tar --mtime="$REFERENCE_DATETIME" --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-${i}.tar.gz find ${DISTNAME} -name "*.dbg" | sort | tar --mtime="$REFERENCE_DATETIME" --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-${i}-debug.tar.gz cd ../../ rm -rf distsrc-${i} done - mkdir -p $OUTDIR/src - mv $SOURCEDIST $OUTDIR/src + + mkdir -p ${OUTDIR}/src + git archive --output=${OUTDIR}/src/${DISTNAME}.tar.gz HEAD diff --git a/contrib/gitian-descriptors/gitian-osx.yml b/contrib/gitian-descriptors/gitian-osx.yml index 2b6aa599e0..58531c81b4 100644 --- a/contrib/gitian-descriptors/gitian-osx.yml +++ b/contrib/gitian-descriptors/gitian-osx.yml @@ -110,13 +110,6 @@ script: | SOURCEDIST=$(echo bitcoin-*.tar.gz) DISTNAME=${SOURCEDIST/%.tar.gz} - # Correct tar file order - mkdir -p temp - pushd temp - tar -xf ../$SOURCEDIST - find bitcoin-* | sort | tar --mtime="$REFERENCE_DATETIME" --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ../$SOURCEDIST - popd - # Workaround for tarball not building with the bare tag version (prep) make -C src obj/build.h @@ -166,6 +159,8 @@ script: | find ${DISTNAME} | sort | tar --mtime="$REFERENCE_DATETIME" --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-${i}.tar.gz cd ../../ done - mkdir -p $OUTDIR/src - mv $SOURCEDIST $OUTDIR/src + + mkdir -p ${OUTDIR}/src + git archive --output=${OUTDIR}/src/${DISTNAME}.tar.gz HEAD + mv ${OUTDIR}/${DISTNAME}-x86_64-*.tar.gz ${OUTDIR}/${DISTNAME}-osx64.tar.gz diff --git a/contrib/gitian-descriptors/gitian-win.yml b/contrib/gitian-descriptors/gitian-win.yml index 952e5ba596..c5eea97c77 100644 --- a/contrib/gitian-descriptors/gitian-win.yml +++ b/contrib/gitian-descriptors/gitian-win.yml @@ -117,13 +117,6 @@ script: | SOURCEDIST=$(echo bitcoin-*.tar.gz) DISTNAME=${SOURCEDIST/%.tar.gz} - # Correct tar file order - mkdir -p temp - pushd temp - tar -xf ../$SOURCEDIST - find bitcoin-* | sort | tar --mtime="$REFERENCE_DATETIME" --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ../$SOURCEDIST - popd - # Workaround for tarball not building with the bare tag version (prep) make -C src obj/build.h @@ -162,8 +155,10 @@ script: | cd ../../ rm -rf distsrc-${i} done - mkdir -p $OUTDIR/src - mv $SOURCEDIST $OUTDIR/src + + mkdir -p ${OUTDIR}/src + git archive --output=${OUTDIR}/src/${DISTNAME}.tar.gz HEAD + cp -rf contrib/windeploy $BUILD_DIR cd $BUILD_DIR/windeploy mkdir unsigned diff --git a/doc/developer-notes.md b/doc/developer-notes.md index 6e8bde47f8..da07080724 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -43,6 +43,7 @@ Developer Notes - [Suggestions and examples](#suggestions-and-examples) - [Release notes](#release-notes) - [RPC interface guidelines](#rpc-interface-guidelines) + - [Internal interface guidelines](#internal-interface-guidelines) <!-- markdown-toc end --> @@ -1100,3 +1101,124 @@ A few guidelines for introducing and reviewing new RPC interfaces: timestamps in the documentation. - *Rationale*: User-facing consistency. + +Internal interface guidelines +----------------------------- + +Internal interfaces between parts of the codebase that are meant to be +independent (node, wallet, GUI), are defined in +[`src/interfaces/`](../src/interfaces/). The main interface classes defined +there are [`interfaces::Chain`](../src/interfaces/chain.h), used by wallet to +access the node's latest chain state, +[`interfaces::Node`](../src/interfaces/node.h), used by the GUI to control the +node, and [`interfaces::Wallet`](../src/interfaces/wallet.h), used by the GUI +to control an individual wallet. There are also more specialized interface +types like [`interfaces::Handler`](../src/interfaces/handler.h) +[`interfaces::ChainClient`](../src/interfaces/chain.h) passed to and from +various interface methods. + +Interface classes are written in a particular style so node, wallet, and GUI +code doesn't need to run in the same process, and so the class declarations +work more easily with tools and libraries supporting interprocess +communication: + +- Interface classes should be abstract and have methods that are [pure + virtual](https://en.cppreference.com/w/cpp/language/abstract_class). This + allows multiple implementations to inherit from the same interface class, + particularly so one implementation can execute functionality in the local + process, and other implementations can forward calls to remote processes. + +- Interface method definitions should wrap existing functionality instead of + implementing new functionality. Any substantial new node or wallet + functionality should be implemented in [`src/node/`](../src/node/) or + [`src/wallet/`](../src/wallet/) and just exposed in + [`src/interfaces/`](../src/interfaces/) instead of being implemented there, + so it can be more modular and accessible to unit tests. + +- Interface method parameter and return types should either be serializable or + be other interface classes. Interface methods shouldn't pass references to + objects that can't be serialized or accessed from another process. + + Examples: + + ```c++ + // Good: takes string argument and returns interface class pointer + virtual unique_ptr<interfaces::Wallet> loadWallet(std::string filename) = 0; + + // Bad: returns CWallet reference that can't be used from another process + virtual CWallet& loadWallet(std::string filename) = 0; + ``` + + ```c++ + // Good: accepts and returns primitive types + virtual bool findBlock(const uint256& hash, int& out_height, int64_t& out_time) = 0; + + // Bad: returns pointer to internal node in a linked list inaccessible to + // other processes + virtual const CBlockIndex* findBlock(const uint256& hash) = 0; + ``` + + ```c++ + // Good: takes plain callback type and returns interface pointer + using TipChangedFn = std::function<void(int block_height, int64_t block_time)>; + virtual std::unique_ptr<interfaces::Handler> handleTipChanged(TipChangedFn fn) = 0; + + // Bad: returns boost connection specific to local process + using TipChangedFn = std::function<void(int block_height, int64_t block_time)>; + virtual boost::signals2::scoped_connection connectTipChanged(TipChangedFn fn) = 0; + ``` + +- For consistency and friendliness to code generation tools, interface method + input and inout parameters should be ordered first and output parameters + should come last. + + Example: + + ```c++ + // Good: error output param is last + virtual bool broadcastTransaction(const CTransactionRef& tx, CAmount max_fee, std::string& error) = 0; + + // Bad: error output param is between input params + virtual bool broadcastTransaction(const CTransactionRef& tx, std::string& error, CAmount max_fee) = 0; + ``` + +- For friendliness to code generation tools, interface methods should not be + overloaded: + + Example: + + ```c++ + // Good: method names are unique + virtual bool disconnectByAddress(const CNetAddr& net_addr) = 0; + virtual bool disconnectById(NodeId id) = 0; + + // Bad: methods are overloaded by type + virtual bool disconnect(const CNetAddr& net_addr) = 0; + virtual bool disconnect(NodeId id) = 0; + ``` + +- For consistency and friendliness to code generation tools, interface method + names should be `lowerCamelCase` and standalone function names should be + `UpperCamelCase`. + + Examples: + + ```c++ + // Good: lowerCamelCase method name + virtual void blockConnected(const CBlock& block, int height) = 0; + + // Bad: uppercase class method + virtual void BlockConnected(const CBlock& block, int height) = 0; + ``` + + ```c++ + // Good: UpperCamelCase standalone function name + std::unique_ptr<Node> MakeNode(LocalInit& init); + + // Bad: lowercase standalone function + std::unique_ptr<Node> makeNode(LocalInit& init); + ``` + + Note: This last convention isn't generally followed outside of + [`src/interfaces/`](../src/interfaces/), though it did come up for discussion + before in [#14635](https://github.com/bitcoin/bitcoin/pull/14635). diff --git a/doc/fuzzing.md b/doc/fuzzing.md index af82371d58..9642337821 100644 --- a/doc/fuzzing.md +++ b/doc/fuzzing.md @@ -1,125 +1,93 @@ -Fuzz-testing Bitcoin Core -========================== - -A special test harness in `src/test/fuzz/` is provided for each fuzz target to -provide an easy entry point for fuzzers and the like. In this document we'll -describe how to use it with AFL and libFuzzer. - -## Preparing fuzzing - -The fuzzer needs some inputs to work on, but the inputs or seeds can be used -interchangeably between libFuzzer and AFL. - -Extract the example seeds (or other starting inputs) into the inputs -directory before starting fuzzing. - -``` -git clone https://github.com/bitcoin-core/qa-assets -export DIR_FUZZ_IN=$PWD/qa-assets/fuzz_seed_corpus -``` - -AFL needs an input directory with examples, and an output directory where it -will place examples that it found. These can be anywhere in the file system, -we'll define environment variables to make it easy to reference them. - -So, only for AFL you need to configure the outputs path: - -``` -mkdir outputs -export AFLOUT=$PWD/outputs -``` - -libFuzzer will use the input directory as output directory. - -## AFL - -### Building AFL - -It is recommended to always use the latest version of afl: -``` -wget http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz -tar -zxvf afl-latest.tgz -cd afl-<version> -make -export AFLPATH=$PWD -``` - -For macOS you may need to ignore x86 compilation checks when running `make`: -`AFL_NO_X86=1 make`. - -### Instrumentation - -To build Bitcoin Core using AFL instrumentation (this assumes that the -`AFLPATH` was set as above): -``` -./configure --disable-shared --enable-tests --enable-fuzz CC=${AFLPATH}/afl-gcc CXX=${AFLPATH}/afl-g++ -export AFL_HARDEN=1 -make -``` - -If you are using clang you will need to substitute `afl-gcc` with `afl-clang` -and `afl-g++` with `afl-clang++`, so the first line above becomes: -``` -./configure --disable-shared --enable-tests --enable-fuzz CC=${AFLPATH}/afl-clang CXX=${AFLPATH}/afl-clang++ -``` - -We disable ccache because we don't want to pollute the ccache with instrumented -objects, and similarly don't want to use non-instrumented cached objects linked -in. - -The fuzzing can be sped up significantly (~200x) by using `afl-clang-fast` and -`afl-clang-fast++` in place of `afl-gcc` and `afl-g++` when compiling. When -compiling using `afl-clang-fast`/`afl-clang-fast++` the resulting -binary will be instrumented in such a way that the AFL -features "persistent mode" and "deferred forkserver" can be used. See -https://github.com/google/AFL/tree/master/llvm_mode for details. - -### Fuzzing - -To start the actual fuzzing use: - -``` -export FUZZ_TARGET=bech32 # Pick a fuzz_target -mkdir ${AFLOUT}/${FUZZ_TARGET} -$AFLPATH/afl-fuzz -i ${DIR_FUZZ_IN}/${FUZZ_TARGET} -o ${AFLOUT}/${FUZZ_TARGET} -m52 -- src/test/fuzz/${FUZZ_TARGET} -``` - -You may have to change a few kernel parameters to test optimally - `afl-fuzz` -will print an error and suggestion if so. - -On macOS you may need to set `AFL_NO_FORKSRV=1` to get the target to run. -``` -export FUZZ_TARGET=bech32 # Pick a fuzz_target -mkdir ${AFLOUT}/${FUZZ_TARGET} -AFL_NO_FORKSRV=1 $AFLPATH/afl-fuzz -i ${DIR_FUZZ_IN}/${FUZZ_TARGET} -o ${AFLOUT}/${FUZZ_TARGET} -m52 -- src/test/fuzz/${FUZZ_TARGET} -``` - -## libFuzzer - -A recent version of `clang`, the address/undefined sanitizers (ASan/UBSan) and -libFuzzer is needed (all found in the `compiler-rt` runtime libraries package). - -To build all fuzz targets with libFuzzer, run - -``` -./configure --enable-fuzz --with-sanitizers=fuzzer,address,undefined CC=clang CXX=clang++ -make -``` - -See https://llvm.org/docs/LibFuzzer.html#running on how to run the libFuzzer -instrumented executable. - -Alternatively, you can run the script through the fuzzing test harness (only -libFuzzer supported so far). You need to pass it the inputs directory and -the specific test target you want to run. - -``` -./test/fuzz/test_runner.py ${DIR_FUZZ_IN} bech32 -``` - -### macOS hints for libFuzzer - -The default clang/llvm version supplied by Apple on macOS does not include +# Fuzzing Bitcoin Core using libFuzzer + +## Quickstart guide + +To quickly get started fuzzing Bitcoin Core using [libFuzzer](https://llvm.org/docs/LibFuzzer.html): + +```sh +$ git clone https://github.com/bitcoin/bitcoin +$ cd bitcoin/ +$ ./autogen.sh +$ CC=clang CXX=clang++ ./configure --enable-fuzz --with-sanitizers=address,fuzzer,undefined +# macOS users: If you have problem with this step then make sure to read "macOS hints for +# libFuzzer" on https://github.com/bitcoin/bitcoin/blob/master/doc/fuzzing.md#macos-hints-for-libfuzzer +$ make +$ src/test/fuzz/process_message +# abort fuzzing using ctrl-c +``` + +## Fuzzing harnesses, fuzzing output and fuzzing corpora + +[`process_message`](https://github.com/bitcoin/bitcoin/blob/master/src/test/fuzz/process_message.cpp) is a fuzzing harness for the [`ProcessMessage(...)` function (`net_processing`)](https://github.com/bitcoin/bitcoin/blob/master/src/net_processing.cpp). The available fuzzing harnesses are found in [`src/test/fuzz/`](https://github.com/bitcoin/bitcoin/tree/master/src/test/fuzz). + +The fuzzer will output `NEW` every time it has created a test input that covers new areas of the code under test. For more information on how to interpret the fuzzer output, see the [libFuzzer documentation](https://llvm.org/docs/LibFuzzer.html). + +If you specify a corpus directory then any new coverage increasing inputs will be saved there: + +```sh +$ mkdir -p process_message-seeded-from-thin-air/ +$ src/test/fuzz/process_message process_message-seeded-from-thin-air/ +INFO: Seed: 840522292 +INFO: Loaded 1 modules (424174 inline 8-bit counters): 424174 [0x55e121ef9ab8, 0x55e121f613a6), +INFO: Loaded 1 PC tables (424174 PCs): 424174 [0x55e121f613a8,0x55e1225da288), +INFO: 0 files found in process_message-seeded-from-thin-air/ +INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes +INFO: A corpus is not provided, starting from an empty corpus +#2 INITED cov: 94 ft: 95 corp: 1/1b exec/s: 0 rss: 150Mb +#3 NEW cov: 95 ft: 96 corp: 2/3b lim: 4 exec/s: 0 rss: 150Mb L: 2/2 MS: 1 InsertByte- +#4 NEW cov: 96 ft: 98 corp: 3/7b lim: 4 exec/s: 0 rss: 150Mb L: 4/4 MS: 1 CrossOver- +#21 NEW cov: 96 ft: 100 corp: 4/11b lim: 4 exec/s: 0 rss: 150Mb L: 4/4 MS: 2 ChangeBit-CrossOver- +#324 NEW cov: 101 ft: 105 corp: 5/12b lim: 6 exec/s: 0 rss: 150Mb L: 6/6 MS: 5 CrossOver-ChangeBit-CopyPart-ChangeBit-ChangeBinInt- +#1239 REDUCE cov: 102 ft: 106 corp: 6/24b lim: 14 exec/s: 0 rss: 150Mb L: 13/13 MS: 5 ChangeBit-CrossOver-EraseBytes-ChangeBit-InsertRepeatedBytes- +#1272 REDUCE cov: 102 ft: 106 corp: 6/23b lim: 14 exec/s: 0 rss: 150Mb L: 12/12 MS: 3 ChangeBinInt-ChangeBit-EraseBytes- + NEW_FUNC[1/677]: 0x55e11f456690 in std::_Function_base::~_Function_base() /usr/lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/bits/std_function.h:255 + NEW_FUNC[2/677]: 0x55e11f465800 in CDataStream::CDataStream(std::vector<unsigned char, std::allocator<unsigned char> > const&, int, int) src/./streams.h:248 +#2125 REDUCE cov: 4820 ft: 4867 corp: 7/29b lim: 21 exec/s: 0 rss: 155Mb L: 6/12 MS: 2 CopyPart-CMP- DE: "block"- + NEW_FUNC[1/9]: 0x55e11f64d790 in std::_Rb_tree<uint256, std::pair<uint256 const, std::chrono::duration<long, std::ratio<1l, 1000000l> > >, std::_Select1st<std::pair<uint256 const, std::chrono::duration<long, std::ratio<1l, 1000000l> > > >, std::less<uint256>, std::allocator<std::pair<uint256 const, std::chrono::duration<long, std::ratio<1l, 1000000l> > > > >::~_Rb_tree() /usr/lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/bits/stl_tree.h:972 + NEW_FUNC[2/9]: 0x55e11f64d870 in std::_Rb_tree<uint256, std::pair<uint256 const, std::chrono::duration<long, std::ratio<1l, 1000000l> > >, std::_Select1st<std::pair<uint256 const, std::chrono::duration<long, std::ratio<1l, 1000000l> > > >, std::less<uint256>, std::allocator<std::pair<uint256 const, std::chrono::duration<long, std::ratio<1l, 1000000l> > > > >::_M_erase(std::_Rb_tree_node<std::pair<uint256 const, std::chrono::duration<long, std::ratio<1l, 1000000l> > > >*) /usr/lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/bits/stl_tree.h:1875 +#2228 NEW cov: 4898 ft: 4971 corp: 8/35b lim: 21 exec/s: 0 rss: 156Mb L: 6/12 MS: 3 EraseBytes-CopyPart-PersAutoDict- DE: "block"- + NEW_FUNC[1/5]: 0x55e11f46df70 in std::enable_if<__and_<std::allocator_traits<zero_after_free_allocator<char> >::__construct_helper<char, unsigned char const&>::type>::value, void>::type std::allocator_traits<zero_after_free_allocator<char> >::_S_construct<char, unsigned char const&>(zero_after_free_allocator<char>&, char*, unsigned char const&) /usr/lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/bits/alloc_traits.h:243 + NEW_FUNC[2/5]: 0x55e11f477390 in std::vector<unsigned char, std::allocator<unsigned char> >::data() /usr/lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/bits/stl_vector.h:1056 +#2456 NEW cov: 4933 ft: 5042 corp: 9/55b lim: 21 exec/s: 0 rss: 160Mb L: 20/20 MS: 3 ChangeByte-InsertRepeatedBytes-PersAutoDict- DE: "block"- +#2467 NEW cov: 4933 ft: 5043 corp: 10/76b lim: 21 exec/s: 0 rss: 161Mb L: 21/21 MS: 1 InsertByte- +#4215 NEW cov: 4941 ft: 5129 corp: 17/205b lim: 29 exec/s: 4215 rss: 350Mb L: 29/29 MS: 5 InsertByte-ChangeBit-CopyPart-InsertRepeatedBytes-CrossOver- +#4567 REDUCE cov: 4941 ft: 5129 corp: 17/204b lim: 29 exec/s: 4567 rss: 404Mb L: 24/29 MS: 2 ChangeByte-EraseBytes- +#6642 NEW cov: 4941 ft: 5138 corp: 18/244b lim: 43 exec/s: 2214 rss: 450Mb L: 43/43 MS: 3 CopyPart-CMP-CrossOver- DE: "verack"- +# abort fuzzing using ctrl-c +$ ls process_message-seeded-from-thin-air/ +349ac589fc66a09abc0b72bb4ae445a7a19e2cd8 4df479f1f421f2ea64b383cd4919a272604087a7 +a640312c98dcc55d6744730c33e41c5168c55f09 b135de16e4709558c0797c15f86046d31c5d86d7 +c000f7b41b05139de8b63f4cbf7d1ad4c6e2aa7f fc52cc00ec1eb1c08470e69f809ae4993fa70082 +$ cat --show-nonprinting process_message-seeded-from-thin-air/349ac589fc66a09abc0b72bb4ae445a7a19e2cd8 +block^@M-^?M-^?M-^?M-^?M-^?nM-^?M-^? +``` + +In this case the fuzzer managed to create a `block` message which when passed to `ProcessMessage(...)` increased coverage. + +The project's collection of seed corpora is found in the [`bitcoin-core/qa-assets`](https://github.com/bitcoin-core/qa-assets) repo. + +To fuzz `process_message` using the [`bitcoin-core/qa-assets`](https://github.com/bitcoin-core/qa-assets) seed corpus: + +```sh +$ git clone https://github.com/bitcoin-core/qa-assets +$ src/test/fuzz/process_message qa-assets/fuzz_seed_corpus/process_message/ +INFO: Seed: 1346407872 +INFO: Loaded 1 modules (424174 inline 8-bit counters): 424174 [0x55d8a9004ab8, 0x55d8a906c3a6), +INFO: Loaded 1 PC tables (424174 PCs): 424174 [0x55d8a906c3a8,0x55d8a96e5288), +INFO: 991 files found in qa-assets/fuzz_seed_corpus/process_message/ +INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes +INFO: seed corpus: files: 991 min: 1b max: 1858b total: 288291b rss: 150Mb +#993 INITED cov: 7063 ft: 8236 corp: 25/3821b exec/s: 0 rss: 181Mb +… +``` + +If you find coverage increasing inputs when fuzzing you are highly encouraged to submit them for inclusion in the [`bitcoin-core/qa-assets`](https://github.com/bitcoin-core/qa-assets) repo. + +Every single pull request submitted against the Bitcoin Core repo is automatically tested against all inputs in the [`bitcoin-core/qa-assets`](https://github.com/bitcoin-core/qa-assets) repo. Contributing new coverage increasing inputs is an easy way to help make Bitcoin Core more robust. + +## macOS hints for libFuzzer + +The default Clang/LLVM version supplied by Apple on macOS does not include fuzzing libraries, so macOS users will need to install a full version, for example using `brew install llvm`. @@ -128,11 +96,40 @@ may need to run `./configure` with `--disable-asm` to avoid errors with certain assembly code from Bitcoin Core's code. See [developer notes on sanitizers](https://github.com/bitcoin/bitcoin/blob/master/doc/developer-notes.md#sanitizers) for more information. -You may also need to take care of giving the correct path for clang and -clang++, like `CC=/path/to/clang CXX=/path/to/clang++` if the non-systems -clang does not come first in your path. +You may also need to take care of giving the correct path for `clang` and +`clang++`, like `CC=/path/to/clang CXX=/path/to/clang++` if the non-systems +`clang` does not come first in your path. Full configure that was tested on macOS Catalina with `brew` installed `llvm`: -``` + +```sh ./configure --enable-fuzz --with-sanitizers=fuzzer,address,undefined CC=/usr/local/opt/llvm/bin/clang CXX=/usr/local/opt/llvm/bin/clang++ --disable-asm ``` + +Read the [libFuzzer documentation](https://llvm.org/docs/LibFuzzer.html) for more information. This [libFuzzer tutorial](https://github.com/google/fuzzing/blob/master/tutorial/libFuzzerTutorial.md) might also be of interest. + +# Fuzzing Bitcoin Core using american fuzzy lop (`afl-fuzz`) + +## Quickstart guide + +To quickly get started fuzzing Bitcoin Core using [`afl-fuzz`](https://github.com/google/afl): + +```sh +$ git clone https://github.com/bitcoin/bitcoin +$ cd bitcoin/ +$ git clone https://github.com/google/afl +$ make -C afl/ +$ make -C afl/llvm_mode/ +$ ./autogen.sh +$ CC=$(pwd)/afl/afl-clang-fast CXX=$(pwd)/afl/afl-clang-fast++ ./configure --enable-fuzz +$ make +# For macOS you may need to ignore x86 compilation checks when running "make". If so, +# try compiling using: AFL_NO_X86=1 make +$ mkdir -p inputs/ outputs/ +$ echo A > inputs/thin-air-input +$ afl/afl-fuzz -i inputs/ -o outputs/ -- src/test/fuzz/bech32 +# You may have to change a few kernel parameters to test optimally - afl-fuzz +# will print an error and suggestion if so. +``` + +Read the [`afl-fuzz` documentation](https://github.com/google/afl) for more information. diff --git a/src/Makefile.test.include b/src/Makefile.test.include index e92b02a9bc..45077ccbd9 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -4,6 +4,7 @@ FUZZ_TARGETS = \ test/fuzz/addr_info_deserialize \ + test/fuzz/addrdb \ test/fuzz/address_deserialize \ test/fuzz/addrman_deserialize \ test/fuzz/asmap \ @@ -16,6 +17,7 @@ FUZZ_TARGETS = \ test/fuzz/block_filter_deserialize \ test/fuzz/block_header \ test/fuzz/block_header_and_short_txids_deserialize \ + test/fuzz/blockfilter \ test/fuzz/blockheader_deserialize \ test/fuzz/blocklocator_deserialize \ test/fuzz/blockmerkleroot \ @@ -43,6 +45,7 @@ FUZZ_TARGETS = \ test/fuzz/merkle_block_deserialize \ test/fuzz/messageheader_deserialize \ test/fuzz/multiplication_overflow \ + test/fuzz/net_permissions \ test/fuzz/netaddr_deserialize \ test/fuzz/netaddress \ test/fuzz/out_point_deserialize \ @@ -97,6 +100,7 @@ FUZZ_TARGETS = \ test/fuzz/string \ test/fuzz/strprintf \ test/fuzz/sub_net_deserialize \ + test/fuzz/timedata \ test/fuzz/transaction \ test/fuzz/tx_in \ test/fuzz/tx_in_deserialize \ @@ -288,6 +292,12 @@ test_fuzz_addr_info_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) test_fuzz_addr_info_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) test_fuzz_addr_info_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_addrdb_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +test_fuzz_addrdb_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_addrdb_LDADD = $(FUZZ_SUITE_LD_COMMON) +test_fuzz_addrdb_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_addrdb_SOURCES = $(FUZZ_SUITE) test/fuzz/addrdb.cpp + test_fuzz_address_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DADDRESS_DESERIALIZE=1 test_fuzz_address_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) test_fuzz_address_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) @@ -360,6 +370,12 @@ test_fuzz_block_header_and_short_txids_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMO test_fuzz_block_header_and_short_txids_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) test_fuzz_block_header_and_short_txids_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_blockfilter_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +test_fuzz_blockfilter_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_blockfilter_LDADD = $(FUZZ_SUITE_LD_COMMON) +test_fuzz_blockfilter_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_blockfilter_SOURCES = $(FUZZ_SUITE) test/fuzz/blockfilter.cpp + test_fuzz_blockheader_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCKHEADER_DESERIALIZE=1 test_fuzz_blockheader_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) test_fuzz_blockheader_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) @@ -522,6 +538,12 @@ test_fuzz_multiplication_overflow_LDADD = $(FUZZ_SUITE_LD_COMMON) test_fuzz_multiplication_overflow_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) test_fuzz_multiplication_overflow_SOURCES = $(FUZZ_SUITE) test/fuzz/multiplication_overflow.cpp +test_fuzz_net_permissions_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +test_fuzz_net_permissions_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_net_permissions_LDADD = $(FUZZ_SUITE_LD_COMMON) +test_fuzz_net_permissions_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_net_permissions_SOURCES = $(FUZZ_SUITE) test/fuzz/net_permissions.cpp + test_fuzz_netaddr_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DNETADDR_DESERIALIZE=1 test_fuzz_netaddr_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) test_fuzz_netaddr_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) @@ -846,6 +868,12 @@ test_fuzz_sub_net_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) test_fuzz_sub_net_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) test_fuzz_sub_net_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_timedata_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +test_fuzz_timedata_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_timedata_LDADD = $(FUZZ_SUITE_LD_COMMON) +test_fuzz_timedata_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_timedata_SOURCES = $(FUZZ_SUITE) test/fuzz/timedata.cpp + test_fuzz_transaction_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) test_fuzz_transaction_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) test_fuzz_transaction_LDADD = $(FUZZ_SUITE_LD_COMMON) diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp index d16f4e7ccc..9dc0d37cd9 100644 --- a/src/interfaces/chain.cpp +++ b/src/interfaces/chain.cpp @@ -166,25 +166,25 @@ public: } void TransactionAddedToMempool(const CTransactionRef& tx) override { - m_notifications->TransactionAddedToMempool(tx); + m_notifications->transactionAddedToMempool(tx); } void TransactionRemovedFromMempool(const CTransactionRef& tx) override { - m_notifications->TransactionRemovedFromMempool(tx); + m_notifications->transactionRemovedFromMempool(tx); } void BlockConnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* index) override { - m_notifications->BlockConnected(*block, index->nHeight); + m_notifications->blockConnected(*block, index->nHeight); } void BlockDisconnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* index) override { - m_notifications->BlockDisconnected(*block, index->nHeight); + m_notifications->blockDisconnected(*block, index->nHeight); } void UpdatedBlockTip(const CBlockIndex* index, const CBlockIndex* fork_index, bool is_ibd) override { - m_notifications->UpdatedBlockTip(); + m_notifications->updatedBlockTip(); } - void ChainStateFlushed(const CBlockLocator& locator) override { m_notifications->ChainStateFlushed(locator); } + void ChainStateFlushed(const CBlockLocator& locator) override { m_notifications->chainStateFlushed(locator); } Chain& m_chain; Chain::Notifications* m_notifications; }; @@ -278,7 +278,10 @@ public: auto it = ::mempool.GetIter(txid); return it && (*it)->GetCountWithDescendants() > 1; } - bool broadcastTransaction(const CTransactionRef& tx, std::string& err_string, const CAmount& max_tx_fee, bool relay) override + bool broadcastTransaction(const CTransactionRef& tx, + const CAmount& max_tx_fee, + bool relay, + std::string& err_string) override { const TransactionError err = BroadcastTransaction(m_node, tx, err_string, max_tx_fee, relay, /*wait_callback*/ false); // Chain clients only care about failures to accept the tx to the mempool. Disregard non-mempool related failures. @@ -366,7 +369,7 @@ public: { LOCK2(::cs_main, ::mempool.cs); for (const CTxMemPoolEntry& entry : ::mempool.mapTx) { - notifications.TransactionAddedToMempool(entry.GetSharedTx()); + notifications.transactionAddedToMempool(entry.GetSharedTx()); } } NodeContext& m_node; diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h index 03a600d5a3..caefa87e11 100644 --- a/src/interfaces/chain.h +++ b/src/interfaces/chain.h @@ -154,7 +154,10 @@ public: //! Transaction is added to memory pool, if the transaction fee is below the //! amount specified by max_tx_fee, and broadcast to all peers if relay is set to true. //! Return false if the transaction could not be added due to the fee or for another reason. - virtual bool broadcastTransaction(const CTransactionRef& tx, std::string& err_string, const CAmount& max_tx_fee, bool relay) = 0; + virtual bool broadcastTransaction(const CTransactionRef& tx, + const CAmount& max_tx_fee, + bool relay, + std::string& err_string) = 0; //! Calculate mempool ancestor and descendant counts for the given transaction. virtual void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) = 0; @@ -217,12 +220,12 @@ public: { public: virtual ~Notifications() {} - virtual void TransactionAddedToMempool(const CTransactionRef& tx) {} - virtual void TransactionRemovedFromMempool(const CTransactionRef& ptx) {} - virtual void BlockConnected(const CBlock& block, int height) {} - virtual void BlockDisconnected(const CBlock& block, int height) {} - virtual void UpdatedBlockTip() {} - virtual void ChainStateFlushed(const CBlockLocator& locator) {} + virtual void transactionAddedToMempool(const CTransactionRef& tx) {} + virtual void transactionRemovedFromMempool(const CTransactionRef& ptx) {} + virtual void blockConnected(const CBlock& block, int height) {} + virtual void blockDisconnected(const CBlock& block, int height) {} + virtual void updatedBlockTip() {} + virtual void chainStateFlushed(const CBlockLocator& locator) {} }; //! Register handler for notifications. @@ -245,7 +248,7 @@ public: //! Current RPC serialization flags. virtual int rpcSerializationFlags() = 0; - //! Synchronously send TransactionAddedToMempool notifications about all + //! Synchronously send transactionAddedToMempool notifications about all //! current mempool transactions to the specified handler and return after //! the last one is sent. These notifications aren't coordinated with async //! notifications sent by handleNotifications, so out of date async diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp index b720a63017..905173d20b 100644 --- a/src/interfaces/node.cpp +++ b/src/interfaces/node.cpp @@ -152,14 +152,14 @@ public: } return false; } - bool disconnect(const CNetAddr& net_addr) override + bool disconnectByAddress(const CNetAddr& net_addr) override { if (m_context.connman) { return m_context.connman->DisconnectNode(net_addr); } return false; } - bool disconnect(NodeId id) override + bool disconnectById(NodeId id) override { if (m_context.connman) { return m_context.connman->DisconnectNode(id); @@ -262,12 +262,11 @@ public: { return MakeWallet(LoadWallet(*m_context.chain, name, error, warnings)); } - WalletCreationStatus createWallet(const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, std::string& error, std::vector<std::string>& warnings, std::unique_ptr<Wallet>& result) override + std::unique_ptr<Wallet> createWallet(const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, std::string& error, std::vector<std::string>& warnings, WalletCreationStatus& status) override { std::shared_ptr<CWallet> wallet; - WalletCreationStatus status = CreateWallet(*m_context.chain, passphrase, wallet_creation_flags, name, error, warnings, wallet); - result = MakeWallet(wallet); - return status; + status = CreateWallet(*m_context.chain, passphrase, wallet_creation_flags, name, error, warnings, wallet); + return MakeWallet(wallet); } std::unique_ptr<Handler> handleInitMessage(InitMessageFn fn) override { diff --git a/src/interfaces/node.h b/src/interfaces/node.h index 38aeb06324..53a20886cd 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -124,10 +124,10 @@ public: virtual bool unban(const CSubNet& ip) = 0; //! Disconnect node by address. - virtual bool disconnect(const CNetAddr& net_addr) = 0; + virtual bool disconnectByAddress(const CNetAddr& net_addr) = 0; //! Disconnect node by id. - virtual bool disconnect(NodeId id) = 0; + virtual bool disconnectById(NodeId id) = 0; //! Get total bytes recv. virtual int64_t getTotalBytesRecv() = 0; @@ -204,7 +204,7 @@ public: virtual std::unique_ptr<Wallet> loadWallet(const std::string& name, std::string& error, std::vector<std::string>& warnings) = 0; //! Create a wallet from file - virtual WalletCreationStatus createWallet(const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, std::string& error, std::vector<std::string>& warnings, std::unique_ptr<Wallet>& result) = 0; + virtual std::unique_ptr<Wallet> createWallet(const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, std::string& error, std::vector<std::string>& warnings, WalletCreationStatus& status) = 0; //! Register handler for init messages. using InitMessageFn = std::function<void(const std::string& message)>; diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp index 01ade56b2a..c3a62cf73d 100644 --- a/src/interfaces/wallet.cpp +++ b/src/interfaces/wallet.cpp @@ -352,11 +352,11 @@ public: } return {}; } - TransactionError fillPSBT(PartiallySignedTransaction& psbtx, - bool& complete, - int sighash_type = 1 /* SIGHASH_ALL */, - bool sign = true, - bool bip32derivs = false) const override + TransactionError fillPSBT(int sighash_type, + bool sign, + bool bip32derivs, + PartiallySignedTransaction& psbtx, + bool& complete) override { return m_wallet->FillPSBT(psbtx, complete, sighash_type, sign, bip32derivs); } @@ -463,8 +463,8 @@ public: } unsigned int getConfirmTarget() override { return m_wallet->m_confirm_target; } bool hdEnabled() override { return m_wallet->IsHDEnabled(); } - bool canGetAddresses() const override { return m_wallet->CanGetAddresses(); } - bool IsWalletFlagSet(uint64_t flag) override { return m_wallet->IsWalletFlagSet(flag); } + bool canGetAddresses() override { return m_wallet->CanGetAddresses(); } + bool privateKeysDisabled() override { return m_wallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS); } OutputType getDefaultAddressType() override { return m_wallet->m_default_address_type; } OutputType getDefaultChangeType() override { return m_wallet->m_default_change_type; } CAmount getDefaultMaxTxFee() override { return m_wallet->m_default_max_tx_fee; } diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h index 9476c9f77f..487a7c3a5a 100644 --- a/src/interfaces/wallet.h +++ b/src/interfaces/wallet.h @@ -193,11 +193,11 @@ public: int& num_blocks) = 0; //! Fill PSBT. - virtual TransactionError fillPSBT(PartiallySignedTransaction& psbtx, - bool& complete, - int sighash_type = 1 /* SIGHASH_ALL */, - bool sign = true, - bool bip32derivs = false) const = 0; + virtual TransactionError fillPSBT(int sighash_type, + bool sign, + bool bip32derivs, + PartiallySignedTransaction& psbtx, + bool& complete) = 0; //! Get balances. virtual WalletBalances getBalances() = 0; @@ -247,10 +247,10 @@ public: virtual bool hdEnabled() = 0; // Return whether the wallet is blank. - virtual bool canGetAddresses() const = 0; + virtual bool canGetAddresses() = 0; - // check if a certain wallet flag is set. - virtual bool IsWalletFlagSet(uint64_t flag) = 0; + // Return whether private keys enabled. + virtual bool privateKeysDisabled() = 0; // Get default address type. virtual OutputType getDefaultAddressType() = 0; diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 5fab267610..2918676c22 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -1258,7 +1258,7 @@ void BitcoinGUI::updateWalletStatus() } WalletModel * const walletModel = walletView->getWalletModel(); setEncryptionStatus(walletModel->getEncryptionStatus()); - setHDStatus(walletModel->privateKeysDisabled(), walletModel->wallet().hdEnabled()); + setHDStatus(walletModel->wallet().privateKeysDisabled(), walletModel->wallet().hdEnabled()); } #endif // ENABLE_WALLET diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui index ebb6bbd4f5..8b70800838 100644 --- a/src/qt/forms/debugwindow.ui +++ b/src/qt/forms/debugwindow.ui @@ -216,17 +216,17 @@ </widget> </item> <item row="7" column="0"> - <widget class="QLabel" name="labelNetwork"> - <property name="font"> - <font> - <weight>75</weight> - <bold>true</bold> - </font> - </property> - <property name="text"> - <string>Network</string> - </property> - </widget> + <widget class="QLabel" name="labelNetwork"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>Network</string> + </property> + </widget> </item> <item row="8" column="0"> <widget class="QLabel" name="label_8"> @@ -503,12 +503,12 @@ <height>24</height> </size> </property> - <property name="text"> - <string/> - </property> <property name="toolTip"> <string>Decrease font size</string> </property> + <property name="text"> + <string/> + </property> <property name="icon"> <iconset resource="../bitcoin.qrc"> <normaloff>:/icons/fontsmaller</normaloff>:/icons/fontsmaller</iconset> @@ -652,12 +652,12 @@ </item> <item> <widget class="QLineEdit" name="lineEdit"> - <property name="placeholderText"> - <string/> - </property> <property name="enabled"> <bool>false</bool> </property> + <property name="placeholderText"> + <string/> + </property> </widget> </item> </layout> @@ -1503,6 +1503,32 @@ </widget> </item> <item row="18" column="0"> + <widget class="QLabel" name="peerMappedASLabel"> + <property name="toolTip"> + <string>The mapped Autonomous System used for diversifying peer selection.</string> + </property> + <property name="text"> + <string>Mapped AS</string> + </property> + </widget> + </item> + <item row="18" column="1"> + <widget class="QLabel" name="peerMappedAS"> + <property name="cursor"> + <cursorShape>IBeamCursor</cursorShape> + </property> + <property name="text"> + <string>N/A</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item row="19" column="0"> <spacer name="verticalSpacer_3"> <property name="orientation"> <enum>Qt::Vertical</enum> diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 342c7cce31..c376921b72 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -161,7 +161,7 @@ void OverviewPage::setBalance(const interfaces::WalletBalances& balances) { int unit = walletModel->getOptionsModel()->getDisplayUnit(); m_balances = balances; - if (walletModel->privateKeysDisabled()) { + if (walletModel->wallet().privateKeysDisabled()) { ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance, false, BitcoinUnits::separatorAlways)); ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, balances.unconfirmed_watch_only_balance, false, BitcoinUnits::separatorAlways)); ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways)); @@ -184,7 +184,7 @@ void OverviewPage::setBalance(const interfaces::WalletBalances& balances) // for symmetry reasons also show immature label when the watch-only one is shown ui->labelImmature->setVisible(showImmature || showWatchOnlyImmature); ui->labelImmatureText->setVisible(showImmature || showWatchOnlyImmature); - ui->labelWatchImmature->setVisible(!walletModel->privateKeysDisabled() && showWatchOnlyImmature); // show watch-only immature balance + ui->labelWatchImmature->setVisible(!walletModel->wallet().privateKeysDisabled() && showWatchOnlyImmature); // show watch-only immature balance } // show/hide watch-only labels @@ -236,9 +236,9 @@ void OverviewPage::setWalletModel(WalletModel *model) connect(model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &OverviewPage::updateDisplayUnit); - updateWatchOnlyLabels(wallet.haveWatchOnly() && !model->privateKeysDisabled()); + updateWatchOnlyLabels(wallet.haveWatchOnly() && !model->wallet().privateKeysDisabled()); connect(model, &WalletModel::notifyWatchonlyChanged, [this](bool showWatchOnly) { - updateWatchOnlyLabels(showWatchOnly && !walletModel->privateKeysDisabled()); + updateWatchOnlyLabels(showWatchOnly && !walletModel->wallet().privateKeysDisabled()); }); } diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp index 16597e4758..180550c5ae 100644 --- a/src/qt/receivecoinsdialog.cpp +++ b/src/qt/receivecoinsdialog.cpp @@ -99,11 +99,11 @@ void ReceiveCoinsDialog::setModel(WalletModel *_model) } // Set the button to be enabled or disabled based on whether the wallet can give out new addresses. - ui->receiveButton->setEnabled(model->canGetAddresses()); + ui->receiveButton->setEnabled(model->wallet().canGetAddresses()); // Enable/disable the receive button if the wallet is now able/unable to give out new addresses. connect(model, &WalletModel::canGetAddressesChanged, [this] { - ui->receiveButton->setEnabled(model->canGetAddresses()); + ui->receiveButton->setEnabled(model->wallet().canGetAddresses()); }); } } diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index b82ab9ffe8..0ffdc892c5 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -1113,11 +1113,12 @@ void RPCConsole::updateNodeDetail(const CNodeCombinedStats *stats) ui->peerPingWait->setText(GUIUtil::formatPingTime(stats->nodeStats.m_ping_wait_usec)); ui->peerMinPing->setText(GUIUtil::formatPingTime(stats->nodeStats.m_min_ping_usec)); ui->timeoffset->setText(GUIUtil::formatTimeOffset(stats->nodeStats.nTimeOffset)); - ui->peerVersion->setText(QString("%1").arg(QString::number(stats->nodeStats.nVersion))); + ui->peerVersion->setText(QString::number(stats->nodeStats.nVersion)); ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer)); ui->peerDirection->setText(stats->nodeStats.fInbound ? tr("Inbound") : tr("Outbound")); - ui->peerHeight->setText(QString("%1").arg(QString::number(stats->nodeStats.nStartingHeight))); + ui->peerHeight->setText(QString::number(stats->nodeStats.nStartingHeight)); ui->peerWhitelisted->setText(stats->nodeStats.m_legacyWhitelisted ? tr("Yes") : tr("No")); + ui->peerMappedAS->setText(stats->nodeStats.m_mapped_as != 0 ? QString::number(stats->nodeStats.m_mapped_as) : tr("N/A")); // This check fails for example if the lock was busy and // nodeStateStats couldn't be fetched. @@ -1191,7 +1192,7 @@ void RPCConsole::disconnectSelectedNode() // Get currently selected peer address NodeId id = nodes.at(i).data().toLongLong(); // Find the node, disconnect it and clear the selected node - if(m_node.disconnect(id)) + if(m_node.disconnectById(id)) clearSelectedNode(); } } @@ -1216,7 +1217,7 @@ void RPCConsole::banSelectedNode(int bantime) const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(detailNodeRow); if (stats) { m_node.ban(stats->nodeStats.addr, BanReasonManuallyAdded, bantime); - m_node.disconnect(stats->nodeStats.addr); + m_node.disconnectByAddress(stats->nodeStats.addr); } } clearSelectedNode(); diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 4ddee513a1..a8c82aaf6c 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -187,7 +187,7 @@ void SendCoinsDialog::setModel(WalletModel *_model) // set default rbf checkbox state ui->optInRBF->setCheckState(Qt::Checked); - if (model->privateKeysDisabled()) { + if (model->wallet().privateKeysDisabled()) { ui->sendButton->setText(tr("Cr&eate Unsigned")); ui->sendButton->setToolTip(tr("Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(PACKAGE_NAME)); } @@ -312,14 +312,14 @@ void SendCoinsDialog::on_sendButton_clicked() } QString questionString; - if (model->privateKeysDisabled()) { + if (model->wallet().privateKeysDisabled()) { questionString.append(tr("Do you want to draft this transaction?")); } else { questionString.append(tr("Are you sure you want to send?")); } questionString.append("<br /><span style='font-size:10pt;'>"); - if (model->privateKeysDisabled()) { + if (model->wallet().privateKeysDisabled()) { questionString.append(tr("Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(PACKAGE_NAME)); } else { questionString.append(tr("Please, review your transaction.")); @@ -374,8 +374,8 @@ void SendCoinsDialog::on_sendButton_clicked() } else { questionString = questionString.arg("<br /><br />" + formatted.at(0)); } - const QString confirmation = model->privateKeysDisabled() ? tr("Confirm transaction proposal") : tr("Confirm send coins"); - const QString confirmButtonText = model->privateKeysDisabled() ? tr("Copy PSBT to clipboard") : tr("Send"); + const QString confirmation = model->wallet().privateKeysDisabled() ? tr("Confirm transaction proposal") : tr("Confirm send coins"); + const QString confirmButtonText = model->wallet().privateKeysDisabled() ? tr("Copy PSBT to clipboard") : tr("Send"); SendConfirmationDialog confirmationDialog(confirmation, questionString, informative_text, detailed_text, SEND_CONFIRM_DELAY, confirmButtonText, this); confirmationDialog.exec(); QMessageBox::StandardButton retval = static_cast<QMessageBox::StandardButton>(confirmationDialog.result()); @@ -387,11 +387,11 @@ void SendCoinsDialog::on_sendButton_clicked() } bool send_failure = false; - if (model->privateKeysDisabled()) { + if (model->wallet().privateKeysDisabled()) { CMutableTransaction mtx = CMutableTransaction{*(currentTransaction.getWtx())}; PartiallySignedTransaction psbtx(mtx); bool complete = false; - const TransactionError err = model->wallet().fillPSBT(psbtx, complete, SIGHASH_ALL, false /* sign */, true /* bip32derivs */); + const TransactionError err = model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, psbtx, complete); assert(!complete); assert(err == TransactionError::OK); // Serialize the PSBT @@ -562,7 +562,7 @@ void SendCoinsDialog::setBalance(const interfaces::WalletBalances& balances) if(model && model->getOptionsModel()) { CAmount balance = balances.balance; - if (model->privateKeysDisabled()) { + if (model->wallet().privateKeysDisabled()) { balance = balances.watch_only_balance; ui->labelBalanceName->setText(tr("Watch-only balance:")); } @@ -652,7 +652,7 @@ void SendCoinsDialog::useAvailableBalance(SendCoinsEntry* entry) } // Include watch-only for wallets without private key - coin_control.fAllowWatchOnly = model->privateKeysDisabled(); + coin_control.fAllowWatchOnly = model->wallet().privateKeysDisabled(); // Calculate available amount to send. CAmount amount = model->wallet().getAvailableBalance(coin_control); @@ -707,7 +707,7 @@ void SendCoinsDialog::updateCoinControlState(CCoinControl& ctrl) ctrl.m_confirm_target = getConfTargetForIndex(ui->confTargetSelector->currentIndex()); ctrl.m_signal_bip125_rbf = ui->optInRBF->isChecked(); // Include watch-only for wallets without private key - ctrl.fAllowWatchOnly = model->privateKeysDisabled(); + ctrl.fAllowWatchOnly = model->wallet().privateKeysDisabled(); } void SendCoinsDialog::updateSmartFeeLabel() diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp index 7413a1f09e..233c0ab6be 100644 --- a/src/qt/walletcontroller.cpp +++ b/src/qt/walletcontroller.cpp @@ -218,8 +218,8 @@ void CreateWalletActivity::createWallet() } QTimer::singleShot(500, worker(), [this, name, flags] { - std::unique_ptr<interfaces::Wallet> wallet; - WalletCreationStatus status = node().createWallet(m_passphrase, flags, name, m_error_message, m_warning_message, wallet); + WalletCreationStatus status; + std::unique_ptr<interfaces::Wallet> wallet = node().createWallet(m_passphrase, flags, name, m_error_message, m_warning_message, status); if (status == WalletCreationStatus::SUCCESS) m_wallet_model = m_wallet_controller->getOrCreateWallet(std::move(wallet)); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 8a84a8c168..94e7a05e40 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -23,7 +23,7 @@ #include <ui_interface.h> #include <util/system.h> // for GetBoolArg #include <wallet/coincontrol.h> -#include <wallet/wallet.h> +#include <wallet/wallet.h> // for CRecipient #include <stdint.h> @@ -184,7 +184,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact std::string strFailReason; auto& newTx = transaction.getWtx(); - newTx = m_wallet->createTransaction(vecSend, coinControl, !privateKeysDisabled() /* sign */, nChangePosRet, nFeeRequired, strFailReason); + newTx = m_wallet->createTransaction(vecSend, coinControl, !wallet().privateKeysDisabled() /* sign */, nChangePosRet, nFeeRequired, strFailReason); transaction.setTransactionFee(nFeeRequired); if (fSubtractFeeFromAmount && newTx) transaction.reassignAmounts(nChangePosRet); @@ -488,7 +488,7 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash) return false; } - const bool create_psbt = privateKeysDisabled(); + const bool create_psbt = m_wallet->privateKeysDisabled(); // allow a user based fee verification QString questionString = create_psbt ? tr("Do you want to draft a transaction with fee increase?") : tr("Do you want to increase the fee?"); @@ -526,7 +526,7 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash) if (create_psbt) { PartiallySignedTransaction psbtx(mtx); bool complete = false; - const TransactionError err = wallet().fillPSBT(psbtx, complete, SIGHASH_ALL, false /* sign */, true /* bip32derivs */); + const TransactionError err = wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, psbtx, complete); if (err != TransactionError::OK || complete) { QMessageBox::critical(nullptr, tr("Fee bump error"), tr("Can't draft transaction.")); return false; @@ -558,16 +558,6 @@ bool WalletModel::isWalletEnabled() return !gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET); } -bool WalletModel::privateKeysDisabled() const -{ - return m_wallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS); -} - -bool WalletModel::canGetAddresses() const -{ - return m_wallet->canGetAddresses(); -} - QString WalletModel::getWalletName() const { return QString::fromStdString(m_wallet->getWalletName()); diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 8087356f5e..7936014af9 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -140,8 +140,6 @@ public: bool bumpFee(uint256 hash, uint256& new_hash); static bool isWalletEnabled(); - bool privateKeysDisabled() const; - bool canGetAddresses() const; interfaces::Node& node() const { return m_node; } interfaces::Wallet& wallet() const { return *m_wallet; } diff --git a/src/test/fuzz/addrdb.cpp b/src/test/fuzz/addrdb.cpp new file mode 100644 index 0000000000..f21ff3fac3 --- /dev/null +++ b/src/test/fuzz/addrdb.cpp @@ -0,0 +1,43 @@ +// Copyright (c) 2020 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 <addrdb.h> +#include <optional.h> +#include <test/fuzz/FuzzedDataProvider.h> +#include <test/fuzz/fuzz.h> +#include <test/fuzz/util.h> + +#include <cassert> +#include <cstdint> +#include <string> +#include <vector> + +void test_one_input(const std::vector<uint8_t>& buffer) +{ + FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); + + const CBanEntry ban_entry = [&] { + switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 3)) { + case 0: + return CBanEntry{fuzzed_data_provider.ConsumeIntegral<int64_t>()}; + break; + case 1: + return CBanEntry{fuzzed_data_provider.ConsumeIntegral<int64_t>(), fuzzed_data_provider.PickValueInArray<BanReason>({ + BanReason::BanReasonUnknown, + BanReason::BanReasonNodeMisbehaving, + BanReason::BanReasonManuallyAdded, + })}; + break; + case 2: { + const Optional<CBanEntry> ban_entry = ConsumeDeserializable<CBanEntry>(fuzzed_data_provider); + if (ban_entry) { + return *ban_entry; + } + break; + } + } + return CBanEntry{}; + }(); + assert(!ban_entry.banReasonToString().empty()); +} diff --git a/src/test/fuzz/blockfilter.cpp b/src/test/fuzz/blockfilter.cpp new file mode 100644 index 0000000000..be9320dcbf --- /dev/null +++ b/src/test/fuzz/blockfilter.cpp @@ -0,0 +1,44 @@ +// Copyright (c) 2020 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 <blockfilter.h> +#include <optional.h> +#include <test/fuzz/FuzzedDataProvider.h> +#include <test/fuzz/fuzz.h> +#include <test/fuzz/util.h> + +#include <cstdint> +#include <string> +#include <vector> + +void test_one_input(const std::vector<uint8_t>& buffer) +{ + FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); + const Optional<BlockFilter> block_filter = ConsumeDeserializable<BlockFilter>(fuzzed_data_provider); + if (!block_filter) { + return; + } + { + (void)block_filter->ComputeHeader(ConsumeUInt256(fuzzed_data_provider)); + (void)block_filter->GetBlockHash(); + (void)block_filter->GetEncodedFilter(); + (void)block_filter->GetHash(); + } + { + const BlockFilterType block_filter_type = block_filter->GetFilterType(); + (void)BlockFilterTypeName(block_filter_type); + } + { + const GCSFilter gcs_filter = block_filter->GetFilter(); + (void)gcs_filter.GetN(); + (void)gcs_filter.GetParams(); + (void)gcs_filter.GetEncoded(); + (void)gcs_filter.Match(ConsumeRandomLengthByteVector(fuzzed_data_provider)); + GCSFilter::ElementSet element_set; + while (fuzzed_data_provider.ConsumeBool()) { + element_set.insert(ConsumeRandomLengthByteVector(fuzzed_data_provider)); + gcs_filter.MatchAny(element_set); + } + } +} diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp index 24459c21be..63b9296574 100644 --- a/src/test/fuzz/integer.cpp +++ b/src/test/fuzz/integer.cpp @@ -227,4 +227,44 @@ void test_one_input(const std::vector<uint8_t>& buffer) (void)HasAllDesirableServiceFlags(service_flags); (void)MayHaveUsefulAddressDB(service_flags); } + + { + CDataStream stream(SER_NETWORK, INIT_PROTO_VERSION); + + ser_writedata64(stream, u64); + const uint64_t deserialized_u64 = ser_readdata64(stream); + assert(u64 == deserialized_u64 && stream.empty()); + + ser_writedata32(stream, u32); + const uint32_t deserialized_u32 = ser_readdata32(stream); + assert(u32 == deserialized_u32 && stream.empty()); + + ser_writedata32be(stream, u32); + const uint32_t deserialized_u32be = ser_readdata32be(stream); + assert(u32 == deserialized_u32be && stream.empty()); + + ser_writedata16(stream, u16); + const uint16_t deserialized_u16 = ser_readdata16(stream); + assert(u16 == deserialized_u16 && stream.empty()); + + ser_writedata16be(stream, u16); + const uint16_t deserialized_u16be = ser_readdata16be(stream); + assert(u16 == deserialized_u16be && stream.empty()); + + ser_writedata8(stream, u8); + const uint8_t deserialized_u8 = ser_readdata8(stream); + assert(u8 == deserialized_u8 && stream.empty()); + } + + { + CDataStream stream(SER_NETWORK, INIT_PROTO_VERSION); + + WriteCompactSize(stream, u64); + try { + const uint64_t deserialized_u64 = ReadCompactSize(stream); + assert(u64 == deserialized_u64 && stream.empty()); + } + catch (const std::ios_base::failure&) { + } + } } diff --git a/src/test/fuzz/multiplication_overflow.cpp b/src/test/fuzz/multiplication_overflow.cpp index 923db8058b..a4b158c18b 100644 --- a/src/test/fuzz/multiplication_overflow.cpp +++ b/src/test/fuzz/multiplication_overflow.cpp @@ -10,6 +10,14 @@ #include <string> #include <vector> +#if defined(__has_builtin) +#if __has_builtin(__builtin_mul_overflow) +#define HAVE_BUILTIN_MUL_OVERFLOW +#endif +#elif defined(__GNUC__) && (__GNUC__ >= 5) +#define HAVE_BUILTIN_MUL_OVERFLOW +#endif + namespace { template <typename T> void TestMultiplicationOverflow(FuzzedDataProvider& fuzzed_data_provider) @@ -17,12 +25,18 @@ void TestMultiplicationOverflow(FuzzedDataProvider& fuzzed_data_provider) const T i = fuzzed_data_provider.ConsumeIntegral<T>(); const T j = fuzzed_data_provider.ConsumeIntegral<T>(); const bool is_multiplication_overflow_custom = MultiplicationOverflow(i, j); +#if defined(HAVE_BUILTIN_MUL_OVERFLOW) T result_builtin; const bool is_multiplication_overflow_builtin = __builtin_mul_overflow(i, j, &result_builtin); assert(is_multiplication_overflow_custom == is_multiplication_overflow_builtin); if (!is_multiplication_overflow_custom) { assert(i * j == result_builtin); } +#else + if (!is_multiplication_overflow_custom) { + (void)(i * j); + } +#endif } } // namespace @@ -38,5 +52,4 @@ void test_one_input(const std::vector<uint8_t>& buffer) TestMultiplicationOverflow<char>(fuzzed_data_provider); TestMultiplicationOverflow<unsigned char>(fuzzed_data_provider); TestMultiplicationOverflow<signed char>(fuzzed_data_provider); - TestMultiplicationOverflow<bool>(fuzzed_data_provider); } diff --git a/src/test/fuzz/net_permissions.cpp b/src/test/fuzz/net_permissions.cpp new file mode 100644 index 0000000000..bfc5d21427 --- /dev/null +++ b/src/test/fuzz/net_permissions.cpp @@ -0,0 +1,51 @@ +// Copyright (c) 2020 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 <net_permissions.h> +#include <optional.h> +#include <test/fuzz/FuzzedDataProvider.h> +#include <test/fuzz/fuzz.h> +#include <test/fuzz/util.h> + +#include <cassert> +#include <cstdint> +#include <string> +#include <vector> + +void test_one_input(const std::vector<uint8_t>& buffer) +{ + FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); + const std::string s = fuzzed_data_provider.ConsumeRandomLengthString(32); + const NetPermissionFlags net_permission_flags = fuzzed_data_provider.ConsumeBool() ? fuzzed_data_provider.PickValueInArray<NetPermissionFlags>({ + NetPermissionFlags::PF_NONE, + NetPermissionFlags::PF_BLOOMFILTER, + NetPermissionFlags::PF_RELAY, + NetPermissionFlags::PF_FORCERELAY, + NetPermissionFlags::PF_NOBAN, + NetPermissionFlags::PF_MEMPOOL, + NetPermissionFlags::PF_ISIMPLICIT, + NetPermissionFlags::PF_ALL, + }) : + static_cast<NetPermissionFlags>(fuzzed_data_provider.ConsumeIntegral<uint32_t>()); + + NetWhitebindPermissions net_whitebind_permissions; + std::string error_net_whitebind_permissions; + if (NetWhitebindPermissions::TryParse(s, net_whitebind_permissions, error_net_whitebind_permissions)) { + (void)NetPermissions::ToStrings(net_whitebind_permissions.m_flags); + (void)NetPermissions::AddFlag(net_whitebind_permissions.m_flags, net_permission_flags); + assert(NetPermissions::HasFlag(net_whitebind_permissions.m_flags, net_permission_flags)); + (void)NetPermissions::ClearFlag(net_whitebind_permissions.m_flags, net_permission_flags); + (void)NetPermissions::ToStrings(net_whitebind_permissions.m_flags); + } + + NetWhitelistPermissions net_whitelist_permissions; + std::string error_net_whitelist_permissions; + if (NetWhitelistPermissions::TryParse(s, net_whitelist_permissions, error_net_whitelist_permissions)) { + (void)NetPermissions::ToStrings(net_whitelist_permissions.m_flags); + (void)NetPermissions::AddFlag(net_whitelist_permissions.m_flags, net_permission_flags); + assert(NetPermissions::HasFlag(net_whitelist_permissions.m_flags, net_permission_flags)); + (void)NetPermissions::ClearFlag(net_whitelist_permissions.m_flags, net_permission_flags); + (void)NetPermissions::ToStrings(net_whitelist_permissions.m_flags); + } +} diff --git a/src/test/fuzz/timedata.cpp b/src/test/fuzz/timedata.cpp new file mode 100644 index 0000000000..a0e579a88f --- /dev/null +++ b/src/test/fuzz/timedata.cpp @@ -0,0 +1,29 @@ +// Copyright (c) 2020 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 <test/fuzz/FuzzedDataProvider.h> +#include <test/fuzz/fuzz.h> +#include <test/fuzz/util.h> +#include <timedata.h> + +#include <cstdint> +#include <string> +#include <vector> + +void test_one_input(const std::vector<uint8_t>& buffer) +{ + FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); + const unsigned int max_size = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, 1000); + // Divide by 2 to avoid signed integer overflow in .median() + const int64_t initial_value = fuzzed_data_provider.ConsumeIntegral<int64_t>() / 2; + CMedianFilter<int64_t> median_filter{max_size, initial_value}; + while (fuzzed_data_provider.remaining_bytes() > 0) { + (void)median_filter.median(); + assert(median_filter.size() > 0); + assert(static_cast<size_t>(median_filter.size()) == median_filter.sorted().size()); + assert(static_cast<unsigned int>(median_filter.size()) <= max_size || max_size == 0); + // Divide by 2 to avoid signed integer overflow in .median() + median_filter.input(fuzzed_data_provider.ConsumeIntegral<int64_t>() / 2); + } +} diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h index 47f8d3fb27..10be2ebaf7 100644 --- a/src/test/fuzz/util.h +++ b/src/test/fuzz/util.h @@ -13,6 +13,7 @@ #include <streams.h> #include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/fuzz.h> +#include <uint256.h> #include <version.h> #include <cstdint> @@ -70,6 +71,15 @@ NODISCARD inline CScriptNum ConsumeScriptNum(FuzzedDataProvider& fuzzed_data_pro return CScriptNum{fuzzed_data_provider.ConsumeIntegral<int64_t>()}; } +NODISCARD inline uint256 ConsumeUInt256(FuzzedDataProvider& fuzzed_data_provider) noexcept +{ + const std::vector<unsigned char> v256 = fuzzed_data_provider.ConsumeBytes<unsigned char>(sizeof(uint256)); + if (v256.size() != sizeof(uint256)) { + return {}; + } + return uint256{v256}; +} + template <typename T> bool MultiplicationOverflow(T i, T j) { diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 44d4e0890a..96520079d7 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -784,6 +784,40 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) BOOST_CHECK(!IsStandardTx(CTransaction(t), reason)); BOOST_CHECK_EQUAL(reason, "scriptsig-size"); + // Check scriptSig format (non-standard if there are any other ops than just PUSHs) + t.vin[0].scriptSig = CScript() + << OP_TRUE << OP_0 << OP_1NEGATE << OP_16 // OP_n (single byte pushes: n = 1, 0, -1, 16) + << std::vector<unsigned char>(75, 0) // OP_PUSHx [...x bytes...] + << std::vector<unsigned char>(235, 0) // OP_PUSHDATA1 x [...x bytes...] + << std::vector<unsigned char>(1234, 0) // OP_PUSHDATA2 x [...x bytes...] + << OP_9; + BOOST_CHECK(IsStandardTx(CTransaction(t), reason)); + + const std::vector<unsigned char> non_push_ops = { // arbitrary set of non-push operations + OP_NOP, OP_VERIFY, OP_IF, OP_ROT, OP_3DUP, OP_SIZE, OP_EQUAL, OP_ADD, OP_SUB, + OP_HASH256, OP_CODESEPARATOR, OP_CHECKSIG, OP_CHECKLOCKTIMEVERIFY }; + + CScript::const_iterator pc = t.vin[0].scriptSig.begin(); + while (pc < t.vin[0].scriptSig.end()) { + opcodetype opcode; + CScript::const_iterator prev_pc = pc; + t.vin[0].scriptSig.GetOp(pc, opcode); // advance to next op + // for the sake of simplicity, we only replace single-byte push operations + if (opcode >= 1 && opcode <= OP_PUSHDATA4) + continue; + + int index = prev_pc - t.vin[0].scriptSig.begin(); + unsigned char orig_op = *prev_pc; // save op + // replace current push-op with each non-push-op + for (auto op : non_push_ops) { + t.vin[0].scriptSig[index] = op; + BOOST_CHECK(!IsStandardTx(CTransaction(t), reason)); + BOOST_CHECK_EQUAL(reason, "scriptsig-not-pushonly"); + } + t.vin[0].scriptSig[index] = orig_op; // restore op + BOOST_CHECK(IsStandardTx(CTransaction(t), reason)); + } + // Check tx-size (non-standard if transaction weight is > MAX_STANDARD_TX_WEIGHT) t.vin.clear(); t.vin.resize(2438); // size per input (empty scriptSig): 41 bytes diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 01aadcdd6d..9a972febab 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -344,7 +344,7 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, return false; } -void CWallet::ChainStateFlushed(const CBlockLocator& loc) +void CWallet::chainStateFlushed(const CBlockLocator& loc) { WalletBatch batch(*database); batch.WriteBestBlock(loc); @@ -1089,7 +1089,7 @@ void CWallet::SyncTransaction(const CTransactionRef& ptx, CWalletTx::Confirmatio MarkInputsDirty(ptx); } -void CWallet::TransactionAddedToMempool(const CTransactionRef& ptx) { +void CWallet::transactionAddedToMempool(const CTransactionRef& ptx) { auto locked_chain = chain().lock(); LOCK(cs_wallet); CWalletTx::Confirmation confirm(CWalletTx::Status::UNCONFIRMED, /* block_height */ 0, {}, /* nIndex */ 0); @@ -1101,7 +1101,7 @@ void CWallet::TransactionAddedToMempool(const CTransactionRef& ptx) { } } -void CWallet::TransactionRemovedFromMempool(const CTransactionRef &ptx) { +void CWallet::transactionRemovedFromMempool(const CTransactionRef &ptx) { LOCK(cs_wallet); auto it = mapWallet.find(ptx->GetHash()); if (it != mapWallet.end()) { @@ -1109,7 +1109,7 @@ void CWallet::TransactionRemovedFromMempool(const CTransactionRef &ptx) { } } -void CWallet::BlockConnected(const CBlock& block, int height) +void CWallet::blockConnected(const CBlock& block, int height) { const uint256& block_hash = block.GetHash(); auto locked_chain = chain().lock(); @@ -1120,11 +1120,11 @@ void CWallet::BlockConnected(const CBlock& block, int height) for (size_t index = 0; index < block.vtx.size(); index++) { CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, height, block_hash, index); SyncTransaction(block.vtx[index], confirm); - TransactionRemovedFromMempool(block.vtx[index]); + transactionRemovedFromMempool(block.vtx[index]); } } -void CWallet::BlockDisconnected(const CBlock& block, int height) +void CWallet::blockDisconnected(const CBlock& block, int height) { auto locked_chain = chain().lock(); LOCK(cs_wallet); @@ -1141,7 +1141,7 @@ void CWallet::BlockDisconnected(const CBlock& block, int height) } } -void CWallet::UpdatedBlockTip() +void CWallet::updatedBlockTip() { m_best_block_time = GetTime(); } @@ -1785,7 +1785,7 @@ bool CWalletTx::SubmitMemoryPoolAndRelay(std::string& err_string, bool relay) // Irrespective of the failure reason, un-marking fInMempool // out-of-order is incorrect - it should be unmarked when // TransactionRemovedFromMempool fires. - bool ret = pwallet->chain().broadcastTransaction(tx, err_string, pwallet->m_default_max_tx_fee, relay); + bool ret = pwallet->chain().broadcastTransaction(tx, pwallet->m_default_max_tx_fee, relay, err_string); fInMempool |= ret; return ret; } @@ -3875,7 +3875,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, } auto locked_chain = chain.lock(); - walletInstance->ChainStateFlushed(locked_chain->getTipLocator()); + walletInstance->chainStateFlushed(locked_chain->getTipLocator()); } else if (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS) { // Make it impossible to disable private keys after creation error = strprintf(_("Error loading %s: Private keys can only be disabled during creation").translated, walletFile); @@ -4056,7 +4056,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, return nullptr; } } - walletInstance->ChainStateFlushed(locked_chain->getTipLocator()); + walletInstance->chainStateFlushed(locked_chain->getTipLocator()); walletInstance->database->IncrementUpdateCounter(); // Restore wallet transaction metadata after -zapwallettxes=1 diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 1fb72c83cd..75fd14a80e 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -875,10 +875,10 @@ public: void MarkDirty(); bool AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose=true); void LoadToWallet(CWalletTx& wtxIn) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - void TransactionAddedToMempool(const CTransactionRef& tx) override; - void BlockConnected(const CBlock& block, int height) override; - void BlockDisconnected(const CBlock& block, int height) override; - void UpdatedBlockTip() override; + void transactionAddedToMempool(const CTransactionRef& tx) override; + void blockConnected(const CBlock& block, int height) override; + void blockDisconnected(const CBlock& block, int height) override; + void updatedBlockTip() override; int64_t RescanFromTime(int64_t startTime, const WalletRescanReserver& reserver, bool update); struct ScanResult { @@ -897,7 +897,7 @@ public: uint256 last_failed_block; }; ScanResult ScanForWalletTransactions(const uint256& first_block, const uint256& last_block, const WalletRescanReserver& reserver, bool fUpdate); - void TransactionRemovedFromMempool(const CTransactionRef &ptx) override; + void transactionRemovedFromMempool(const CTransactionRef &ptx) override; void ReacceptWalletTransactions() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void ResendWalletTransactions(); struct Balance { @@ -1033,7 +1033,7 @@ public: bool IsAllFromMe(const CTransaction& tx, const isminefilter& filter) const; CAmount GetCredit(const CTransaction& tx, const isminefilter& filter) const; CAmount GetChange(const CTransaction& tx) const; - void ChainStateFlushed(const CBlockLocator& loc) override; + void chainStateFlushed(const CBlockLocator& loc) override; DBErrors LoadWallet(bool& fFirstRunRet); DBErrors ZapWalletTx(std::vector<CWalletTx>& vWtx); diff --git a/test/functional/feature_abortnode.py b/test/functional/feature_abortnode.py index 9b878e8bf8..e47e709431 100755 --- a/test/functional/feature_abortnode.py +++ b/test/functional/feature_abortnode.py @@ -14,11 +14,12 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import wait_until, get_datadir_path, connect_nodes import os -class AbortNodeTest(BitcoinTestFramework): +class AbortNodeTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 + self.rpc_timeout = 240 def setup_network(self): self.setup_nodes() @@ -44,5 +45,6 @@ class AbortNodeTest(BitcoinTestFramework): self.log.info("Node crashed - now verifying restart fails") self.nodes[0].assert_start_raises_init_error() + if __name__ == '__main__': AbortNodeTest().main() diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py index 9e8e11e64f..0c591de869 100755 --- a/test/functional/feature_block.py +++ b/test/functional/feature_block.py @@ -1263,7 +1263,7 @@ class FullBlockTest(BitcoinTestFramework): self.save_spendable_output() spend = self.get_spendable_output() - self.send_blocks(blocks, True, timeout=1920) + self.send_blocks(blocks, True, timeout=2440) chain1_tip = i # now create alt chain of same length @@ -1275,14 +1275,14 @@ class FullBlockTest(BitcoinTestFramework): # extend alt chain to trigger re-org block = self.next_block("alt" + str(chain1_tip + 1), version=4) - self.send_blocks([block], True, timeout=1920) + self.send_blocks([block], True, timeout=2440) # ... and re-org back to the first chain self.move_tip(chain1_tip) block = self.next_block(chain1_tip + 1, version=4) self.send_blocks([block], False, force_send=True) block = self.next_block(chain1_tip + 2, version=4) - self.send_blocks([block], True, timeout=1920) + self.send_blocks([block], True, timeout=2440) self.log.info("Reject a block with an invalid block header version") b_v1 = self.next_block('b_v1', version=1) diff --git a/test/functional/rpc_estimatefee.py b/test/functional/rpc_estimatefee.py index 8bdecfc8cd..8bdecfc8cd 100644..100755 --- a/test/functional/rpc_estimatefee.py +++ b/test/functional/rpc_estimatefee.py diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 3bac8d5d95..8eb60c36df 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -19,9 +19,8 @@ import datetime import os import time import shutil -import signal -import sys import subprocess +import sys import tempfile import re import logging @@ -366,11 +365,10 @@ def main(): args=passon_args, combined_logs_len=args.combinedlogslen, failfast=args.failfast, - runs_ci=args.ci, use_term_control=args.ansi, ) -def run_tests(*, test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=False, args=None, combined_logs_len=0, failfast=False, runs_ci, use_term_control): +def run_tests(*, test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=False, args=None, combined_logs_len=0, failfast=False, use_term_control): args = args or [] # Warn if bitcoind is already running @@ -412,7 +410,6 @@ def run_tests(*, test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage= tmpdir=tmpdir, test_list=test_list, flags=flags, - timeout_duration=40 * 60 if runs_ci else float('inf'), # in seconds use_term_control=use_term_control, ) start_time = time.time() @@ -497,12 +494,11 @@ class TestHandler: Trigger the test scripts passed in via the list. """ - def __init__(self, *, num_tests_parallel, tests_dir, tmpdir, test_list, flags, timeout_duration, use_term_control): + def __init__(self, *, num_tests_parallel, tests_dir, tmpdir, test_list, flags, use_term_control): assert num_tests_parallel >= 1 self.num_jobs = num_tests_parallel self.tests_dir = tests_dir self.tmpdir = tmpdir - self.timeout_duration = timeout_duration self.test_list = test_list self.flags = flags self.num_running = 0 @@ -543,10 +539,6 @@ class TestHandler: time.sleep(.5) for job in self.jobs: (name, start_time, proc, testdir, log_out, log_err) = job - if int(time.time() - start_time) > self.timeout_duration: - # Timeout individual tests if timeout is specified (to stop - # tests hanging and not providing useful output). - proc.send_signal(signal.SIGINT) if proc.poll() is not None: log_out.seek(0), log_err.seek(0) [stdout, stderr] = [log_file.read().decode('utf-8') for log_file in (log_out, log_err)] |