Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add more property based tests for basic bitcoin data structures #14430

Closed
wants to merge 2 commits into from

Conversation

Christewart
Copy link
Contributor

@Christewart Christewart commented Oct 8, 2018

This is the second half of #12775.

In general property based testing is a great testing idiom for making sure invariants that you believe about your code hold true under a range of "valid" values.

This PR includes basic properties for

  1. Keys
  2. Blocks
  3. Bloom Filters
  4. Merkleblocks
  5. Scripts
  6. Transactions

Currently rapidcheck is not enabled by default on travis -- work has been done to enable in #14171 there appears to be a memory access violation in one of the environments.

@fanquake fanquake added the Tests label Oct 8, 2018
src/test/gen/block_gen.h Outdated Show resolved Hide resolved
@practicalswift
Copy link
Contributor

practicalswift commented Oct 8, 2018

Concept ACK

Very nice! Thanks for working on this

@Empact
Copy link
Member

Empact commented Oct 9, 2018

Ref #12775 which this builds on.

uint32_t nTime;
uint32_t nBits;
uint32_t nNonce;
std::tie(nVersion, hashPrevBlock, hashMerkleRoot, nTime, nBits, nNonce) = primitives;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems you could directly tie into the header fields, to make this more succinct.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what you mean about this

@Empact
Copy link
Member

Empact commented Oct 9, 2018

How about responding to @MarcoFalke's suggestions?
#12775 (comment)
#12775 (comment)

});
}

rc::Gen<unsigned int> Between1And100()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: looks like this method is defined with the same name here and in merkleblock_gen.h, how about:

  • applying static more liberally?
  • renaming one or the other?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So basically mark as static in merkleblock_gen.h and then include that header file and use it here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well they have different implementations for different return results. Calling this bloom_gen.cpp implementation static will prevent it from being used outside this file, which would reduce the risk of conflicts and otherwise be appropriate as the method is only used here. I don't think you want to apply static in the header, as that will result in the function being compiled into every including translation unit. Naming this to not have a potential conflict would also be positive.

src/test/gen/script_gen.h Outdated Show resolved Hide resolved
@Christewart
Copy link
Contributor Author

why gen::nonEmpty here?

I believe blocks, by consensus, are required to have at least one transaction in them (citation needed).

@Empact
Copy link
Member

Empact commented Oct 9, 2018

why gen::nonEmpty here?

I believe blocks, by consensus, are required to have at least one transaction in them (citation needed).

I believe that's correct. Relevant check in CheckBlock here:

bitcoin/src/validation.cpp

Lines 3112 to 3118 in 4de0b5f

// Size limits
if (block.vtx.empty() || block.vtx.size() * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT || ::GetSerializeSize(block, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT)
return state.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, "size limits failed");
// First transaction must be coinbase, the rest must not be
if (block.vtx.empty() || !block.vtx[0]->IsCoinBase())
return state.DoS(100, false, REJECT_INVALID, "bad-cb-missing", false, "first tx is not coinbase");

}

/** Loads an arbitrary bloom filter with the given hashes */
rc::Gen<std::pair<CBloomFilter, std::vector<uint256>>> LoadBloomFilter(const std::vector<uint256>& hashes)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the presence of LoadBloomFilter auto-detected?

Same question applies to LoadedBloomFilter, P2SHSPK, P2WSHSPK and SignedTx which all looks like unused functions when taking a naïve look :-)

@fanquake
Copy link
Member

Rebooted the tests, as they were still stuck on the initial linting error.

This would be a good opportunity to address some of the nits left over from #12775, as well as possibly moving to a newer version of rapidcheck, which we can host at https://github.com/bitcoin-core.
I bought that up on IRC with @laanwj here.

We should probably also pull in the depends documentation update from #14171.

FWIW I updated the PR description to point to #12775 instead of ##8469.

@Christewart
Copy link
Contributor Author

So I tried to adjust our build script that is found here and failed to sift through the cmake weeds.

It isn't as trivial as running

$ cmake . 
$ make install 

since we needs extra functionality located in this package

https://github.com/bitcoin-core/rapidcheck/tree/master/extras

specifically the boost_test package. This is what is used to hook into the existing boost testing framework.

It would be much appreciated if someone who is more adept at cmake commenting about how to properly add that package in extras to have it properly installed in the rapidcheck.mk file.

@Empact
Copy link
Member

Empact commented Nov 8, 2018

@Christewart
Copy link
Contributor Author

Christewart commented Nov 8, 2018

@Empact Yes! That is how I hook nicely into the existing boost testing framework.

What I am currently blocked on is adding that submodule to our ci build. Please see the comment above for details.

With these proeprties, if they are enabled, you can just run them simply with

$ ./test_bitcoin 

since we integrate with boost_test

EDIT:

Also I am short on time at the moment so I wont be able to work too much on getting the build stuff to work nicely

@Christewart
Copy link
Contributor Author

It looks like there is now an easy configuration option in rapidcheck to allow for the installing of extra/boost_test by setting a simple configuration option:

emil-e/rapidcheck#222 (comment)

We will need to update the rapidcheck dependency to at least emil-e/rapidcheck@5f0eb30

@fanquake
Copy link
Member

fanquake commented Dec 1, 2018

@Christewart I've made an attempt to use the new install flags in #14853.

When I merge these changes on top of that PR (using the new rapidcheck) I'm seeing some compilation errors. This is on a macOS 10.14.1 machine, using Xcode 10.1 (10B61):

In file included from test/gen/transaction_gen.cpp:1:
In file included from ./test/gen/transaction_gen.h:4:
In file included from ./test/gen/crypto_gen.h:4:
In file included from ./key.h:10:
In file included from ./pubkey.h:10:
In file included from ./hash.h:11:
In file included from ./crypto/sha256.h:10:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/string:477:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/string_view:176:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/__string:56:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/algorithm:643:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/memory:662:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/tuple:227:10: error: static_assert failed due to requirement '__can_bind_reference<std::__1::vector<CTxIn, std::__1::allocator<CTxIn> > >()' "Attempted to construct a reference element in a tuple with an rvalue"
        {static_assert(__can_bind_reference<_Tp>(),
         ^             ~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/tuple:411:15: note: in instantiation of function template specialization 'std::__1::__tuple_leaf<1, const std::__1::vector<CTxIn, std::__1::allocator<CTxIn> > &, false>::__tuple_leaf<std::__1::vector<CTxIn, std::__1::allocator<CTxIn> >, void>' requested here
            : __tuple_leaf<_Indx, _Tp>(_VSTD::forward<typename tuple_element<_Indx,
              ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/tuple:828:15: note: in instantiation of function template specialization 'std::__1::__tuple_impl<std::__1::__tuple_indices<0, 1, 2, 3>, int, const std::__1::vector<CTxIn, std::__1::allocator<CTxIn> > &, const std::__1::vector<CTxOut, std::__1::allocator<CTxOut> > &, unsigned int>::__tuple_impl<std::__1::tuple<int, std::__1::vector<CTxIn, std::__1::allocator<CTxIn> >, std::__1::vector<CTxOut, std::__1::allocator<CTxOut> >, unsigned int>, void>' requested here
            : __base_(_VSTD::forward<_Tuple>(__t)) {}
              ^
/Users/michael/github/bitcoin/depends/x86_64-apple-darwin18.2.0/share/../include/rapidcheck/shrinkable/Transform.hpp:20:37: note: in instantiation of function template specialization 'std::__1::tuple<int, const std::__1::vector<CTxIn, std::__1::allocator<CTxIn> > &, const std::__1::vector<CTxOut, std::__1::allocator<CTxOut> > &, unsigned int>::tuple<std::__1::tuple<int, std::__1::vector<CTxIn, std::__1::allocator<CTxIn> >, std::__1::vector<CTxOut, std::__1::allocator<CTxOut> >, unsigned int>, false>' requested here
  U value() const { return m_mapper(m_shrinkable.value()); }
                                    ^
/Users/michael/github/bitcoin/depends/x86_64-apple-darwin18.2.0/share/../include/rapidcheck/Shrinkable.hpp:26:44: note: in instantiation of member function 'rc::shrinkable::detail::MapShrinkable<std::__1::tuple<int, std::__1::vector<CTxIn, std::__1::allocator<CTxIn> >, std::__1::vector<CTxOut, std::__1::allocator<CTxOut> >, unsigned int>, (lambda at ./test/gen/transaction_gen.h:97:13)>::value' requested here
  T value() const override { return m_impl.value(); }
                                           ^
/Users/michael/github/bitcoin/depends/x86_64-apple-darwin18.2.0/share/../include/rapidcheck/Shrinkable.hpp:22:12: note: in instantiation of member function 'rc::Shrinkable<CTransaction>::ShrinkableImpl<rc::shrinkable::detail::MapShrinkable<std::__1::tuple<int, std::__1::vector<CTxIn, std::__1::allocator<CTxIn> >, std::__1::vector<CTxOut, std::__1::allocator<CTxOut> >, unsigned int>, (lambda at ./test/gen/transaction_gen.h:97:13)> >::value' requested here
  explicit ShrinkableImpl(Args &&... args)
           ^
/Users/michael/github/bitcoin/depends/x86_64-apple-darwin18.2.0/share/../include/rapidcheck/Shrinkable.hpp:101:27: note: (skipping 3 contexts in backtrace; use -ftemplate-backtrace-limit=0 to see all)
  shrinkable.m_impl = new ShrinkableImpl(std::forward<Args>(args)...);
                          ^
/Users/michael/github/bitcoin/depends/x86_64-apple-darwin18.2.0/share/../include/rapidcheck/Gen.hpp:39:12: note: in instantiation of member function 'rc::gen::detail::MapGen<std::__1::tuple<int, std::__1::vector<CTxIn, std::__1::allocator<CTxIn> >, std::__1::vector<CTxOut, std::__1::allocator<CTxOut> >, unsigned int>, (lambda at ./test/gen/transaction_gen.h:97:13)>::operator()' requested here
    return m_impl(random, size);
           ^
/Users/michael/github/bitcoin/depends/x86_64-apple-darwin18.2.0/share/../include/rapidcheck/Gen.hpp:34:3: note: in instantiation of member function 'rc::Gen<CTransaction>::GenImpl<rc::gen::detail::MapGen<std::__1::tuple<int, std::__1::vector<CTxIn, std::__1::allocator<CTxIn> >, std::__1::vector<CTxOut, std::__1::allocator<CTxOut> >, unsigned int>, (lambda at ./test/gen/transaction_gen.h:97:13)> >::generate' requested here
  GenImpl(Args &&... args)
  ^
/Users/michael/github/bitcoin/depends/x86_64-apple-darwin18.2.0/share/../include/rapidcheck/Gen.hpp:58:18: note: in instantiation of function template specialization 'rc::Gen<CTransaction>::GenImpl<rc::gen::detail::MapGen<std::__1::tuple<int, std::__1::vector<CTxIn, std::__1::allocator<CTxIn> >, std::__1::vector<CTxOut, std::__1::allocator<CTxOut> >, unsigned int>, (lambda at ./test/gen/transaction_gen.h:97:13)> >::GenImpl<rc::gen::detail::MapGen<std::__1::tuple<int, std::__1::vector<CTxIn, std::__1::allocator<CTxIn> >, std::__1::vector<CTxOut, std::__1::allocator<CTxOut> >, unsigned int>, (lambda at ./test/gen/transaction_gen.h:97:13)> >' requested here
    : m_impl(new GenImpl<Decay<Impl>>(std::forward<Impl>(impl))) {}
                 ^
/Users/michael/github/bitcoin/depends/x86_64-apple-darwin18.2.0/share/../include/rapidcheck/gen/Transform.hpp:78:10: note: in instantiation of function template specialization 'rc::Gen<CTransaction>::Gen<rc::gen::detail::MapGen<std::__1::tuple<int, std::__1::vector<CTxIn, std::__1::allocator<CTxIn> >, std::__1::vector<CTxOut, std::__1::allocator<CTxOut> >, unsigned int>, (lambda at ./test/gen/transaction_gen.h:97:13)>, void>' requested here
  return detail::MapGen<T, Decay<Mapper>>(std::move(gen),
         ^
./test/gen/transaction_gen.h:95:21: note: in instantiation of function template specialization 'rc::gen::map<std::__1::tuple<int, std::__1::vector<CTxIn, std::__1::allocator<CTxIn> >, std::__1::vector<CTxOut, std::__1::allocator<CTxOut> >, unsigned int>, (lambda at ./test/gen/transaction_gen.h:97:13)>' requested here
        return gen::map(gen::tuple(gen::arbitrary<int32_t>(),

The entire build output is available here.

maflcko pushed a commit that referenced this pull request Apr 2, 2019
ac67582 depends: latest rapidcheck, use INSTALL_ALL_EXTRAS (fanquake)

Pull request description:

  This updates RapidCheck to the latest version available from https://github.com/emil-e/rapidcheck.

  RapidCheck now uses the new `RC_INSTALL_ALL_EXTRAS` option, to install the extra `boost_test` packages, which should unblock progress in #14430.

ACKs for commit ac6758:
  MarcoFalke:
    utACK ac67582

Tree-SHA512: a4a4ef0ec09cf61cdc0de241703f5f8e98f6fa92f4024a0fbbf4d4ef91d9d3bc8d662c55d896aced8de68aa9429728b2bc5001c91c6f92d63d60c47f5adf41a0
@fanquake
Copy link
Member

fanquake commented Apr 3, 2019

@Christewart Could you rebase this, now that #14853 has been merged?

@Christewart
Copy link
Contributor Author

Rebased this branch, but i am getting a new error when running these tests locally. This doesn't seem related to my PR AFAICT

chris@chris:~/dev/bitcoin/src/test$ ./test_bitcoin
Running 365 test cases...
Using configuration: seed=16472686160129483926
Error: Specified -walletdir "/tmp/test_common_Bitcoin Core/1555532449_497584857/tempdir/path_does_not_exist" does not exist
Error: Specified -walletdir "/tmp/test_common_Bitcoin Core/1555532449_512688440/tempdir/not_a_directory.dat" is not a directory
Error: Specified -walletdir "wallets" is a relative path

@practicalswift
Copy link
Contributor

@Christewart Yes, those annoying errors are unrelated: see discussion in #15352 :-)

Copy link
Member

@fanquake fanquake left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Christewart Do you have time to continue with this RapidCheck test work going forward? If so, could you rebase this on master (to pull in a build fix), address outstanding nits and squash your commits etc.

I checked this out and ran the tests. For some reason I thought I had (or used to have?) rapidcheck installed via brew, but it doesn't seem to be available from there at the moment.

I'm wondering what the state of the RapidCheck project is, as the repo hasn't seen any activity for 6 months, and the author seems to have gone quiet on GitHub. There are a few issues as well as PRs piling up on that repo.

src/test/bloom_properties.cpp Outdated Show resolved Hide resolved
src/test/gen/crypto_gen.cpp Show resolved Hide resolved
@jonatack
Copy link
Contributor

@fanquake I wrote @Christewart about this PR 3 days ago and he might have some time this week.

@Christewart
Copy link
Contributor Author

Christewart commented Jul 29, 2019

So i rebased onto master, and fixed various things that needed to be fixed with API changes in master.

Do you have time to continue with this RapidCheck test work going forward? If so, could you rebase this on master (to pull in a build fix), address outstanding nits and squash your commits etc.

I'm wondering what the state of the RapidCheck project is, as the repo hasn't seen any activity for 6 months, and the author seems to have gone quiet on GitHub. There are a few issues as well as PRs piling up on that repo.

I think this is concerning as well, I do not have the time or know how to maintain rapidcheck itself. I see you have an issue on the repo about keeping rapidcheck working with newer versions of gcc, and it hasn't been answered which is concerning.

I checked this out and ran the tests. For some reason I thought I had (or used to have?) rapidcheck installed via brew, but it doesn't seem to be available from there at the moment.

I don't think rapidcheck can be installed with brew (admittedly, I don't have a mac). I think you need to do

rapidcheck $ cmake . && make install
-- Configuring done
-- Generating done
-- Build files have been written to: /home/chris/dev/rapidcheck
[100%] Built target rapidcheck
Install the project...
-- Install configuration: ""
-- Up-to-date: /usr/local/lib/librapidcheck.a
-- Up-to-date: /usr/local/include
-- Up-to-date: /usr/local/include/rapidcheck.h
-- Up-to-date: /usr/local/include/rapidcheck
....
-- Up-to-date: /usr/local/include
-- Up-to-date: /usr/local/include/rapidcheck
-- Up-to-date: /usr/local/include/rapidcheck/boost_test.h

Copy link
Contributor

@jonatack jonatack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concept ACK, approach ACK. Built and ran all tests successfully, both this branch and also rebased on latest master, with Linux Debian and a fresh build of RapidCheck on master at emil-e/rapidcheck@d9482c6.

  CXX      test/gen/test_bitcoin-block_gen.o
  CXX      test/gen/test_bitcoin-bloom_gen.o
  CXX      test/gen/test_bitcoin-crypto_gen.o
  CXX      test/gen/test_bitcoin-script_gen.o
  CXX      test/gen/test_bitcoin-transaction_gen.o

[...]

  CXX      test/test_bitcoin-block_properties.o
  CXX      test/test_bitcoin-bloom_properties.o
  CXX      test/test_bitcoin-key_properties.o
  CXX      test/test_bitcoin-merkleblock_properties.o
  CXX      test/test_bitcoin-script_properties.o
  CXX      test/test_bitcoin-transaction_properties.o

[...]

Running tests: block_properties from test/block_properties.cpp
Running tests: bloom_properties from test/bloom_properties.cpp
Running tests: key_properties from test/key_properties.cpp
Running tests: merkleblock_properties from test/merkleblock_properties.cpp
Running tests: script_properties from test/script_properties.cpp
Running tests: transaction_properties from test/transaction_properties.cpp

@Christewart could you put this PR into final ACK-ready form? I'm guessing multiple commits for the different tests or Makefile changes would probably be fine (given the work put into it) but would be good to squash the linter, typo, rebase, and clang commits.

Rapidcheck build aspects could be addressed in a separate PR as this one concentrates on adding tests.

Note that Rapidcheck has recently seen a bit of renewed maintainer activity, though afaik emil-e/rapidcheck#232 filed by @fanquake hasn't been addressed yet.

@@ -153,11 +153,25 @@ BITCOIN_TESTS =\

if ENABLE_PROPERTY_TESTS
BITCOIN_TESTS += \
test/key_properties.cpp
test/block_properties.cpp \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: could update "2013-2016" in this file's copyright header

fix typo in Makefile.test.include

rebase and redo imports

Rebase onto master, fix compile erros with various API changes in master, address nits

Run clang-format-diff.py

Add missing copyright header
@Christewart
Copy link
Contributor Author

@jonatack I think everything is good to go now?

@fanquake
Copy link
Member

fanquake commented Apr 3, 2020

Closing this in light of #18514.

@bitcoin bitcoin locked as resolved and limited conversation to collaborators Feb 15, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants