[POC] Introducing property based testing to Core #8469
Open
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
67a9b8a
Adding rapidcheck dependency, adding CKey properties
Christewart e3f2f47
Adding Bloom filter properties and bloom filter generator
Christewart 2737955
Adding serialization symmetry for CBloomFilter
Christewart 179337a
Adding block header serialization property
Christewart 0aa0d0a
Creating transaction_gen.h, adding generator for COutPoint and serial…
Christewart 92a9724
Adding script generator and script_propertes, first property is CScri…
Christewart fdc0cf1
Adding CTransaction generator, adding serialization symmetry property…
Christewart c767e6b
Adding CTransactionRef and CBlock generators, adding property for CBl…
Christewart 867d8de
Adding Generators outside of the rc name space, adding generator for …
Christewart 607725b
Adding merkleblock_gen.h, merkleblock serialization symmetry test
Christewart 31a1ca5
Adding merkle_block properties
Christewart
Jump to file or symbol
Failed to load files and symbols.
| @@ -0,0 +1,13 @@ | ||
| +package=rapidcheck | ||
| +$(package)_version=f5d3afa | ||
| +$(package)_download_path=https://bitcoin-10596.firebaseapp.com/depends | ||
| +$(package)_file_name=$(package)-$($(package)_version).tar.gz | ||
| +$(package)_sha256_hash=78cdb8d0185b602e32e66f4e5d1a6ceec1f801dd9641b8a9456c386b1eaaf0e5 | ||
| + | ||
| +define $(package)_config_cmds | ||
| + cmake -DCMAKE_INSTALL_PREFIX=$(build_prefix) | ||
| +endef | ||
| + | ||
| +define $(package)_build_cmds | ||
| + $(MAKE) | ||
| +endef |
| @@ -0,0 +1,32 @@ | ||
| +#include <boost/test/unit_test.hpp> | ||
| +#include <rapidcheck/boost_test.h> | ||
| +#include <rapidcheck/gen/Arbitrary.h> | ||
| +#include <rapidcheck/Gen.h> | ||
| + | ||
| +#include "test/test_bitcoin.h" | ||
| +#include "primitives/block.h" | ||
| +#include "test/gen/block_gen.h" | ||
| + | ||
| + | ||
| +BOOST_FIXTURE_TEST_SUITE(block_properties, BasicTestingSetup) | ||
| + | ||
| +RC_BOOST_PROP(blockheader_serialization_symmetry, (CBlockHeader header)) { | ||
| + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); | ||
| + ss << header; | ||
| + CBlockHeader header2; | ||
| + ss >> header2; | ||
| + CDataStream ss1(SER_NETWORK, PROTOCOL_VERSION); | ||
| + ss << header; | ||
| + ss1 << header2; | ||
| + RC_ASSERT(ss.str() == ss1.str()); | ||
| +} | ||
| + | ||
| +RC_BOOST_PROP(block_serialization_symmetry, (CBlock block)) { | ||
| + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); | ||
| + ss << block; | ||
| + CBlock block2; | ||
| + ss >> block2; | ||
| + RC_ASSERT(block.GetHash() == block2.GetHash()); | ||
| +} | ||
| + | ||
| +BOOST_AUTO_TEST_SUITE_END() |
| @@ -0,0 +1,33 @@ | ||
| +// Copyright (c) 2012-2016 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 "key.h" | ||
| + | ||
| +#include "test/test_bitcoin.h" | ||
| + | ||
| +#include <boost/test/unit_test.hpp> | ||
| +#include <rapidcheck/boost_test.h> | ||
| +#include <rapidcheck/gen/Arbitrary.h> | ||
| +#include <rapidcheck/Gen.h> | ||
| + | ||
| +#include "test/gen/bloom_gen.h" | ||
| +#include "test/gen/crypto_gen.h" | ||
| + | ||
| +BOOST_FIXTURE_TEST_SUITE(bloom_properties, BasicTestingSetup) | ||
| + | ||
| +RC_BOOST_PROP(no_false_negatives, (CBloomFilter bloomFilter, uint256 hash)) { | ||
| + bloomFilter.insert(hash); | ||
| + RC_ASSERT(bloomFilter.contains(hash)); | ||
| +} | ||
| + | ||
| +RC_BOOST_PROP(serialization_symmetry, (CBloomFilter bloomFilter)) { | ||
| + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); | ||
| + ss << bloomFilter; | ||
| + CBloomFilter bloomFilter2; | ||
| + ss >> bloomFilter2; | ||
| + CDataStream ss1(SER_NETWORK, PROTOCOL_VERSION); | ||
| + ss << bloomFilter; | ||
| + ss1 << bloomFilter2; | ||
| + RC_ASSERT(ss.str() == ss1.str()); | ||
| +} | ||
| +BOOST_AUTO_TEST_SUITE_END() |
| @@ -0,0 +1,57 @@ | ||
| +#ifndef BITCOIN_TEST_GEN_BLOCK_GEN_H | ||
| +#define BITCOIN_TEST_GEN_BLOCK_GEN_H | ||
| + | ||
| +#include "test/gen/crypto_gen.h" | ||
| +#include "test/gen/transaction_gen.h" | ||
| +#include "uint256.h" | ||
| +#include "primitives/block.h" | ||
| + | ||
| +#include <rapidcheck/gen/Arbitrary.h> | ||
| +#include <rapidcheck/Gen.h> | ||
| + | ||
| +/** Generator for the primitives of a block header */ | ||
| +inline rc::Gen<std::tuple<int32_t, uint256, uint256, uint32_t, uint32_t, uint32_t>> blockHeaderPrimitives() { | ||
| + return rc::gen::tuple(rc::gen::arbitrary<int32_t>(), | ||
| + rc::gen::arbitrary<uint256>(), rc::gen::arbitrary<uint256>(), | ||
| + rc::gen::arbitrary<uint32_t>(), rc::gen::arbitrary<uint32_t>(), rc::gen::arbitrary<uint32_t>()); | ||
| +} | ||
| + | ||
| +namespace rc { | ||
| + | ||
| + /** Generator for a new CBlockHeader */ | ||
| + template<> | ||
| + struct Arbitrary<CBlockHeader> { | ||
| + static Gen<CBlockHeader> arbitrary() { | ||
| + return gen::map(blockHeaderPrimitives(), [](std::tuple<int32_t, uint256, uint256, uint32_t, uint32_t, uint32_t> headerPrimitives) { | ||
| + int32_t nVersion; | ||
| + uint256 hashPrevBlock; | ||
| + uint256 hashMerkleRoot; | ||
| + uint32_t nTime; | ||
| + uint32_t nBits; | ||
| + uint32_t nNonce; | ||
| + std::tie(nVersion,hashPrevBlock, hashMerkleRoot, nTime,nBits,nNonce) = headerPrimitives; | ||
| + CBlockHeader header; | ||
| + header.nVersion = nVersion; | ||
| + header.hashPrevBlock = hashPrevBlock; | ||
| + header.hashMerkleRoot = hashMerkleRoot; | ||
| + header.nTime = nTime; | ||
| + header.nBits = nBits; | ||
| + header.nNonce = nNonce; | ||
| + return header; | ||
| + }); | ||
| + }; | ||
| + }; | ||
| + | ||
| + /** Generator for a new CBlock */ | ||
| + template<> | ||
| + struct Arbitrary<CBlock> { | ||
| + static Gen<CBlock> arbitrary() { | ||
| + return gen::map(gen::nonEmpty<std::vector<CTransactionRef>>(), [](std::vector<CTransactionRef> txRefs) { | ||
| + CBlock block; | ||
| + block.vtx = txRefs; | ||
| + return block; | ||
| + }); | ||
| + } | ||
| + }; | ||
| +} | ||
| +#endif |
| @@ -0,0 +1,43 @@ | ||
| +#ifndef BITCOIN_TEST_GEN_BLOOM_GEN_H | ||
| +#define BITCOIN_TEST_GEN_BLOOM_GEN_H | ||
| + | ||
| +#include "bloom.h" | ||
| +#include "merkleblock.h" | ||
| + | ||
| +#include <math.h> | ||
| + | ||
| +#include <rapidcheck/Gen.h> | ||
| +#include <rapidcheck/gen/Arbitrary.h> | ||
| + | ||
| +/** Generates a double between 0,1 exclusive */ | ||
| +rc::Gen<double> BetweenZeroAndOne(); | ||
| + | ||
| +rc::Gen<std::tuple<unsigned int, double, unsigned int, unsigned int>> BloomFilterPrimitives(); | ||
| + | ||
| +namespace rc { | ||
| + | ||
| + | ||
| + /** Generator for a new CBloomFilter*/ | ||
| + template<> | ||
| + struct Arbitrary<CBloomFilter> { | ||
| + static Gen<CBloomFilter> arbitrary() { | ||
| + return gen::map(BloomFilterPrimitives(), [](std::tuple<unsigned int, double, unsigned int, unsigned int> filterPrimitives) { | ||
| + unsigned int numElements; | ||
| + double fpRate; | ||
| + unsigned int nTweakIn; | ||
| + unsigned int bloomFlag; | ||
| + std::tie(numElements, fpRate, nTweakIn, bloomFlag) = filterPrimitives; | ||
| + return CBloomFilter(numElements,fpRate,nTweakIn,bloomFlag); | ||
| + }); | ||
| + }; | ||
| + }; | ||
| +} | ||
| + | ||
| +/** Returns a bloom filter loaded with the given uint256s */ | ||
| +rc::Gen<std::tuple<CBloomFilter, std::vector<uint256>>> LoadedBloomFilter(); | ||
| + | ||
| +/** Loads an arbitrary bloom filter with the given hashes */ | ||
| +rc::Gen<std::tuple<CBloomFilter, std::vector<uint256>>> LoadBloomFilter(std::vector<uint256>& hashes); | ||
| + | ||
| + | ||
| +#endif |
| @@ -0,0 +1,53 @@ | ||
| +#ifndef BITCOIN_TEST_GEN_CRYPTO_GEN_H | ||
| +#define BITCOIN_TEST_GEN_CRYPTO_GEN_H | ||
| + | ||
| +#include "key.h" | ||
| +#include "random.h" | ||
| +#include "uint256.h" | ||
| +#include <rapidcheck/gen/Arbitrary.h> | ||
| +#include <rapidcheck/Gen.h> | ||
| +#include <rapidcheck/gen/Create.h> | ||
| +#include <rapidcheck/gen/Numeric.h> | ||
| +namespace rc { | ||
| + | ||
| + /** Generator for a new CKey */ | ||
| + template<> | ||
| + struct Arbitrary<CKey> { | ||
| + static Gen<CKey> arbitrary() { | ||
| + return rc::gen::map<int>([](int x) { | ||
| + CKey key; | ||
| + key.MakeNewKey(true); | ||
| + return key; | ||
| + }); | ||
| + }; | ||
| + }; | ||
| + | ||
| + /** Generator for a CPrivKey */ | ||
| + template<> | ||
| + struct Arbitrary<CPrivKey> { | ||
| + static Gen<CPrivKey> arbitrary() { | ||
| + return gen::map(gen::arbitrary<CKey>(), [](CKey key) { | ||
| + return key.GetPrivKey(); | ||
| + }); | ||
| + }; | ||
| + }; | ||
| + | ||
| + /** Generator for a new CPubKey */ | ||
| + template<> | ||
| + struct Arbitrary<CPubKey> { | ||
| + static Gen<CPubKey> arbitrary() { | ||
| + return gen::map(gen::arbitrary<CKey>(), [](CKey key) { | ||
| + return key.GetPubKey(); | ||
| + }); | ||
| + }; | ||
| + }; | ||
| + | ||
| + /** Generates a arbitrary uint256 */ | ||
| + template<> | ||
| + struct Arbitrary<uint256> { | ||
| + static Gen<uint256> arbitrary() { | ||
| + return rc::gen::just(GetRandHash()); | ||
| + }; | ||
| + }; | ||
| +} | ||
| +#endif |
| @@ -0,0 +1,89 @@ | ||
| +#ifndef BITCOIN_TEST_GEN_MERKLEBLOCK_GEN_H | ||
| +#define BITCOIN_TEST_GEN_MERKLEBLOCK_GEN_H | ||
| + | ||
| +#include <rapidcheck/gen/Arbitrary.h> | ||
| +#include <rapidcheck/Gen.h> | ||
| + | ||
| +#include "merkleblock.h" | ||
| +#include "test/gen/block_gen.h" | ||
| + | ||
| +#include <iostream> | ||
| +namespace rc { | ||
| + /** Returns a CMerkleblock with the hashes that match inside of the CPartialMerkleTree */ | ||
| + template<> | ||
| + struct Arbitrary<std::pair<CMerkleBlock, std::set<uint256>>> { | ||
| + static Gen<std::pair<CMerkleBlock,std::set<uint256>>> arbitrary() { | ||
| + return gen::map(gen::arbitrary<CBlock>(), [](CBlock block) { | ||
| + std::set<uint256> hashes; | ||
| + for(unsigned int i = 0; i < block.vtx.size(); i++) { | ||
| + //pretty naive to include every other txid in the merkle block | ||
| + //but this will work for now. | ||
| + if (i % 2 == 0) { | ||
| + hashes.insert(block.vtx[i]->GetHash()); | ||
| + } | ||
| + } | ||
| + return std::make_pair(CMerkleBlock(block,hashes),hashes); | ||
| + }); | ||
| + }; | ||
| + }; | ||
| + | ||
| + | ||
| + Gen<std::vector<uint256>> betweenZeroAnd100 = gen::suchThat<std::vector<uint256>>([](std::vector<uint256> hashes) { | ||
| + return hashes.size() <= 100; | ||
| + }); | ||
| + | ||
| + /** Returns an arbitrary CMerkleBlock */ | ||
| + template<> | ||
| + struct Arbitrary<CMerkleBlock> { | ||
| + static Gen<CMerkleBlock> arbitrary() { | ||
| + return gen::map(gen::arbitrary<std::pair<CMerkleBlock, std::set<uint256>>>(), | ||
| + [](std::pair<CMerkleBlock, std::set<uint256>> merkleBlockWithTxIds) { | ||
| + CMerkleBlock merkleBlock = merkleBlockWithTxIds.first; | ||
| + std::set<uint256> insertedHashes = merkleBlockWithTxIds.second; | ||
| + return merkleBlock; | ||
| + }); | ||
| + }; | ||
| + }; | ||
| + | ||
| + | ||
| + | ||
| + /** Generates a CPartialMerkleTree and returns the PartialMerkleTree along | ||
| + * with the txids that should be matched inside of it */ | ||
| + template<> | ||
| + struct Arbitrary<std::pair<CPartialMerkleTree, std::vector<uint256>>> { | ||
| + static Gen<std::pair<CPartialMerkleTree, std::vector<uint256>>> arbitrary() { | ||
| + return gen::map(gen::arbitrary<std::vector<uint256>>(), [](std::vector<uint256> txids) { | ||
| + //note this use of 'gen::nonEmpty' above, if we have an empty vector of txids | ||
| + //we will get a memory access violation when calling the CPartialMerkleTree | ||
| + //constructor below. On one hand we shouldn't every have CPartialMerkleTree | ||
| + //with no txids, but on the other hand, it seems we should call | ||
| + //CPartialMerkleTree() inside of CPartialMerkleTree(txids,matches) | ||
| + //if we have zero txids. | ||
| + //Some one who knows more than me will have to elaborate if the memory access violation | ||
| + //is a desirable failure mode or not... | ||
| + std::vector<bool> matches; | ||
| + std::vector<uint256> matchedTxs; | ||
| + for(unsigned int i = 0; i < txids.size(); i++) { | ||
| + //pretty naive to include every other txid in the merkle block | ||
| + //but this will work for now. | ||
| + matches.push_back(i % 2 == 1); | ||
| + if (i % 2 == 1) { | ||
| + matchedTxs.push_back(txids[i]); | ||
| + } | ||
| + } | ||
| + return std::make_pair(CPartialMerkleTree(txids,matches),matchedTxs); | ||
| + }); | ||
| + }; | ||
| + }; | ||
| + | ||
| + template<> | ||
| + struct Arbitrary<CPartialMerkleTree> { | ||
| + static Gen<CPartialMerkleTree> arbitrary() { | ||
| + return gen::map(gen::arbitrary<std::pair<CPartialMerkleTree,std::vector<uint256>>>(), | ||
| + [](std::pair<CPartialMerkleTree,std::vector<uint256>> p) { | ||
| + return p.first; | ||
| + }); | ||
| + }; | ||
| + }; | ||
| +} | ||
| +#endif |
| @@ -0,0 +1,20 @@ | ||
| +#ifndef BITCOIN_TEST_GEN_SCRIPT_GEN_H | ||
| +#define BITCOIN_TEST_GEN_SCRIPT_GEN_H | ||
| + | ||
| +#include "script/script.h" | ||
| +#include <rapidcheck/gen/Arbitrary.h> | ||
| +#include <rapidcheck/Gen.h> | ||
| +#include <rapidcheck/gen/Numeric.h> | ||
| +#include <rapidcheck/gen/Container.h> | ||
| +namespace rc { | ||
| + | ||
| + template<> | ||
| + struct Arbitrary<CScript> { | ||
| + static Gen<CScript> arbitrary() { | ||
| + return gen::map(gen::arbitrary<std::vector<unsigned char>>(), [](std::vector<unsigned char> script) { | ||
| + return CScript(script); | ||
| + }); | ||
| + }; | ||
| + }; | ||
| +} | ||
| +#endif |
Oops, something went wrong.