Page MenuHomePhabricator

No OneTemporary

diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index cfa0dfda5..47d9d5b75 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -1,223 +1,223 @@
// Copyright (c) 2011-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 <test/test_bitcoin.h>
#include <chain.h>
#include <chainparams.h>
#include <config.h>
#include <consensus/consensus.h>
#include <consensus/validation.h>
#include <crypto/sha256.h>
#include <fs.h>
#include <key.h>
#include <logging.h>
#include <miner.h>
#include <net_processing.h>
#include <noui.h>
#include <pubkey.h>
#include <random.h>
#include <rpc/register.h>
#include <rpc/server.h>
#include <script/scriptcache.h>
#include <script/sigcache.h>
#include <txdb.h>
#include <txmempool.h>
#include <ui_interface.h>
#include <validation.h>
#include <memory>
+FastRandomContext g_insecure_rand_ctx;
+
void CConnmanTest::AddNode(CNode &node) {
LOCK(g_connman->cs_vNodes);
g_connman->vNodes.push_back(&node);
}
void CConnmanTest::ClearNodes() {
LOCK(g_connman->cs_vNodes);
g_connman->vNodes.clear();
}
-thread_local FastRandomContext g_insecure_rand_ctx;
-
std::ostream &operator<<(std::ostream &os, const uint256 &num) {
os << num.ToString();
return os;
}
BasicTestingSetup::BasicTestingSetup(const std::string &chainName)
: m_path_root(fs::temp_directory_path() / "test_bitcoin" /
strprintf("%lu_%i", static_cast<unsigned long>(GetTime()),
int(InsecureRandRange(1 << 30)))) {
SHA256AutoDetect();
RandomInit();
ECC_Start();
SetupEnvironment();
SetupNetworking();
InitSignatureCache();
InitScriptExecutionCache();
// Don't want to write to debug.log file.
GetLogger().m_print_to_file = false;
fCheckBlockIndex = true;
SelectParams(chainName);
noui_connect();
// Set config parameters to default.
GlobalConfig config;
config.SetMaxBlockSize(DEFAULT_MAX_BLOCK_SIZE);
}
BasicTestingSetup::~BasicTestingSetup() {
fs::remove_all(m_path_root);
ECC_Stop();
}
fs::path BasicTestingSetup::SetDataDir(const std::string &name) {
fs::path ret = m_path_root / name;
fs::create_directories(ret);
gArgs.ForceSetArg("-datadir", ret.string());
return ret;
}
TestingSetup::TestingSetup(const std::string &chainName)
: BasicTestingSetup(chainName) {
SetDataDir("tempdir");
const Config &config = GetConfig();
const CChainParams &chainparams = config.GetChainParams();
// Ideally we'd move all the RPC tests to the functional testing framework
// instead of unit tests, but for now we need these here.
RPCServer rpcServer;
RegisterAllRPCCommands(config, rpcServer, tableRPC);
/**
* RPC does not come out of the warmup state on its own. Normally, this is
* handled in bitcoind's init path, but unit tests do not trigger this
* codepath, so we call it explicitly as part of setup.
*/
std::string rpcWarmupStatus;
if (RPCIsInWarmup(&rpcWarmupStatus)) {
SetRPCWarmupFinished();
}
ClearDatadirCache();
// We have to run a scheduler thread to prevent ActivateBestChain
// from blocking due to queue overrun.
threadGroup.create_thread(
boost::bind(&CScheduler::serviceQueue, &scheduler));
GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
g_mempool.setSanityCheck(1.0);
pblocktree.reset(new CBlockTreeDB(1 << 20, true));
pcoinsdbview.reset(new CCoinsViewDB(1 << 23, true));
pcoinsTip.reset(new CCoinsViewCache(pcoinsdbview.get()));
if (!LoadGenesisBlock(chainparams)) {
throw std::runtime_error("LoadGenesisBlock failed.");
}
{
CValidationState state;
if (!ActivateBestChain(config, state)) {
throw std::runtime_error("ActivateBestChain failed.");
}
}
nScriptCheckThreads = 3;
for (int i = 0; i < nScriptCheckThreads - 1; i++) {
threadGroup.create_thread(&ThreadScriptCheck);
}
// Deterministic randomness for tests.
g_connman = std::unique_ptr<CConnman>(new CConnman(config, 0x1337, 0x1337));
connman = g_connman.get();
peerLogic.reset(new PeerLogicValidation(connman, scheduler));
}
TestingSetup::~TestingSetup() {
threadGroup.interrupt_all();
threadGroup.join_all();
GetMainSignals().FlushBackgroundCallbacks();
GetMainSignals().UnregisterBackgroundSignalScheduler();
g_connman.reset();
peerLogic.reset();
UnloadBlockIndex();
pcoinsTip.reset();
pcoinsdbview.reset();
pblocktree.reset();
}
TestChain100Setup::TestChain100Setup()
: TestingSetup(CBaseChainParams::REGTEST) {
// Generate a 100-block chain:
coinbaseKey.MakeNewKey(true);
CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey())
<< OP_CHECKSIG;
for (int i = 0; i < COINBASE_MATURITY; i++) {
std::vector<CMutableTransaction> noTxns;
CBlock b = CreateAndProcessBlock(noTxns, scriptPubKey);
m_coinbase_txns.push_back(b.vtx[0]);
}
}
//
// Create a new block with just given transactions, coinbase paying to
// scriptPubKey, and try to add it to the current chain.
//
CBlock TestChain100Setup::CreateAndProcessBlock(
const std::vector<CMutableTransaction> &txns, const CScript &scriptPubKey) {
const Config &config = GetConfig();
std::unique_ptr<CBlockTemplate> pblocktemplate =
BlockAssembler(config, g_mempool).CreateNewBlock(scriptPubKey);
CBlock &block = pblocktemplate->block;
// Replace mempool-selected txns with just coinbase plus passed-in txns:
block.vtx.resize(1);
for (const CMutableTransaction &tx : txns) {
block.vtx.push_back(MakeTransactionRef(tx));
}
// Order transactions by canonical order
std::sort(std::begin(block.vtx) + 1, std::end(block.vtx),
[](const std::shared_ptr<const CTransaction> &txa,
const std::shared_ptr<const CTransaction> &txb) -> bool {
return txa->GetId() < txb->GetId();
});
// IncrementExtraNonce creates a valid coinbase and merkleRoot
unsigned int extraNonce = 0;
{
LOCK(cs_main);
IncrementExtraNonce(config, &block, chainActive.Tip(), extraNonce);
}
while (!CheckProofOfWork(block.GetHash(), block.nBits, config)) {
++block.nNonce;
}
std::shared_ptr<const CBlock> shared_pblock =
std::make_shared<const CBlock>(block);
ProcessNewBlock(GetConfig(), shared_pblock, true, nullptr);
CBlock result = block;
return result;
}
TestChain100Setup::~TestChain100Setup() {}
CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction &tx,
CTxMemPool *pool) {
return FromTx(MakeTransactionRef(tx), pool);
}
CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CTransactionRef &tx,
CTxMemPool *pool) {
// Hack to assume either it's completely dependent on other mempool txs or
// not at all.
Amount inChainValue =
pool && pool->HasNoInputsOf(*tx) ? tx->GetValueOut() : Amount::zero();
return CTxMemPoolEntry(tx, nFee, nTime, dPriority, nHeight, inChainValue,
spendsCoinbase, sigOpCost, lp);
}
diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h
index 4b7e4012e..09d80e75e 100644
--- a/src/test/test_bitcoin.h
+++ b/src/test/test_bitcoin.h
@@ -1,164 +1,171 @@
// Copyright (c) 2015-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.
#ifndef BITCOIN_TEST_TEST_BITCOIN_H
#define BITCOIN_TEST_TEST_BITCOIN_H
#include <chainparamsbase.h>
#include <fs.h>
#include <key.h>
#include <pubkey.h>
#include <random.h>
#include <scheduler.h>
#include <txmempool.h>
/**
* Version of Boost::test prior to 1.64 have issues when dealing with nullptr_t.
* In order to work around this, we ensure that the null pointers are typed in a
* way that Boost will like better.
*
* TODO: Use nullptr directly once the minimum version of boost is 1.64 or more.
*/
#define NULLPTR(T) static_cast<T *>(nullptr)
-thread_local extern FastRandomContext g_insecure_rand_ctx;
+/**
+ * This global and the helpers that use it are not thread-safe.
+ *
+ * If thread-safety is needed, the global could be made thread_local (given
+ * that thread_local is supported on all architectures we support) or a
+ * per-thread instance could be used in the multi-threaded test.
+ */
+extern FastRandomContext g_insecure_rand_ctx;
static inline void SeedInsecureRand(bool deterministic = false) {
g_insecure_rand_ctx = FastRandomContext(deterministic);
}
static inline uint32_t InsecureRand32() {
return g_insecure_rand_ctx.rand32();
}
static inline uint256 InsecureRand256() {
return g_insecure_rand_ctx.rand256();
}
static inline uint64_t InsecureRandBits(int bits) {
return g_insecure_rand_ctx.randbits(bits);
}
static inline uint64_t InsecureRandRange(uint64_t range) {
return g_insecure_rand_ctx.randrange(range);
}
static inline bool InsecureRandBool() {
return g_insecure_rand_ctx.randbool();
}
/**
* Basic testing setup.
* This just configures logging and chain parameters.
*/
struct BasicTestingSetup {
ECCVerifyHandle globalVerifyHandle;
explicit BasicTestingSetup(
const std::string &chainName = CBaseChainParams::MAIN);
~BasicTestingSetup();
fs::path SetDataDir(const std::string &name);
private:
const fs::path m_path_root;
};
/**
* Testing setup that configures a complete environment.
* Included are data directory, coins database, script check threads setup.
*/
class CConnman;
class CNode;
struct CConnmanTest {
static void AddNode(CNode &node);
static void ClearNodes();
};
class PeerLogicValidation;
struct TestingSetup : public BasicTestingSetup {
boost::thread_group threadGroup;
CConnman *connman;
CScheduler scheduler;
std::unique_ptr<PeerLogicValidation> peerLogic;
explicit TestingSetup(
const std::string &chainName = CBaseChainParams::MAIN);
~TestingSetup();
};
class CBlock;
class CMutableTransaction;
class CScript;
//
// Testing fixture that pre-creates a
// 100-block REGTEST-mode block chain
//
struct TestChain100Setup : public TestingSetup {
TestChain100Setup();
// Create a new block with just given transactions, coinbase paying to
// scriptPubKey, and try to add it to the current chain.
CBlock CreateAndProcessBlock(const std::vector<CMutableTransaction> &txns,
const CScript &scriptPubKey);
~TestChain100Setup();
// For convenience, coinbase transactions.
std::vector<CTransactionRef> m_coinbase_txns;
// private/public key needed to spend coinbase transactions.
CKey coinbaseKey;
};
class CTxMemPoolEntry;
class CTxMemPool;
struct TestMemPoolEntryHelper {
// Default values
Amount nFee;
int64_t nTime;
double dPriority;
unsigned int nHeight;
bool spendsCoinbase;
unsigned int sigOpCost;
LockPoints lp;
TestMemPoolEntryHelper()
: nFee(), nTime(0), dPriority(0.0), nHeight(1), spendsCoinbase(false),
sigOpCost(4) {}
CTxMemPoolEntry FromTx(const CMutableTransaction &tx,
CTxMemPool *pool = nullptr);
CTxMemPoolEntry FromTx(const CTransactionRef &tx,
CTxMemPool *pool = nullptr);
// Change the default value
TestMemPoolEntryHelper &Fee(Amount _fee) {
nFee = _fee;
return *this;
}
TestMemPoolEntryHelper &Time(int64_t _time) {
nTime = _time;
return *this;
}
TestMemPoolEntryHelper &Priority(double _priority) {
dPriority = _priority;
return *this;
}
TestMemPoolEntryHelper &Height(unsigned int _height) {
nHeight = _height;
return *this;
}
TestMemPoolEntryHelper &SpendsCoinbase(bool _flag) {
spendsCoinbase = _flag;
return *this;
}
TestMemPoolEntryHelper &SigOpsCost(unsigned int _sigopsCost) {
sigOpCost = _sigopsCost;
return *this;
}
};
// define an implicit conversion here so that uint256 may be used directly in
// BOOST_CHECK_*
std::ostream &operator<<(std::ostream &os, const uint256 &num);
#endif
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index 609061981..57a35b1c2 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -1,203 +1,206 @@
// Copyright (c) 2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <boost/test/unit_test.hpp>
#include <chain.h>
#include <chainparams.h>
#include <config.h>
#include <consensus/merkle.h>
#include <consensus/validation.h>
#include <miner.h>
#include <pow.h>
#include <random.h>
#include <test/test_bitcoin.h>
#include <utiltime.h>
#include <validation.h>
#include <validationinterface.h>
struct RegtestingSetup : public TestingSetup {
RegtestingSetup() : TestingSetup(CBaseChainParams::REGTEST) {}
};
BOOST_FIXTURE_TEST_SUITE(validation_block_tests, RegtestingSetup)
struct TestSubscriber : public CValidationInterface {
uint256 m_expected_tip;
TestSubscriber(uint256 tip) : m_expected_tip(tip) {}
void UpdatedBlockTip(const CBlockIndex *pindexNew,
const CBlockIndex *pindexFork, bool fInitialDownload) {
BOOST_CHECK_EQUAL(m_expected_tip, pindexNew->GetBlockHash());
}
void BlockConnected(const std::shared_ptr<const CBlock> &block,
const CBlockIndex *pindex,
const std::vector<CTransactionRef> &txnConflicted) {
BOOST_CHECK_EQUAL(m_expected_tip, block->hashPrevBlock);
BOOST_CHECK_EQUAL(m_expected_tip, pindex->pprev->GetBlockHash());
m_expected_tip = block->GetHash();
}
void BlockDisconnected(const std::shared_ptr<const CBlock> &block) {
BOOST_CHECK_EQUAL(m_expected_tip, block->GetHash());
m_expected_tip = block->hashPrevBlock;
}
};
std::shared_ptr<CBlock> Block(const Config &config, const uint256 &prev_hash) {
static int i = 0;
static uint64_t time = config.GetChainParams().GenesisBlock().nTime;
CScript pubKey;
pubKey << i++ << OP_TRUE;
auto ptemplate = BlockAssembler(config, g_mempool).CreateNewBlock(pubKey);
auto pblock = std::make_shared<CBlock>(ptemplate->block);
pblock->hashPrevBlock = prev_hash;
pblock->nTime = ++time;
CMutableTransaction txCoinbase(*pblock->vtx[0]);
txCoinbase.vout.resize(1);
pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));
return pblock;
}
std::shared_ptr<CBlock> FinalizeBlock(const Config &config,
std::shared_ptr<CBlock> pblock) {
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
while (!CheckProofOfWork(pblock->GetHash(), pblock->nBits, config)) {
++(pblock->nNonce);
}
return pblock;
}
// construct a valid block
const std::shared_ptr<const CBlock> GoodBlock(const Config &config,
const uint256 &prev_hash) {
return FinalizeBlock(config, Block(config, prev_hash));
}
// construct an invalid block (but with a valid header)
const std::shared_ptr<const CBlock> BadBlock(const Config &config,
const uint256 &prev_hash) {
auto pblock = Block(config, prev_hash);
CMutableTransaction coinbase_spend;
coinbase_spend.vin.push_back(
CTxIn(COutPoint(pblock->vtx[0]->GetHash(), 0), CScript(), 0));
coinbase_spend.vout.push_back(pblock->vtx[0]->vout[0]);
CTransactionRef tx = MakeTransactionRef(coinbase_spend);
pblock->vtx.push_back(tx);
auto ret = FinalizeBlock(config, pblock);
return ret;
}
void BuildChain(const Config &config, const uint256 &root, int height,
const unsigned int invalid_rate, const unsigned int branch_rate,
const unsigned int max_size,
std::vector<std::shared_ptr<const CBlock>> &blocks) {
if (height <= 0 || blocks.size() >= max_size) {
return;
}
bool gen_invalid = InsecureRandRange(100) < invalid_rate;
bool gen_fork = InsecureRandRange(100) < branch_rate;
const std::shared_ptr<const CBlock> pblock =
gen_invalid ? BadBlock(config, root) : GoodBlock(config, root);
blocks.push_back(pblock);
if (!gen_invalid) {
BuildChain(config, pblock->GetHash(), height - 1, invalid_rate,
branch_rate, max_size, blocks);
}
if (gen_fork) {
blocks.push_back(GoodBlock(config, root));
BuildChain(config, blocks.back()->GetHash(), height - 1, invalid_rate,
branch_rate, max_size, blocks);
}
}
BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering) {
GlobalConfig config;
const CChainParams &chainParams = config.GetChainParams();
// build a large-ish chain that's likely to have some forks
std::vector<std::shared_ptr<const CBlock>> blocks;
while (blocks.size() < 50) {
blocks.clear();
BuildChain(config, chainParams.GenesisBlock().GetHash(), 100, 15, 10,
500, blocks);
}
bool ignored;
CValidationState state;
std::vector<CBlockHeader> headers;
std::transform(
blocks.begin(), blocks.end(), std::back_inserter(headers),
[](std::shared_ptr<const CBlock> b) { return b->GetBlockHeader(); });
// Process all the headers so we understand the toplogy of the chain
BOOST_CHECK(ProcessNewBlockHeaders(config, headers, state));
// Connect the genesis block and drain any outstanding events
ProcessNewBlock(config,
std::make_shared<CBlock>(chainParams.GenesisBlock()), true,
&ignored);
SyncWithValidationInterfaceQueue();
// subscribe to events (this subscriber will validate event ordering)
const CBlockIndex *initial_tip = nullptr;
{
LOCK(cs_main);
initial_tip = chainActive.Tip();
}
TestSubscriber sub(initial_tip->GetBlockHash());
RegisterValidationInterface(&sub);
// create a bunch of threads that repeatedly process a block generated above
// at random this will create parallelism and randomness inside validation -
// the ValidationInterface will subscribe to events generated during block
// validation and assert on ordering invariance
- boost::thread_group threads;
+ std::vector<std::thread> threads;
for (int i = 0; i < 10; i++) {
- threads.create_thread([&config, &blocks]() {
+ threads.emplace_back([&config, &blocks]() {
bool tlignored;
+ FastRandomContext insecure;
for (int j = 0; j < 1000; j++) {
- auto block = blocks[InsecureRandRange(blocks.size() - 1)];
+ auto block = blocks[insecure.randrange(blocks.size() - 1)];
ProcessNewBlock(config, block, true, &tlignored);
}
// to make sure that eventually we process the full chain - do it
// here
for (auto block : blocks) {
if (block->vtx.size() == 1) {
bool processed =
ProcessNewBlock(config, block, true, &tlignored);
assert(processed);
}
}
});
}
- threads.join_all();
+ for (auto &t : threads) {
+ t.join();
+ }
while (GetMainSignals().CallbacksPending() > 0) {
MilliSleep(100);
}
UnregisterValidationInterface(&sub);
BOOST_CHECK_EQUAL(sub.m_expected_tip, chainActive.Tip()->GetBlockHash());
}
BOOST_AUTO_TEST_SUITE_END()

File Metadata

Mime Type
text/x-diff
Expires
Sun, Mar 2, 08:32 (20 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5180460
Default Alt Text
(20 KB)

Event Timeline