Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F13114840
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
20 KB
Subscribers
None
View Options
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
Details
Attached
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)
Attached To
rABC Bitcoin ABC
Event Timeline
Log In to Comment