Auto merge of #3307 - gtank:3187-refactor-checkblock, r=bitcartel

Refactor ContextualCheckBlock tests

This cleans up and condenses our quadratically-scaling transaction format / consensus rule tests. They still technically scale quadratically, but should be much more readable and manageable now - all that remains in the individual test cases is the specific version flag tweaking we're trying to test.

Closes #3187.
This commit is contained in:
Homu 2018-09-11 14:53:59 -07:00
commit 396bdaf00f
1 changed files with 170 additions and 248 deletions

View File

@ -67,27 +67,99 @@ TEST(CheckBlock, BlockSproutRejectsBadVersion) {
} }
TEST(ContextualCheckBlock, BadCoinbaseHeight) { class ContextualCheckBlockTest : public ::testing::Test {
protected:
virtual void SetUp() {
SelectParams(CBaseChainParams::MAIN); SelectParams(CBaseChainParams::MAIN);
}
// Create a block with no height in scriptSig virtual void TearDown() {
// Revert to test default. No-op on mainnet params.
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
}
// Returns a valid but empty mutable transaction at block height 1.
CMutableTransaction GetFirstBlockCoinbaseTx() {
CMutableTransaction mtx; CMutableTransaction mtx;
// No inputs.
mtx.vin.resize(1); mtx.vin.resize(1);
mtx.vin[0].prevout.SetNull(); mtx.vin[0].prevout.SetNull();
mtx.vin[0].scriptSig = CScript() << OP_0;
// Set height to 1.
mtx.vin[0].scriptSig = CScript() << 1 << OP_0;
// Give it a single zero-valued, always-valid output.
mtx.vout.resize(1); mtx.vout.resize(1);
mtx.vout[0].scriptPubKey = CScript() << OP_TRUE; mtx.vout[0].scriptPubKey = CScript() << OP_TRUE;
mtx.vout[0].nValue = 0; mtx.vout[0].nValue = 0;
CTransaction tx {mtx};
// Give it a Founder's Reward vout for height 1.
mtx.vout.push_back(CTxOut(
GetBlockSubsidy(1, Params().GetConsensus())/5,
Params().GetFoundersRewardScriptAtHeight(1)));
return mtx;
}
// Expects a height-1 block containing a given transaction to pass
// ContextualCheckBlock. This is used in accepting (Sprout-Sprout,
// Overwinter-Overwinter, ...) tests. You should not call it without
// calling a SCOPED_TRACE macro first to usefully label any failures.
void ExpectValidBlockFromTx(const CTransaction& tx) {
// Create a block and add the transaction to it.
CBlock block; CBlock block;
block.vtx.push_back(tx); block.vtx.push_back(tx);
// Set the previous block index to the genesis block.
CBlockIndex indexPrev {Params().GenesisBlock()};
// We now expect this to be a valid block.
MockCValidationState state;
EXPECT_TRUE(ContextualCheckBlock(block, state, &indexPrev));
}
// Expects a height-1 block containing a given transaction to fail
// ContextualCheckBlock. This is used in rejecting (Sprout-Overwinter,
// Overwinter-Sprout, ...) tests. You should not call it without
// calling a SCOPED_TRACE macro first to usefully label any failures.
void ExpectInvalidBlockFromTx(const CTransaction& tx, int level, std::string reason) {
// Create a block and add the transaction to it.
CBlock block;
block.vtx.push_back(tx);
// Set the previous block index to the genesis block.
CBlockIndex indexPrev {Params().GenesisBlock()};
// We now expect this to be an invalid block, for the given reason.
MockCValidationState state;
EXPECT_CALL(state, DoS(level, false, REJECT_INVALID, reason, false)).Times(1);
EXPECT_FALSE(ContextualCheckBlock(block, state, &indexPrev));
}
};
TEST_F(ContextualCheckBlockTest, BadCoinbaseHeight) {
// Put a transaction in a block with no height in scriptSig
CMutableTransaction mtx = GetFirstBlockCoinbaseTx();
mtx.vin[0].scriptSig = CScript() << OP_0;
mtx.vout.pop_back(); // remove the FR output
CBlock block;
block.vtx.push_back(mtx);
// Treating block as genesis should pass // Treating block as genesis should pass
MockCValidationState state; MockCValidationState state;
EXPECT_TRUE(ContextualCheckBlock(block, state, NULL)); EXPECT_TRUE(ContextualCheckBlock(block, state, NULL));
// Give the transaction a Founder's Reward vout
mtx.vout.push_back(CTxOut(
GetBlockSubsidy(1, Params().GetConsensus())/5,
Params().GetFoundersRewardScriptAtHeight(1)));
// Treating block as non-genesis should fail // Treating block as non-genesis should fail
mtx.vout.push_back(CTxOut(GetBlockSubsidy(1, Params().GetConsensus())/5, Params().GetFoundersRewardScriptAtHeight(1)));
CTransaction tx2 {mtx}; CTransaction tx2 {mtx};
block.vtx[0] = tx2; block.vtx[0] = tx2;
CBlock prev; CBlock prev;
@ -110,292 +182,142 @@ TEST(ContextualCheckBlock, BadCoinbaseHeight) {
EXPECT_TRUE(ContextualCheckBlock(block, state, &indexPrev)); EXPECT_TRUE(ContextualCheckBlock(block, state, &indexPrev));
} }
// TEST PLAN: first, check that each ruleset accepts its own transaction type.
// Currently (May 2018) this means we'll test Sprout-Sprout,
// Overwinter-Overwinter, and Sapling-Sapling.
// Test that a block evaluated under Sprout rules cannot contain Sapling transactions. // Test block evaluated under Sprout rules will accept Sprout transactions.
// This test assumes that mainnet Overwinter activation is at least height 2. // This test assumes that mainnet Overwinter activation is at least height 2.
TEST(ContextualCheckBlock, BlockSproutRulesRejectSaplingTx) { TEST_F(ContextualCheckBlockTest, BlockSproutRulesAcceptSproutTx) {
SelectParams(CBaseChainParams::MAIN); CMutableTransaction mtx = GetFirstBlockCoinbaseTx();
CMutableTransaction mtx; // Make it a Sprout transaction w/o JoinSplits
mtx.vin.resize(1);
mtx.vin[0].prevout.SetNull();
mtx.vin[0].scriptSig = CScript() << 1 << OP_0;
mtx.vout.resize(1);
mtx.vout[0].scriptPubKey = CScript() << OP_TRUE;
mtx.vout[0].nValue = 0;
mtx.fOverwintered = true;
mtx.nVersion = SAPLING_TX_VERSION;
mtx.nVersionGroupId = SAPLING_VERSION_GROUP_ID;
CTransaction tx {mtx};
CBlock block;
block.vtx.push_back(tx);
MockCValidationState state;
CBlockIndex indexPrev {Params().GenesisBlock()};
EXPECT_CALL(state, DoS(0, false, REJECT_INVALID, "tx-overwinter-not-active", false)).Times(1);
EXPECT_FALSE(ContextualCheckBlock(block, state, &indexPrev));
}
// Test that a block evaluated under Sprout rules cannot contain Overwinter transactions.
// This test assumes that mainnet Overwinter activation is at least height 2.
TEST(ContextualCheckBlock, BlockSproutRulesRejectOverwinterTx) {
SelectParams(CBaseChainParams::MAIN);
CMutableTransaction mtx;
mtx.vin.resize(1);
mtx.vin[0].prevout.SetNull();
mtx.vin[0].scriptSig = CScript() << 1 << OP_0;
mtx.vout.resize(1);
mtx.vout[0].scriptPubKey = CScript() << OP_TRUE;
mtx.vout[0].nValue = 0;
mtx.fOverwintered = true;
mtx.nVersion = OVERWINTER_TX_VERSION;
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
CTransaction tx {mtx};
CBlock block;
block.vtx.push_back(tx);
MockCValidationState state;
CBlockIndex indexPrev {Params().GenesisBlock()};
EXPECT_CALL(state, DoS(0, false, REJECT_INVALID, "tx-overwinter-not-active", false)).Times(1);
EXPECT_FALSE(ContextualCheckBlock(block, state, &indexPrev));
}
// Test block evaluated under Sprout rules will accept Sprout transactions
TEST(ContextualCheckBlock, BlockSproutRulesAcceptSproutTx) {
SelectParams(CBaseChainParams::REGTEST);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
CMutableTransaction mtx;
mtx.vin.resize(1);
mtx.vin[0].prevout.SetNull();
mtx.vin[0].scriptSig = CScript() << 1 << OP_0;
mtx.vout.resize(1);
mtx.vout[0].scriptPubKey = CScript() << OP_TRUE;
mtx.vout[0].nValue = 0;
mtx.vout.push_back(CTxOut(
GetBlockSubsidy(1, Params().GetConsensus())/5,
Params().GetFoundersRewardScriptAtHeight(1)));
mtx.fOverwintered = false; mtx.fOverwintered = false;
mtx.nVersion = 1; mtx.nVersion = 1;
CTransaction tx {mtx}; SCOPED_TRACE("BlockSproutRulesAcceptSproutTx");
CBlock block; ExpectValidBlockFromTx(CTransaction(mtx));
block.vtx.push_back(tx);
MockCValidationState state;
CBlockIndex indexPrev {Params().GenesisBlock()};
EXPECT_TRUE(ContextualCheckBlock(block, state, &indexPrev));
// Revert to default
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
} }
// Test that a block evaluated under Overwinter rules cannot contain Sapling transactions. // Test block evaluated under Overwinter rules will accept Overwinter transactions.
TEST(ContextualCheckBlock, BlockOverwinterRulesRejectSaplingTx) { TEST_F(ContextualCheckBlockTest, BlockOverwinterRulesAcceptOverwinterTx) {
SelectParams(CBaseChainParams::REGTEST); SelectParams(CBaseChainParams::REGTEST);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, 1); UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, 1);
CMutableTransaction mtx; CMutableTransaction mtx = GetFirstBlockCoinbaseTx();
mtx.vin.resize(1);
mtx.vin[0].prevout.SetNull();
mtx.vin[0].scriptSig = CScript() << 1 << OP_0;
mtx.vout.resize(1);
mtx.vout[0].scriptPubKey = CScript() << OP_TRUE;
mtx.vout[0].nValue = 0;
mtx.vout.push_back(CTxOut(
GetBlockSubsidy(1, Params().GetConsensus())/5,
Params().GetFoundersRewardScriptAtHeight(1)));
mtx.fOverwintered = true; // Make it an Overwinter transaction
mtx.nVersion = SAPLING_TX_VERSION;
mtx.nVersionGroupId = SAPLING_VERSION_GROUP_ID;
CTransaction tx {mtx};
CBlock block;
block.vtx.push_back(tx);
MockCValidationState state;
CBlockIndex indexPrev {Params().GenesisBlock()};
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-overwinter-tx-version-group-id", false)).Times(1);
EXPECT_FALSE(ContextualCheckBlock(block, state, &indexPrev));
// Revert to default
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
}
// Test block evaluated under Overwinter rules will accept Overwinter transactions
TEST(ContextualCheckBlock, BlockOverwinterRulesAcceptOverwinterTx) {
SelectParams(CBaseChainParams::REGTEST);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, 1);
CMutableTransaction mtx;
mtx.vin.resize(1);
mtx.vin[0].prevout.SetNull();
mtx.vin[0].scriptSig = CScript() << 1 << OP_0;
mtx.vout.resize(1);
mtx.vout[0].scriptPubKey = CScript() << OP_TRUE;
mtx.vout[0].nValue = 0;
mtx.vout.push_back(CTxOut(
GetBlockSubsidy(1, Params().GetConsensus())/5,
Params().GetFoundersRewardScriptAtHeight(1)));
mtx.fOverwintered = true; mtx.fOverwintered = true;
mtx.nVersion = OVERWINTER_TX_VERSION; mtx.nVersion = OVERWINTER_TX_VERSION;
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID; mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
CTransaction tx {mtx}; SCOPED_TRACE("BlockOverwinterRulesAcceptOverwinterTx");
CBlock block; ExpectValidBlockFromTx(CTransaction(mtx));
block.vtx.push_back(tx);
MockCValidationState state;
CBlockIndex indexPrev {Params().GenesisBlock()};
EXPECT_TRUE(ContextualCheckBlock(block, state, &indexPrev));
// Revert to default
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
}
// Test block evaluated under Overwinter rules will reject Sprout transactions
TEST(ContextualCheckBlock, BlockOverwinterRulesRejectSproutTx) {
SelectParams(CBaseChainParams::REGTEST);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, 1);
CMutableTransaction mtx;
mtx.vin.resize(1);
mtx.vin[0].prevout.SetNull();
mtx.vin[0].scriptSig = CScript() << 1 << OP_0;
mtx.vout.resize(1);
mtx.vout[0].scriptPubKey = CScript() << OP_TRUE;
mtx.vout[0].nValue = 0;
mtx.nVersion = 2;
CTransaction tx {mtx};
CBlock block;
block.vtx.push_back(tx);
MockCValidationState state;
CBlockIndex indexPrev {Params().GenesisBlock()};
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "tx-overwinter-active", false)).Times(1);
EXPECT_FALSE(ContextualCheckBlock(block, state, &indexPrev));
// Revert to default
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
} }
// Test that a block evaluated under Sapling rules can contain Sapling transactions. // Test that a block evaluated under Sapling rules can contain Sapling transactions.
TEST(ContextualCheckBlock, BlockSaplingRulesAcceptSaplingTx) { TEST_F(ContextualCheckBlockTest, BlockSaplingRulesAcceptSaplingTx) {
SelectParams(CBaseChainParams::REGTEST); SelectParams(CBaseChainParams::REGTEST);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, 1); UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, 1);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, 1); UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, 1);
CMutableTransaction mtx; CMutableTransaction mtx = GetFirstBlockCoinbaseTx();
mtx.vin.resize(1);
mtx.vin[0].prevout.SetNull();
mtx.vin[0].scriptSig = CScript() << 1 << OP_0;
mtx.vout.resize(1);
mtx.vout[0].scriptPubKey = CScript() << OP_TRUE;
mtx.vout[0].nValue = 0;
mtx.vout.push_back(CTxOut(
GetBlockSubsidy(1, Params().GetConsensus())/5,
Params().GetFoundersRewardScriptAtHeight(1)));
// Make it a Sapling transaction
mtx.fOverwintered = true; mtx.fOverwintered = true;
mtx.nVersion = SAPLING_TX_VERSION; mtx.nVersion = SAPLING_TX_VERSION;
mtx.nVersionGroupId = SAPLING_VERSION_GROUP_ID; mtx.nVersionGroupId = SAPLING_VERSION_GROUP_ID;
CTransaction tx {mtx}; SCOPED_TRACE("BlockSaplingRulesAcceptSaplingTx");
CBlock block; ExpectValidBlockFromTx(CTransaction(mtx));
block.vtx.push_back(tx);
MockCValidationState state;
CBlockIndex indexPrev {Params().GenesisBlock()};
EXPECT_TRUE(ContextualCheckBlock(block, state, &indexPrev));
// Revert to default
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
} }
// TEST PLAN: next, check that each ruleset will not accept other transaction
// types. Currently (May 2018) this means we'll test Sprout-Overwinter,
// Sprout-Sapling, Overwinter-Sprout, Overwinter-Sapling, Sapling-Sprout, and
// Sapling-Overwinter.
// Test block evaluated under Sapling rules cannot contain Overwinter transactions // Test that a block evaluated under Sprout rules cannot contain non-Sprout
TEST(ContextualCheckBlock, BlockSaplingRulesRejectOverwinterTx) { // transactions which require Overwinter to be active. This test assumes that
SelectParams(CBaseChainParams::REGTEST); // mainnet Overwinter activation is at least height 2.
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, 1); TEST_F(ContextualCheckBlockTest, BlockSproutRulesRejectOtherTx) {
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, 1); CMutableTransaction mtx = GetFirstBlockCoinbaseTx();
CMutableTransaction mtx; // Make it an Overwinter transaction
mtx.vin.resize(1);
mtx.vin[0].prevout.SetNull();
mtx.vin[0].scriptSig = CScript() << 1 << OP_0;
mtx.vout.resize(1);
mtx.vout[0].scriptPubKey = CScript() << OP_TRUE;
mtx.vout[0].nValue = 0;
mtx.vout.push_back(CTxOut(
GetBlockSubsidy(1, Params().GetConsensus())/5,
Params().GetFoundersRewardScriptAtHeight(1)));
mtx.fOverwintered = true; mtx.fOverwintered = true;
mtx.nVersion = OVERWINTER_TX_VERSION; mtx.nVersion = OVERWINTER_TX_VERSION;
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID; mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
CTransaction tx {mtx}; {
CBlock block; SCOPED_TRACE("BlockSproutRulesRejectOverwinterTx");
block.vtx.push_back(tx); ExpectInvalidBlockFromTx(CTransaction(mtx), 0, "tx-overwinter-not-active");
MockCValidationState state; }
CBlockIndex indexPrev {Params().GenesisBlock()};
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-sapling-tx-version-group-id", false)).Times(1); // Make it a Sapling transaction
EXPECT_FALSE(ContextualCheckBlock(block, state, &indexPrev)); mtx.fOverwintered = true;
mtx.nVersion = SAPLING_TX_VERSION;
mtx.nVersionGroupId = SAPLING_VERSION_GROUP_ID;
// Revert to default {
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); SCOPED_TRACE("BlockSproutRulesRejectSaplingTx");
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); ExpectInvalidBlockFromTx(CTransaction(mtx), 0, "tx-overwinter-not-active");
}
};
// Test block evaluated under Overwinter rules cannot contain non-Overwinter
// transactions.
TEST_F(ContextualCheckBlockTest, BlockOverwinterRulesRejectOtherTx) {
SelectParams(CBaseChainParams::REGTEST);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, 1);
CMutableTransaction mtx = GetFirstBlockCoinbaseTx();
// Set the version to Sprout+JoinSplit (but nJoinSplit will be 0).
mtx.nVersion = 2;
{
SCOPED_TRACE("BlockOverwinterRulesRejectSproutTx");
ExpectInvalidBlockFromTx(CTransaction(mtx), 100, "tx-overwinter-active");
}
// Make it a Sapling transaction
mtx.fOverwintered = true;
mtx.nVersion = SAPLING_TX_VERSION;
mtx.nVersionGroupId = SAPLING_VERSION_GROUP_ID;
{
SCOPED_TRACE("BlockOverwinterRulesRejectSaplingTx");
ExpectInvalidBlockFromTx(CTransaction(mtx), 100, "bad-overwinter-tx-version-group-id");
}
} }
// Test block evaluated under Sapling rules cannot contain non-Sapling transactions.
// Test block evaluated under Sapling rules cannot contain Sprout transactions TEST_F(ContextualCheckBlockTest, BlockSaplingRulesRejectOtherTx) {
TEST(ContextualCheckBlock, BlockSaplingRulesRejectSproutTx) {
SelectParams(CBaseChainParams::REGTEST); SelectParams(CBaseChainParams::REGTEST);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, 1); UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, 1);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, 1); UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, 1);
CMutableTransaction mtx; CMutableTransaction mtx = GetFirstBlockCoinbaseTx();
mtx.vin.resize(1);
mtx.vin[0].prevout.SetNull();
mtx.vin[0].scriptSig = CScript() << 1 << OP_0;
mtx.vout.resize(1);
mtx.vout[0].scriptPubKey = CScript() << OP_TRUE;
mtx.vout[0].nValue = 0;
// Set the version to Sprout+JoinSplit (but nJoinSplit will be 0).
mtx.nVersion = 2; mtx.nVersion = 2;
CTransaction tx {mtx}; {
CBlock block; SCOPED_TRACE("BlockSaplingRulesRejectSproutTx");
block.vtx.push_back(tx); ExpectInvalidBlockFromTx(CTransaction(mtx), 100, "tx-overwinter-active");
}
MockCValidationState state;
CBlockIndex indexPrev {Params().GenesisBlock()}; // Make it an Overwinter transaction
mtx.fOverwintered = true;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "tx-overwinter-active", false)).Times(1); mtx.nVersion = OVERWINTER_TX_VERSION;
EXPECT_FALSE(ContextualCheckBlock(block, state, &indexPrev)); mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
// Revert to default {
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); SCOPED_TRACE("BlockSaplingRulesRejectOverwinterTx");
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); ExpectInvalidBlockFromTx(CTransaction(mtx), 100, "bad-sapling-tx-version-group-id");
}
} }