mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-05-22 11:55:22 +02:00
If a coding bug of some sort let a bad transaction enter the memory pool, CreateNewBlock would throw a runtime_error which would cause bitcoind to crash. It is better for miners if CreateNewBlock is robust against programming errors, and this pull request makes it more robust. If an invalid block is created, all of the transactions that went into the block are removed from the memory pool and then CreateNewBlock is tried again.
310 lines
13 KiB
C++
310 lines
13 KiB
C++
// Copyright (c) 2011-2015 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 "chainparams.h"
|
|
#include "coins.h"
|
|
#include "consensus/consensus.h"
|
|
#include "consensus/merkle.h"
|
|
#include "consensus/validation.h"
|
|
#include "main.h"
|
|
#include "miner.h"
|
|
#include "pubkey.h"
|
|
#include "script/standard.h"
|
|
#include "txmempool.h"
|
|
#include "uint256.h"
|
|
#include "util.h"
|
|
#include "utilstrencodings.h"
|
|
|
|
#include "test/test_bitcoin.h"
|
|
|
|
#include <boost/test/unit_test.hpp>
|
|
|
|
BOOST_FIXTURE_TEST_SUITE(miner_tests, TestingSetup)
|
|
|
|
static
|
|
struct {
|
|
unsigned char extranonce;
|
|
unsigned int nonce;
|
|
} blockinfo[] = {
|
|
{4, 0xa4a3e223}, {2, 0x15c32f9e}, {1, 0x0375b547}, {1, 0x7004a8a5},
|
|
{2, 0xce440296}, {2, 0x52cfe198}, {1, 0x77a72cd0}, {2, 0xbb5d6f84},
|
|
{2, 0x83f30c2c}, {1, 0x48a73d5b}, {1, 0xef7dcd01}, {2, 0x6809c6c4},
|
|
{2, 0x0883ab3c}, {1, 0x087bbbe2}, {2, 0x2104a814}, {2, 0xdffb6daa},
|
|
{1, 0xee8a0a08}, {2, 0xba4237c1}, {1, 0xa70349dc}, {1, 0x344722bb},
|
|
{3, 0xd6294733}, {2, 0xec9f5c94}, {2, 0xca2fbc28}, {1, 0x6ba4f406},
|
|
{2, 0x015d4532}, {1, 0x6e119b7c}, {2, 0x43e8f314}, {2, 0x27962f38},
|
|
{2, 0xb571b51b}, {2, 0xb36bee23}, {2, 0xd17924a8}, {2, 0x6bc212d9},
|
|
{1, 0x630d4948}, {2, 0x9a4c4ebb}, {2, 0x554be537}, {1, 0xd63ddfc7},
|
|
{2, 0xa10acc11}, {1, 0x759a8363}, {2, 0xfb73090d}, {1, 0xe82c6a34},
|
|
{1, 0xe33e92d7}, {3, 0x658ef5cb}, {2, 0xba32ff22}, {5, 0x0227a10c},
|
|
{1, 0xa9a70155}, {5, 0xd096d809}, {1, 0x37176174}, {1, 0x830b8d0f},
|
|
{1, 0xc6e3910e}, {2, 0x823f3ca8}, {1, 0x99850849}, {1, 0x7521fb81},
|
|
{1, 0xaacaabab}, {1, 0xd645a2eb}, {5, 0x7aea1781}, {5, 0x9d6e4b78},
|
|
{1, 0x4ce90fd8}, {1, 0xabdc832d}, {6, 0x4a34f32a}, {2, 0xf2524c1c},
|
|
{2, 0x1bbeb08a}, {1, 0xad47f480}, {1, 0x9f026aeb}, {1, 0x15a95049},
|
|
{2, 0xd1cb95b2}, {2, 0xf84bbda5}, {1, 0x0fa62cd1}, {1, 0xe05f9169},
|
|
{1, 0x78d194a9}, {5, 0x3e38147b}, {5, 0x737ba0d4}, {1, 0x63378e10},
|
|
{1, 0x6d5f91cf}, {2, 0x88612eb8}, {2, 0xe9639484}, {1, 0xb7fabc9d},
|
|
{2, 0x19b01592}, {1, 0x5a90dd31}, {2, 0x5bd7e028}, {2, 0x94d00323},
|
|
{1, 0xa9b9c01a}, {1, 0x3a40de61}, {1, 0x56e7eec7}, {5, 0x859f7ef6},
|
|
{1, 0xfd8e5630}, {1, 0x2b0c9f7f}, {1, 0xba700e26}, {1, 0x7170a408},
|
|
{1, 0x70de86a8}, {1, 0x74d64cd5}, {1, 0x49e738a1}, {2, 0x6910b602},
|
|
{0, 0x643c565f}, {1, 0x54264b3f}, {2, 0x97ea6396}, {2, 0x55174459},
|
|
{2, 0x03e8779a}, {1, 0x98f34d8f}, {1, 0xc07b2b07}, {1, 0xdfe29668},
|
|
{1, 0x3141c7c1}, {1, 0xb3b595f4}, {1, 0x735abf08}, {5, 0x623bfbce},
|
|
{2, 0xd351e722}, {1, 0xf4ca48c9}, {1, 0x5b19c670}, {1, 0xa164bf0e},
|
|
{2, 0xbbbeb305}, {2, 0xfe1c810a},
|
|
};
|
|
|
|
// NOTE: These tests rely on CreateNewBlock doing its own self-validation!
|
|
BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|
{
|
|
const CChainParams& chainparams = Params(CBaseChainParams::MAIN);
|
|
CScript scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG;
|
|
CBlockTemplate *pblocktemplate;
|
|
CMutableTransaction tx,tx2;
|
|
CScript script;
|
|
uint256 hash;
|
|
TestMemPoolEntryHelper entry;
|
|
entry.nFee = 11;
|
|
entry.dPriority = 111.0;
|
|
entry.nHeight = 11;
|
|
|
|
LOCK(cs_main);
|
|
fCheckpointsEnabled = false;
|
|
|
|
// Simple block creation, nothing special yet:
|
|
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
|
|
|
|
// We can't make transactions until we have inputs
|
|
// Therefore, load 100 blocks :)
|
|
std::vector<CTransaction*>txFirst;
|
|
for (unsigned int i = 0; i < sizeof(blockinfo)/sizeof(*blockinfo); ++i)
|
|
{
|
|
CBlock *pblock = &pblocktemplate->block; // pointer for convenience
|
|
pblock->nVersion = 1;
|
|
pblock->nTime = chainActive.Tip()->GetMedianTimePast()+1;
|
|
CMutableTransaction txCoinbase(pblock->vtx[0]);
|
|
txCoinbase.nVersion = 1;
|
|
txCoinbase.vin[0].scriptSig = CScript();
|
|
txCoinbase.vin[0].scriptSig.push_back(blockinfo[i].extranonce);
|
|
txCoinbase.vin[0].scriptSig.push_back(chainActive.Height());
|
|
txCoinbase.vout[0].scriptPubKey = CScript();
|
|
pblock->vtx[0] = CTransaction(txCoinbase);
|
|
if (txFirst.size() < 2)
|
|
txFirst.push_back(new CTransaction(pblock->vtx[0]));
|
|
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
|
|
pblock->nNonce = blockinfo[i].nonce;
|
|
CValidationState state;
|
|
BOOST_CHECK(ProcessNewBlock(state, chainparams, NULL, pblock, true, NULL));
|
|
BOOST_CHECK(state.IsValid());
|
|
pblock->hashPrevBlock = pblock->GetHash();
|
|
}
|
|
delete pblocktemplate;
|
|
|
|
// Just to make sure we can still make simple blocks
|
|
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
|
|
delete pblocktemplate;
|
|
|
|
// block sigops > limit: 1000 CHECKMULTISIG + 1
|
|
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 < 1001; ++i)
|
|
{
|
|
tx.vout[0].nValue -= 1000000;
|
|
hash = tx.GetHash();
|
|
bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase
|
|
// If we don't set the # of sig ops in the CTxMemPoolEntry, template creation fails
|
|
mempool.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
|
|
tx.vin[0].prevout.hash = hash;
|
|
}
|
|
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
|
|
BOOST_CHECK(!mempool.exists(hash));
|
|
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 1);
|
|
delete pblocktemplate;
|
|
mempool.clear();
|
|
|
|
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
|
|
tx.vout[0].nValue = 5000000000LL;
|
|
for (unsigned int i = 0; i < 1001; ++i)
|
|
{
|
|
tx.vout[0].nValue -= 1000000;
|
|
hash = tx.GetHash();
|
|
bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase
|
|
// If we do set the # of sig ops in the CTxMemPoolEntry, template creation passes
|
|
mempool.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).SpendsCoinbase(spendsCoinbase).SigOps(20).FromTx(tx));
|
|
tx.vin[0].prevout.hash = hash;
|
|
}
|
|
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
|
|
BOOST_CHECK(pblocktemplate->block.vtx.size() > 1);
|
|
delete pblocktemplate;
|
|
mempool.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
|
|
mempool.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
|
|
tx.vin[0].prevout.hash = hash;
|
|
}
|
|
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
|
|
delete pblocktemplate;
|
|
mempool.clear();
|
|
|
|
// orphan in mempool not mined
|
|
hash = tx.GetHash();
|
|
mempool.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).FromTx(tx));
|
|
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
|
|
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 1);
|
|
delete pblocktemplate;
|
|
mempool.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();
|
|
mempool.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();
|
|
mempool.addUnchecked(hash, entry.Fee(400000000LL).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
|
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
|
|
delete pblocktemplate;
|
|
mempool.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
|
|
mempool.addUnchecked(hash, entry.Fee(100000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
|
|
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
|
|
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 1);
|
|
delete pblocktemplate;
|
|
mempool.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();
|
|
mempool.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();
|
|
mempool.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
|
|
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
|
|
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 1); // Just coinbase
|
|
mempool.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();
|
|
mempool.addUnchecked(hash, entry.Fee(100000000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
|
tx.vout[0].scriptPubKey = CScript() << OP_2;
|
|
hash = tx.GetHash();
|
|
mempool.addUnchecked(hash, entry.Fee(100000000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
|
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
|
|
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 1); // Just coinbase
|
|
delete pblocktemplate;
|
|
mempool.clear();
|
|
|
|
// subsidy changing
|
|
int nHeight = chainActive.Height();
|
|
chainActive.Tip()->nHeight = 209999;
|
|
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
|
|
delete pblocktemplate;
|
|
chainActive.Tip()->nHeight = 210000;
|
|
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
|
|
delete pblocktemplate;
|
|
chainActive.Tip()->nHeight = nHeight;
|
|
|
|
// non-final txs in mempool
|
|
SetMockTime(chainActive.Tip()->GetMedianTimePast()+1);
|
|
|
|
// height locked
|
|
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
|
|
tx.vin[0].scriptSig = CScript() << OP_1;
|
|
tx.vin[0].nSequence = 0;
|
|
tx.vout[0].nValue = 4900000000LL;
|
|
tx.vout[0].scriptPubKey = CScript() << OP_1;
|
|
tx.nLockTime = chainActive.Tip()->nHeight+1;
|
|
hash = tx.GetHash();
|
|
mempool.addUnchecked(hash, entry.Fee(100000000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
|
BOOST_CHECK(!CheckFinalTx(tx, LOCKTIME_MEDIAN_TIME_PAST));
|
|
|
|
// time locked
|
|
tx2.vin.resize(1);
|
|
tx2.vin[0].prevout.hash = txFirst[1]->GetHash();
|
|
tx2.vin[0].prevout.n = 0;
|
|
tx2.vin[0].scriptSig = CScript() << OP_1;
|
|
tx2.vin[0].nSequence = 0;
|
|
tx2.vout.resize(1);
|
|
tx2.vout[0].nValue = 4900000000LL;
|
|
tx2.vout[0].scriptPubKey = CScript() << OP_1;
|
|
tx2.nLockTime = chainActive.Tip()->GetMedianTimePast()+1;
|
|
hash = tx2.GetHash();
|
|
mempool.addUnchecked(hash, entry.Fee(100000000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx2));
|
|
BOOST_CHECK(!CheckFinalTx(tx2, LOCKTIME_MEDIAN_TIME_PAST));
|
|
|
|
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
|
|
|
|
// Neither tx should have make it into the template.
|
|
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 1);
|
|
delete pblocktemplate;
|
|
|
|
// However if we advance height and time by one, both will.
|
|
chainActive.Tip()->nHeight++;
|
|
SetMockTime(chainActive.Tip()->GetMedianTimePast()+2);
|
|
|
|
// FIXME: we should *actually* create a new block so the following test
|
|
// works; CheckFinalTx() isn't fooled by monkey-patching nHeight.
|
|
//BOOST_CHECK(CheckFinalTx(tx));
|
|
//BOOST_CHECK(CheckFinalTx(tx2));
|
|
|
|
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
|
|
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 2);
|
|
delete pblocktemplate;
|
|
|
|
chainActive.Tip()->nHeight--;
|
|
SetMockTime(0);
|
|
mempool.clear();
|
|
|
|
BOOST_FOREACH(CTransaction *tx, txFirst)
|
|
delete tx;
|
|
|
|
fCheckpointsEnabled = true;
|
|
}
|
|
|
|
BOOST_AUTO_TEST_SUITE_END()
|