From 7f8fb121ba1cb232a7966070aca9837d1fce491f Mon Sep 17 00:00:00 2001 From: furszy Date: Sat, 31 Jul 2021 19:56:06 -0300 Subject: [PATCH 1/5] Introduce v5.3 network upgrade. --- src/chainparams.cpp | 5 +++++ src/consensus/params.h | 1 + src/consensus/upgrades.cpp | 14 +++++++++----- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 4062bbf815d4b..ba5672c84cee4 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -201,6 +201,8 @@ class CMainParams : public CChainParams consensus.vUpgrades[Consensus::UPGRADE_V4_0].nActivationHeight = 2153200; consensus.vUpgrades[Consensus::UPGRADE_V5_0].nActivationHeight = 2700500; consensus.vUpgrades[Consensus::UPGRADE_V5_2].nActivationHeight = 2927000; + consensus.vUpgrades[Consensus::UPGRADE_V5_3].nActivationHeight = + Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; consensus.vUpgrades[Consensus::UPGRADE_V6_0].nActivationHeight = Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; @@ -340,6 +342,8 @@ class CTestNetParams : public CChainParams consensus.vUpgrades[Consensus::UPGRADE_V4_0].nActivationHeight = 201; consensus.vUpgrades[Consensus::UPGRADE_V5_0].nActivationHeight = 201; consensus.vUpgrades[Consensus::UPGRADE_V5_2].nActivationHeight = 262525; + consensus.vUpgrades[Consensus::UPGRADE_V5_3].nActivationHeight = + Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; consensus.vUpgrades[Consensus::UPGRADE_V6_0].nActivationHeight = Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; @@ -472,6 +476,7 @@ class CRegTestParams : public CChainParams Consensus::NetworkUpgrade::ALWAYS_ACTIVE; consensus.vUpgrades[Consensus::UPGRADE_V5_0].nActivationHeight = 300; consensus.vUpgrades[Consensus::UPGRADE_V5_2].nActivationHeight = 300; + consensus.vUpgrades[Consensus::UPGRADE_V5_3].nActivationHeight = 251; consensus.vUpgrades[Consensus::UPGRADE_V6_0].nActivationHeight = Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; diff --git a/src/consensus/params.h b/src/consensus/params.h index dd34b9cbc9c94..fb1ee3c471bcf 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -35,6 +35,7 @@ enum UpgradeIndex : uint32_t { UPGRADE_V4_0, UPGRADE_V5_0, UPGRADE_V5_2, + UPGRADE_V5_3, UPGRADE_V6_0, UPGRADE_TESTDUMMY, // NOTE: Also add new upgrades to NetworkUpgradeInfo in upgrades.cpp diff --git a/src/consensus/upgrades.cpp b/src/consensus/upgrades.cpp index 68d77e2aee17c..01ccae051d4fb 100644 --- a/src/consensus/upgrades.cpp +++ b/src/consensus/upgrades.cpp @@ -31,7 +31,7 @@ const struct NUInfo NetworkUpgradeInfo[Consensus::MAX_NETWORK_UPGRADES] = { }, { /*.strName =*/ "Zerocoin_v2", - /*.strInfo =*/ "new zerocoin serials and zPOS start", + /*.strInfo =*/ "New zerocoin serials and zPOS start", }, { /*.strName =*/ "BIP65", @@ -39,15 +39,15 @@ const struct NUInfo NetworkUpgradeInfo[Consensus::MAX_NETWORK_UPGRADES] = { }, { /*.strName =*/ "Zerocoin_Public", - /*.strInfo =*/ "activation of zerocoin public spends (spend v3)", + /*.strInfo =*/ "Activation of zerocoin public spends (spend v3)", }, { /*.strName =*/ "PIVX_v3.4", - /*.strInfo =*/ "new 256-bit stake modifier - start block v6", + /*.strInfo =*/ "New 256-bit stake modifier - start block v6", }, { /*.strName =*/ "PIVX_v4.0", - /*.strInfo =*/ "new message sigs - start block v7 - time protocol - zc spend v4", + /*.strInfo =*/ "New message sigs - start block v7 - time protocol - zc spend v4", }, { /*.strName =*/ "v5_shield", @@ -55,7 +55,11 @@ const struct NUInfo NetworkUpgradeInfo[Consensus::MAX_NETWORK_UPGRADES] = { }, { /*.strName =*/ "PIVX_v5.2", - /*.strInfo =*/ "new cold-staking rules", + /*.strInfo =*/ "New cold-staking rules", + }, + { + /*.strName =*/ "PIVX_v5.3", + /*.strInfo =*/ "New staking rules", }, { /*.strName =*/ "v6_evo", From e98c1e853ede9382e2d7cf1610938ab21b0c9249 Mon Sep 17 00:00:00 2001 From: furszy Date: Sat, 31 Jul 2021 20:19:57 -0300 Subject: [PATCH 2/5] Add and guard new max time window for mnb and mnp sigtime. --- src/masternode-payments.cpp | 22 ++++++++++++++-------- src/masternode-payments.h | 2 +- src/masternode.cpp | 25 ++++++++++++++++--------- src/masternode.h | 6 +++--- src/masternodeman.cpp | 6 +++--- src/test/budget_tests.cpp | 2 +- 6 files changed, 38 insertions(+), 25 deletions(-) diff --git a/src/masternode-payments.cpp b/src/masternode-payments.cpp index a700e9e884745..a3231981b84e4 100644 --- a/src/masternode-payments.cpp +++ b/src/masternode-payments.cpp @@ -161,11 +161,11 @@ std::string CMasternodePaymentWinner::GetStrMessage() const return vinMasternode.prevout.ToStringShort() + std::to_string(nBlockHeight) + HexStr(payee); } -bool CMasternodePaymentWinner::IsValid(CNode* pnode, std::string& strError) +bool CMasternodePaymentWinner::IsValid(CNode* pnode, std::string& strError, int chainHeight) { int n = mnodeman.GetMasternodeRank(vinMasternode, nBlockHeight - 100); - - if (n < 1 || n > MNPAYMENTS_SIGNATURES_TOTAL) { + bool guard = Params().GetConsensus().NetworkUpgradeActive(chainHeight, Consensus::UPGRADE_V5_3); + if ((guard && n < 1) || n > MNPAYMENTS_SIGNATURES_TOTAL) { //It's common to have masternodes mistakenly think they are in the top 10 // We don't want to print all of these messages, or punish them unless they're way off if (n > MNPAYMENTS_SIGNATURES_TOTAL * 2) { @@ -176,6 +176,12 @@ bool CMasternodePaymentWinner::IsValid(CNode* pnode, std::string& strError) return false; } + // Must be a P2PKH + if (guard && !payee.IsPayToPublicKeyHash()) { + LogPrint(BCLog::MASTERNODE, "%s - payee must be a P2PKH\n", __func__); + return false; + } + return true; } @@ -219,9 +225,9 @@ bool IsBlockValueValid(int nHeight, CAmount& nExpectedValue, CAmount nMinted, CA } } - // !todo: remove after V6 enforcement and default it to true - const bool isV6UpgradeEnforced = consensus.NetworkUpgradeActive(nHeight, Consensus::UPGRADE_V6_0); - return (!isV6UpgradeEnforced || nMinted >= 0) && nMinted <= nExpectedValue; + // !todo: remove after V5.3 enforcement and default it to true + const bool isUpgradeEnforced = consensus.NetworkUpgradeActive(nHeight, Consensus::UPGRADE_V5_3); + return (!isUpgradeEnforced || nMinted >= 0) && nMinted <= nExpectedValue; } bool IsBlockPayeeValid(const CBlock& block, const CBlockIndex* pindexPrev) @@ -326,7 +332,7 @@ bool CMasternodePayments::GetLegacyMasternodeTxOut(int nHeight, std::vector sigTime) { // TODO: lock cs. Need to be careful as mnb.lastPing.CheckAndUpdate locks cs_main internally. @@ -147,7 +154,7 @@ bool CMasternode::UpdateFromNewBroadcast(CMasternodeBroadcast& mnb) protocolVersion = mnb.protocolVersion; addr = mnb.addr; int nDoS = 0; - if (mnb.lastPing.IsNull() || (!mnb.lastPing.IsNull() && mnb.lastPing.CheckAndUpdate(nDoS, false))) { + if (mnb.lastPing.IsNull() || (!mnb.lastPing.IsNull() && mnb.lastPing.CheckAndUpdate(nDoS, chainHeight, false))) { lastPing = mnb.lastPing; mnodeman.mapSeenMasternodePing.emplace(lastPing.GetHash(), lastPing); } @@ -393,10 +400,10 @@ bool CMasternodeBroadcast::CheckDefaultPort(CService service, std::string& strEr return true; } -bool CMasternodeBroadcast::CheckAndUpdate(int& nDos) +bool CMasternodeBroadcast::CheckAndUpdate(int& nDos, int nChainHeight) { // make sure signature isn't in the future (past is OK) - if (sigTime > GetAdjustedTime() + 60 * 60) { + if (sigTime > GetMaxTimeWindow(nChainHeight)) { LogPrint(BCLog::MASTERNODE,"mnb - Signature rejected, too far into the future %s\n", vin.prevout.hash.ToString()); nDos = 1; return false; @@ -444,7 +451,7 @@ bool CMasternodeBroadcast::CheckAndUpdate(int& nDos) return false; // incorrect ping or its sigTime - if(lastPing.IsNull() || !lastPing.CheckAndUpdate(nDos, false, true)) { + if(lastPing.IsNull() || !lastPing.CheckAndUpdate(nDos, nChainHeight, false, true)) { return false; } @@ -470,7 +477,7 @@ bool CMasternodeBroadcast::CheckAndUpdate(int& nDos) if (pmn->pubKeyCollateralAddress == pubKeyCollateralAddress && !pmn->IsBroadcastedWithin(MasternodeBroadcastSeconds())) { //take the newest entry LogPrint(BCLog::MASTERNODE,"mnb - Got updated entry for %s\n", vin.prevout.hash.ToString()); - if (pmn->UpdateFromNewBroadcast((*this))) { + if (pmn->UpdateFromNewBroadcast((*this), nChainHeight)) { if (pmn->IsEnabled()) Relay(); } masternodeSync.AddedMasternodeList(GetHash()); @@ -491,7 +498,7 @@ bool CMasternodeBroadcast::CheckInputsAndAdd(int nChainHeight, int& nDoS) } // incorrect ping or its sigTime - if(lastPing.IsNull() || !lastPing.CheckAndUpdate(nDoS, false, true)) { + if(lastPing.IsNull() || !lastPing.CheckAndUpdate(nDoS, nChainHeight, false, true)) { return false; } @@ -589,9 +596,9 @@ std::string CMasternodePing::GetStrMessage() const return vin.ToString() + blockHash.ToString() + std::to_string(sigTime); } -bool CMasternodePing::CheckAndUpdate(int& nDos, bool fRequireAvailable, bool fCheckSigTimeOnly) +bool CMasternodePing::CheckAndUpdate(int& nDos, int nChainHeight, bool fRequireAvailable, bool fCheckSigTimeOnly) { - if (sigTime > GetAdjustedTime() + 60 * 60) { + if (sigTime > GetMaxTimeWindow(nChainHeight)) { LogPrint(BCLog::MNPING,"%s: Signature rejected, too far into the future %s\n", __func__, vin.prevout.hash.ToString()); nDos = 30; return false; diff --git a/src/masternode.h b/src/masternode.h index 8dc5812b88f5e..0e717a10ae384 100644 --- a/src/masternode.h +++ b/src/masternode.h @@ -58,7 +58,7 @@ class CMasternodePing : public CSignedMessage const CTxIn GetVin() const { return vin; }; bool IsNull() const { return blockHash.IsNull() || vin.prevout.IsNull(); } - bool CheckAndUpdate(int& nDos, bool fRequireAvailable = true, bool fCheckSigTimeOnly = false); + bool CheckAndUpdate(int& nDos, int nChainHeight, bool fRequireAvailable = true, bool fCheckSigTimeOnly = false); void Relay(); CMasternodePing& operator=(const CMasternodePing& other) = default; @@ -156,7 +156,7 @@ class CMasternode : public CSignedMessage Unserialize(s); } - bool UpdateFromNewBroadcast(CMasternodeBroadcast& mnb); + bool UpdateFromNewBroadcast(CMasternodeBroadcast& mnb, int chainHeight); CMasternode::state GetActiveState() const; @@ -243,7 +243,7 @@ class CMasternodeBroadcast : public CMasternode CMasternodeBroadcast(CService newAddr, CTxIn newVin, CPubKey newPubkey, CPubKey newPubkey2, int protocolVersionIn, const CMasternodePing& _lastPing); CMasternodeBroadcast(const CMasternode& mn); - bool CheckAndUpdate(int& nDoS); + bool CheckAndUpdate(int& nDoS, int nChainHeight); bool CheckInputsAndAdd(int chainHeight, int& nDos); uint256 GetHash() const; diff --git a/src/masternodeman.cpp b/src/masternodeman.cpp index 441d44695c142..8fd10a466eb33 100644 --- a/src/masternodeman.cpp +++ b/src/masternodeman.cpp @@ -713,7 +713,7 @@ int CMasternodeMan::ProcessMNBroadcast(CNode* pfrom, CMasternodeBroadcast& mnb) } int nDoS = 0; - if (!mnb.CheckAndUpdate(nDoS)) { + if (!mnb.CheckAndUpdate(nDoS, GetBestHeight())) { return nDoS; } @@ -747,7 +747,7 @@ int CMasternodeMan::ProcessMNPing(CNode* pfrom, CMasternodePing& mnp) if (mapSeenMasternodePing.count(mnpHash)) return 0; //seen int nDoS = 0; - if (mnp.CheckAndUpdate(nDoS)) return 0; + if (mnp.CheckAndUpdate(nDoS, GetBestHeight())) return 0; if (nDoS > 0) { // if anything significant failed, mark that node @@ -894,7 +894,7 @@ void CMasternodeMan::UpdateMasternodeList(CMasternodeBroadcast& mnb) CMasternode mn(mnb); Add(mn); } else { - pmn->UpdateFromNewBroadcast(mnb); + pmn->UpdateFromNewBroadcast(mnb, GetBestHeight()); } } diff --git a/src/test/budget_tests.cpp b/src/test/budget_tests.cpp index 076b183a4fff0..b8044d89d8b2c 100644 --- a/src/test/budget_tests.cpp +++ b/src/test/budget_tests.cpp @@ -136,7 +136,7 @@ BOOST_FIXTURE_TEST_CASE(block_value_undermint, RegTestingSetup) CAmount nBudgetAmtRet = 0; // under-minting blocks are invalid after v6 BOOST_CHECK(IsBlockValueValid(nHeight, nExpectedRet, -1, nBudgetAmtRet)); - UpdateNetworkUpgradeParameters(Consensus::UPGRADE_V6_0, Consensus::NetworkUpgrade::ALWAYS_ACTIVE); + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_V5_3, Consensus::NetworkUpgrade::ALWAYS_ACTIVE); BOOST_CHECK(!IsBlockValueValid(nHeight, nExpectedRet, -1, nBudgetAmtRet)); } From a195c2e867bf2df8978d4ffcf632ead2b6ca8eff Mon Sep 17 00:00:00 2001 From: furszy Date: Sat, 31 Jul 2021 21:58:42 -0300 Subject: [PATCH 3/5] [Test] Introduce PoS testing suite. --- src/Makefile.test.include | 4 ++- src/test/CMakeLists.txt | 2 ++ src/wallet/test/pos_test_fixture.cpp | 40 ++++++++++++++++++++++++++++ src/wallet/test/pos_test_fixture.h | 24 +++++++++++++++++ 4 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 src/wallet/test/pos_test_fixture.cpp create mode 100644 src/wallet/test/pos_test_fixture.h diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 54efcc8b9f166..986025eb716b3 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -84,7 +84,9 @@ BITCOIN_TEST_SUITE = \ if ENABLE_WALLET BITCOIN_TEST_SUITE += \ wallet/test/wallet_test_fixture.cpp \ - wallet/test/wallet_test_fixture.h + wallet/test/wallet_test_fixture.h \ + wallet/test/pos_test_fixture.cpp \ + wallet/test/pos_test_fixture.h endif # test_pivx binary # diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index cd0e81583d830..cad742bb43bde 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -88,6 +88,8 @@ set(BITCOIN_TEST_SUITE ${CMAKE_CURRENT_SOURCE_DIR}/librust/sapling_test_fixture.cpp ${CMAKE_SOURCE_DIR}/src/wallet/test/wallet_test_fixture.h ${CMAKE_SOURCE_DIR}/src/wallet/test/wallet_test_fixture.cpp + ${CMAKE_SOURCE_DIR}/src/wallet/test/pos_test_fixture.h + ${CMAKE_SOURCE_DIR}/src/wallet/test/pos_test_fixture.cpp ${CMAKE_CURRENT_SOURCE_DIR}/librust/utiltest.h ${CMAKE_CURRENT_SOURCE_DIR}/librust/utiltest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/librust/json_test_vectors.h diff --git a/src/wallet/test/pos_test_fixture.cpp b/src/wallet/test/pos_test_fixture.cpp new file mode 100644 index 0000000000000..85dae892e1cb0 --- /dev/null +++ b/src/wallet/test/pos_test_fixture.cpp @@ -0,0 +1,40 @@ +// Copyright (c) 2021 The PIVX developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php. + +#include "wallet/test/pos_test_fixture.h" +#include "wallet/wallet.h" + +#include + +TestPoSChainSetup::TestPoSChainSetup() : TestChainSetup(0) +{ + initZKSNARKS(); // init zk-snarks lib + + bool fFirstRun; + pwalletMain = std::make_unique("testWallet", CWalletDBWrapper::CreateMock()); + pwalletMain->LoadWallet(fFirstRun); + RegisterValidationInterface(pwalletMain.get()); + + { + LOCK(pwalletMain->cs_wallet); + pwalletMain->SetMinVersion(FEATURE_SAPLING); + gArgs.ForceSetArg("-keypool", "5"); + pwalletMain->SetupSPKM(true); + + // import coinbase key used to generate the 100-blocks chain + BOOST_CHECK(pwalletMain->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey())); + } + + int posActivation = Params().GetConsensus().vUpgrades[Consensus::UPGRADE_POS].nActivationHeight - 1; + for (int i = 0; i < posActivation; i++) { + CBlock b = CreateAndProcessBlock({}, coinbaseKey); + coinbaseTxns.emplace_back(*b.vtx[0]); + } +} + +TestPoSChainSetup::~TestPoSChainSetup() +{ + SyncWithValidationInterfaceQueue(); + UnregisterValidationInterface(pwalletMain.get()); +} diff --git a/src/wallet/test/pos_test_fixture.h b/src/wallet/test/pos_test_fixture.h new file mode 100644 index 0000000000000..8948c8d83b27d --- /dev/null +++ b/src/wallet/test/pos_test_fixture.h @@ -0,0 +1,24 @@ +// Copyright (c) 2021 The PIVX developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php. + +#ifndef PIVX_POS_TEST_FIXTURE_H +#define PIVX_POS_TEST_FIXTURE_H + +#include "test/test_pivx.h" + +class CWallet; + +/* + * A text fixture with a preloaded 250-blocks regtest chain running running on PoS + * and a wallet containing the key used for the coinbase outputs. + */ +struct TestPoSChainSetup: public TestChainSetup +{ + std::unique_ptr pwalletMain; + + TestPoSChainSetup(); + ~TestPoSChainSetup(); +}; + +#endif // PIVX_POS_TEST_FIXTURE_H From 599bd42de29d8c049f8d22257f26c05305f914d3 Mon Sep 17 00:00:00 2001 From: furszy Date: Sat, 31 Jul 2021 22:01:22 -0300 Subject: [PATCH 4/5] Test: Add coinstake multi-inputs and multi-empty-outputs test coverage. --- src/Makefile.test.include | 3 +- src/test/CMakeLists.txt | 1 + src/wallet/test/pos_validations_tests.cpp | 91 +++++++++++++++++++++++ 3 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 src/wallet/test/pos_validations_tests.cpp diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 986025eb716b3..50866b607902f 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -175,7 +175,8 @@ SAPLING_TESTS +=\ test/librust/sapling_rpc_wallet_tests.cpp \ test/librust/sapling_wallet_tests.cpp \ wallet/test/wallet_shielded_balances_tests.cpp \ - wallet/test/wallet_sapling_transactions_validations_tests.cpp + wallet/test/wallet_sapling_transactions_validations_tests.cpp \ + wallet/test/pos_validations_tests.cpp endif test_test_pivx_SOURCES = $(BITCOIN_TEST_SUITE) $(BITCOIN_TESTS) $(SAPLING_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES) diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index cad742bb43bde..3865a69579189 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -176,6 +176,7 @@ set(BITCOIN_TESTS ${CMAKE_SOURCE_DIR}/src/wallet/test/crypto_tests.cpp ${CMAKE_SOURCE_DIR}/src/wallet/test/wallet_shielded_balances_tests.cpp ${CMAKE_SOURCE_DIR}/src/wallet/test/wallet_sapling_transactions_validations_tests.cpp + ${CMAKE_SOURCE_DIR}/src/wallet/test/pos_validations_tests.cpp ) set(test_test_pivx_SOURCES ${BITCOIN_TEST_SUITE} ${BITCOIN_TESTS} ${JSON_TEST_FILES}) diff --git a/src/wallet/test/pos_validations_tests.cpp b/src/wallet/test/pos_validations_tests.cpp new file mode 100644 index 0000000000000..1dd1d83949caa --- /dev/null +++ b/src/wallet/test/pos_validations_tests.cpp @@ -0,0 +1,91 @@ +// Copyright (c) 2020 The PIVX developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php. + +#include "wallet/test/pos_test_fixture.h" + +#include "blockassembler.h" +#include "util/blockstatecatcher.h" +#include "blocksignature.h" +#include "consensus/merkle.h" +#include "primitives/block.h" +#include "script/sign.h" +#include "test/util/blocksutil.h" +#include "wallet/wallet.h" + +#include + +BOOST_AUTO_TEST_SUITE(pos_validations_tests) + +void reSignTx(CMutableTransaction& mtx, + const std::vector& txPrevOutputs, + CWallet* wallet) +{ + CTransaction txNewConst(mtx); + for (int index=0; index < (int) txPrevOutputs.size(); index++) { + const CTxOut& prevOut = txPrevOutputs.at(index); + SignatureData sigdata; + BOOST_ASSERT(ProduceSignature( + TransactionSignatureCreator(wallet, &txNewConst, index, prevOut.nValue, SIGHASH_ALL), + prevOut.scriptPubKey, + sigdata, + txNewConst.GetRequiredSigVersion(), + true)); + UpdateTransaction(mtx, index, sigdata); + } +} + +BOOST_FIXTURE_TEST_CASE(coinstake_tests, TestPoSChainSetup) +{ + // Verify that we are at block 251 + BOOST_CHECK_EQUAL(WITH_LOCK(cs_main, return chainActive.Tip()->nHeight), 250); + SyncWithValidationInterfaceQueue(); + + // Let's create the block + std::vector availableCoins; + BOOST_CHECK(pwalletMain->StakeableCoins(&availableCoins)); + std::unique_ptr pblocktemplate = BlockAssembler( + Params(), false).CreateNewBlock(CScript(), + pwalletMain.get(), + true, + &availableCoins, + true); + std::shared_ptr pblock = std::make_shared(pblocktemplate->block); + BOOST_CHECK(pblock->IsProofOfStake()); + + // Add a second input to a coinstake + CMutableTransaction mtx(*pblock->vtx[1]); + const CStakeableOutput& in2 = availableCoins.back(); + availableCoins.pop_back(); + CTxIn vin2(in2.tx->GetHash(), in2.i); + mtx.vin.emplace_back(vin2); + + CTxOut prevOutput1 = pwalletMain->GetWalletTx(mtx.vin[0].prevout.hash)->tx->vout[mtx.vin[0].prevout.n]; + std::vector txPrevOutputs{prevOutput1, in2.tx->tx->vout[in2.i]}; + + reSignTx(mtx, txPrevOutputs, pwalletMain.get()); + pblock->vtx[1] = MakeTransactionRef(mtx); + pblock->hashMerkleRoot = BlockMerkleRoot(*pblock); + BOOST_CHECK(SignBlock(*pblock, *pwalletMain)); + ProcessBlockAndCheckRejectionReason(pblock, "bad-cs-multi-inputs", 250); + + // Check multi-empty-outputs now + pblock = std::make_shared(pblocktemplate->block); + mtx = CMutableTransaction(*pblock->vtx[1]); + for (int i = 0; i < 999; ++i) { + mtx.vout.emplace_back(); + mtx.vout.back().SetEmpty(); + } + reSignTx(mtx, {prevOutput1}, pwalletMain.get()); + pblock->vtx[1] = MakeTransactionRef(mtx); + pblock->hashMerkleRoot = BlockMerkleRoot(*pblock); + BOOST_CHECK(SignBlock(*pblock, *pwalletMain)); + ProcessBlockAndCheckRejectionReason(pblock, "bad-txns-vout-empty", 250); + + // Now connect the proper block + pblock = std::make_shared(pblocktemplate->block); + ProcessNewBlock(pblock, nullptr); + BOOST_CHECK_EQUAL(WITH_LOCK(cs_main, return chainActive.Tip()->GetBlockHash()), pblock->GetHash()); +} + +BOOST_AUTO_TEST_SUITE_END() From 1355b20dfe139766097ffb97834c92d0d6bcb974 Mon Sep 17 00:00:00 2001 From: furszy Date: Sat, 31 Jul 2021 22:02:22 -0300 Subject: [PATCH 5/5] Validation: Add new multi-inputs and multi-empty-outputs coinstake rules. --- src/validation.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/validation.cpp b/src/validation.cpp index 70423773650f0..9e662f5e119df 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2935,6 +2935,22 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn } } + bool fActiveV5_3 = chainparams.GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_V5_3); + if (fActiveV5_3 && block.IsProofOfStake()) { + CTransactionRef csTx = block.vtx[1]; + if (csTx->vin.size() > 1) { + return state.DoS(100, false, REJECT_INVALID, "bad-cs-multi-inputs", false, + "invalid multi-inputs coinstake"); + } + + // Prevent multi-empty-outputs + for (int i=1; ivout.size(); i++ ) { + if (csTx->vout[i].IsEmpty()) { + return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-empty"); + } + } + } + return true; }