PIVX Core  5.6.99
P2P Digital Currency
evo_deterministicmns_tests.cpp
Go to the documentation of this file.
1 // Copyright (c) 2018-2021 The Dash Core developers
2 // Copyright (c) 2021-2022 The PIVX Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 
6 #include "test/test_pivx.h"
7 
8 #include "blockassembler.h"
9 #include "consensus/merkle.h"
10 #include "consensus/params.h"
12 #include "evo/deterministicmns.h"
15 #include "llmq/quorums_utils.h"
16 #include "masternode-payments.h"
17 #include "messagesigner.h"
18 #include "netbase.h"
19 #include "policy/policy.h"
20 #include "primitives/transaction.h"
21 #include "script/sign.h"
22 #include "spork.h"
24 #include "util/blocksutil.h"
25 #include "validation.h"
26 #include "validationinterface.h"
27 
28 #include <boost/test/unit_test.hpp>
29 
30 typedef std::map<COutPoint, std::pair<int, CAmount>> SimpleUTXOMap;
31 
32 // static 0.1 PIV fee used for the special txes in these tests
33 static const CAmount fee = 10000000;
34 
35 static SimpleUTXOMap BuildSimpleUtxoMap(const std::vector<CTransaction>& txs)
36 {
37  SimpleUTXOMap utxos;
38  for (size_t i = 0; i < txs.size(); i++) {
39  auto& tx = txs[i];
40  for (size_t j = 0; j < tx.vout.size(); j++) {
41  utxos.emplace(std::piecewise_construct,
42  std::forward_as_tuple(tx.GetHash(), j),
43  std::forward_as_tuple((int)i + 1, tx.vout[j].nValue));
44  }
45  }
46  return utxos;
47 }
48 
49 static std::vector<COutPoint> SelectUTXOs(SimpleUTXOMap& utxos, CAmount amount, CAmount& changeRet)
50 {
51  changeRet = 0;
52  amount += fee;
53 
54  std::vector<COutPoint> selectedUtxos;
55  CAmount selectedAmount = 0;
56  int chainHeight = WITH_LOCK(cs_main, return chainActive.Height(); );
57  while (!utxos.empty()) {
58  bool found = false;
59  for (auto it = utxos.begin(); it != utxos.end(); ++it) {
60  if (chainHeight - it->second.first < 100) {
61  continue;
62  }
63 
64  found = true;
65  selectedAmount += it->second.second;
66  selectedUtxos.emplace_back(it->first);
67  utxos.erase(it);
68  break;
69  }
70  BOOST_ASSERT(found);
71  if (selectedAmount >= amount) {
72  changeRet = selectedAmount - amount;
73  break;
74  }
75  }
76 
77  return selectedUtxos;
78 }
79 
80 static void FundTransaction(CMutableTransaction& tx, SimpleUTXOMap& utxos, const CScript& scriptPayout, const CScript& scriptChange, CAmount amount)
81 {
82  CAmount change;
83  auto inputs = SelectUTXOs(utxos, amount, change);
84  for (size_t i = 0; i < inputs.size(); i++) {
85  tx.vin.emplace_back(inputs[i]);
86  }
87  tx.vout.emplace_back(CTxOut(amount, scriptPayout));
88  if (change != 0) {
89  tx.vout.emplace_back(change, scriptChange);
90  }
91 }
92 
93 static void SignTransaction(CMutableTransaction& tx, const CKey& coinbaseKey)
94 {
95  CBasicKeyStore tempKeystore;
96  tempKeystore.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
97 
98  for (size_t i = 0; i < tx.vin.size(); i++) {
99  CTransactionRef txFrom;
100  uint256 hashBlock;
101  BOOST_ASSERT(GetTransaction(tx.vin[i].prevout.hash, txFrom, hashBlock));
102  BOOST_ASSERT(SignSignature(tempKeystore, *txFrom, tx, i, SIGHASH_ALL));
103  }
104 }
105 
106 // Makes a new tx with a single out of given amount to given destination (or coinbase address, if nullopt)
107 static COutPoint CreateNewUTXO(SimpleUTXOMap& utxos,
108  CMutableTransaction& mtx,
109  const CKey& coinbaseKey, CAmount amount,
110  Optional<CScript> scriptDest = nullopt)
111 {
112  const CScript& s = (scriptDest != nullopt ? *scriptDest
113  : GetScriptForDestination(coinbaseKey.GetPubKey().GetID()));
114  FundTransaction(mtx, utxos, s, s, amount);
115  SignTransaction(mtx, coinbaseKey);
116  int idx = -1;
117  for (size_t i = 0; i < mtx.vout.size() && idx < 0; i++) {
118  if (mtx.vout[i].nValue == amount) idx = i;
119  }
120  BOOST_CHECK(idx >= 0);
121  return COutPoint(mtx.GetHash(), idx);
122 }
123 
124 static CKey GetRandomKey()
125 {
126  CKey keyRet;
127  keyRet.MakeNewKey(true);
128  return keyRet;
129 }
130 
131 static CBLSSecretKey GetRandomBLSKey()
132 {
133  CBLSSecretKey sk;
134  sk.MakeNewKey();
135  return sk;
136 }
137 
138 // Creates a ProRegTx.
139 // - if optCollateralOut is nullopt, generate a new collateral in the first output of the tx
140 // - otherwise reference *optCollateralOut as external collateral
141 static CMutableTransaction CreateProRegTx(Optional<COutPoint> optCollateralOut,
142  SimpleUTXOMap& utxos, int port, const CScript& scriptPayout, const CKey& coinbaseKey,
143  const CKey& ownerKey,
144  const CBLSPublicKey& operatorPubKey,
145  uint16_t operatorReward = 0,
146  bool fInvalidCollateral = false)
147 {
148  ProRegPL pl;
149  pl.collateralOutpoint = (optCollateralOut ? *optCollateralOut : COutPoint(UINT256_ZERO, 0));
150  pl.addr = LookupNumeric("1.1.1.1", port);
151  pl.keyIDOwner = ownerKey.GetPubKey().GetID();
152  pl.pubKeyOperator = operatorPubKey;
153  pl.keyIDVoting = ownerKey.GetPubKey().GetID();
154  pl.scriptPayout = scriptPayout;
156 
159  tx.nType = CTransaction::TxType::PROREG;
160  FundTransaction(tx, utxos, scriptPayout,
161  GetScriptForDestination(coinbaseKey.GetPubKey().GetID()),
162  (optCollateralOut ? 0 : Params().GetConsensus().nMNCollateralAmt - (fInvalidCollateral ? 1 : 0)));
163 
164  pl.inputsHash = CalcTxInputsHash(tx);
165  SetTxPayload(tx, pl);
166  SignTransaction(tx, coinbaseKey);
167 
168  return tx;
169 }
170 
171 static CMutableTransaction CreateProUpServTx(SimpleUTXOMap& utxos, const uint256& proTxHash, const CBLSSecretKey& operatorKey, int port, const CScript& scriptOperatorPayout, const CKey& coinbaseKey)
172 {
173  CAmount change;
174  auto inputs = SelectUTXOs(utxos, 1 * COIN, change);
175 
176  ProUpServPL pl;
177  pl.proTxHash = proTxHash;
178  pl.addr = LookupNumeric("1.1.1.1", port);
179  pl.scriptOperatorPayout = scriptOperatorPayout;
180 
183  tx.nType = CTransaction::TxType::PROUPSERV;
184  const CScript& s = GetScriptForDestination(coinbaseKey.GetPubKey().GetID());
185  FundTransaction(tx, utxos, s, s, 1 * COIN);
186  pl.inputsHash = CalcTxInputsHash(tx);
187  pl.sig = operatorKey.Sign(::SerializeHash(pl));
188  BOOST_ASSERT(pl.sig.IsValid());
189  SetTxPayload(tx, pl);
190  SignTransaction(tx, coinbaseKey);
191 
192  return tx;
193 }
194 
195 static CMutableTransaction CreateProUpRegTx(SimpleUTXOMap& utxos, const uint256& proTxHash, const CKey& ownerKey, const CBLSPublicKey& operatorPubKey, const CKey& votingKey, const CScript& scriptPayout, const CKey& coinbaseKey)
196 {
197  CAmount change;
198  auto inputs = SelectUTXOs(utxos, 1 * COIN, change);
199 
200  ProUpRegPL pl;
201  pl.proTxHash = proTxHash;
202  pl.pubKeyOperator = operatorPubKey;
203  pl.keyIDVoting = votingKey.GetPubKey().GetID();
204  pl.scriptPayout = scriptPayout;
205 
208  tx.nType = CTransaction::TxType::PROUPREG;
209  const CScript& s = GetScriptForDestination(coinbaseKey.GetPubKey().GetID());
210  FundTransaction(tx, utxos, s, s, 1 * COIN);
211  pl.inputsHash = CalcTxInputsHash(tx);
212  BOOST_ASSERT(CHashSigner::SignHash(::SerializeHash(pl), ownerKey, pl.vchSig));
213  SetTxPayload(tx, pl);
214  SignTransaction(tx, coinbaseKey);
215 
216  return tx;
217 }
218 
219 static CMutableTransaction CreateProUpRevTx(SimpleUTXOMap& utxos, const uint256& proTxHash, ProUpRevPL::RevocationReason reason, const CBLSSecretKey& operatorKey, const CKey& coinbaseKey)
220 {
221  CAmount change;
222  auto inputs = SelectUTXOs(utxos, 1 * COIN, change);
223 
224  ProUpRevPL pl;
225  pl.proTxHash = proTxHash;
226  pl.nReason = reason;
227 
230  tx.nType = CTransaction::TxType::PROUPREV;
231  const CScript& s = GetScriptForDestination(coinbaseKey.GetPubKey().GetID());
232  FundTransaction(tx, utxos, s, s, 1 * COIN);
233  pl.inputsHash = CalcTxInputsHash(tx);
234  pl.sig = operatorKey.Sign(::SerializeHash(pl));
235  BOOST_ASSERT(pl.sig.IsValid());
236  SetTxPayload(tx, pl);
237  SignTransaction(tx, coinbaseKey);
238 
239  return tx;
240 }
241 
242 static CScript GenerateRandomAddress()
243 {
244  CKey key;
245  key.MakeNewKey(false);
246  return GetScriptForDestination(key.GetPubKey().GetID());
247 }
248 
249 template<typename ProPL>
250 static CMutableTransaction MalleateProTxPayout(const CMutableTransaction& tx)
251 {
252  ProPL pl;
253  GetTxPayload(tx, pl);
254  pl.scriptPayout = GenerateRandomAddress();
255  CMutableTransaction tx2 = tx;
256  SetTxPayload(tx2, pl);
257  return tx2;
258 }
259 
260 static CMutableTransaction MalleateProUpServTx(const CMutableTransaction& tx)
261 {
262  ProUpServPL pl;
263  GetTxPayload(tx, pl);
264  pl.addr = LookupNumeric("1.1.1.1", 1001 + InsecureRandRange(100));
265  if (!pl.scriptOperatorPayout.empty()) {
266  pl.scriptOperatorPayout = GenerateRandomAddress();
267  }
268  CMutableTransaction tx2 = tx;
269  SetTxPayload(tx2, pl);
270  return tx2;
271 }
272 
273 static CMutableTransaction MalleateProUpRevTx(const CMutableTransaction& tx)
274 {
275  ProUpRevPL pl;
276  GetTxPayload(tx, pl);
277  BOOST_ASSERT(pl.nReason != ProUpRevPL::RevocationReason::REASON_CHANGE_OF_KEYS);
278  pl.nReason = ProUpRevPL::RevocationReason::REASON_CHANGE_OF_KEYS;
279  CMutableTransaction tx2 = tx;
280  SetTxPayload(tx2, pl);
281  return tx2;
282 }
283 
284 static bool CheckTransactionSignature(const CMutableTransaction& tx)
285 {
286  for (unsigned int i = 0; i < tx.vin.size(); i++) {
287  const auto& txin = tx.vin[i];
288  CTransactionRef txFrom;
289  uint256 hashBlock;
290  BOOST_ASSERT(GetTransaction(txin.prevout.hash, txFrom, hashBlock));
291 
292  CAmount amount = txFrom->vout[txin.prevout.n].nValue;
293  if (!VerifyScript(txin.scriptSig, txFrom->vout[txin.prevout.n].scriptPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&tx, i, amount), tx.GetRequiredSigVersion())) {
294  return false;
295  }
296  }
297  return true;
298 }
299 
300 static bool IsMNPayeeInBlock(const CBlock& block, const CScript& expected)
301 {
302  for (const auto& txout : block.vtx[0]->vout) {
303  if (txout.scriptPubKey == expected) return true;
304  }
305  return false;
306 }
307 
308 static void CheckPayments(const std::map<uint256, int>& mp, size_t mapSize, int minCount)
309 {
310  BOOST_CHECK_EQUAL(mp.size(), mapSize);
311  for (const auto& it : mp) {
312  BOOST_CHECK_MESSAGE(it.second >= minCount,
313  strprintf("MN %s didn't receive expected num of payments (%d<%d)",it.first.ToString(), it.second, minCount)
314  );
315  }
316 }
317 
318 BOOST_AUTO_TEST_SUITE(deterministicmns_tests)
319 
321 {
322  auto utxos = BuildSimpleUtxoMap(coinbaseTxns);
323 
324  CBlockIndex* chainTip = chainActive.Tip();
325  CCoinsViewCache* view = pcoinsTip.get();
326 
327  int nHeight = chainTip->nHeight;
331 
332  // load empty list (last block before enforcement)
333  CreateAndProcessBlock({}, coinbaseKey);
334  chainTip = chainActive.Tip();
335  BOOST_CHECK_EQUAL(chainTip->nHeight, ++nHeight);
336 
337  // force mnsync complete and enable spork 8
339  int64_t nTime = GetTime() - 10;
340  const CSporkMessage& sporkMnPayment = CSporkMessage(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT, nTime + 1, nTime);
341  sporkManager.AddOrUpdateSporkMessage(sporkMnPayment);
343 
344  int port = 1;
345 
346  std::vector<uint256> dmnHashes;
347  std::map<uint256, CKey> ownerKeys;
348  std::map<uint256, CBLSSecretKey> operatorKeys;
349 
350  // register one MN per block
351  for (size_t i = 0; i < 6; i++) {
352  const CKey& ownerKey = GetRandomKey();
353  const CBLSSecretKey& operatorKey = GetRandomBLSKey();
354  auto tx = CreateProRegTx(nullopt, utxos, port++, GenerateRandomAddress(), coinbaseKey, ownerKey, operatorKey.GetPublicKey());
355  const uint256& txid = tx.GetHash();
356  dmnHashes.emplace_back(txid);
357  ownerKeys.emplace(txid, ownerKey);
358  operatorKeys.emplace(txid, operatorKey);
359 
360  CValidationState dummyState;
361  BOOST_CHECK(WITH_LOCK(cs_main, return CheckSpecialTx(tx, chainTip, view, dummyState); ));
362  BOOST_CHECK(CheckTransactionSignature(tx));
363 
364  // also verify that payloads are not malleable after they have been signed
365  // the form of ProRegTx we use here is one with a collateral included, so there is no signature inside the
366  // payload itself. This means, we need to rely on script verification, which takes the hash of the extra payload
367  // into account
368  auto tx2 = MalleateProTxPayout<ProRegPL>(tx);
369  // Technically, the payload is still valid...
370  BOOST_CHECK(WITH_LOCK(cs_main, return CheckSpecialTx(tx2, chainTip, view, dummyState); ));
371  // But the signature should not verify anymore
372  BOOST_CHECK(!CheckTransactionSignature(tx2));
373 
374  CreateAndProcessBlock({tx}, coinbaseKey);
375  chainTip = chainActive.Tip();
376  BOOST_CHECK_EQUAL(chainTip->nHeight, nHeight + 1);
377  BOOST_CHECK(deterministicMNManager->GetListAtChainTip().HasMN(txid));
378 
379  // Add change to the utxos map
380  if (tx.vout.size() > 1) {
381  utxos.emplace(COutPoint(tx.GetHash(), 1), std::make_pair(nHeight + 1, tx.vout[1].nValue));
382  }
383 
384  nHeight++;
385  }
386 
387  // enable SPORK_21
390  BOOST_CHECK(deterministicMNManager->LegacyMNObsolete(nHeight + 1));
391 
392  // Mine 20 blocks, checking MN reward payments
393  std::map<uint256, int> mapPayments;
394  for (size_t i = 0; i < 20; i++) {
395  auto mnList = deterministicMNManager->GetListAtChainTip();
396  BOOST_CHECK_EQUAL(mnList.GetValidMNsCount(), 6);
397  BOOST_CHECK_EQUAL(mnList.GetHeight(), nHeight);
398 
399  // get next payee
400  auto dmnExpectedPayee = mnList.GetMNPayee();
401  CBlock block = CreateAndProcessBlock({}, coinbaseKey);
402  chainTip = chainActive.Tip();
403  BOOST_ASSERT(!block.vtx.empty());
404  BOOST_CHECK(IsMNPayeeInBlock(block, dmnExpectedPayee->pdmnState->scriptPayout));
405  mapPayments[dmnExpectedPayee->proTxHash]++;
406  BOOST_CHECK_EQUAL(chainTip->nHeight, ++nHeight);
407  }
408  // 20 blocks, 6 masternodes. Must have been paid at least 3 times each.
409  CheckPayments(mapPayments, 6, 3);
410 
411 
412  // Try to register with non-existent external collateral
413  {
415  auto tx = CreateProRegTx(o, utxos, port, GenerateRandomAddress(), coinbaseKey, GetRandomKey(), GetRandomBLSKey().GetPublicKey());
416  CValidationState state;
417  BOOST_CHECK(!WITH_LOCK(cs_main, return CheckSpecialTx(tx, chainTip, view, state); ));
418  BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-collateral");
419  }
420  // Try to register with invalid external collateral
421  {
422  // create an output of value 1 sat less than the required collateral amount
424  const COutPoint& coll_out = CreateNewUTXO(utxos, mtx, coinbaseKey, Params().GetConsensus().nMNCollateralAmt-1);
425  CreateAndProcessBlock({mtx}, coinbaseKey);
426  chainTip = chainActive.Tip();
427  BOOST_CHECK_EQUAL(chainTip->nHeight, ++nHeight);
428  Coin coll_coin;
429  BOOST_CHECK(view->GetUTXOCoin(coll_out, coll_coin));
430  BOOST_CHECK_EQUAL(coll_coin.out.nValue, Params().GetConsensus().nMNCollateralAmt-1);
431 
432  // create the ProReg tx referencing the invalid collateral
433  auto tx = CreateProRegTx(Optional<COutPoint>(coll_out), utxos, port, GenerateRandomAddress(), coinbaseKey, GetRandomKey(), GetRandomBLSKey().GetPublicKey());
434  CValidationState state;
435  BOOST_CHECK(!WITH_LOCK(cs_main, return CheckSpecialTx(tx, chainTip, view, state); ));
436  BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-collateral-amount");
437 
438  // add the coin back to the utxo map
439  utxos.emplace(coll_out, std::make_pair(coll_coin.nHeight, coll_coin.out.nValue));
440  }
441  // Try to register with spent external collateral
442  {
443  // create an output of collateral amount
445  const COutPoint& coll_out = CreateNewUTXO(utxos, mtx, coinbaseKey, Params().GetConsensus().nMNCollateralAmt);
446  CreateAndProcessBlock({mtx}, coinbaseKey);
447  chainTip = chainActive.Tip();
448  BOOST_CHECK_EQUAL(chainTip->nHeight, ++nHeight);
449  Coin coll_coin;
450  BOOST_CHECK(view->GetUTXOCoin(coll_out, coll_coin));
451  BOOST_CHECK_EQUAL(coll_coin.out.nValue, Params().GetConsensus().nMNCollateralAmt);
452 
453  // spend it
454  CMutableTransaction spendTx;
455  spendTx.vin.emplace_back(coll_out);
456  spendTx.vout.emplace_back(Params().GetConsensus().nMNCollateralAmt - 1000,
457  GetScriptForDestination(coinbaseKey.GetPubKey().GetID()));
458  SignTransaction(spendTx, coinbaseKey);
459  CreateAndProcessBlock({spendTx}, coinbaseKey);
460  chainTip = chainActive.Tip();
461  BOOST_CHECK_EQUAL(chainTip->nHeight, ++nHeight);
462  BOOST_CHECK(!view->GetUTXOCoin(coll_out, coll_coin));
463 
464  // create the ProReg tx referencing the spent collateral
465  auto tx = CreateProRegTx(Optional<COutPoint>(coll_out), utxos, port, GenerateRandomAddress(), coinbaseKey, GetRandomKey(), GetRandomBLSKey().GetPublicKey());
466  CValidationState state;
467  BOOST_CHECK(!WITH_LOCK(cs_main, return CheckSpecialTx(tx, chainTip, view, state); ));
468  BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-collateral");
469  }
470  // Try to register with invalid internal collateral
471  {
472  auto tx = CreateProRegTx(nullopt, utxos, port, GenerateRandomAddress(), coinbaseKey, GetRandomKey(), GetRandomBLSKey().GetPublicKey(), 0, true);
473  CValidationState state;
474  BOOST_CHECK(!WITH_LOCK(cs_main, return CheckSpecialTx(tx, chainTip, view, state); ));
475  BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-collateral-amount");
476  }
477  // Try to register reusing the collateral key as owner/voting key
478  {
479  const CKey& coll_key = GetRandomKey();
480  // create a valid collateral
482  const COutPoint& coll_out = CreateNewUTXO(utxos, mtx, coinbaseKey, Params().GetConsensus().nMNCollateralAmt,
483  GetScriptForDestination(coll_key.GetPubKey().GetID()));
484  CreateAndProcessBlock({mtx}, coinbaseKey);
485  chainTip = chainActive.Tip();
486  BOOST_CHECK_EQUAL(chainTip->nHeight, ++nHeight);
487  Coin coll_coin;
488  BOOST_CHECK(view->GetUTXOCoin(coll_out, coll_coin));
489  BOOST_CHECK_EQUAL(coll_coin.out.nValue, Params().GetConsensus().nMNCollateralAmt);
490 
491  // create the ProReg tx reusing the collateral key
492  auto tx = CreateProRegTx(Optional<COutPoint>(coll_out), utxos, port, GenerateRandomAddress(), coinbaseKey, coll_key, GetRandomBLSKey().GetPublicKey());
493  CValidationState state;
494  BOOST_CHECK(!WITH_LOCK(cs_main, return CheckSpecialTx(tx, chainTip, view, state); ));
495  BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-collateral-reuse");
496  }
497  // Try to register used owner key
498  {
499  const CKey& ownerKey = ownerKeys.at(dmnHashes[InsecureRandRange(dmnHashes.size())]);
500  auto tx = CreateProRegTx(nullopt, utxos, port, GenerateRandomAddress(), coinbaseKey, ownerKey, GetRandomBLSKey().GetPublicKey());
501  CValidationState state;
502  BOOST_CHECK(!WITH_LOCK(cs_main, return CheckSpecialTx(tx, chainTip, view, state); ));
503  BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-dup-owner-key");
504  }
505  // Try to register used operator key
506  {
507  const CBLSSecretKey& operatorKey = operatorKeys.at(dmnHashes[InsecureRandRange(dmnHashes.size())]);
508  auto tx = CreateProRegTx(nullopt, utxos, port, GenerateRandomAddress(), coinbaseKey, GetRandomKey(), operatorKey.GetPublicKey());
509  CValidationState state;
510  BOOST_CHECK(!WITH_LOCK(cs_main, return CheckSpecialTx(tx, chainTip, view, state); ));
511  BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-dup-operator-key");
512  }
513  // Try to register used IP address
514  {
515  auto tx = CreateProRegTx(nullopt, utxos, 1 + InsecureRandRange(port-1), GenerateRandomAddress(), coinbaseKey, GetRandomKey(), GetRandomBLSKey().GetPublicKey());
516  CValidationState state;
517  BOOST_CHECK(!WITH_LOCK(cs_main, return CheckSpecialTx(tx, chainTip, view, state); ));
518  BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-dup-IP-address");
519  }
520  // Block with two ProReg txes using same owner key
521  {
522  const CKey& ownerKey = GetRandomKey();
523  const CBLSSecretKey& operatorKey1 = GetRandomBLSKey();
524  const CBLSSecretKey& operatorKey2 = GetRandomBLSKey();
525  auto tx1 = CreateProRegTx(nullopt, utxos, port, GenerateRandomAddress(), coinbaseKey, ownerKey, operatorKey1.GetPublicKey());
526  auto tx2 = CreateProRegTx(nullopt, utxos, (port+1), GenerateRandomAddress(), coinbaseKey, ownerKey, operatorKey2.GetPublicKey());
527  CBlock block = CreateBlock({tx1, tx2}, coinbaseKey);
528  CBlockIndex indexFake(block);
529  indexFake.nHeight = nHeight;
530  indexFake.pprev = chainTip;
531  CValidationState state;
532  BOOST_CHECK(!WITH_LOCK(cs_main, return ProcessSpecialTxsInBlock(block, &indexFake, view, state, true); ));
533  BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-dup-owner-key");
534  ProcessNewBlock(std::make_shared<const CBlock>(block), nullptr); // todo: move to check reject reason
535  BOOST_CHECK_EQUAL(WITH_LOCK(cs_main, return chainActive.Height(); ), nHeight); // bad block not connected
536  }
537  // Block with two ProReg txes using same operator key
538  {
539  const CKey& ownerKey1 = GetRandomKey();
540  const CKey& ownerKey2 = GetRandomKey();
541  const CBLSSecretKey& operatorKey = GetRandomBLSKey();
542  auto tx1 = CreateProRegTx(nullopt, utxos, port, GenerateRandomAddress(), coinbaseKey, ownerKey1, operatorKey.GetPublicKey());
543  auto tx2 = CreateProRegTx(nullopt, utxos, (port+1), GenerateRandomAddress(), coinbaseKey, ownerKey2, operatorKey.GetPublicKey());
544  CBlock block = CreateBlock({tx1, tx2}, coinbaseKey);
545  CBlockIndex indexFake(block);
546  indexFake.nHeight = nHeight;
547  indexFake.pprev = chainTip;
548  CValidationState state;
549  BOOST_CHECK(!WITH_LOCK(cs_main, return ProcessSpecialTxsInBlock(block, &indexFake, view, state, true); ));
550  BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-dup-operator-key");
551  ProcessNewBlock(std::make_shared<const CBlock>(block), nullptr); // todo: move to check reject reason
552  BOOST_CHECK_EQUAL(WITH_LOCK(cs_main, return chainActive.Height(); ), nHeight); // bad block not connected
553  }
554  // Block with two ProReg txes using same ip address
555  {
556  auto tx1 = CreateProRegTx(nullopt, utxos, port, GenerateRandomAddress(), coinbaseKey, GetRandomKey(), GetRandomBLSKey().GetPublicKey());
557  auto tx2 = CreateProRegTx(nullopt, utxos, port, GenerateRandomAddress(), coinbaseKey, GetRandomKey(), GetRandomBLSKey().GetPublicKey());
558  CBlock block = CreateBlock({tx1, tx2}, coinbaseKey);
559  CBlockIndex indexFake(block);
560  indexFake.nHeight = nHeight;
561  indexFake.pprev = chainTip;
562  CValidationState state;
563  BOOST_CHECK(!WITH_LOCK(cs_main, return ProcessSpecialTxsInBlock(block, &indexFake, view, state, true); ));
564  BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-dup-IP-address");
565  ProcessNewBlock(std::make_shared<const CBlock>(block), nullptr); // todo: move to check reject reason
566  BOOST_CHECK_EQUAL(WITH_LOCK(cs_main, return chainActive.Height(); ), nHeight); // bad block not connected
567  }
568 
569  // register multiple MNs per block
570  for (size_t i = 0; i < 3; i++) {
571  std::vector<CMutableTransaction> txns;
572  for (size_t j = 0; j < 3; j++) {
573  const CKey& ownerKey = GetRandomKey();
574  const CBLSSecretKey& operatorKey = GetRandomBLSKey();
575  auto tx = CreateProRegTx(nullopt, utxos, port++, GenerateRandomAddress(), coinbaseKey, ownerKey, operatorKey.GetPublicKey());
576  const uint256& txid = tx.GetHash();
577  dmnHashes.emplace_back(txid);
578  ownerKeys.emplace(txid, ownerKey);
579  operatorKeys.emplace(txid, operatorKey);
580 
581  CValidationState dummyState;
582  BOOST_CHECK(WITH_LOCK(cs_main, return CheckSpecialTx(tx, chainActive.Tip(), view, dummyState); ));
583  BOOST_CHECK(CheckTransactionSignature(tx));
584  txns.emplace_back(tx);
585  }
586  CreateAndProcessBlock(txns, coinbaseKey);
587  chainTip = chainActive.Tip();
588  BOOST_CHECK_EQUAL(chainTip->nHeight, nHeight + 1);
589  auto mnList = deterministicMNManager->GetListAtChainTip();
590  for (size_t j = 0; j < 3; j++) {
591  BOOST_CHECK(mnList.HasMN(txns[j].GetHash()));
592  }
593 
594  nHeight++;
595  }
596 
597  // Mine 30 blocks, checking MN reward payments
598  mapPayments.clear();
599  for (size_t i = 0; i < 30; i++) {
600  auto mnList = deterministicMNManager->GetListAtChainTip();
601  auto dmnExpectedPayee = mnList.GetMNPayee();
602  CBlock block = CreateAndProcessBlock({}, coinbaseKey);
603  chainTip = chainActive.Tip();
604  BOOST_ASSERT(!block.vtx.empty());
605  BOOST_CHECK(IsMNPayeeInBlock(block, dmnExpectedPayee->pdmnState->scriptPayout));
606  mapPayments[dmnExpectedPayee->proTxHash]++;
607 
608  nHeight++;
609  }
610  // 30 blocks, 15 masternodes. Must have been paid exactly 2 times each.
611  CheckPayments(mapPayments, 15, 2);
612 
613  // Check that the prev DMN winner is different that the tip one
614  std::vector<CTxOut> vecMnOutsPrev;
615  BOOST_CHECK(masternodePayments.GetMasternodeTxOuts(chainTip->pprev, vecMnOutsPrev));
616  std::vector<CTxOut> vecMnOutsNow;
617  BOOST_CHECK(masternodePayments.GetMasternodeTxOuts(chainTip, vecMnOutsNow));
618  BOOST_CHECK(vecMnOutsPrev != vecMnOutsNow);
619 
620  // Craft an invalid block paying to the previous block DMN again
621  CBlock invalidBlock = CreateBlock({}, coinbaseKey);
622  std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>(invalidBlock);
623  CMutableTransaction invalidCoinbaseTx = CreateCoinbaseTx(CScript(), chainTip);
624  invalidCoinbaseTx.vout.clear();
625  for (const CTxOut& mnOut: vecMnOutsPrev) {
626  invalidCoinbaseTx.vout.emplace_back(mnOut);
627  }
628  invalidCoinbaseTx.vout.emplace_back(
629  CTxOut(GetBlockValue(nHeight + 1) - GetMasternodePayment(nHeight + 1),
630  GetScriptForDestination(coinbaseKey.GetPubKey().GetID())));
631  pblock->vtx[0] = MakeTransactionRef(invalidCoinbaseTx);
632  pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
633  ProcessNewBlock(pblock, nullptr);
634  // block not connected
635  chainTip = WITH_LOCK(cs_main, return chainActive.Tip());
636  BOOST_CHECK(chainTip->nHeight == nHeight);
637  BOOST_CHECK(chainTip->GetBlockHash() != pblock->GetHash());
638 
639  // ProUpServ: change masternode IP
640  {
641  const uint256& proTx = dmnHashes[InsecureRandRange(dmnHashes.size())]; // pick one at random
642  auto tx = CreateProUpServTx(utxos, proTx, operatorKeys.at(proTx), 1000, CScript(), coinbaseKey);
643 
644  CValidationState dummyState;
645  BOOST_CHECK(WITH_LOCK(cs_main, return CheckSpecialTx(tx, chainTip, view, dummyState); ));
646  BOOST_CHECK(CheckTransactionSignature(tx));
647  // also verify that payloads are not malleable after they have been signed
648  auto tx2 = MalleateProUpServTx(tx);
649  BOOST_CHECK(!WITH_LOCK(cs_main, return CheckSpecialTx(tx2, chainTip, view, dummyState); ));
650  BOOST_CHECK_EQUAL(dummyState.GetRejectReason(), "bad-protx-sig");
651 
652  CreateAndProcessBlock({tx}, coinbaseKey);
653  chainTip = chainActive.Tip();
654  BOOST_CHECK_EQUAL(chainTip->nHeight, nHeight + 1);
655 
656  auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(proTx);
657  BOOST_ASSERT(dmn != nullptr);
658  BOOST_CHECK_EQUAL(dmn->pdmnState->addr.GetPort(), 1000);
659 
660  nHeight++;
661  }
662 
663  // ProUpServ: Try to change the IP of a masternode to the one of another registered masternode
664  {
665  int randomIdx = InsecureRandRange(dmnHashes.size());
666  int randomIdx2 = 0;
667  do { randomIdx2 = InsecureRandRange(dmnHashes.size()); } while (randomIdx2 == randomIdx);
668  const uint256& proTx = dmnHashes[randomIdx]; // mn to update
669  int new_port = deterministicMNManager->GetListAtChainTip().GetMN(dmnHashes[randomIdx2])->pdmnState->addr.GetPort();
670 
671  auto tx = CreateProUpServTx(utxos, proTx, operatorKeys.at(proTx), new_port, CScript(), coinbaseKey);
672 
673  CValidationState state;
674  BOOST_CHECK(!WITH_LOCK(cs_main, return CheckSpecialTx(tx, chainTip, view, state); ));
675  BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-dup-addr");
676  }
677 
678  // ProUpServ: Try to change the IP of a masternode that doesn't exist
679  {
680  const CBLSSecretKey& operatorKey = GetRandomBLSKey();
681  auto tx = CreateProUpServTx(utxos, GetRandHash(), operatorKey, port, CScript(), coinbaseKey);
682 
683  CValidationState state;
684  BOOST_CHECK(!WITH_LOCK(cs_main, return CheckSpecialTx(tx, chainTip, view, state); ));
685  BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-hash");
686  }
687 
688  // ProUpServ: Change masternode operator payout. (new masternode created here)
689  {
690  // first create a ProRegTx with 5% reward for the operator, and mine it
691  const CKey& ownerKey = GetRandomKey();
692  const CBLSSecretKey& operatorKey = GetRandomBLSKey();
693  auto tx = CreateProRegTx(nullopt, utxos, port++, GenerateRandomAddress(), coinbaseKey, ownerKey, operatorKey.GetPublicKey(), 500);
694  const uint256& txid = tx.GetHash();
695  CreateAndProcessBlock({tx}, coinbaseKey);
696  chainTip = chainActive.Tip();
697  BOOST_CHECK_EQUAL(chainTip->nHeight, ++nHeight);
698  auto mnList = deterministicMNManager->GetListAtChainTip();
699  BOOST_CHECK(mnList.HasMN(txid));
700  auto dmn = mnList.GetMN(txid);
701  BOOST_CHECK(dmn->pdmnState->scriptOperatorPayout.empty());
702  BOOST_CHECK_EQUAL(dmn->nOperatorReward, 500);
703 
704  // then send the ProUpServTx and check the operator payee
705  const CScript& operatorPayee = GenerateRandomAddress();
706  auto tx2 = CreateProUpServTx(utxos, txid, operatorKey, (port-1), operatorPayee, coinbaseKey);
707  CreateAndProcessBlock({tx2}, coinbaseKey);
708  chainTip = chainActive.Tip();
709  BOOST_CHECK_EQUAL(chainTip->nHeight, ++nHeight);
710  dmn = deterministicMNManager->GetListAtChainTip().GetMN(txid);
711  BOOST_ASSERT(dmn != nullptr);
712  BOOST_CHECK(dmn->pdmnState->scriptOperatorPayout == operatorPayee);
713  }
714 
715  // ProUpServ: Try to change masternode operator payout when the operator reward is zero
716  {
717  const CScript& operatorPayee = GenerateRandomAddress();
718  auto tx = CreateProUpServTx(utxos, dmnHashes[0], operatorKeys.at(dmnHashes[0]), 1, operatorPayee, coinbaseKey);
719  CValidationState state;
720  BOOST_CHECK(!WITH_LOCK(cs_main, return CheckSpecialTx(tx, chainTip, view, state); ));
721  BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-operator-payee");
722  }
723 
724  // Block including
725  // - (1) ProRegTx registering a masternode
726  // - (2) ProUpServTx changing the IP of another masternode, to the one used by (1)
727  {
728  auto tx1 = CreateProRegTx(nullopt, utxos, port++, GenerateRandomAddress(), coinbaseKey, GetRandomKey(), GetRandomBLSKey().GetPublicKey());
729  const uint256& proTx = dmnHashes[InsecureRandRange(dmnHashes.size())]; // pick one at random
730  auto tx2 = CreateProUpServTx(utxos, proTx, operatorKeys.at(proTx), (port-1), CScript(), coinbaseKey);
731  CBlock block = CreateBlock({tx1, tx2}, coinbaseKey);
732  CBlockIndex indexFake(block);
733  indexFake.nHeight = nHeight;
734  indexFake.pprev = chainTip;
735  CValidationState state;
736  BOOST_CHECK(!WITH_LOCK(cs_main, return ProcessSpecialTxsInBlock(block, &indexFake, view, state, true); ));
737  BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-dup-addr");
738  ProcessNewBlock(std::make_shared<const CBlock>(block), nullptr); // todo: move to ProcessBlockAndCheckRejectionReason.
739  BOOST_CHECK_EQUAL(WITH_LOCK(cs_main, return chainActive.Height(); ), nHeight); // bad block not connected
740  }
741 
742  // ProUpReg: change voting key, operator key and payout address
743  {
744  const uint256& proTx = dmnHashes[InsecureRandRange(dmnHashes.size())]; // pick one at random
745  CBLSSecretKey new_operatorKey = GetRandomBLSKey();
746  const CKey& new_votingKey = GetRandomKey();
747  const CScript& new_payee = GenerateRandomAddress();
748  // try first with wrong owner key
749  CValidationState state;
750  auto tx = CreateProUpRegTx(utxos, proTx, GetRandomKey(), new_operatorKey.GetPublicKey(), new_votingKey, new_payee, coinbaseKey);
751  BOOST_CHECK_MESSAGE(!WITH_LOCK(cs_main, return CheckSpecialTx(tx, chainTip, view, state); ), "ProUpReg verifies with wrong owner key");
752  BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-sig");
753  // then use the proper key
754  state = CValidationState();
755  tx = CreateProUpRegTx(utxos, proTx, ownerKeys.at(proTx), new_operatorKey.GetPublicKey(), new_votingKey, new_payee, coinbaseKey);
756  BOOST_CHECK_MESSAGE(WITH_LOCK(cs_main, return CheckSpecialTx(tx, chainTip, view, state); ), state.GetRejectReason());
757  BOOST_CHECK_MESSAGE(CheckTransactionSignature(tx), "ProUpReg signature verification failed");
758  // also verify that payloads are not malleable after they have been signed
759  auto tx2 = MalleateProTxPayout<ProUpRegPL>(tx);
760  BOOST_CHECK_MESSAGE(!WITH_LOCK(cs_main, return CheckSpecialTx(tx2, chainTip, view, state); ), "Malleated ProUpReg accepted");
761  BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-sig");
762 
763  CreateAndProcessBlock({tx}, coinbaseKey);
764  chainTip = chainActive.Tip();
765  BOOST_CHECK_EQUAL(chainTip->nHeight, ++nHeight);
766 
767  auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(proTx);
768  BOOST_ASSERT(dmn != nullptr);
769  BOOST_CHECK_MESSAGE(dmn->pdmnState->pubKeyOperator.Get() == new_operatorKey.GetPublicKey(), "mn operator key not changed");
770  BOOST_CHECK_MESSAGE(dmn->pdmnState->keyIDVoting == new_votingKey.GetPubKey().GetID(), "mn voting key not changed");
771  BOOST_CHECK_MESSAGE(dmn->pdmnState->scriptPayout == new_payee, "mn script payout not changed");
772 
773  operatorKeys[proTx] = std::move(new_operatorKey);
774 
775  // check that changing the operator key puts the MN in PoSe banned state
776  BOOST_CHECK_MESSAGE(dmn->pdmnState->addr == CService(), "IP address not cleared after changing operator");
777  BOOST_CHECK_MESSAGE(dmn->pdmnState->scriptOperatorPayout.empty(), "operator payee not empty after changing operator");
778  BOOST_CHECK(dmn->IsPoSeBanned());
779  BOOST_CHECK_EQUAL(dmn->pdmnState->nPoSeBanHeight, nHeight);
780 
781  // revive the MN
782  auto tx3 = CreateProUpServTx(utxos, proTx, operatorKeys.at(proTx), 2000, CScript(), coinbaseKey);
783  CreateAndProcessBlock({tx3}, coinbaseKey);
784  chainTip = chainActive.Tip();
785  BOOST_CHECK_EQUAL(chainTip->nHeight, ++nHeight);
786  dmn = deterministicMNManager->GetListAtChainTip().GetMN(proTx);
787 
788  // check updated dmn state
789  BOOST_CHECK_EQUAL(dmn->pdmnState->addr.GetPort(), 2000);
790  BOOST_CHECK_EQUAL(dmn->pdmnState->nPoSeBanHeight, -1);
791  BOOST_CHECK(!dmn->IsPoSeBanned());
792  BOOST_CHECK_EQUAL(dmn->pdmnState->nPoSeRevivedHeight, nHeight);
793 
794  // Mine 32 blocks, checking MN reward payments
795  mapPayments.clear();
796  for (size_t i = 0; i < 32; i++) {
797  auto dmnExpectedPayee = deterministicMNManager->GetListAtChainTip().GetMNPayee();
798  CBlock block = CreateAndProcessBlock({}, coinbaseKey);
799  chainTip = chainActive.Tip();
800  BOOST_CHECK_EQUAL(chainTip->nHeight, ++nHeight);
801  BOOST_ASSERT(!block.vtx.empty());
802  BOOST_CHECK(IsMNPayeeInBlock(block, dmnExpectedPayee->pdmnState->scriptPayout));
803  mapPayments[dmnExpectedPayee->proTxHash]++;
804  }
805  // 16 masternodes: 2 rewards each
806  CheckPayments(mapPayments, 16, 2);
807  }
808 
809  // ProUpReg: Try to change the voting key of a masternode that doesn't exist
810  {
811  const CKey& votingKey = GetRandomKey();
812  const CBLSSecretKey& operatorKey = GetRandomBLSKey();
813  auto tx = CreateProUpRegTx(utxos, GetRandHash(), GetRandomKey(), operatorKey.GetPublicKey(), votingKey, GenerateRandomAddress(), coinbaseKey);
814 
815  CValidationState state;
816  BOOST_CHECK_MESSAGE(!WITH_LOCK(cs_main, return CheckSpecialTx(tx, chainTip, view, state); ), "Accepted ProUpReg with invalid protx hash");
817  BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-hash");
818  }
819 
820  // ProUpReg: Try to change the operator key of a masternode to the one of another registered masternode
821  {
822  int randomIdx = InsecureRandRange(dmnHashes.size());
823  int randomIdx2 = 0;
824  do { randomIdx2 = InsecureRandRange(dmnHashes.size()); } while (randomIdx2 == randomIdx);
825  const uint256& proTx = dmnHashes[randomIdx]; // mn to update
826  const CBLSSecretKey& new_operatorKey = operatorKeys.at(dmnHashes[randomIdx2]);
827 
828  auto tx = CreateProUpRegTx(utxos, proTx, ownerKeys.at(proTx), new_operatorKey.GetPublicKey(), GetRandomKey(), GenerateRandomAddress(), coinbaseKey);
829 
830  CValidationState state;
831  BOOST_CHECK_MESSAGE(!WITH_LOCK(cs_main, return CheckSpecialTx(tx, chainTip, view, state); ), "Accepted ProUpReg with duplicate operator key");
832  BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-dup-key");
833  }
834 
835  // Block with two ProUpReg txes using same operator key
836  {
837  int randomIdx1 = InsecureRandRange(dmnHashes.size());
838  int randomIdx2 = 0;
839  do { randomIdx2 = InsecureRandRange(dmnHashes.size()); } while (randomIdx2 == randomIdx1);
840  const uint256& proTx1 = dmnHashes[randomIdx1];
841  const uint256& proTx2 = dmnHashes[randomIdx2];
842  BOOST_ASSERT(proTx1 != proTx2);
843  const CBLSSecretKey& new_operatorKey = GetRandomBLSKey();
844  const CKey& new_votingKey = GetRandomKey();
845  const CScript& new_payee = GenerateRandomAddress();
846  auto tx1 = CreateProUpRegTx(utxos, proTx1, ownerKeys.at(proTx1), new_operatorKey.GetPublicKey(), new_votingKey, new_payee, coinbaseKey);
847  auto tx2 = CreateProUpRegTx(utxos, proTx2, ownerKeys.at(proTx2), new_operatorKey.GetPublicKey(), new_votingKey, new_payee, coinbaseKey);
848  CBlock block = CreateBlock({tx1, tx2}, coinbaseKey);
849  CBlockIndex indexFake(block);
850  indexFake.nHeight = nHeight;
851  indexFake.pprev = chainTip;
852  CValidationState state;
853  BOOST_CHECK_MESSAGE(!WITH_LOCK(cs_main, return ProcessSpecialTxsInBlock(block, &indexFake, view, state, true); ),
854  "Accepted block with duplicate operator key in ProUpReg txes");
855  BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-dup-operator-key");
856  ProcessNewBlock(std::make_shared<const CBlock>(block), nullptr);
857  BOOST_CHECK_EQUAL(WITH_LOCK(cs_main, return chainActive.Height(); ), nHeight); // bad block not connected
858  }
859 
860  // Block including
861  // - (1) ProRegTx registering a masternode
862  // - (2) ProUpRegTx changing the operator key of another masternode, to the one used by (1)
863  {
864  const CBLSSecretKey& new_operatorKey = GetRandomBLSKey();
865  auto tx1 = CreateProRegTx(nullopt, utxos, port++, GenerateRandomAddress(), coinbaseKey, GetRandomKey(), new_operatorKey.GetPublicKey());
866  const uint256& proTx = dmnHashes[InsecureRandRange(dmnHashes.size())]; // pick one at random
867  auto tx2 = CreateProUpRegTx(utxos, proTx, ownerKeys.at(proTx), new_operatorKey.GetPublicKey(), GetRandomKey(), GenerateRandomAddress(), coinbaseKey);
868  CBlock block = CreateBlock({tx1, tx2}, coinbaseKey);
869  CBlockIndex indexFake(block);
870  indexFake.nHeight = nHeight;
871  indexFake.pprev = chainTip;
872  CValidationState state;
873  BOOST_CHECK_MESSAGE(!WITH_LOCK(cs_main, return ProcessSpecialTxsInBlock(block, &indexFake, view, state, true); ),
874  "Accepted block with duplicate operator key in ProReg+ProUpReg txes");
875  BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-dup-operator-key");
876  ProcessNewBlock(std::make_shared<const CBlock>(block), nullptr);
877  BOOST_CHECK_EQUAL(WITH_LOCK(cs_main, return chainActive.Height(); ), nHeight); // bad block not connected
878  }
879 
880  // ProUpRev: revoke masternode service
881  {
882  const uint256& proTx = dmnHashes[InsecureRandRange(dmnHashes.size())]; // pick one at random
883  ProUpRevPL::RevocationReason reason = ProUpRevPL::RevocationReason::REASON_TERMINATION_OF_SERVICE;
884  // try first with wrong operator key
885  CValidationState state;
886  auto tx = CreateProUpRevTx(utxos, proTx, reason, GetRandomBLSKey(), coinbaseKey);
887  BOOST_CHECK_MESSAGE(!WITH_LOCK(cs_main, return CheckSpecialTx(tx, chainTip, view, state); ), "ProUpReg verifies with wrong owner key");
888  BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-sig");
889  // then use the proper key
890  state = CValidationState();
891  tx = CreateProUpRevTx(utxos, proTx, reason, operatorKeys.at(proTx), coinbaseKey);
892  BOOST_CHECK(WITH_LOCK(cs_main, return CheckSpecialTx(tx, chainTip, view, state); ));
893  BOOST_CHECK_MESSAGE(CheckTransactionSignature(tx), "ProUpReg signature verification failed");
894  // also verify that payloads are not malleable after they have been signed
895  auto tx2 = MalleateProUpRevTx(tx);
896  BOOST_CHECK_MESSAGE(!WITH_LOCK(cs_main, return CheckSpecialTx(tx2, chainTip, view, state); ), "Malleated ProUpReg accepted");
897  BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-sig");
898 
899  CreateAndProcessBlock({tx}, coinbaseKey);
900  chainTip = chainActive.Tip();
901  BOOST_CHECK_EQUAL(chainTip->nHeight, ++nHeight);
902 
903  auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(proTx);
904  BOOST_ASSERT(dmn != nullptr);
905  BOOST_CHECK_MESSAGE(!dmn->pdmnState->pubKeyOperator.Get().IsValid(), "mn operator key not removed");
906  BOOST_CHECK_MESSAGE(dmn->pdmnState->addr == CService(), "mn IP address not removed");
907  BOOST_CHECK_MESSAGE(dmn->pdmnState->scriptOperatorPayout.empty(), "mn operator payout not removed");
908  BOOST_CHECK_EQUAL(dmn->pdmnState->nRevocationReason, reason);
909  BOOST_CHECK(dmn->IsPoSeBanned());
910  BOOST_CHECK_EQUAL(dmn->pdmnState->nPoSeBanHeight, nHeight);
911  }
912 
914 }
915 
916 // Dummy commitment where the DKG shares are replaced with the operator keys of each member.
917 // members at index skeys.size(), ..., llmqType.size - 1 are invalid
918 static llmq::CFinalCommitment CreateFinalCommitment(std::vector<CBLSPublicKey>& pkeys,
919  std::vector<CBLSSecretKey>& skeys,
920  const uint256& quorumHash)
921 {
922  size_t m = skeys.size();
923  BOOST_ASSERT(pkeys.size() == m);
924 
926  qfl.llmqType = (uint8_t)Consensus::LLMQ_TEST;
927  const auto& params = Params().GetConsensus().llmqs.at(Consensus::LLMQ_TEST);
928  BOOST_ASSERT(m <= (size_t) params.size); // m-of-n
929 
930  // non-included members are marked invalid
931  qfl.signers.resize(params.size);
932  qfl.validMembers.resize(params.size);
933  for (size_t i = 0; i < (size_t) params.size; i++) {
934  qfl.signers[i] = i < m;
935  qfl.validMembers[i] = i < m;
936  }
937 
938  qfl.quorumHash = quorumHash;
939 
940  // create dummy quorum keys, just aggregating operator BLS keys
942 
943  // use dummy non-null verification vector hash
945 
946  // add signatures
948  std::vector<CBLSSignature> sigs;
949  for (size_t i = 0; i < m; i++) {
950  sigs.emplace_back(skeys[i].Sign(commitmentHash));
951  }
952  qfl.membersSig = CBLSSignature::AggregateSecure(sigs, pkeys, commitmentHash);
953  qfl.quorumSig = CBLSSecretKey::AggregateInsecure(skeys).Sign(commitmentHash);
954 
955  return qfl;
956 }
957 
959 {
960  llmq::LLMQCommPL pl;
961  pl.commitment = opt_qfc ? *opt_qfc : llmq::CFinalCommitment(Params().GetConsensus().llmqs.at(Consensus::LLMQ_TEST), quorumHash);
962  pl.nHeight = nHeight;
965  tx.nType = CTransaction::TxType::LLMQCOMM;
966  SetTxPayload(tx, pl);
967  return tx;
968 }
969 
970 CMutableTransaction CreateNullQfcTx(const uint256& quorumHash, int nHeight)
971 {
972  return CreateQfcTx(quorumHash, nHeight, nullopt);
973 }
974 
975 CService ip(uint32_t i)
976 {
977  struct in_addr s;
978  s.s_addr = i;
979  return CService(CNetAddr(s), Params().GetDefaultPort());
980 }
981 
982 static void ProcessQuorum(llmq::CQuorumBlockProcessor* processor, const llmq::CFinalCommitment& qfc, CNode* node, int expected_banscore = 0)
983 {
984  CDataStream vRecv(SER_NETWORK, PROTOCOL_VERSION);
985  vRecv << qfc;
986  int banScore{0};
987  processor->ProcessMessage(node, vRecv, banScore);
988  BOOST_CHECK_EQUAL(banScore, expected_banscore);
989 }
990 
991 static NodeId id = 0;
992 
993 // future: split dkg_pose from qfc_invalid_paths test coverage.
994 BOOST_FIXTURE_TEST_CASE(dkg_pose_and_qfc_invalid_paths, TestChain400Setup)
995 {
996  auto utxos = BuildSimpleUtxoMap(coinbaseTxns);
997 
998  CBlockIndex* chainTip = chainActive.Tip();
999  int nHeight = chainTip->nHeight;
1001 
1002  // load empty list (last block before enforcement)
1003  CreateAndProcessBlock({}, coinbaseKey);
1004  chainTip = chainActive.Tip();
1005  BOOST_CHECK_EQUAL(chainTip->nHeight, ++nHeight);
1006 
1007  // force mnsync complete and enable spork 8
1009  int64_t nTime = GetTime() - 10;
1010  const CSporkMessage& sporkMnPayment = CSporkMessage(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT, nTime + 1, nTime);
1011  sporkManager.AddOrUpdateSporkMessage(sporkMnPayment);
1013 
1014  int port = 1;
1015 
1016  std::vector<uint256> dmnHashes;
1017  std::map<uint256, CKey> ownerKeys;
1018  std::map<uint256, CBLSSecretKey> operatorKeys;
1019 
1020  // register one MN per block
1021  for (size_t i = 0; i < 6; i++) {
1022  const CKey& ownerKey = GetRandomKey();
1023  const CBLSSecretKey& operatorKey = GetRandomBLSKey();
1024  auto tx = CreateProRegTx(nullopt, utxos, port++, GenerateRandomAddress(), coinbaseKey, ownerKey, operatorKey.GetPublicKey());
1025  const uint256& txid = tx.GetHash();
1026  dmnHashes.emplace_back(txid);
1027  ownerKeys.emplace(txid, ownerKey);
1028  operatorKeys.emplace(txid, operatorKey);
1029  CreateAndProcessBlock({tx}, coinbaseKey);
1030  chainTip = chainActive.Tip();
1031  BOOST_CHECK_EQUAL(chainTip->nHeight, ++nHeight);
1032  BOOST_CHECK(deterministicMNManager->GetListAtChainTip().HasMN(txid));
1033  }
1034 
1035  // enable SPORK_21
1038  BOOST_CHECK(deterministicMNManager->LegacyMNObsolete(nHeight + 1));
1039 
1040  // Mine 20 blocks
1041  for (size_t i = 0; i < 20; i++) {
1042  CreateAndProcessBlock({}, coinbaseKey);
1043  chainTip = chainActive.Tip();
1044  BOOST_CHECK_EQUAL(chainTip->nHeight, ++nHeight);
1045  }
1046 
1047  BOOST_CHECK_EQUAL(nHeight, 427);
1048  // dkg starts at 420
1049  auto& params = Params().GetConsensus().llmqs.at(Consensus::LLMQ_TEST);
1050  uint256 quorumHash = chainActive[nHeight - (nHeight % params.dkgInterval)]->GetBlockHash();
1051  const CBlockIndex* quorumIndex = mapBlockIndex.at(quorumHash);
1052 
1053  // get quorum mns
1054  auto members = deterministicMNManager->GetAllQuorumMembers(Consensus::LLMQ_TEST, quorumIndex);
1055  std::vector<CBLSPublicKey> pkeys;
1056  std::vector<CBLSSecretKey> skeys;
1057  for (size_t i = 0; i < members.size()-1; i++) { // all, except the last one...
1058  pkeys.emplace_back(members[i]->pdmnState->pubKeyOperator.Get());
1059  skeys.emplace_back(operatorKeys.at(members[i]->proTxHash));
1060  }
1061  const uint256& invalidmn_proTx = members.back()->proTxHash; // ...which must be punished.
1062 
1063  // create final commitment
1064  llmq::CFinalCommitment qfc = CreateFinalCommitment(pkeys, skeys, quorumHash);
1065  BOOST_CHECK(!qfc.IsNull());
1066  {
1067  LOCK(cs_main);
1068  CValidationState state;
1069  BOOST_CHECK(VerifyLLMQCommitment(qfc, chainTip, state));
1070  }
1071 
1072  // verify that it fails changing the key of one of the signers
1073  std::vector<CBLSPublicKey> allkeys(pkeys);
1074  allkeys.emplace_back(members.back()->pdmnState->pubKeyOperator.Get());
1075  BOOST_CHECK(qfc.Verify(allkeys, params)); // already checked with VerifyLLMQCommitment
1076  allkeys[0] = GetRandomBLSKey().GetPublicKey();
1077  BOOST_CHECK(!qfc.Verify(allkeys, params));
1078 
1079  // receive final commitment message
1080  CNode dummyNode(id++, NODE_NETWORK, 0, INVALID_SOCKET, CAddress(ip(0xa0b0c001), NODE_NONE), 0, 0, "", true);
1081  ProcessQuorum(llmq::quorumBlockProcessor.get(), qfc, &dummyNode);
1082  BOOST_CHECK(llmq::quorumBlockProcessor->HasMinableCommitment(::SerializeHash(qfc)));
1083 
1084  // Generate blocks up to be able to mine a null qfc at block 430
1085  CreateAndProcessBlock({}, coinbaseKey);
1086  chainTip = chainActive.Tip();
1087  BOOST_CHECK_EQUAL(chainTip->nHeight, ++nHeight);
1088  BOOST_CHECK_EQUAL(nHeight, 428);
1089 
1090  // Coverage for the following qfc paths:
1091  // 1) Mine a qfc with an invalid height, which should end up being rejected.
1092  // 2) Mine a null qfc before the mining phase, which should end up being rejected.
1093  // 3) Mine two qfc in the same block, which should end up being rejected.
1094  // 4) Mine block without qfc during the mining phase, which should end up being rejected.
1095  // 5) Mine two blocks with a null qfc.
1096  // 6) Try to relay the valid qfc to the mempool, which should end up being rejected.
1097  // 7a) Mine a qfc with an invalid quorum hash (invalid height), which should end up being rejected.
1098  // 7b) Mine a qfc with an invalid quorum hash (non-existent), which should end up being rejected.
1099  // 7c) Mine a qfc with an invalid quorum hash (forked), which should end up being rejected.
1100  // 7d) Mine a qfc with an old quorum hash, which should end up being rejected.
1101  // 8) Mine the final valid qfc in a block.
1102  // 9) Mine a null qfc after mining a valid qfc, which should end up being rejected.
1103 
1104  // 1) Mine a qfc with an invalid height, which should end up being rejected.
1105  CMutableTransaction nullQfcTx = CreateNullQfcTx(quorumHash, nHeight);
1106  CScript coinsbaseScript = GetScriptForRawPubKey(coinbaseKey.GetPubKey());
1107  auto pblock_invalid = std::make_shared<CBlock>(CreateBlock({nullQfcTx}, coinsbaseScript, true, false, false));
1108  ProcessBlockAndCheckRejectionReason(pblock_invalid, "bad-qc-height", nHeight);
1109 
1110  // 2) Mine a null qfc before the mining phase, which should end up being rejected.
1111  nullQfcTx = CreateNullQfcTx(quorumHash, nHeight + 1);
1112  pblock_invalid = std::make_shared<CBlock>(
1113  CreateBlock({nullQfcTx}, coinsbaseScript, true, false, false));
1114  ProcessBlockAndCheckRejectionReason(pblock_invalid, "bad-qc-not-allowed", nHeight);
1115 
1116  // One more block, 429.
1117  CreateAndProcessBlock({}, coinbaseKey);
1118  chainTip = chainActive.Tip();
1119  BOOST_CHECK_EQUAL(chainTip->nHeight, ++nHeight);
1120 
1121  // 3) Mine two qfc in the same block, which should end up on a rejection. (one null, one valid)
1122  nullQfcTx = CreateNullQfcTx(quorumHash, nHeight + 1);
1123  pblock_invalid = std::make_shared<CBlock>(
1124  CreateBlock({nullQfcTx}, coinsbaseScript, true, false, true));
1125  ProcessBlockAndCheckRejectionReason(pblock_invalid, "bad-qc-dup", nHeight);
1126 
1127  // 4) Mine block without qfc during the mining phase, which should end up being rejected.
1128  pblock_invalid = std::make_shared<CBlock>(CreateBlock({}, coinsbaseScript, true, false, false));
1129  ProcessBlockAndCheckRejectionReason(pblock_invalid, "bad-qc-missing", nHeight);
1130 
1131  // 5) Mine two blocks with a null qfc.
1132  for (int i = 0; i < 2; i++) {
1133  const auto& block = CreateBlock({nullQfcTx}, coinsbaseScript, true, false, false);
1134  ProcessNewBlock(std::make_shared<const CBlock>(block), nullptr);
1135  chainTip = chainActive.Tip();
1136  BOOST_CHECK_EQUAL(chainTip->nHeight, ++nHeight);
1137  nullQfcTx = CreateNullQfcTx(quorumHash, nHeight + 1);
1138  }
1139  BOOST_CHECK_EQUAL(nHeight, 431);
1140 
1141  // 6) Try to relay the valid qfc to the mempool, which should end up on a rejection.
1142  CTransactionRef qcTx;
1143  BOOST_CHECK(llmq::quorumBlockProcessor->GetMinableCommitmentTx(Consensus::LLMQ_TEST, nHeight + 1, qcTx));
1144  CValidationState mempoolState;
1145  BOOST_CHECK(!WITH_LOCK(cs_main, return AcceptToMemoryPool(mempool, mempoolState, qcTx, true, nullptr); ));
1146  BOOST_CHECK_EQUAL(mempoolState.GetRejectReason(), "llmqcomm");
1147 
1148  // 7a) Mine a qfc with an invalid quorum hash (invalid height), which should end up being rejected.
1149  nullQfcTx = CreateNullQfcTx(chainTip->GetBlockHash(), nHeight + 1);
1150  pblock_invalid = std::make_shared<CBlock>(CreateBlock({nullQfcTx}, coinsbaseScript, true, false, false));
1151  ProcessBlockAndCheckRejectionReason(pblock_invalid, "bad-qc-quorum-height", nHeight);
1152 
1153  // 7b) Mine a qfc with an invalid quorum hash (non-existent), which should end up being rejected.
1154  nullQfcTx = CreateNullQfcTx(UINT256_ONE, nHeight + 1);
1155  pblock_invalid = std::make_shared<CBlock>(CreateBlock({nullQfcTx}, coinsbaseScript, true, false, false));
1156  ProcessBlockAndCheckRejectionReason(pblock_invalid, "bad-qc-quorum-hash-not-found", nHeight);
1157 
1158  // 7c) Mine a qfc with an invalid quorum hash (forked), which should end up being rejected.
1159  // -- first create a secondary chain at height 420
1160  CBlockIndex* pblock_419 = WITH_LOCK(cs_main, return mapBlockIndex.at(chainActive[419]->GetBlockHash()); );
1161  auto pblock_forked = std::make_shared<CBlock>(CreateBlock({}, coinsbaseScript, true, false, false, pblock_419));
1162  // increment nonce and re-solve to get a different block
1163  pblock_forked->nNonce++;
1164  BOOST_CHECK(SolveBlock(pblock_forked, 420));
1165  BOOST_CHECK(ProcessNewBlock(pblock_forked, nullptr));
1166  {
1167  LOCK(cs_main);
1168  const auto it = mapBlockIndex.find(pblock_forked->GetHash());
1169  BOOST_CHECK(it != mapBlockIndex.end());
1170  BOOST_CHECK(!chainActive.Contains(it->second));
1171  }
1172 
1173  // -- then mine a commitment referencing the quorum hash from the secondary chain
1174  nullQfcTx = CreateNullQfcTx(pblock_forked->GetHash(), nHeight + 1);
1175  pblock_invalid = std::make_shared<CBlock>(CreateBlock({nullQfcTx}, coinsbaseScript, true, false, false));
1176  ProcessBlockAndCheckRejectionReason(pblock_invalid, "bad-qc-quorum-hash-not-active-chain", nHeight);
1177 
1178  // 7d) Mine a qfc with an old quorum hash, which should end up being rejected.
1179  int old_quorum_hash_height = nHeight - (nHeight % params.dkgInterval) - params.cacheDkgInterval - params.dkgInterval;
1180  uint256 old_quorum_hash = chainActive[old_quorum_hash_height]->GetBlockHash();
1181  nullQfcTx = CreateNullQfcTx(old_quorum_hash, nHeight + 1);
1182  pblock_invalid = std::make_shared<CBlock>(CreateBlock({nullQfcTx}, coinsbaseScript, true, false, false));
1183  ProcessBlockAndCheckRejectionReason(pblock_invalid, "bad-qc-quorum-height-old", nHeight);
1184 
1185  // Now check the message over the wire. future: add error rejection code.
1186  auto old_qfc = CreateFinalCommitment(pkeys, skeys, old_quorum_hash);
1187  ProcessQuorum(llmq::quorumBlockProcessor.get(), old_qfc, &dummyNode, 100);
1188 
1189  // 8) Mine the final valid qfc in a block.
1190  CreateAndProcessBlock({}, coinbaseKey);
1191  chainTip = chainActive.Tip();
1192  BOOST_CHECK_EQUAL(chainTip->nHeight, ++nHeight);
1193 
1194  // 9) Mine a null qfc after mining a valid qfc, which should end up being rejected.
1195  nullQfcTx = CreateNullQfcTx(quorumHash, nHeight + 1);
1196  pblock_invalid = std::make_shared<CBlock>(CreateBlock({nullQfcTx}, coinsbaseScript, true, false, false));
1197  ProcessBlockAndCheckRejectionReason(pblock_invalid, "bad-qc-not-allowed", nHeight);
1198 
1199  // final commitment has been mined
1201  uint256 retMinedBlockHash;
1202  BOOST_CHECK(llmq::quorumBlockProcessor->GetMinedCommitment(Consensus::LLMQ_TEST, quorumHash, ret, retMinedBlockHash));
1203  BOOST_CHECK(chainTip->GetBlockHash() == retMinedBlockHash);
1206  BOOST_CHECK(qfc.quorumSig == ret.quorumSig);
1207  BOOST_CHECK(qfc.membersSig == ret.membersSig);
1208 
1209  // non-participating mn has been punished
1210  auto punished_mn = deterministicMNManager->GetListAtChainTip().GetMN(invalidmn_proTx);
1211  BOOST_CHECK_EQUAL(punished_mn->pdmnState->nPoSePenalty, 66);
1212 
1213  // penalty is decreased each block
1214  CreateAndProcessBlock({}, coinbaseKey);
1215  chainTip = chainActive.Tip();
1216  BOOST_CHECK_EQUAL(chainTip->nHeight, ++nHeight);
1217  punished_mn = deterministicMNManager->GetListAtChainTip().GetMN(invalidmn_proTx);
1218  BOOST_CHECK_EQUAL(punished_mn->pdmnState->nPoSePenalty, 65);
1219 
1220  // New DKG starts at block 440. Mine till block 441 and create another valid 2-of-3 commitment
1221  for (size_t i = 0; i < 8; i++) {
1222  CreateAndProcessBlock({}, coinbaseKey);
1223  chainTip = chainActive.Tip();
1224  BOOST_CHECK_EQUAL(chainTip->nHeight, ++nHeight);
1225  }
1226  BOOST_CHECK_EQUAL(nHeight, 441);
1227  quorumHash = chainActive[nHeight - (nHeight % params.dkgInterval)]->GetBlockHash();
1228  quorumIndex = mapBlockIndex.at(quorumHash);
1229  members = deterministicMNManager->GetAllQuorumMembers(Consensus::LLMQ_TEST, quorumIndex);
1230  pkeys.clear();
1231  skeys.clear();
1232  for (size_t i = 0; i < members.size(); i++) {
1233  pkeys.emplace_back(members[i]->pdmnState->pubKeyOperator.Get());
1234  skeys.emplace_back(operatorKeys.at(members[i]->proTxHash));
1235  }
1236  std::vector<CBLSPublicKey> pkeys2(pkeys.begin(), pkeys.end()-1); // remove the last one.
1237  std::vector<CBLSSecretKey> skeys2(skeys.begin(), skeys.end()-1);
1238  llmq::CFinalCommitment qfc2 = CreateFinalCommitment(pkeys2, skeys2, quorumHash);
1239  BOOST_CHECK(!qfc2.IsNull());
1240  {
1241  LOCK(cs_main);
1242  CValidationState state;
1243  BOOST_CHECK(VerifyLLMQCommitment(qfc2, chainTip, state));
1244  }
1245  ProcessQuorum(llmq::quorumBlockProcessor.get(), qfc2, &dummyNode);
1246  // final commitment received and accepted
1247  BOOST_CHECK(llmq::quorumBlockProcessor->HasMinableCommitment(::SerializeHash(qfc2)));
1248 
1249  // Now receive another commitment for the same quorum hash, but with all 3 signatures
1250  qfc = CreateFinalCommitment(pkeys, skeys, quorumHash);
1251  BOOST_CHECK(!qfc.IsNull());
1252  {
1253  LOCK(cs_main);
1254  CValidationState state;
1255  BOOST_CHECK(VerifyLLMQCommitment(qfc, chainTip, state));
1256  }
1257  ProcessQuorum(llmq::quorumBlockProcessor.get(), qfc, &dummyNode);
1258  BOOST_CHECK(qfc.CountSigners() > qfc2.CountSigners());
1259 
1260  // final commitment received, accepted, and replaced the previous one (with less members)
1261  BOOST_CHECK(llmq::quorumBlockProcessor->HasMinableCommitment(::SerializeHash(qfc)));
1262 
1263  // activate spork 22 and try to mine a non-null commitment
1264  nTime = GetTime() - 10;
1267  auto qtx = CreateQfcTx(quorumHash, nHeight + 1, Optional<llmq::CFinalCommitment>(qfc2));
1268  pblock_invalid = std::make_shared<CBlock>(CreateBlock({qtx}, coinsbaseScript, true, false, false));
1269  ProcessBlockAndCheckRejectionReason(pblock_invalid, "bad-qc-not-null-spork22", nHeight);
1270 
1271  // mine a null commitment
1272  for (size_t i = 0; i < 8; i++) {
1273  CreateAndProcessBlock({}, coinbaseKey);
1274  }
1275  nHeight = WITH_LOCK(cs_main, return chainActive.Height(); );
1276  BOOST_CHECK_EQUAL(nHeight, 449);
1277  nullQfcTx = CreateNullQfcTx(quorumHash, nHeight + 1);
1278  auto pblock = std::make_shared<CBlock>(CreateBlock({nullQfcTx}, coinsbaseScript, true, false, false));
1279  ProcessNewBlock(pblock, nullptr);
1280  chainTip = WITH_LOCK(cs_main, return chainActive.Tip(); );
1281  BOOST_CHECK_EQUAL(chainTip->nHeight, ++nHeight);
1282 
1283  for (size_t i = 0; i < 19; i++) {
1284  CreateAndProcessBlock({}, coinbaseKey);
1285  chainTip = WITH_LOCK(cs_main, return chainActive.Tip(); );
1286  BOOST_CHECK_EQUAL(chainTip->nHeight, ++nHeight);
1287  }
1288  BOOST_CHECK_EQUAL(nHeight, 469);
1289 
1290  // test rejection of non-null commitments over the wire
1291  {
1292  LOCK(cs_main);
1293  quorumHash = chainActive[nHeight - (nHeight % params.dkgInterval)]->GetBlockHash();
1294  quorumIndex = mapBlockIndex.at(quorumHash);
1295  }
1296  members = deterministicMNManager->GetAllQuorumMembers(Consensus::LLMQ_TEST, quorumIndex);
1297  pkeys.clear();
1298  skeys.clear();
1299  for (size_t i = 0; i < members.size(); i++) {
1300  pkeys.emplace_back(members[i]->pdmnState->pubKeyOperator.Get());
1301  skeys.emplace_back(operatorKeys.at(members[i]->proTxHash));
1302  }
1303  llmq::CFinalCommitment qfc3 = CreateFinalCommitment(pkeys, skeys, quorumHash);
1304  BOOST_CHECK(!qfc3.IsNull());
1305  {
1306  LOCK(cs_main);
1307  CValidationState state;
1308  BOOST_CHECK(!VerifyLLMQCommitment(qfc3, chainTip, state));
1309  BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-qc-not-null-spork22");
1310  }
1311  // final commitment not accepted
1312  uint256 qfc3_hash = ::SerializeHash(qfc3);
1313  ProcessQuorum(llmq::quorumBlockProcessor.get(), qfc3, &dummyNode, 50);
1314  BOOST_CHECK(!llmq::quorumBlockProcessor->HasMinableCommitment(qfc3_hash));
1315 
1316  // disable spork 22 and accept it
1319  ProcessQuorum(llmq::quorumBlockProcessor.get(), qfc3, &dummyNode);
1320  BOOST_CHECK(llmq::quorumBlockProcessor->HasMinableCommitment(qfc3_hash));
1321 
1322  // and mine it
1323  CreateAndProcessBlock({}, coinbaseKey);
1324  chainTip = WITH_LOCK(cs_main, return chainActive.Tip(); );
1325  BOOST_CHECK_EQUAL(chainTip->nHeight, ++nHeight);
1326  BOOST_CHECK(llmq::quorumBlockProcessor->GetMinedCommitment(Consensus::LLMQ_TEST, quorumHash, ret, retMinedBlockHash));
1327  BOOST_CHECK(chainTip->GetBlockHash() == retMinedBlockHash);
1330  BOOST_CHECK(qfc3.quorumSig == ret.quorumSig);
1331  BOOST_CHECK(qfc3.membersSig == ret.membersSig);
1332 
1334 }
1335 
int64_t CAmount
Amount in PIV (Can be negative)
Definition: amount.h:13
CMutableTransaction CreateCoinbaseTx(const CScript &scriptPubKeyIn, CBlockIndex *pindexPrev)
bool SolveBlock(std::shared_ptr< CBlock > &pblock, int nHeight)
Modify the nonce/extranonce in a block.
void ProcessBlockAndCheckRejectionReason(std::shared_ptr< CBlock > &pblock, const std::string &blockRejectionReason, int expectedChainHeight)
Definition: blocksutil.cpp:13
void UpdateNetworkUpgradeParameters(Consensus::UpgradeIndex idx, int nActivationHeight)
Allows modifying the network upgrade regtest parameters.
const CChainParams & Params()
Return the currently selected parameters.
A CService with information about it as peer.
Definition: protocol.h:338
void AggregateInsecure(const CBLSPublicKey &o)
void MakeNewKey()
Definition: bls_wrapper.cpp:54
CBLSPublicKey GetPublicKey() const
Definition: bls_wrapper.cpp:99
void AggregateInsecure(const CBLSSecretKey &o)
Definition: bls_wrapper.cpp:27
static CBLSSignature AggregateSecure(const std::vector< CBLSSignature > &sigs, const std::vector< CBLSPublicKey > &pks, const uint256 &hash)
bool IsValid() const
Definition: bls_wrapper.h:87
Basic key store, that keeps keys in an address->secret map.
Definition: keystore.h:99
bool AddKeyPubKey(const CKey &key, const CPubKey &pubkey)
Add a key to the store.
Definition: keystore.cpp:34
Definition: block.h:80
std::vector< CTransactionRef > vtx
Definition: block.h:83
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: chain.h:139
CBlockIndex * pprev
pointer to the index of the predecessor of this block
Definition: chain.h:145
uint256 GetBlockHash() const
Definition: chain.h:215
int nHeight
height of the entry in the chain. The genesis block has height 0
Definition: chain.h:151
CBlockIndex * Tip(bool fProofOfStake=false) const
Returns the index entry for the tip of this chain, or nullptr if none.
Definition: chain.h:405
int Height() const
Return the maximal height in the chain.
Definition: chain.h:450
bool Contains(const CBlockIndex *pindex) const
Efficiently check whether a block is present in this chain.
Definition: chain.h:435
const Consensus::Params & GetConsensus() const
Definition: chainparams.h:72
CCoinsView that adds a memory cache for transactions to another CCoinsView.
Definition: coins.h:283
bool GetUTXOCoin(const COutPoint &outpoint, Coin &coin) const
Get the coin and check if it's spent.
Definition: coins.cpp:584
static bool SignHash(const uint256 &hash, const CKey &key, std::vector< unsigned char > &vchSigRet)
Sign the hash, returns true if successful.
An encapsulated private key.
Definition: key.h:30
void MakeNewKey(bool fCompressed)
Generate a new private key using a cryptographic PRNG.
Definition: key.cpp:158
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition: key.cpp:186
bool GetMasternodeTxOuts(const CBlockIndex *pindexPrev, std::vector< CTxOut > &voutMasternodePaymentsRet) const
Network address.
Definition: netaddress.h:120
Information about a peer.
Definition: net.h:669
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:72
CKeyID GetID() const
Get the KeyID of this public key (hash of its serialization)
Definition: pubkey.h:167
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:381
A combination of a network address (CNetAddr) and a (TCP) port.
Definition: netaddress.h:484
bool IsSporkActive(SporkId nSporkID)
Definition: spork.cpp:220
void AddOrUpdateSporkMessage(const CSporkMessage &spork, bool flush=false)
Definition: spork.cpp:206
An output of a transaction.
Definition: transaction.h:137
CAmount nValue
Definition: transaction.h:139
Capture information about block/transaction validation.
Definition: validation.h:24
std::string GetRejectReason() const
Definition: validation.h:94
A UTXO entry.
Definition: coins.h:32
CTxOut out
unspent transaction output
Definition: coins.h:41
uint32_t nHeight
at which height the containing transaction was included in the active block chain
Definition: coins.h:44
CKeyID keyIDVoting
Definition: providertx.h:33
COutPoint collateralOutpoint
Definition: providertx.h:29
uint16_t nOperatorReward
Definition: providertx.h:35
CScript scriptPayout
Definition: providertx.h:34
CBLSPublicKey pubKeyOperator
Definition: providertx.h:32
uint256 inputsHash
Definition: providertx.h:37
CKeyID keyIDOwner
Definition: providertx.h:31
CService addr
Definition: providertx.h:30
CKeyID keyIDVoting
Definition: providertx.h:113
CScript scriptPayout
Definition: providertx.h:114
uint256 proTxHash
Definition: providertx.h:110
std::vector< unsigned char > vchSig
Definition: providertx.h:116
CBLSPublicKey pubKeyOperator
Definition: providertx.h:112
uint256 inputsHash
Definition: providertx.h:115
uint16_t nReason
Definition: providertx.h:152
uint256 inputsHash
Definition: providertx.h:153
uint256 proTxHash
Definition: providertx.h:151
CBLSSignature sig
Definition: providertx.h:154
uint256 inputsHash
Definition: providertx.h:83
CService addr
Definition: providertx.h:81
uint256 proTxHash
Definition: providertx.h:80
CBLSSignature sig
Definition: providertx.h:84
CScript scriptOperatorPayout
Definition: providertx.h:82
void SetCurrentSyncPhase(int sync_phase)
bool Verify(const std::vector< CBLSPublicKey > &allkeys, const Consensus::LLMQParams &params) const
std::vector< bool > validMembers
std::vector< bool > signers
void ProcessMessage(CNode *pfrom, CDataStream &vRecv, int &retMisbehavingScore)
CFinalCommitment commitment
bool empty() const
Definition: prevector.h:281
256-bit opaque blob.
Definition: uint256.h:138
#define INVALID_SOCKET
Definition: compat.h:64
BOOST_AUTO_TEST_SUITE(cuckoocache_tests)
Test Suite for CuckooCache.
BOOST_AUTO_TEST_SUITE_END()
std::unique_ptr< CDeterministicMNManager > deterministicMNManager
BOOST_FIXTURE_TEST_CASE(dip3_protx, TestChain400Setup)
CMutableTransaction CreateNullQfcTx(const uint256 &quorumHash, int nHeight)
std::map< COutPoint, std::pair< int, CAmount > > SimpleUTXOMap
CMutableTransaction CreateQfcTx(const uint256 &quorumHash, int nHeight, Optional< llmq::CFinalCommitment > opt_qfc)
CService ip(uint32_t i)
uint256 SerializeHash(const T &obj, int nType=SER_GETHASH, int nVersion=PROTOCOL_VERSION)
Compute the 256-bit hash of an object's serialization.
Definition: hash.h:289
bool VerifyScript(const CScript &scriptSig, const CScript &scriptPubKey, unsigned int flags, const BaseSignatureChecker &checker, SigVersion sigversion, ScriptError *serror)
@ SIGHASH_ALL
Definition: interpreter.h:24
@ LOCK
Definition: lockunlock.h:16
CMasternodePayments masternodePayments
Object for who's going to get paid on which blocks.
uint256 BlockMerkleRoot(const CBlock &block, bool *mutated)
Definition: merkle.cpp:154
UniValue spork(const JSONRPCRequest &request)
Definition: misc.cpp:286
@ SAPLING
Definition: logging.h:63
@ UPGRADE_V3_4
Definition: params.h:34
@ UPGRADE_V6_0
Definition: params.h:41
@ UPGRADE_POS
Definition: params.h:28
LLMQType
Definition: params.h:90
@ LLMQ_TEST
Definition: params.h:98
uint256 BuildCommitmentHash(Consensus::LLMQType llmqType, const uint256 &blockHash, const std::vector< bool > &validMembers, const CBLSPublicKey &pubKey, const uint256 &vvecHash)
std::unique_ptr< CQuorumBlockProcessor > quorumBlockProcessor
int NodeId
Definition: net.h:109
RecursiveMutex cs_main
Global state.
Definition: validation.cpp:80
CService LookupNumeric(const std::string &name, int portDefault)
Definition: netbase.cpp:209
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
#define BOOST_CHECK(expr)
Definition: object.cpp:17
boost::optional< T > Optional
Substitute for C++17 std::optional.
Definition: optional.h:12
@ NODE_NONE
Definition: protocol.h:314
@ NODE_NETWORK
Definition: protocol.h:318
uint256 GetRandHash() noexcept
Definition: random.cpp:596
@ operatorKey
Definition: rpcevo.cpp:47
@ operatorReward
Definition: rpcevo.cpp:46
@ proTxHash
Definition: rpcevo.cpp:50
@ ownerKey
Definition: rpcevo.cpp:49
@ SER_NETWORK
Definition: serialize.h:174
bool SignSignature(const CKeyStore &keystore, const CScript &fromPubKey, CMutableTransaction &txTo, unsigned int nIn, const CAmount &amount, int nHashType, bool fColdStake)
Produce a script signature for a transaction.
Definition: sign.cpp:192
bool VerifyLLMQCommitment(const llmq::CFinalCommitment &qfc, const CBlockIndex *pindexPrev, CValidationState &state)
bool CheckSpecialTx(const CTransaction &tx, const CBlockIndex *pindexPrev, const CCoinsViewCache *view, CValidationState &state)
Payload validity checks (including duplicate unique properties against list at pindexPrev)
uint256 CalcTxInputsHash(const CTransaction &tx)
bool ProcessSpecialTxsInBlock(const CBlock &block, const CBlockIndex *pindex, const CCoinsViewCache *view, CValidationState &state, bool fJustCheck)
CSporkManager sporkManager
Definition: spork.cpp:29
@ SPORK_22_LLMQ_DKG_MAINTENANCE
Definition: sporkid.h:29
@ SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT
Definition: sporkid.h:18
@ SPORK_21_LEGACY_MNS_MAX_HEIGHT
Definition: sporkid.h:28
CScript GetScriptForRawPubKey(const CPubKey &pubKey)
Generate a P2PK script for the given pubkey.
Definition: standard.cpp:286
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a PIVX scriptPubKey for the given CTxDestination.
Definition: standard.cpp:278
A mutable version of CTransaction.
Definition: transaction.h:409
uint256 GetHash() const
Compute the hash of this CMutableTransaction.
Definition: transaction.cpp:96
SigVersion GetRequiredSigVersion() const
Definition: transaction.h:450
std::vector< CTxOut > vout
Definition: transaction.h:411
std::vector< CTxIn > vin
Definition: transaction.h:410
static constexpr int NO_ACTIVATION_HEIGHT
Special value for nActivationHeight indicating that the upgrade will never activate.
Definition: params.h:74
std::map< LLMQType, LLMQParams > llmqs
Definition: params.h:279
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:247
TierTwoSyncState g_tiertwo_sync_state
#define MASTERNODE_SYNC_FINISHED
#define strprintf
Definition: tinyformat.h:1056
void SetTxPayload(CMutableTransaction &tx, const T &payload)
Definition: transaction.h:486
bool GetTxPayload(const std::vector< unsigned char > &payload, T &obj)
Definition: transaction.h:464
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:456
const uint256 UINT256_ZERO
constant uint256 instances
Definition: uint256.h:175
const uint256 UINT256_ONE
Definition: uint256.h:176
int64_t GetTime()
DEPRECATED Use either GetSystemTimeInSeconds (not mockable) or GetTime<T> (mockable)
Definition: utiltime.cpp:27
bool GetTransaction(const uint256 &hash, CTransactionRef &txOut, uint256 &hashBlock, bool fAllowSlow, CBlockIndex *blockIndex)
Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock.
Definition: validation.cpp:671
CTxMemPool mempool(::minRelayTxFee)
bool ProcessNewBlock(const std::shared_ptr< const CBlock > &pblock, const FlatFilePos *dbp)
Process an incoming block.
std::unique_ptr< CCoinsViewCache > pcoinsTip
Global variable that points to the active CCoinsView (protected by cs_main)
Definition: validation.cpp:206
int64_t GetMasternodePayment(int nHeight)
Definition: validation.cpp:852
CAmount GetBlockValue(int nHeight)
Definition: validation.cpp:816
BlockMap mapBlockIndex
Definition: validation.cpp:82
bool AcceptToMemoryPool(CTxMemPool &pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree, bool *pfMissingInputs, bool fOverrideMempoolLimit, bool fRejectInsaneFee, bool ignoreFees)
(try to) add transaction to memory pool
Definition: validation.cpp:649
CChain chainActive
The currently-connected chain of blocks (protected by cs_main).
Definition: validation.cpp:84