Skip to content

Commit

Permalink
Integration of property based testing into Bitcoin Cash Node
Browse files Browse the repository at this point in the history
Summary:
This is an initial integration of the rapidcheck property based tests.
The original test has been slightly modified to adapt to our codebase.
The feature has been added to CMake and the documentation updated
accordingly.
Note that as opposed to autotools, which enables the test automatically
if `rapidcheck` is installed, it is disabled by default with cmake and
requires a flag to be set by the user.

Backport of core [[bitcoin/bitcoin#12775 | PR12775]], [[bitcoin/bitcoin#16622 | PR16622]] and [[bitcoin/bitcoin#16645 | PR16645]].

Test Plan:
Read the doc and install rapidcheck.

  ../configure
Ensure that rapidcheck is used by looking at the summary.
  make check

Ensure there is no regression:
  cmake -GNinja ..
  ninja check

  cmake -GNinja .. -DENABLE_PROPERTY_BASED_TESTS=ON
Check in the output that `rapidcheck` is enabled by searching for a
couple lines similar to:
```
  -- Found Rapidcheck: /usr/local/include
  -- Found Rapidcheck: /usr/local/lib/librapidcheck.a
```
  ninja check

Assuming you are running a 64-bit Linux machine:
  cd depends
  RAPIDCHECK=1 make build-linux64
Check the `rapidcheck` package is built.
  cd .. && mkdir buildLinux64 && cd buildLinux64
  cmake -GNinja .. \
    -DCMAKE_TOOLCHAIN_FILE=../cmake/platforms/Linux64.cmake \
    -DENABLE_PROPERTY_BASED_TESTS=ON
Check the `rapidcheck` lib found is the one from the `depends`.
  ninja check

Reviewers: #bitcoin_abc, jasonbcox

Reviewed By: #bitcoin_abc, jasonbcox

Subscribers: jasonbcox

Differential Revision: https://reviews.bitcoinabc.org/D5323
  • Loading branch information
Christewart authored and ftrader committed Apr 24, 2020
1 parent 093c91f commit 9613af3
Show file tree
Hide file tree
Showing 12 changed files with 348 additions and 1 deletion.
64 changes: 64 additions & 0 deletions cmake/modules/FindRapidcheck.cmake
@@ -0,0 +1,64 @@
# Copyright (c) 2020 The Bitcoin developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.

# .rst:
# FindRapidcheck
# --------------
#
# Find the Rapidcheck library.
#
# This will define the following variables::
#
# Rapidcheck_FOUND - True if the Rapidcheck library is found.
# Rapidcheck_INCLUDE_DIRS - List of the header include directories.
# Rapidcheck_LIBRARIES - List of the libraries.
#
# And the following imported targets::
#
# Rapidcheck::Rapidcheck

find_path(Rapidcheck_INCLUDE_DIR
NAMES rapidcheck.h
PATH_SUFFIXES rapidcheck
)

find_library(Rapidcheck_LIBRARY
NAMES rapidcheck
PATH_SUFFIXES rapidcheck
)

# TODO: extract a version number.
# For now rapidcheck does not provide such a version number, and has no release.
# See https://github.com/emil-e/rapidcheck/issues/235 for reference.

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Rapidcheck
DEFAULT_MSG
Rapidcheck_INCLUDE_DIR
Rapidcheck_LIBRARY
)

if(Rapidcheck_FOUND)
set(Rapidcheck_INCLUDE_DIRS "${Rapidcheck_INCLUDE_DIR}")
set(Rapidcheck_LIBRARIES "${Rapidcheck_LIBRARY}")

include(FindPackageMessage)
find_package_message(Rapidcheck
"Found Rapidcheck: ${Rapidcheck_LIBRARIES}"
"[${Rapidcheck_LIBRARIES}][${Rapidcheck_INCLUDE_DIRS}]"
)

if(NOT TARGET Rapidcheck::Rapidcheck)
add_library(Rapidcheck::Rapidcheck UNKNOWN IMPORTED)
set_target_properties(Rapidcheck::Rapidcheck PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${Rapidcheck_INCLUDE_DIR}"
IMPORTED_LOCATION "${Rapidcheck_LIBRARY}"
)
endif()
endif()

mark_as_advanced(
Rapidcheck_INCLUDE_DIR
Rapidcheck_LIBRARY
)
23 changes: 23 additions & 0 deletions configure.ac
Expand Up @@ -124,6 +124,12 @@ AC_ARG_ENABLE(gui-tests,
[use_gui_tests=$enableval],
[use_gui_tests=$use_tests])

AC_ARG_WITH([rapidcheck],
[AS_HELP_STRING([--with-rapidcheck],
[enable RapidCheck property-based tests (default is yes if librapidcheck is found)])],
[use_rapidcheck=$withval],
[use_rapidcheck=auto])

AC_ARG_ENABLE(bench,
AS_HELP_STRING([--disable-bench],[do not compile benchmarks (default is to compile)]),
[use_bench=$enableval],
Expand Down Expand Up @@ -1047,6 +1053,22 @@ AC_CHECK_DECLS([EVP_MD_CTX_new],,,[AC_INCLUDES_DEFAULT
])
CXXFLAGS="${save_CXXFLAGS}"

dnl RapidCheck property-based testing

enable_property_tests=no
if test "x$use_rapidcheck" = xauto; then
AC_CHECK_HEADERS([rapidcheck.h], [enable_property_tests=yes])
elif test "x$use_rapidcheck" != xno; then
enable_property_tests=yes
fi

RAPIDCHECK_LIBS=
if test "x$enable_property_tests" = xyes; then
RAPIDCHECK_LIBS=-lrapidcheck
fi
AC_SUBST(RAPIDCHECK_LIBS)
AM_CONDITIONAL([ENABLE_PROPERTY_TESTS], [test x$enable_property_tests = xyes])

dnl univalue check

need_bundled_univalue=yes
Expand Down Expand Up @@ -1402,6 +1424,7 @@ fi
echo " with zmq = $use_zmq"
echo " with test = $use_tests"
if test x$use_tests != xno; then
echo " with prop = $enable_property_tests"
echo " with fuzz = $enable_fuzz"
fi
echo " with bench = $use_bench"
Expand Down
7 changes: 7 additions & 0 deletions depends/Makefile
Expand Up @@ -4,6 +4,7 @@ SOURCES_PATH ?= $(BASEDIR)/sources
BASE_CACHE ?= $(BASEDIR)/built
SDK_PATH ?= $(BASEDIR)/SDKs
NO_QT ?=
RAPIDCHECK ?=
NO_WALLET ?=
NO_UPNP ?=
FALLBACK_DOWNLOAD_PATH ?= https://download.bitcoincashnode.org/depends-sources
Expand Down Expand Up @@ -94,13 +95,19 @@ qt_packages_$(NO_QT) = $(qt_packages) $(qt_$(host_os)_packages) $(qt_$(host_arch
wallet_packages_$(NO_WALLET) = $(wallet_packages)
upnp_packages_$(NO_UPNP) = $(upnp_packages)

rapidcheck_packages_$(RAPIDCHECK) = $(rapidcheck_packages)

packages += $($(host_arch)_$(host_os)_packages) $($(host_os)_packages) $(qt_packages_) $(wallet_packages_) $(upnp_packages_)
native_packages += $($(host_arch)_$(host_os)_native_packages) $($(host_os)_native_packages)

ifneq ($(qt_packages_),)
native_packages += $(qt_native_packages)
endif

ifeq ($(rapidcheck_packages_),)
packages += $(rapidcheck_packages)
endif

all_packages = $(packages) $(native_packages)

meta_depends = Makefile funcs.mk builders/default.mk hosts/default.mk hosts/$(host_os).mk builders/$(build_os).mk
Expand Down
1 change: 1 addition & 0 deletions depends/README.md
Expand Up @@ -39,6 +39,7 @@ The following can be set when running make: make FOO=bar
NO_WALLET: Don't download/build/cache libs needed to enable the wallet
NO_UPNP: Don't download/build/cache packages needed for enabling upnp
DEBUG: disable some optimizations and enable more runtime checking
RAPIDCHECK: build rapidcheck (experimental)
HOST_ID_SALT: Optional salt to use when generating host package ids
BUILD_ID_SALT: Optional salt to use when generating build package ids
JOBS: Number of jobs to use for each package build
Expand Down
2 changes: 2 additions & 0 deletions depends/packages/packages.mk
Expand Up @@ -5,6 +5,8 @@ qt_packages = qrencode protobuf zlib

qt_linux_packages:=qt expat dbus libxcb xcb_proto libXau xproto freetype fontconfig libX11 xextproto libXext xtrans

rapidcheck_packages = rapidcheck

qt_darwin_packages=qt
qt_mingw32_packages=qt

Expand Down
18 changes: 18 additions & 0 deletions depends/packages/rapidcheck.mk
@@ -0,0 +1,18 @@
package=rapidcheck
$(package)_version=10fc0cb
$(package)_download_path=https://github.com/MarcoFalke/rapidcheck/archive
$(package)_file_name=$(package)-$($(package)_version).tar.gz
$(package)_sha256_hash=9640926223c00af45bce4c7df8b756b5458a89b2ba74cfe3e404467f13ce26df

define $(package)_config_cmds
cmake -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=true .
endef

define $(package)_build_cmds
$(MAKE) -j$(JOBS) && \
mkdir -p $($(package)_staging_dir)$(host_prefix)/include && \
cp -a include/* $($(package)_staging_dir)$(host_prefix)/include/ && \
cp -a extras/boost_test/include/rapidcheck/* $($(package)_staging_dir)$(host_prefix)/include/rapidcheck/ && \
mkdir -p $($(package)_staging_dir)$(host_prefix)/lib && \
cp -a librapidcheck.a $($(package)_staging_dir)$(host_prefix)/lib/
endef
69 changes: 69 additions & 0 deletions doc/rapidcheck.md
@@ -0,0 +1,69 @@
# RapidCheck property-based testing for Bitcoin Cash Node

## Concept

Property-based testing is experimentally being added to Bitcoin Cash Node with
[RapidCheck](https://github.com/emil-e/rapidcheck), a C++ framework for
property-based testing inspired by the Haskell library
[QuickCheck](https://hackage.haskell.org/package/QuickCheck).

RapidCheck performs random testing of program properties. A specification of the
program is given in the form of properties which functions should satisfy, and
RapidCheck tests that the properties hold in a large number of randomly
generated cases.

If an exception is found, RapidCheck tries to find the smallest case, for some
definition of smallest, for which the property is still false and displays it as
a counter-example. For example, if the input is an integer, RapidCheck tries to
find the smallest integer for which the property is false.

## Setup

The following instructions have been tested with Linux Debian and macOS.

1. Clone the RapidCheck source code and cd into the repository.

```shell
git clone https://github.com/emil-e/rapidcheck.git
cd rapidcheck
```

2. Build RapidCheck.

```shell
mkdir build
cd build
cmake -GNinja .. -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DRC_ENABLE_BOOST_TEST=ON
ninja
sudo ninja install/strip
```

3. Configure Bitcoin Cash Node with RapidCheck.

`cd` to the directory of your local Bitcoin Cash Node repository, create a build
directory and run cmake:

```shell
mkdir build
cd build
cmake -GNinja .. -DENABLE_PROPERTY_BASED_TESTS=ON
```

In the output you should see something similar to:

```shell
[...]
-- Found Rapidcheck: /usr/local/include
-- Found Rapidcheck: /usr/local/lib/librapidcheck.a
[...]
```

4. Build Bitcoin Cash Node with RapidCheck.

Now you can run `ninja check` to build and run the unit tests, including the
property-based tests. You can also build and run a single test by using
`ninja check-bitcoin-<test_name>`.

Example: `ninja check-bitcoin-key_properties`

That's it! You are now running property-based tests in Bitcoin Cash Node.
12 changes: 11 additions & 1 deletion src/Makefile.test.include
Expand Up @@ -35,6 +35,7 @@ TEST_SRCDIR = test
TEST_BINARY=test/test_bitcoin$(EXEEXT)

JSON_TEST_FILES = \
test/data/script_tests.json \
test/data/base58_encode_decode.json \
test/data/blockfilters.json \
test/data/key_io_valid.json \
Expand Down Expand Up @@ -168,6 +169,15 @@ BITCOIN_TESTS =\
test/work_comparator_tests.cpp \
rpc/test/server_tests.cpp

if ENABLE_PROPERTY_TESTS
BITCOIN_TESTS += \
test/key_properties.cpp

BITCOIN_TEST_SUITE += \
test/gen/crypto_gen.cpp \
test/gen/crypto_gen.h
endif

if ENABLE_WALLET
BITCOIN_TESTS += \
wallet/test/db_tests.cpp \
Expand Down Expand Up @@ -196,7 +206,7 @@ test_test_bitcoin_LDADD += $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_C
$(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_LIBS) $(EVENT_PTHREADS_LIBS)
test_test_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)

test_test_bitcoin_LDADD += $(LIBBITCOIN_CONSENSUS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS)
test_test_bitcoin_LDADD += $(LIBBITCOIN_CONSENSUS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(RAPIDCHECK_LIBS)
test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static

if ENABLE_ZMQ
Expand Down
23 changes: 23 additions & 0 deletions src/test/CMakeLists.txt
Expand Up @@ -2,6 +2,8 @@

project(bitcoin-test)

option(ENABLE_PROPERTY_BASED_TESTS "Enable property based tests" OFF)

# Process json files.
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/data")

Expand Down Expand Up @@ -59,6 +61,17 @@ if(BUILD_BITCOIN_WALLET)
)
endif()

if(ENABLE_PROPERTY_BASED_TESTS)
find_package(Rapidcheck REQUIRED)

set(BITCOIN_PROPERTY_BASED_TEST_FIXTURE
gen/crypto_gen.cpp
)
set(BITCOIN_PROPERTY_BASED_TESTS
key_properties.cpp
)
endif()

add_boost_unit_tests_to_suite(bitcoin test_bitcoin
jsonutil.cpp
main.cpp
Expand All @@ -72,6 +85,9 @@ add_boost_unit_tests_to_suite(bitcoin test_bitcoin
# Wallet test fixture
${BITCOIN_WALLET_TEST_FIXTURE}

# Properties based test fixture
${BITCOIN_PROPERTY_BASED_TEST_FIXTURE}

TESTS
activation_tests.cpp
addrman_tests.cpp
Expand Down Expand Up @@ -178,8 +194,15 @@ add_boost_unit_tests_to_suite(bitcoin test_bitcoin

# Wallet tests
${BITCOIN_WALLET_TESTS}

# Properties based tests
${BITCOIN_PROPERTY_BASED_TESTS}
)

if(ENABLE_PROPERTY_BASED_TESTS)
target_link_libraries(test_bitcoin Rapidcheck::Rapidcheck)
endif()

target_link_libraries(test_bitcoin rpcclient server)
if(TARGET bitcoinconsensus-shared)
target_link_libraries(test_bitcoin bitcoinconsensus-shared)
Expand Down
19 changes: 19 additions & 0 deletions src/test/gen/crypto_gen.cpp
@@ -0,0 +1,19 @@
// 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 <test/gen/crypto_gen.h>

#include <key.h>

#include <rapidcheck/Gen.h>
#include <rapidcheck/gen/Arbitrary.h>
#include <rapidcheck/gen/Container.h>
#include <rapidcheck/gen/Predicate.h>

/** Generates 1 to 20 keys for OP_CHECKMULTISIG */
rc::Gen<std::vector<CKey>> MultisigKeys() {
return rc::gen::suchThat(rc::gen::arbitrary<std::vector<CKey>>(),
[](const std::vector<CKey> &keys) {
return keys.size() >= 1 && keys.size() <= 15;
});
};

0 comments on commit 9613af3

Please sign in to comment.