Files
thehub/testing/test/miner_tests.cpp
tomFlowee 008eb35f95 Make compile faster
The IDE include checker got to the point where it is actually useful and
this removes a lot of unneeded includes.
Naturally, especially for headers like util.h, this may mean we need to
re-add includes in consuming cpp files that bloats the diff a bit.
2026-05-14 13:27:17 +02:00

368 lines
15 KiB
C++

/*
* This file is part of the Flowee project
* Copyright (C) 2011-2015 The Bitcoin Core developers
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "chainparams.h"
#include "consensus/consensus.h"
#include <merkle.h>
#include "consensus/validation.h"
#include "main.h"
#include "miner.h"
#include "primitives/PublicKey.h"
#include "script/standard.h"
#include "txmempool.h"
#include "uint256.h"
#include "util.h"
#include "utiltime.h"
#include "utilstrencodings.h"
#include <primitives/Block.h>
#include <utxo/UnspentOutputDatabase.h>
#include "BlocksDB_p.h" // to access the blockMap directly and use erase
#include "test/test_bitcoin.h"
#include <boost/test/unit_test.hpp>
#include <boost/foreach.hpp>
BOOST_FIXTURE_TEST_SUITE(miner_tests, MainnetTestingSetup)
CBlockIndex CreateBlockIndex(CBlockIndex *parent, int offset)
{
CBlockIndex index;
index.nHeight = parent->nHeight + offset;
index.pprev = parent;
return index;
}
bool TestSequenceLocks(CTxMemPool &mempool, const CTransaction &tx, int flags)
{
LOCK(mempool.cs);
return CheckSequenceLocks(mempool, tx, flags);
}
// NOTE: These tests rely on CreateNewBlock doing its own self-validation!
BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
{
CScript scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG;
CBlockTemplate *pblocktemplate;
CScript script;
uint256 hash;
TestMemPoolEntryHelper entry;
entry.nFee = 11;
entry.dPriority = 111.0;
entry.nHeight = 11;
fCheckpointsEnabled = false;
BOOST_CHECK_EQUAL(bv.blockchain()->Height(), 0);
// Simple block creation, nothing special yet:
Mining miner;
miner.SetCoinbase(scriptPubKey);
BOOST_CHECK(pblocktemplate = miner.CreateNewBlock(bv));
BOOST_CHECK_EQUAL(bv.blockchain()->Height(), 0);
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 1);
delete pblocktemplate;
// We can't make transactions until we have inputs
// Therefore, load 110 blocks :)
int baseheight = bv.blockchain()->Height();
auto chain = bv.appendChain(110, MockBlockValidation::EmptyOutScript);
std::vector<CTransaction*>txFirst;
for (int i = 0; i < 4; ++i) {
MutableBlock block = chain[i].createOldBlock();
txFirst.push_back(new CTransaction(block.vtx[0]));
}
BOOST_CHECK_EQUAL(bv.blockchain()->Height(), 110);
// Just to make sure we can still make simple blocks
BOOST_CHECK(pblocktemplate = miner.CreateNewBlock(bv));
delete pblocktemplate;
// block sigops > limit: 1000 CHECKMULTISIG + 1
CMutableTransaction tx;
tx.vin.resize(1);
// NOTE: OP_NOP is used to force 20 SigOps for the CHECKMULTISIG
tx.vin[0].scriptSig = CScript() << OP_0 << OP_0 << OP_0 << OP_NOP << OP_CHECKMULTISIG << OP_1;
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
tx.vin[0].prevout.n = 0;
tx.vout.resize(1);
tx.vout[0].nValue = 5000000000LL;
for (unsigned int i = 0; i < 1; ++i) {
tx.vout[0].nValue -= 1000000;
hash = tx.GetHash();
const bool spendsCoinbase = i == 0; // only first tx spends coinbase
// If we do set the # of sig ops in the CTxMemPoolEntry, template creation passes
bv.mp.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
tx.vin[0].prevout.hash = hash;
}
BOOST_CHECK(pblocktemplate = miner.CreateNewBlock(bv));
BOOST_CHECK(pblocktemplate->block.vtx.size() > 1);
delete pblocktemplate;
bv.mp.clear();
// block size > limit
tx.vin[0].scriptSig = CScript();
// 18 * (520char + DROP) + OP_1 = 9433 bytes
std::vector<unsigned char> vchData(520);
for (unsigned int i = 0; i < 18; ++i)
tx.vin[0].scriptSig << vchData << OP_DROP;
tx.vin[0].scriptSig << OP_1;
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
tx.vout[0].nValue = 5000000000LL;
for (unsigned int i = 0; i < 128; ++i)
{
tx.vout[0].nValue -= 10000000;
hash = tx.GetHash();
bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase
bv.mp.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
tx.vin[0].prevout.hash = hash;
}
BOOST_CHECK(pblocktemplate = miner.CreateNewBlock(bv));
delete pblocktemplate;
bv.mp.clear();
// orphan in mempool not mined
hash = tx.GetHash();
bv.mp.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).FromTx(tx));
BOOST_CHECK(pblocktemplate = miner.CreateNewBlock(bv));
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 1);
delete pblocktemplate;
bv.mp.clear();
// child with higher priority than parent
tx.vin[0].scriptSig = CScript() << OP_1;
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
tx.vout[0].nValue = 4900000000LL;
hash = tx.GetHash();
bv.mp.addUnchecked(hash, entry.Fee(100000000LL).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
tx.vin[0].prevout.hash = hash;
tx.vin.resize(2);
tx.vin[1].scriptSig = CScript() << OP_1;
tx.vin[1].prevout.hash = txFirst[0]->GetHash();
tx.vin[1].prevout.n = 0;
tx.vout[0].nValue = 5900000000LL;
hash = tx.GetHash();
bv.mp.addUnchecked(hash, entry.Fee(400000000LL).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
BOOST_CHECK(pblocktemplate = miner.CreateNewBlock(bv));
delete pblocktemplate;
bv.mp.clear();
// coinbase in mempool, template creation fails
tx.vin.resize(1);
tx.vin[0].prevout.SetNull();
tx.vin[0].scriptSig = CScript() << OP_0 << OP_1;
tx.vout[0].nValue = 0;
hash = tx.GetHash();
// give it a fee so it'll get mined
bv.mp.addUnchecked(hash, entry.Fee(100000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
BOOST_CHECK(pblocktemplate = miner.CreateNewBlock(bv));
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 1);
delete pblocktemplate;
bv.mp.clear();
// invalid (pre-p2sh) txn in mempool, don't mine
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
tx.vin[0].prevout.n = 0;
tx.vin[0].scriptSig = CScript() << OP_1;
tx.vout[0].nValue = 4900000000LL;
script = CScript() << OP_0;
tx.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(script));
hash = tx.GetHash();
bv.mp.addUnchecked(hash, entry.Fee(10000000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
tx.vin[0].prevout.hash = hash;
tx.vin[0].scriptSig = CScript() << std::vector<unsigned char>(script.begin(), script.end());
tx.vout[0].nValue -= 1000000;
hash = tx.GetHash();
bv.mp.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
BOOST_CHECK(pblocktemplate = miner.CreateNewBlock(bv));
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 1); // Just coinbase
delete pblocktemplate;
bv.mp.clear();
// double spend txn pair in mempool, don't mine
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
tx.vin[0].scriptSig = CScript() << OP_1;
tx.vout[0].nValue = 4900000000LL;
tx.vout[0].scriptPubKey = CScript() << OP_1;
hash = tx.GetHash();
bv.mp.addUnchecked(hash, entry.Fee(100000000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
tx.vout[0].scriptPubKey = CScript() << OP_2;
hash = tx.GetHash();
bv.mp.addUnchecked(hash, entry.Fee(100000000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
BOOST_CHECK(pblocktemplate = miner.CreateNewBlock(bv));
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 1); // Just coinbase
delete pblocktemplate;
bv.mp.clear();
// subsidy changing
int nHeight = bv.blockchain()->Height();
// Create an actual 209999-long block chain (without valid blocks).
while (bv.blockchain()->Tip()->nHeight < 209999) {
CBlockIndex* prev = bv.blockchain()->Tip();
CBlockIndex* next = new CBlockIndex();
next->pprev = prev;
next->nHeight = prev->nHeight + 1;
next->phashBlock = Blocks::Index::insert(GetRandHash(), next);
next->RaiseValidity(BLOCK_VALID_TRANSACTIONS);
next->BuildSkip();
bv.blockchain()->SetTip(next);
g_utxo->blockFinished(next->nHeight, next->GetBlockHash());
Blocks::DB::instance()->appendHeader(next);
}
BOOST_CHECK(pblocktemplate = miner.CreateNewBlock(bv));
delete pblocktemplate;
// Extend to a 210000-long block chain.
while (bv.blockchain()->Tip()->nHeight < 210000) {
CBlockIndex* prev = bv.blockchain()->Tip();
CBlockIndex* next = new CBlockIndex();
next->pprev = prev;
next->nHeight = prev->nHeight + 1;
next->phashBlock = Blocks::Index::insert(GetRandHash(), next);
next->RaiseValidity(BLOCK_VALID_TRANSACTIONS);
next->BuildSkip();
bv.blockchain()->SetTip(next);
g_utxo->blockFinished(next->nHeight, next->GetBlockHash());
Blocks::DB::instance()->appendHeader(next);
}
BOOST_CHECK(pblocktemplate = miner.CreateNewBlock(bv));
delete pblocktemplate;
// Delete the dummy blocks again.
while (bv.blockchain()->Tip()->nHeight > nHeight) {
CBlockIndex* del = bv.blockchain()->Tip();
del->nStatus |= BLOCK_FAILED_VALID;
Blocks::DB::instance()->appendHeader(del);
bv.blockchain()->SetTip(del->pprev);
g_utxo->blockFinished(del->pprev->nHeight, del->pprev->GetBlockHash());
Blocks::DB::instance()->priv()->indexMap.erase(del->GetBlockHash());
delete del;
}
// non-final txs in mempool
SetMockTime(bv.blockchain()->Tip()->GetMedianTimePast()+1);
int flags = LOCKTIME_VERIFY_SEQUENCE|LOCKTIME_MEDIAN_TIME_PAST;
// height map
std::vector<int> prevheights;
// relative height locked
tx.nVersion = 2;
tx.vin.resize(1);
prevheights.resize(1);
tx.vin[0].prevout.hash = txFirst[0]->GetHash(); // only 1 transaction
tx.vin[0].prevout.n = 0;
tx.vin[0].scriptSig = CScript() << OP_1;
tx.vin[0].nSequence = bv.blockchain()->Tip()->nHeight + 1; // txFirst[0] is the 2nd block
prevheights[0] = baseheight + 1;
tx.vout.resize(1);
tx.vout[0].nValue = 4900000000LL;
tx.vout[0].scriptPubKey = CScript() << OP_1;
tx.nLockTime = 0;
hash = tx.GetHash();
bv.mp.addUnchecked(hash, entry.Fee(100000000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
Tx newTx = Tx::fromOldTransaction(tx);
BOOST_CHECK(CheckFinalTx(newTx, flags)); // Locktime passes
BOOST_CHECK(!TestSequenceLocks(bv.mp, tx, flags)); // Sequence locks fail
BOOST_CHECK(SequenceLocks(tx, flags, &prevheights, CreateBlockIndex(bv.blockchain()->Tip(), 2))); // Sequence locks pass on 2nd block
// relative time locked
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | (((bv.blockchain()->Tip()->GetMedianTimePast()+1-(*bv.blockchain())[1]->GetMedianTimePast()) >> CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) + 1); // txFirst[1] is the 3rd block
prevheights[0] = baseheight + 2;
hash = tx.GetHash();
bv.mp.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
newTx = Tx::fromOldTransaction(tx);
BOOST_CHECK(CheckFinalTx(newTx, flags)); // Locktime passes
BOOST_CHECK(!TestSequenceLocks(bv.mp, tx, flags)); // Sequence locks fail
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
bv.blockchain()->Tip()->GetAncestor(bv.blockchain()->Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
BOOST_CHECK(SequenceLocks(tx, flags, &prevheights, CreateBlockIndex(bv.blockchain()->Tip(), 1))); // Sequence locks pass 512 seconds later
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
bv.blockchain()->Tip()->GetAncestor(bv.blockchain()->Tip()->nHeight - i)->nTime -= 512; //undo tricked MTP
// absolute height locked
tx.vin[0].prevout.hash = txFirst[2]->GetHash();
tx.vin[0].nSequence = CTxIn::SEQUENCE_FINAL - 1;
prevheights[0] = baseheight + 3;
tx.nLockTime = bv.blockchain()->Tip()->nHeight + 1;
hash = tx.GetHash();
bv.mp.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
newTx = Tx::fromOldTransaction(tx);
BOOST_CHECK(!CheckFinalTx(newTx, flags)); // Locktime fails
BOOST_CHECK(TestSequenceLocks(bv.mp, tx, flags)); // Sequence locks pass
BOOST_CHECK(IsFinalTx(newTx, bv.blockchain()->Tip()->nHeight + 2, bv.blockchain()->Tip()->GetMedianTimePast())); // Locktime passes on 2nd block
// absolute time locked
tx.vin[0].prevout.hash = txFirst[3]->GetHash();
tx.nLockTime = bv.blockchain()->Tip()->GetMedianTimePast();
prevheights.resize(1);
prevheights[0] = baseheight + 4;
hash = tx.GetHash();
bv.mp.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
newTx = Tx::fromOldTransaction(tx);
BOOST_CHECK(!CheckFinalTx(newTx, flags)); // Locktime fails
BOOST_CHECK(TestSequenceLocks(bv.mp, tx, flags)); // Sequence locks pass
BOOST_CHECK(IsFinalTx(newTx, bv.blockchain()->Tip()->nHeight + 2, bv.blockchain()->Tip()->GetMedianTimePast() + 1)); // Locktime passes 1 second later
// mempool-dependent transactions (not added)
tx.vin[0].prevout.hash = hash;
prevheights[0] = bv.blockchain()->Tip()->nHeight + 1;
tx.nLockTime = 0;
tx.vin[0].nSequence = 0;
newTx = Tx::fromOldTransaction(tx);
BOOST_CHECK(CheckFinalTx(newTx, flags)); // Locktime passes
BOOST_CHECK(TestSequenceLocks(bv.mp, tx, flags)); // Sequence locks pass
tx.vin[0].nSequence = 1;
BOOST_CHECK(!TestSequenceLocks(bv.mp, tx, flags)); // Sequence locks fail
tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG;
BOOST_CHECK(TestSequenceLocks(bv.mp, tx, flags)); // Sequence locks pass
tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | 1;
BOOST_CHECK(!TestSequenceLocks(bv.mp, tx, flags)); // Sequence locks fail
BOOST_CHECK(pblocktemplate = miner.CreateNewBlock(bv));
// None of the of the absolute height/time locked tx should have made
// it into the template because we still check IsFinalTx in CreateNewBlock,
// but relative locked txs will if inconsistently added to mempool.
// For now these will still generate a valid template until BIP68 soft fork
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 3);
delete pblocktemplate;
// However if we advance height by 1 and time by 512, all of them should be mined
bv.appendChain(1);
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
bv.blockchain()->Tip()->GetAncestor(bv.blockchain()->Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
SetMockTime(bv.blockchain()->Tip()->GetMedianTimePast() + 1);
BOOST_CHECK(pblocktemplate = miner.CreateNewBlock(bv));
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 5);
delete pblocktemplate;
bv.blockchain()->Tip()->nHeight--;
SetMockTime(0);
bv.mp.clear();
BOOST_FOREACH(CTransaction *tx, txFirst)
delete tx;
fCheckpointsEnabled = true;
}
BOOST_AUTO_TEST_SUITE_END()