Line data Source code
1 : // Copyright (c) 2020-2022 The PIVX 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 "wallet/test/pos_test_fixture.h"
6 :
7 : #include "blockassembler.h"
8 : #include "coincontrol.h"
9 : #include "util/blockstatecatcher.h"
10 : #include "blocksignature.h"
11 : #include "consensus/merkle.h"
12 : #include "primitives/block.h"
13 : #include "script/sign.h"
14 : #include "test/util/blocksutil.h"
15 : #include "wallet/wallet.h"
16 :
17 : #include <boost/test/unit_test.hpp>
18 :
19 : BOOST_AUTO_TEST_SUITE(pos_validations_tests)
20 :
21 2 : void reSignTx(CMutableTransaction& mtx,
22 : const std::vector<CTxOut>& txPrevOutputs,
23 : CWallet* wallet)
24 : {
25 4 : CTransaction txNewConst(mtx);
26 5 : for (int index=0; index < (int) txPrevOutputs.size(); index++) {
27 3 : const CTxOut& prevOut = txPrevOutputs.at(index);
28 6 : SignatureData sigdata;
29 6 : BOOST_ASSERT(ProduceSignature(
30 : TransactionSignatureCreator(wallet, &txNewConst, index, prevOut.nValue, SIGHASH_ALL),
31 : prevOut.scriptPubKey,
32 : sigdata,
33 : txNewConst.GetRequiredSigVersion(),
34 : true));
35 3 : UpdateTransaction(mtx, index, sigdata);
36 : }
37 2 : }
38 :
39 2 : BOOST_FIXTURE_TEST_CASE(coinstake_tests, TestPoSChainSetup)
40 : {
41 : // Verify that we are at block 251
42 3 : BOOST_CHECK_EQUAL(WITH_LOCK(cs_main, return chainActive.Tip()->nHeight), 250);
43 1 : SyncWithValidationInterfaceQueue();
44 :
45 : // Let's create the block
46 1 : std::vector<CStakeableOutput> availableCoins;
47 2 : BOOST_CHECK(pwalletMain->StakeableCoins(&availableCoins));
48 1 : std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(
49 2 : Params(), false).CreateNewBlock(CScript(),
50 : pwalletMain.get(),
51 : true,
52 : &availableCoins,
53 2 : true);
54 2 : std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>(pblocktemplate->block);
55 2 : BOOST_CHECK(pblock->IsProofOfStake());
56 :
57 : // Add a second input to a coinstake
58 2 : CMutableTransaction mtx(*pblock->vtx[1]);
59 1 : const CStakeableOutput& in2 = availableCoins.back();
60 1 : availableCoins.pop_back();
61 2 : CTxIn vin2(in2.tx->GetHash(), in2.i);
62 1 : mtx.vin.emplace_back(vin2);
63 :
64 2 : CTxOut prevOutput1 = pwalletMain->GetWalletTx(mtx.vin[0].prevout.hash)->tx->vout[mtx.vin[0].prevout.n];
65 4 : std::vector<CTxOut> txPrevOutputs{prevOutput1, in2.tx->tx->vout[in2.i]};
66 :
67 1 : reSignTx(mtx, txPrevOutputs, pwalletMain.get());
68 2 : pblock->vtx[1] = MakeTransactionRef(mtx);
69 1 : pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
70 2 : BOOST_CHECK(SignBlock(*pblock, *pwalletMain));
71 1 : ProcessBlockAndCheckRejectionReason(pblock, "bad-cs-multi-inputs", 250);
72 :
73 : // Check multi-empty-outputs now
74 2 : pblock = std::make_shared<CBlock>(pblocktemplate->block);
75 1 : mtx = CMutableTransaction(*pblock->vtx[1]);
76 1000 : for (int i = 0; i < 999; ++i) {
77 999 : mtx.vout.emplace_back();
78 999 : mtx.vout.back().SetEmpty();
79 : }
80 2 : reSignTx(mtx, {prevOutput1}, pwalletMain.get());
81 2 : pblock->vtx[1] = MakeTransactionRef(mtx);
82 1 : pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
83 2 : BOOST_CHECK(SignBlock(*pblock, *pwalletMain));
84 1 : ProcessBlockAndCheckRejectionReason(pblock, "bad-txns-vout-empty", 250);
85 :
86 : // Now connect the proper block
87 2 : pblock = std::make_shared<CBlock>(pblocktemplate->block);
88 1 : ProcessNewBlock(pblock, nullptr);
89 3 : BOOST_CHECK_EQUAL(WITH_LOCK(cs_main, return chainActive.Tip()->GetBlockHash()), pblock->GetHash());
90 1 : }
91 :
92 13 : CTransaction CreateAndCommitTx(CWallet* pwalletMain, const CTxDestination& dest, CAmount destValue, CCoinControl* coinControl = nullptr)
93 : {
94 26 : CTransactionRef txNew;
95 26 : CReserveKey reservekey(pwalletMain);
96 13 : CAmount nFeeRet = 0;
97 26 : std::string strFailReason;
98 : // The minimum depth (100) required to spend coinbase outputs is calculated from the current chain tip.
99 : // Since this transaction could be included in a fork block, at a lower height, this may result in
100 : // selecting non-yet-available inputs, and thus creating non-connectable blocks due to premature-cb-spend.
101 : // So, to be sure, only select inputs which are more than 120-blocks deep in the chain.
102 13 : BOOST_ASSERT(pwalletMain->CreateTransaction(GetScriptForDestination(dest),
103 : destValue,
104 : txNew,
105 : reservekey,
106 : nFeeRet,
107 : strFailReason,
108 : coinControl,
109 : true, /* sign*/
110 : false, /*fIncludeDelegated*/
111 : nullptr, /*fStakeDelegationVoided*/
112 : 0, /*nExtraSize*/
113 : 120 /*nMinDepth*/));
114 26 : pwalletMain->CommitTransaction(txNew, reservekey, nullptr);
115 26 : return *txNew;
116 : }
117 :
118 5 : COutPoint GetOutpointWithAmount(const CTransaction& tx, CAmount outpointValue)
119 : {
120 7 : for (size_t i = 0; i < tx.vout.size(); i++) {
121 7 : if (tx.vout[i].nValue == outpointValue) {
122 5 : return COutPoint(tx.GetHash(), i);
123 : }
124 : }
125 0 : BOOST_ASSERT_MSG(false, "error in test, no output in tx for value");
126 : return {};
127 : }
128 :
129 6294 : static bool IsSpentOnFork(const COutput& coin, std::initializer_list<std::shared_ptr<CBlock>> forkchain = {})
130 : {
131 9010 : for (const auto& block : forkchain) {
132 2738 : const auto& usedOutput = block->vtx[1]->vin.at(0).prevout;
133 2738 : if (coin.tx->GetHash() == usedOutput.hash && coin.i == (int)usedOutput.n) {
134 : // spent on fork
135 : return true;
136 : }
137 : }
138 : return false;
139 : }
140 :
141 49 : std::shared_ptr<CBlock> CreateBlockInternal(CWallet* pwalletMain, const std::vector<CMutableTransaction>& txns = {},
142 : CBlockIndex* customPrevBlock = nullptr,
143 : std::initializer_list<std::shared_ptr<CBlock>> forkchain = {})
144 : {
145 49 : std::vector<CStakeableOutput> availableCoins;
146 98 : BOOST_CHECK(pwalletMain->StakeableCoins(&availableCoins));
147 :
148 : // Remove any utxo which is not deeper than 120 blocks (for the same reasoning
149 : // used when selecting tx inputs in CreateAndCommitTx)
150 : // Also, as the wallet is not prepared to follow several chains at the same time,
151 : // need to manually remove from the stakeable utxo set every already used
152 : // coinstake inputs on the previous blocks of the parallel chain so they
153 : // are not used again.
154 49 : for (auto it = availableCoins.begin(); it != availableCoins.end() ;) {
155 7273 : if (it->nDepth <= 120 || IsSpentOnFork(*it, forkchain)) {
156 1002 : it = availableCoins.erase(it);
157 : } else {
158 13593 : it++;
159 : }
160 : }
161 :
162 49 : std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(
163 49 : Params(), false).CreateNewBlock(CScript(),
164 : pwalletMain,
165 : true,
166 : &availableCoins,
167 : true,
168 : false,
169 : customPrevBlock,
170 98 : false);
171 49 : BOOST_ASSERT(pblocktemplate);
172 49 : auto pblock = std::make_shared<CBlock>(pblocktemplate->block);
173 49 : if (!txns.empty()) {
174 31 : for (const auto& tx : txns) {
175 32 : pblock->vtx.emplace_back(MakeTransactionRef(tx));
176 : }
177 15 : pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
178 15 : assert(SignBlock(*pblock, *pwalletMain));
179 : }
180 98 : return pblock;
181 : }
182 :
183 1 : static COutput GetUnspentCoin(CWallet* pwallet, std::initializer_list<std::shared_ptr<CBlock>> forkchain = {})
184 : {
185 1 : std::vector<COutput> availableCoins;
186 1 : CWallet::AvailableCoinsFilter coinsFilter;
187 1 : coinsFilter.minDepth = 120;
188 2 : BOOST_CHECK(pwallet->AvailableCoins(&availableCoins, nullptr, coinsFilter));
189 1 : for (const auto& coin : availableCoins) {
190 1 : if (!IsSpentOnFork(coin, forkchain)) {
191 2 : return coin;
192 : }
193 : }
194 0 : throw std::runtime_error("Unspent coin not found");
195 : }
196 :
197 2 : BOOST_FIXTURE_TEST_CASE(created_on_fork_tests, TestPoSChainSetup)
198 : {
199 : // Let's create few more PoS blocks
200 31 : for (int i=0; i<30; i++) {
201 60 : std::shared_ptr<CBlock> pblock = CreateBlockInternal(pwalletMain.get());
202 90 : BOOST_CHECK(ProcessNewBlock(pblock, nullptr));
203 : }
204 :
205 : /*
206 : Chains diagram:
207 : A -- B -- C -- D -- E -- F -- G -- H -- I
208 : \
209 : -- D1 -- E1 -- F1
210 : \
211 : -- E2 -- F2
212 : \
213 : -- D3 -- E3 -- F3
214 : \
215 : -- F3_1 -- G3 -- H3 -- I3
216 : */
217 :
218 : // Tests:
219 : // 1) coins created in D1 and spent in E1. --> should pass
220 : // 2) coins created in E, being spent in D4 --> should fail.
221 : // 3) coins created and spent in E2, being double spent in F2. --> should fail
222 : // 4) coins created in D and spent in E3. --> should fail
223 : // 5) coins create in D, spent in F and then double spent in F3. --> should fail
224 : // 6) coins created in G and G3, being spent in H and H3 --> should pass.
225 : // 7) use coinstake on different chains --> should pass.
226 :
227 : // Let's create block C with a valid cTx
228 2 : auto cTx = CreateAndCommitTx(pwalletMain.get(), *pwalletMain->getNewAddress("").getObjResult(), 249 * COIN);
229 1 : const auto& cTx_out = GetOutpointWithAmount(cTx, 249 * COIN);
230 3 : WITH_LOCK(pwalletMain->cs_wallet, pwalletMain->LockCoin(cTx_out));
231 4 : std::shared_ptr<CBlock> pblockC = CreateBlockInternal(pwalletMain.get(), {cTx});
232 3 : BOOST_CHECK(ProcessNewBlock(pblockC, nullptr));
233 :
234 : // Create block D with a valid dTx
235 3 : auto dTx = CreateAndCommitTx(pwalletMain.get(), *pwalletMain->getNewAddress("").getObjResult(), 249 * COIN);
236 1 : auto dTxOutPoint = GetOutpointWithAmount(dTx, 249 * COIN);
237 3 : WITH_LOCK(pwalletMain->cs_wallet, pwalletMain->LockCoin(dTxOutPoint));
238 4 : std::shared_ptr<CBlock> pblockD = CreateBlockInternal(pwalletMain.get(), {dTx});
239 :
240 : // Create D1 forked block that connects a new tx
241 3 : auto dest = pwalletMain->getNewAddress("").getObjResult();
242 2 : auto d1Tx = CreateAndCommitTx(pwalletMain.get(), *dest, 200 * COIN);
243 4 : std::shared_ptr<CBlock> pblockD1 = CreateBlockInternal(pwalletMain.get(), {d1Tx});
244 :
245 : // Process blocks
246 1 : ProcessNewBlock(pblockD, nullptr);
247 1 : ProcessNewBlock(pblockD1, nullptr);
248 5 : BOOST_CHECK(WITH_LOCK(cs_main, return chainActive.Tip()->GetBlockHash() == pblockD->GetHash()));
249 :
250 : // Ensure that the coin does not exist in the main chain
251 1 : const Coin& utxo = pcoinsTip->AccessCoin(COutPoint(d1Tx.GetHash(), 0));
252 2 : BOOST_CHECK(utxo.out.IsNull());
253 :
254 : // Create valid block E
255 2 : auto eTx = CreateAndCommitTx(pwalletMain.get(), *dest, 200 * COIN);
256 4 : std::shared_ptr<CBlock> pblockE = CreateBlockInternal(pwalletMain.get(), {eTx});
257 3 : BOOST_CHECK(ProcessNewBlock(pblockE, nullptr));
258 :
259 : // #################################################
260 : // ### 1) -> coins created in D' and spent in E' ###
261 : // #################################################
262 :
263 : // Create tx spending the previously created tx on the forked chain
264 2 : CCoinControl coinControl;
265 1 : coinControl.fAllowOtherInputs = false;
266 1 : coinControl.Select(COutPoint(d1Tx.GetHash(), 0), d1Tx.vout[0].nValue);
267 2 : auto e1Tx = CreateAndCommitTx(pwalletMain.get(), *dest, d1Tx.vout[0].nValue - 0.1 * COIN, &coinControl);
268 :
269 1 : CBlockIndex* pindexPrev = mapBlockIndex.at(pblockD1->GetHash());
270 6 : std::shared_ptr<CBlock> pblockE1 = CreateBlockInternal(pwalletMain.get(), {e1Tx}, pindexPrev, {pblockD1});
271 3 : BOOST_CHECK(ProcessNewBlock(pblockE1, nullptr));
272 :
273 : // #################################################################
274 : // ### 2) coins created in E, being spent in D4 --> should fail. ###
275 : // #################################################################
276 :
277 1 : coinControl.UnSelectAll();
278 1 : coinControl.Select(GetOutpointWithAmount(eTx, 200 * COIN), 200 * COIN);
279 1 : coinControl.fAllowOtherInputs = false;
280 2 : auto D4_tx1 = CreateAndCommitTx(pwalletMain.get(), *dest, 199 * COIN, &coinControl);
281 5 : std::shared_ptr<CBlock> pblockD4 = CreateBlockInternal(pwalletMain.get(), {D4_tx1}, mapBlockIndex.at(pblockC->GetHash()));
282 3 : BOOST_CHECK(!ProcessNewBlock(pblockD4, nullptr));
283 :
284 : // #####################################################################
285 : // ### 3) -> coins created and spent in E2, being double spent in F2 ###
286 : // #####################################################################
287 :
288 : // Create block E2 with E2_tx1 and E2_tx2. Where E2_tx2 is spending the outputs of E2_tx1
289 2 : CCoinControl coinControlE2;
290 1 : coinControlE2.Select(cTx_out, 249 * COIN);
291 2 : auto E2_tx1 = CreateAndCommitTx(pwalletMain.get(), *dest, 200 * COIN, &coinControlE2);
292 :
293 1 : coinControl.UnSelectAll();
294 1 : coinControl.Select(GetOutpointWithAmount(E2_tx1, 200 * COIN), 200 * COIN);
295 1 : coinControl.fAllowOtherInputs = false;
296 1 : auto E2_tx2 = CreateAndCommitTx(pwalletMain.get(), *dest, 199 * COIN, &coinControl);
297 :
298 1 : std::shared_ptr<CBlock> pblockE2 = CreateBlockInternal(pwalletMain.get(), {E2_tx1, E2_tx2},
299 7 : pindexPrev, {pblockD1});
300 3 : BOOST_CHECK(ProcessNewBlock(pblockE2, nullptr));
301 :
302 : // Create block with F2_tx1 spending E2_tx1 again.
303 2 : auto F2_tx1 = CreateAndCommitTx(pwalletMain.get(), *dest, 199 * COIN, &coinControl);
304 :
305 1 : pindexPrev = mapBlockIndex.at(pblockE2->GetHash());
306 1 : std::shared_ptr<CBlock> pblock5Forked = CreateBlockInternal(pwalletMain.get(), {F2_tx1},
307 7 : pindexPrev, {pblockD1, pblockE2});
308 2 : BlockStateCatcherWrapper stateCatcher(pblock5Forked->GetHash());
309 1 : stateCatcher.registerEvent();
310 3 : BOOST_CHECK(!ProcessNewBlock(pblock5Forked, nullptr));
311 2 : BOOST_CHECK(stateCatcher.get().found);
312 2 : BOOST_CHECK(!stateCatcher.get().state.IsValid());
313 3 : BOOST_CHECK_EQUAL(stateCatcher.get().state.GetRejectReason(), "bad-txns-inputs-spent-fork-post-split");
314 :
315 : // #############################################
316 : // ### 4) coins created in D and spent in E3 ###
317 : // #############################################
318 :
319 : // First create D3
320 1 : pindexPrev = mapBlockIndex.at(pblockC->GetHash());
321 2 : std::shared_ptr<CBlock> pblockD3 = CreateBlockInternal(pwalletMain.get(), {}, pindexPrev);
322 3 : BOOST_CHECK(ProcessNewBlock(pblockD3, nullptr));
323 :
324 : // Now let's try to spend the coins created in D in E3
325 1 : coinControl.UnSelectAll();
326 1 : coinControl.Select(dTxOutPoint, 249 * COIN);
327 1 : coinControl.fAllowOtherInputs = false;
328 2 : auto E3_tx1 = CreateAndCommitTx(pwalletMain.get(), *dest, 200 * COIN, &coinControl);
329 :
330 1 : pindexPrev = mapBlockIndex.at(pblockD3->GetHash());
331 6 : std::shared_ptr<CBlock> pblockE3 = CreateBlockInternal(pwalletMain.get(), {E3_tx1}, pindexPrev, {pblockD3});
332 1 : stateCatcher.get().clear();
333 1 : stateCatcher.get().setBlockHash(pblockE3->GetHash());
334 3 : BOOST_CHECK(!ProcessNewBlock(pblockE3, nullptr));
335 2 : BOOST_CHECK(stateCatcher.get().found);
336 2 : BOOST_CHECK(!stateCatcher.get().state.IsValid());
337 3 : BOOST_CHECK_EQUAL(stateCatcher.get().state.GetRejectReason(), "bad-txns-inputs-created-post-split");
338 :
339 : // ####################################################################
340 : // ### 5) coins create in D, spent in F and then double spent in F3 ###
341 : // ####################################################################
342 :
343 : // Create valid block F spending the coins created in D
344 1 : const auto& F_tx1 = E3_tx1;
345 4 : std::shared_ptr<CBlock> pblockF = CreateBlockInternal(pwalletMain.get(), {F_tx1});
346 3 : BOOST_CHECK(ProcessNewBlock(pblockF, nullptr));
347 5 : BOOST_CHECK(WITH_LOCK(cs_main, return chainActive.Tip()->GetBlockHash() == pblockF->GetHash()));
348 :
349 : // Create valid block E3
350 1 : pindexPrev = mapBlockIndex.at(pblockD3->GetHash());
351 3 : pblockE3 = CreateBlockInternal(pwalletMain.get(), {}, pindexPrev, {pblockD3});
352 3 : BOOST_CHECK(ProcessNewBlock(pblockE3, nullptr));
353 :
354 : // Now double spend F_tx1 in F3
355 1 : pindexPrev = mapBlockIndex.at(pblockE3->GetHash());
356 7 : std::shared_ptr<CBlock> pblockF3 = CreateBlockInternal(pwalletMain.get(), {F_tx1}, pindexPrev, {pblockD3, pblockE3});
357 : // Accepted on disk but not connected.
358 3 : BOOST_CHECK(ProcessNewBlock(pblockF3, nullptr));
359 4 : BOOST_CHECK(WITH_LOCK(cs_main, return chainActive.Tip()->GetBlockHash();) != pblockF3->GetHash());
360 :
361 1 : {
362 : // Trigger a rescan so the wallet cleans up its internal state.
363 2 : WalletRescanReserver reserver(pwalletMain.get());
364 2 : BOOST_CHECK(reserver.reserve());
365 1 : pwalletMain->RescanFromTime(0, reserver, true /* update */);
366 : }
367 :
368 : // ##############################################################################
369 : // ### 6) coins created in G and G3, being spent in H and H3 --> should pass. ###
370 : // ##############################################################################
371 :
372 : // First create new coins in G
373 : // select an input that is not already spent in D3 or E3 (since we want to spend it also in G3)
374 3 : const COutput& input = GetUnspentCoin(pwalletMain.get(), {pblockD3, pblockE3});
375 :
376 1 : coinControl.UnSelectAll();
377 1 : coinControl.Select(COutPoint(input.tx->GetHash(), input.i), input.Value());
378 1 : coinControl.fAllowOtherInputs = false;
379 :
380 1 : dest = pwalletMain->getNewAddress("").getObjResult();
381 2 : auto gTx = CreateAndCommitTx(pwalletMain.get(), *dest, 200 * COIN, &coinControl);
382 1 : auto gOut = GetOutpointWithAmount(gTx, 200 * COIN);
383 4 : std::shared_ptr<CBlock> pblockG = CreateBlockInternal(pwalletMain.get(), {gTx});
384 3 : BOOST_CHECK(ProcessNewBlock(pblockG, nullptr));
385 :
386 : // Now create the same coin in G3
387 5 : pblockF3 = CreateBlockInternal(pwalletMain.get(), {}, mapBlockIndex.at(pblockE3->GetHash()), {pblockD3, pblockE3});
388 3 : BOOST_CHECK(ProcessNewBlock(pblockF3, nullptr));
389 9 : auto pblockG3 = CreateBlockInternal(pwalletMain.get(), {gTx}, mapBlockIndex.at(pblockF3->GetHash()), {pblockD3, pblockE3, pblockF3});
390 3 : BOOST_CHECK(ProcessNewBlock(pblockG3, nullptr));
391 1 : FlushStateToDisk();
392 :
393 : // Now spend the coin in both, H and H3
394 1 : coinControl.UnSelectAll();
395 1 : coinControl.Select(gOut, 200 * COIN);
396 1 : coinControl.fAllowOtherInputs = false;
397 2 : auto hTx = CreateAndCommitTx(pwalletMain.get(), *dest, 199 * COIN, &coinControl);
398 4 : std::shared_ptr<CBlock> pblockH = CreateBlockInternal(pwalletMain.get(), {hTx});
399 3 : BOOST_CHECK(ProcessNewBlock(pblockH, nullptr));
400 5 : BOOST_CHECK(WITH_LOCK(cs_main, return chainActive.Tip()->GetBlockHash() == pblockH->GetHash()));
401 1 : FlushStateToDisk();
402 :
403 : // H3 now..
404 1 : std::shared_ptr<CBlock> pblockH3 = CreateBlockInternal(pwalletMain.get(),
405 : {hTx},
406 1 : mapBlockIndex.at(pblockG3->GetHash()),
407 10 : {pblockD3, pblockE3, pblockF3, pblockG3});
408 3 : BOOST_CHECK(ProcessNewBlock(pblockH3, nullptr));
409 :
410 : // Try to read the forking point manually
411 2 : CBlock bl;
412 3 : BOOST_CHECK(ReadBlockFromDisk(bl, mapBlockIndex.at(pblockC->GetHash())));
413 :
414 : // Make I3 the tip now.
415 1 : std::shared_ptr<CBlock> pblockI3 = CreateBlockInternal(pwalletMain.get(),
416 : {},
417 1 : mapBlockIndex.at(pblockH3->GetHash()),
418 9 : {pblockD3, pblockE3, pblockF3, pblockG3, pblockH3});
419 3 : BOOST_CHECK(ProcessNewBlock(pblockI3, nullptr));
420 5 : BOOST_CHECK(WITH_LOCK(cs_main, return chainActive.Tip()->GetBlockHash() == pblockI3->GetHash()));
421 :
422 : // And rescan the wallet on top of the new chain
423 2 : WalletRescanReserver reserver(pwalletMain.get());
424 2 : BOOST_CHECK(reserver.reserve());
425 1 : pwalletMain->RescanFromTime(0, reserver, true /* update */);
426 :
427 : // #################################################################################
428 : // ### 7) Now try to use the same coinstake on different chains --> should pass. ###
429 : // #################################################################################
430 :
431 : // Take I3 coinstake and use it for block I, changing its hash adding a new tx
432 2 : std::shared_ptr<CBlock> pblockI = std::make_shared<CBlock>(*pblockI3);
433 2 : auto iTx = CreateAndCommitTx(pwalletMain.get(), *dest, 1 * COIN);
434 1 : pblockI->vtx.emplace_back(MakeTransactionRef(iTx));
435 1 : pblockI->hashMerkleRoot = BlockMerkleRoot(*pblockI);
436 1 : assert(SignBlock(*pblockI, *pwalletMain));
437 2 : BOOST_CHECK(pblockI3->GetHash() != pblockI->GetHash());
438 3 : BOOST_CHECK(ProcessNewBlock(pblockI, nullptr));
439 1 : }
440 :
441 : BOOST_AUTO_TEST_SUITE_END()
|