PIVX Core  5.6.99
P2P Digital Currency
validation_block_tests.cpp
Go to the documentation of this file.
1 // Copyright (c) 2018 The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or https://www.opensource.org/licenses/mit-license.php.
4 
5 #include <boost/test/unit_test.hpp>
6 
7 #include "blockassembler.h"
8 #include "chainparams.h"
9 #include "consensus/merkle.h"
10 #include "consensus/validation.h"
11 #include "pow.h"
12 #include "random.h"
13 #include "test/test_pivx.h"
14 #include "util/blockstatecatcher.h"
15 #include "validation.h"
16 #include "validationinterface.h"
17 
18 
19 #define ASSERT_WITH_MSG(cond, msg) if (!cond) { BOOST_ERROR(msg); }
20 
21 
22 BOOST_FIXTURE_TEST_SUITE(validation_block_tests, RegTestingSetup)
23 
26 
27  explicit TestSubscriber(uint256 tip) : m_expected_tip(std::move(tip)) {}
28 
29  void UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork, bool fInitialDownload)
30  {
31  BOOST_CHECK_EQUAL(m_expected_tip, pindexNew->GetBlockHash());
32  }
33 
34  void BlockConnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex)
35  {
36  BOOST_CHECK_EQUAL(m_expected_tip, block->hashPrevBlock);
37  BOOST_CHECK_EQUAL(m_expected_tip, pindex->pprev->GetBlockHash());
38 
39  m_expected_tip = block->GetHash();
40  }
41 
42  void BlockDisconnected(const std::shared_ptr<const CBlock> &block, const uint256& blockHash, int nBlockHeight, int64_t blockTime)
43  {
44  BOOST_CHECK_EQUAL(m_expected_tip, block->GetHash());
45 
46  m_expected_tip = block->hashPrevBlock;
47  }
48 };
49 
50 std::shared_ptr<CBlock> Block(const uint256& prev_hash)
51 {
52  static int i = 0;
53  static uint64_t time = Params().GenesisBlock().nTime;
54 
55  CScript pubKey;
56  pubKey << i++ << OP_TRUE;
57 
58  auto ptemplate = BlockAssembler(Params(), false).CreateNewBlock(pubKey);
59  auto pblock = std::make_shared<CBlock>(ptemplate->block);
60  pblock->hashPrevBlock = prev_hash;
61  pblock->nTime = ++time;
62 
63  CMutableTransaction txCoinbase(*pblock->vtx[0]);
64  txCoinbase.vout.resize(1);
65  pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));
66 
67  return pblock;
68 }
69 
70 // construct a valid block
71 const std::shared_ptr<const CBlock> GoodBlock(const uint256& prev_hash)
72 {
73  return FinalizeBlock(Block(prev_hash));
74 }
75 
76 // construct an invalid block (but with a valid header)
77 const std::shared_ptr<const CBlock> BadBlock(const uint256& prev_hash)
78 {
79  auto pblock = Block(prev_hash);
80 
81  CMutableTransaction coinbase_spend;
82  coinbase_spend.vin.emplace_back(CTxIn(COutPoint(pblock->vtx[0]->GetHash(), 0), CScript(), 0));
83  coinbase_spend.vout.emplace_back(pblock->vtx[0]->vout[0]);
84 
85  CTransactionRef tx = MakeTransactionRef(coinbase_spend);
86  pblock->vtx.emplace_back(tx);
87 
88  auto ret = FinalizeBlock(pblock);
89  return ret;
90 }
91 
92 void BuildChain(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)
93 {
94  if (height <= 0 || blocks.size() >= max_size) return;
95 
96  bool gen_invalid = GetRand(100) < invalid_rate;
97  bool gen_fork = GetRand(100) < branch_rate;
98 
99  const std::shared_ptr<const CBlock> pblock = gen_invalid ? BadBlock(root) : GoodBlock(root);
100  blocks.emplace_back(pblock);
101  if (!gen_invalid) {
102  BuildChain(pblock->GetHash(), height - 1, invalid_rate, branch_rate, max_size, blocks);
103  }
104 
105  if (gen_fork) {
106  blocks.emplace_back(GoodBlock(root));
107  BuildChain(blocks.back()->GetHash(), height - 1, invalid_rate, branch_rate, max_size, blocks);
108  }
109 }
110 
111 BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
112 {
113  // build a large-ish chain that's likely to have some forks
114  std::vector<std::shared_ptr<const CBlock>> blocks;
115  while (blocks.size() < 50) {
116  blocks.clear();
117  BuildChain(Params().GenesisBlock().GetHash(), 100, 15, 10, 500, blocks);
118  }
119 
120  // Connect the genesis block and drain any outstanding events
121  BOOST_CHECK_MESSAGE(ProcessNewBlock(std::make_shared<CBlock>(Params().GenesisBlock()), nullptr), "Error: genesis not connected");
123 
124  // subscribe to events (this subscriber will validate event ordering)
125  const CBlockIndex* initial_tip = WITH_LOCK(cs_main, return chainActive.Tip());
126  TestSubscriber sub(initial_tip->GetBlockHash());
128 
129  // create a bunch of threads that repeatedly process a block generated above at random
130  // this will create parallelism and randomness inside validation - the ValidationInterface
131  // will subscribe to events generated during block validation and assert on ordering invariance
132  boost::thread_group threads;
133  for (int i = 0; i < 10; i++) {
134  threads.create_thread([&blocks]() {
135  for (int i = 0; i < 1000; i++) {
136  auto block = blocks[GetRand(blocks.size() - 1)];
137  ProcessNewBlock(block, nullptr);
138  }
139 
141  sc.registerEvent();
142  // to make sure that eventually we process the full chain - do it here
143  for (const auto& block : blocks) {
144  if (block->vtx.size() == 1) {
145  sc.get().setBlockHash(block->GetHash());
146  bool processed = ProcessNewBlock(block, nullptr);
147  // Future to do: "prevblk-not-found" here is the only valid reason to not check processed flag.
148  std::string stateReason = sc.get().state.GetRejectReason();
149  if (sc.get().found && (stateReason == "duplicate" || stateReason == "prevblk-not-found" ||
150  stateReason == "bad-prevblk" || stateReason == "blk-out-of-order")) continue;
151  ASSERT_WITH_MSG(processed, ("Error: " + sc.get().state.GetRejectReason()).c_str());
152  }
153  }
154  });
155  }
156 
157  threads.join_all();
158  while (GetMainSignals().CallbacksPending() > 0) {
159  MilliSleep(100);
160  }
161 
164 
166 }
167 
const CChainParams & Params()
Return the currently selected parameters.
Generate a new block.
std::unique_ptr< CBlockTemplate > CreateNewBlock(const CScript &scriptPubKeyIn, CWallet *pwallet=nullptr, bool fProofOfStake=false, std::vector< CStakeableOutput > *availableCoins=nullptr, bool fNoMempoolTx=false, bool fTestValidity=true, CBlockIndex *prevBlock=nullptr, bool stopPoSOnNewBlock=true, bool fIncludeQfc=true)
Construct a new block template with coinbase to scriptPubKeyIn.
void setBlockHash(const uint256 &_hash)
CValidationState state
BlockStateCatcher & get() const
uint32_t nTime
Definition: block.h:30
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: chain.h:139
CBlockIndex * pprev
pointer to the index of the predecessor of this block
Definition: chain.h:145
uint256 GetBlockHash() const
Definition: chain.h:215
CBlockIndex * Tip(bool fProofOfStake=false) const
Returns the index entry for the tip of this chain, or nullptr if none.
Definition: chain.h:405
const CBlock & GenesisBlock() const
Definition: chainparams.h:76
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:72
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:381
An input of a transaction.
Definition: transaction.h:94
Implement this to subscribe to events generated in validation.
std::string GetRejectReason() const
Definition: validation.h:94
256-bit opaque blob.
Definition: uint256.h:138
BOOST_AUTO_TEST_SUITE_END()
Definition: uint256.h:212
RecursiveMutex cs_main
Global state.
Definition: validation.cpp:80
#define BOOST_FIXTURE_TEST_SUITE(a, b)
Definition: object.cpp:14
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
uint64_t GetRand(uint64_t nMax) noexcept
Definition: random.cpp:586
@ OP_TRUE
Definition: script.h:60
A mutable version of CTransaction.
Definition: transaction.h:409
std::vector< CTxOut > vout
Definition: transaction.h:411
std::vector< CTxIn > vin
Definition: transaction.h:410
void BlockConnected(const std::shared_ptr< const CBlock > &block, const CBlockIndex *pindex)
Notifies listeners of a block being connected.
void BlockDisconnected(const std::shared_ptr< const CBlock > &block, const uint256 &blockHash, int nBlockHeight, int64_t blockTime)
Notifies listeners of a block being disconnected.
void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload)
Notifies listeners when the block chain tip advances.
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:247
std::shared_ptr< CBlock > FinalizeBlock(std::shared_ptr< CBlock > pblock)
Definition: test_pivx.cpp:239
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:456
const uint256 UINT256_ZERO
constant uint256 instances
Definition: uint256.h:175
void MilliSleep(int64_t n)
Definition: utiltime.cpp:82
bool ProcessNewBlock(const std::shared_ptr< const CBlock > &pblock, const FlatFilePos *dbp)
Process an incoming block.
CChain chainActive
The currently-connected chain of blocks (protected by cs_main).
Definition: validation.cpp:84
void BuildChain(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)
#define ASSERT_WITH_MSG(cond, msg)
const std::shared_ptr< const CBlock > GoodBlock(const uint256 &prev_hash)
BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
std::shared_ptr< CBlock > Block(const uint256 &prev_hash)
const std::shared_ptr< const CBlock > BadBlock(const uint256 &prev_hash)
CMainSignals & GetMainSignals()
void UnregisterValidationInterface(CValidationInterface *pwalletIn)
Unregister a wallet from core.
void RegisterValidationInterface(CValidationInterface *pwalletIn)
Register a wallet to receive updates from core.
void SyncWithValidationInterfaceQueue()
This is a synonym for the following, which asserts certain locks are not held: std::promise<void> pro...