Skip to content

Commit dadae56

Browse files
furszyrandom-zebra
authored andcommitted
test: add test coverage for qfc invalid paths
1) Mine a qfc with an invalid height, which should end up being rejected. 2) Mine a null qfc before the mining phase, which should end up being rejected. 3) Mine two qfc in the same block, which should end up being rejected. 4) Mine block without qfc during the mining phase, which should end up being rejected. 5) Mine two blocks with a null qfc. 6) Try to relay the valid qfc to the mempool, which should end up being rejected. 7) Mine a qfc with an invalid quorum hash, which should end up being rejected. 8) Mine the final valid qfc in a block.
1 parent 81bd881 commit dadae56

File tree

5 files changed

+102
-12
lines changed

5 files changed

+102
-12
lines changed

src/blockassembler.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,8 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
162162
bool fNoMempoolTx,
163163
bool fTestValidity,
164164
CBlockIndex* prevBlock,
165-
bool stopPoSOnNewBlock)
165+
bool stopPoSOnNewBlock,
166+
bool fIncludeQfc)
166167
{
167168
resetBlock();
168169

@@ -193,7 +194,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
193194

194195
// After v6 enforcement, add LLMQ commitments if needed
195196
const Consensus::Params& consensus = Params().GetConsensus();
196-
if (consensus.NetworkUpgradeActive(nHeight, Consensus::UPGRADE_V6_0)) {
197+
if (consensus.NetworkUpgradeActive(nHeight, Consensus::UPGRADE_V6_0) && fIncludeQfc) {
197198
LOCK(cs_main);
198199
for (const auto& p : Params().GetConsensus().llmqs) {
199200
CTransactionRef qcTx;

src/blockassembler.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,8 @@ class BlockAssembler
169169
bool fNoMempoolTx = false,
170170
bool fTestValidity = true,
171171
CBlockIndex* prevBlock = nullptr,
172-
bool stopPoSOnNewBlock = true);
172+
bool stopPoSOnNewBlock = true,
173+
bool fIncludeQfc = true);
173174

174175
private:
175176
// utility functions

src/test/evo_deterministicmns_tests.cpp

Lines changed: 89 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "primitives/transaction.h"
2222
#include "script/sign.h"
2323
#include "spork.h"
24+
#include "util/blocksutil.h"
2425
#include "validation.h"
2526
#include "validationinterface.h"
2627

@@ -304,7 +305,7 @@ static bool IsMNPayeeInBlock(const CBlock& block, const CScript& expected)
304305
return false;
305306
}
306307

307-
static void CheckPayments(std::map<uint256, int> mp, size_t mapSize, int minCount)
308+
static void CheckPayments(const std::map<uint256, int>& mp, size_t mapSize, int minCount)
308309
{
309310
BOOST_CHECK_EQUAL(mp.size(), mapSize);
310311
for (const auto& it : mp) {
@@ -954,6 +955,18 @@ static llmq::CFinalCommitment CreateFinalCommitment(std::vector<CBLSPublicKey>&
954955
return qfl;
955956
}
956957

958+
CMutableTransaction CreateNullQfcTx(const uint256& quorumHash, int nHeight)
959+
{
960+
llmq::LLMQCommPL pl;
961+
pl.commitment = llmq::CFinalCommitment(Params().GetConsensus().llmqs.at(Consensus::LLMQ_TEST), quorumHash);
962+
pl.nHeight = nHeight;
963+
CMutableTransaction tx;
964+
tx.nVersion = CTransaction::TxVersion::SAPLING;
965+
tx.nType = CTransaction::TxType::LLMQCOMM;
966+
SetTxPayload(tx, pl);
967+
return tx;
968+
}
969+
957970
CService ip(uint32_t i)
958971
{
959972
struct in_addr s;
@@ -972,7 +985,8 @@ static void ProcessQuorum(llmq::CQuorumBlockProcessor* processor, const llmq::CF
972985

973986
static NodeId id = 0;
974987

975-
BOOST_FIXTURE_TEST_CASE(dkg_pose, TestChain400Setup)
988+
// future: split dkg_pose from qfc_invalid_paths test coverage.
989+
BOOST_FIXTURE_TEST_CASE(dkg_pose_and_qfc_invalid_paths, TestChain400Setup)
976990
{
977991
auto utxos = BuildSimpleUtxoMap(coinbaseTxns);
978992

@@ -1056,13 +1070,82 @@ BOOST_FIXTURE_TEST_CASE(dkg_pose, TestChain400Setup)
10561070
ProcessQuorum(llmq::quorumBlockProcessor.get(), qfc, &dummyNode);
10571071
BOOST_CHECK(llmq::quorumBlockProcessor->HasMinableCommitment(::SerializeHash(qfc)));
10581072

1059-
// mine final commitment (at block 450)
1060-
for (size_t i = 0; i < 23; i++) {
1073+
// Generate blocks up to be able to mine a null qfc at block 450
1074+
for (size_t i = 0; i < 21; i++) {
10611075
CreateAndProcessBlock({}, coinbaseKey);
10621076
chainTip = chainActive.Tip();
10631077
BOOST_CHECK_EQUAL(chainTip->nHeight, ++nHeight);
10641078
}
1065-
BOOST_CHECK_EQUAL(nHeight, 450);
1079+
BOOST_CHECK_EQUAL(nHeight, 448);
1080+
1081+
// Coverage for the following qfc paths:
1082+
// 1) Mine a qfc with an invalid height, which should end up being rejected.
1083+
// 2) Mine a null qfc before the mining phase, which should end up being rejected.
1084+
// 3) Mine two qfc in the same block, which should end up being rejected.
1085+
// 4) Mine block without qfc during the mining phase, which should end up being rejected.
1086+
// 5) Mine two blocks with a null qfc.
1087+
// 6) Try to relay the valid qfc to the mempool, which should end up being rejected.
1088+
// 7) Mine a qfc with an invalid quorum hash, which should end up being rejected.
1089+
// 8) Mine the final valid qfc in a block.
1090+
// 9) Mine a null qfc after mining a valid qfc, which should end up being rejected.
1091+
1092+
// 1) Mine a null qfc before the mining phase, which should end up being rejected.
1093+
CMutableTransaction nullQfcTx = CreateNullQfcTx(quorumHash, nHeight);
1094+
CScript coinsbaseScript = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
1095+
auto pblock_invalid = std::make_shared<CBlock>(CreateBlock({nullQfcTx}, coinsbaseScript, true, false, false));
1096+
ProcessBlockAndCheckRejectionReason(pblock_invalid, "bad-qc-height", nHeight);
1097+
1098+
// 2) Mine a null qfc before the mining phase, which should end up being rejected.
1099+
nullQfcTx = CreateNullQfcTx(quorumHash, nHeight + 1);
1100+
pblock_invalid = std::make_shared<CBlock>(
1101+
CreateBlock({nullQfcTx}, coinsbaseScript, true, false, false));
1102+
ProcessBlockAndCheckRejectionReason(pblock_invalid, "bad-qc-not-allowed", nHeight);
1103+
1104+
// One more block, 449.
1105+
CreateAndProcessBlock({}, coinbaseKey);
1106+
chainTip = chainActive.Tip();
1107+
BOOST_CHECK_EQUAL(chainTip->nHeight, ++nHeight);
1108+
1109+
// 3) Mine two qfc in the same block, which should end up on a rejection. (one null, one valid)
1110+
nullQfcTx = CreateNullQfcTx(quorumHash, nHeight + 1);
1111+
pblock_invalid = std::make_shared<CBlock>(
1112+
CreateBlock({nullQfcTx}, coinsbaseScript, true, false, true));
1113+
ProcessBlockAndCheckRejectionReason(pblock_invalid, "bad-qc-dup", nHeight);
1114+
1115+
// 4) Mine block without qfc during the mining phase, which should end up being rejected.
1116+
pblock_invalid = std::make_shared<CBlock>(CreateBlock({}, coinsbaseScript, true, false, false));
1117+
ProcessBlockAndCheckRejectionReason(pblock_invalid, "bad-qc-missing", nHeight);
1118+
1119+
// 5) Mine two blocks with a null qfc.
1120+
for (int i = 0; i < 2; i++) {
1121+
const auto& block = CreateBlock({nullQfcTx}, coinsbaseScript, true, false, false);
1122+
ProcessNewBlock(std::make_shared<const CBlock>(block), nullptr);
1123+
chainTip = chainActive.Tip();
1124+
BOOST_CHECK_EQUAL(chainTip->nHeight, ++nHeight);
1125+
nullQfcTx = CreateNullQfcTx(quorumHash, nHeight + 1);
1126+
}
1127+
BOOST_CHECK_EQUAL(nHeight, 451);
1128+
1129+
// 6) Try to relay the valid qfc to the mempool, which should end up on a rejection.
1130+
CTransactionRef qcTx;
1131+
BOOST_CHECK(llmq::quorumBlockProcessor->GetMinableCommitmentTx(Consensus::LLMQ_TEST, nHeight + 1, qcTx));
1132+
CValidationState mempoolState;
1133+
BOOST_CHECK(!AcceptToMemoryPool(mempool, mempoolState, qcTx, true, nullptr));
1134+
1135+
// 7) Mine a qfc with an invalid quorum hash, which should end up being rejected.
1136+
nullQfcTx = CreateNullQfcTx(chainTip->GetBlockHash(), nHeight + 1);
1137+
pblock_invalid = std::make_shared<CBlock>(CreateBlock({nullQfcTx}, coinsbaseScript, true, false, false));
1138+
ProcessBlockAndCheckRejectionReason(pblock_invalid, "bad-qc-block", nHeight);
1139+
1140+
// 8) Mine the final valid qfc in a block.
1141+
CreateAndProcessBlock({}, coinbaseKey);
1142+
chainTip = chainActive.Tip();
1143+
BOOST_CHECK_EQUAL(chainTip->nHeight, ++nHeight);
1144+
1145+
// 9) Mine a null qfc after mining a valid qfc, which should end up being rejected.
1146+
nullQfcTx = CreateNullQfcTx(chainTip->GetBlockHash(), nHeight + 1);
1147+
pblock_invalid = std::make_shared<CBlock>(CreateBlock({nullQfcTx}, coinsbaseScript, true, false, false));
1148+
ProcessBlockAndCheckRejectionReason(pblock_invalid, "bad-qc-not-allowed", nHeight);
10661149

10671150
// final commitment has been mined
10681151
llmq::CFinalCommitment ret;
@@ -1086,7 +1169,7 @@ BOOST_FIXTURE_TEST_CASE(dkg_pose, TestChain400Setup)
10861169
BOOST_CHECK_EQUAL(punished_mn->pdmnState->nPoSePenalty, 65);
10871170

10881171
// New DKG starts at block 480. Mine till block 481 and create another valid 2-of-3 commitment
1089-
for (size_t i = 0; i < 30; i++) {
1172+
for (size_t i = 0; i < 28; i++) {
10901173
CreateAndProcessBlock({}, coinbaseKey);
10911174
chainTip = chainActive.Tip();
10921175
BOOST_CHECK_EQUAL(chainTip->nHeight, ++nHeight);

src/test/test_pivx.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,15 +173,19 @@ CBlock TestChainSetup::CreateAndProcessBlock(const std::vector<CMutableTransacti
173173
CBlock TestChainSetup::CreateBlock(const std::vector<CMutableTransaction>& txns,
174174
const CScript& scriptPubKey,
175175
bool fNoMempoolTx,
176-
bool fTestBlockValidity)
176+
bool fTestBlockValidity,
177+
bool fIncludeQfc)
177178
{
178179
std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(
179180
Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(scriptPubKey,
180181
nullptr, // wallet
181182
false, // fProofOfStake
182183
nullptr, // availableCoins
183184
fNoMempoolTx,
184-
fTestBlockValidity);
185+
fTestBlockValidity,
186+
nullptr,
187+
true,
188+
fIncludeQfc);
185189
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>(pblocktemplate->block);
186190

187191
// Add passed-in txns:

src/test/test_pivx.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ struct TestChainSetup : public TestingSetup
9797
CBlock CreateBlock(const std::vector<CMutableTransaction>& txns,
9898
const CScript& scriptPubKey,
9999
bool fNoMempoolTx = true,
100-
bool fTestBlockValidity = true);
100+
bool fTestBlockValidity = true,
101+
bool fIncludeQfc = true);
101102
CBlock CreateBlock(const std::vector<CMutableTransaction>& txns, const CKey& scriptKey, bool fTestBlockValidity = true);
102103

103104
std::vector<CTransaction> coinbaseTxns; // For convenience, coinbase transactions

0 commit comments

Comments
 (0)