diff --git a/src/Makefile.test.include b/src/Makefile.test.include index fa6765a08..91f461631 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -89,6 +89,8 @@ BITCOIN_TESTS =\ test/crypto_tests.cpp \ test/cuckoocache_tests.cpp \ test/DoS_tests.cpp \ + test/evo_deterministicmns_tests.cpp \ + test/evo_simplifiedmns_tests.cpp \ test/getarg_tests.cpp \ test/governance_validators_tests.cpp \ test/hash_tests.cpp \ diff --git a/src/evo/simplifiedmns.cpp b/src/evo/simplifiedmns.cpp index 0be8f689c..0bf79924b 100644 --- a/src/evo/simplifiedmns.cpp +++ b/src/evo/simplifiedmns.cpp @@ -45,6 +45,15 @@ void CSimplifiedMNListEntry::ToJson(UniValue& obj) const obj.push_back(Pair("isValid", isValid)); } +CSimplifiedMNList::CSimplifiedMNList(const std::vector& smlEntries) +{ + mnList = smlEntries; + + std::sort(mnList.begin(), mnList.end(), [&](const CSimplifiedMNListEntry& a, const CSimplifiedMNListEntry& b) { + return a.proRegTxHash.Compare(b.proRegTxHash) < 0; + }); +} + CSimplifiedMNList::CSimplifiedMNList(const CDeterministicMNList& dmnList) { mnList.reserve(dmnList.all_count()); diff --git a/src/evo/simplifiedmns.h b/src/evo/simplifiedmns.h index 00a701b65..64465dda8 100644 --- a/src/evo/simplifiedmns.h +++ b/src/evo/simplifiedmns.h @@ -54,6 +54,7 @@ public: public: CSimplifiedMNList() {} + CSimplifiedMNList(const std::vector& smlEntries); CSimplifiedMNList(const CDeterministicMNList& dmnList); uint256 CalcMerkleRoot(bool *pmutated = NULL) const; diff --git a/src/test/evo_deterministicmns_tests.cpp b/src/test/evo_deterministicmns_tests.cpp new file mode 100644 index 000000000..f64650eb7 --- /dev/null +++ b/src/test/evo_deterministicmns_tests.cpp @@ -0,0 +1,394 @@ +// Copyright (c) 2018 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "test/test_dash.h" + +#include "script/interpreter.h" +#include "script/standard.h" +#include "script/sign.h" +#include "validation.h" +#include "base58.h" +#include "netbase.h" +#include "messagesigner.h" +#include "keystore.h" +#include "spork.h" + +#include "evo/specialtx.h" +#include "evo/providertx.h" +#include "evo/deterministicmns.h" + +#include + +static const CBitcoinAddress payoutAddress ("yRq1Ky1AfFmf597rnotj7QRxsDUKePVWNF"); +static const std::string payoutKey ("cV3qrPWzDcnhzRMV4MqtTH4LhNPqPo26ZntGvfJhc8nqCi8Ae5xR"); + +typedef std::map> SimpleUTXOMap; + +static SimpleUTXOMap BuildSimpleUtxoMap(const std::vector& txs) +{ + SimpleUTXOMap utxos; + for (size_t i = 0; i < txs.size(); i++) { + auto& tx = txs[i]; + for (size_t j = 0; j < tx.vout.size(); j++) { + utxos.emplace(COutPoint(tx.GetHash(), j), std::make_pair((int)i + 1, tx.vout[j].nValue)); + } + } + return utxos; +} + +static std::vector SelectUTXOs(SimpleUTXOMap& utoxs, CAmount amount, CAmount& changeRet) +{ + changeRet = 0; + + std::vector selectedUtxos; + CAmount selectedAmount = 0; + while (!utoxs.empty()) { + bool found = false; + for (auto it = utoxs.begin(); it != utoxs.end(); ++it) { + if (chainActive.Height() - it->second.first < 101) { + continue; + } + + found = true; + selectedAmount += it->second.second; + selectedUtxos.emplace_back(it->first); + utoxs.erase(it); + break; + } + BOOST_ASSERT(found); + if (selectedAmount >= amount) { + changeRet = selectedAmount - amount; + break; + } + } + + return selectedUtxos; +} + +static void FundTransaction(CMutableTransaction& tx, SimpleUTXOMap& utoxs, const CScript& scriptPayout, CAmount amount, const CKey& coinbaseKey) +{ + CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; + + CAmount change; + auto inputs = SelectUTXOs(utoxs, amount, change); + for (size_t i = 0; i < inputs.size(); i++) { + tx.vin.emplace_back(CTxIn(inputs[i])); + } + tx.vout.emplace_back(CTxOut(amount, scriptPayout)); + if (change != 0) { + tx.vout.emplace_back(CTxOut(change, scriptPayout)); + } +} + +static void SignTransaction(CMutableTransaction& tx, const CKey& coinbaseKey) +{ + CBasicKeyStore tempKeystore; + tempKeystore.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); + + for (size_t i = 0; i < tx.vin.size(); i++) { + CTransactionRef txFrom; + uint256 hashBlock; + BOOST_ASSERT(GetTransaction(tx.vin[i].prevout.hash, txFrom, Params().GetConsensus(), hashBlock)); + BOOST_ASSERT(SignSignature(tempKeystore, *txFrom, tx, i)); + } +} + +static CMutableTransaction CreateProRegTx(SimpleUTXOMap& utxos, int port, const CScript& scriptPayout, const CKey& coinbaseKey, CKey& mnKeyRet) +{ + mnKeyRet.MakeNewKey(true); + + CAmount change; + auto inputs = SelectUTXOs(utxos, 1000 * COIN, change); + + CProRegTx proTx; + proTx.nProtocolVersion = PROTOCOL_VERSION; + proTx.nCollateralIndex = 0; + proTx.addr = LookupNumeric("1.1.1.1", port); + proTx.keyIDOwner = mnKeyRet.GetPubKey().GetID(); + proTx.keyIDOperator = mnKeyRet.GetPubKey().GetID(); + proTx.keyIDVoting = mnKeyRet.GetPubKey().GetID(); + proTx.scriptPayout = scriptPayout; + + CMutableTransaction tx; + tx.nVersion = 3; + tx.nType = TRANSACTION_PROVIDER_REGISTER; + FundTransaction(tx, utxos, scriptPayout, 1000 * COIN, coinbaseKey); + proTx.inputsHash = CalcTxInputsHash(tx); + CHashSigner::SignHash(::SerializeHash(proTx), mnKeyRet, proTx.vchSig); + SetTxPayload(tx, proTx); + SignTransaction(tx, coinbaseKey); + + return tx; +} + +static CMutableTransaction CreateProUpServTx(SimpleUTXOMap& utxos, const uint256& proTxHash, const CKey& mnKey, int port, const CScript& scriptOperatorPayout, const CKey& coinbaseKey) +{ + CAmount change; + auto inputs = SelectUTXOs(utxos, 1 * COIN, change); + + CProUpServTx proTx; + proTx.proTxHash = proTxHash; + proTx.nProtocolVersion = PROTOCOL_VERSION; + proTx.addr = LookupNumeric("1.1.1.1", port); + proTx.scriptOperatorPayout = scriptOperatorPayout; + + CMutableTransaction tx; + tx.nVersion = 3; + tx.nType = TRANSACTION_PROVIDER_UPDATE_SERVICE; + FundTransaction(tx, utxos, GetScriptForDestination(coinbaseKey.GetPubKey().GetID()), 1 * COIN, coinbaseKey); + proTx.inputsHash = CalcTxInputsHash(tx); + CHashSigner::SignHash(::SerializeHash(proTx), mnKey, proTx.vchSig); + SetTxPayload(tx, proTx); + SignTransaction(tx, coinbaseKey); + + return tx; +} + +static CMutableTransaction CreateProUpRegTx(SimpleUTXOMap& utxos, const uint256& proTxHash, const CKey& mnKey, const CKeyID& keyIDOperator, const CKeyID& keyIDVoting, const CScript& scriptPayout, const CKey& coinbaseKey) +{ + CAmount change; + auto inputs = SelectUTXOs(utxos, 1 * COIN, change); + + CProUpRegTx proTx; + proTx.proTxHash = proTxHash; + proTx.keyIDOperator = keyIDOperator; + proTx.keyIDVoting = keyIDVoting; + proTx.scriptPayout = scriptPayout; + + CMutableTransaction tx; + tx.nVersion = 3; + tx.nType = TRANSACTION_PROVIDER_UPDATE_REGISTRAR; + FundTransaction(tx, utxos, GetScriptForDestination(coinbaseKey.GetPubKey().GetID()), 1 * COIN, coinbaseKey); + proTx.inputsHash = CalcTxInputsHash(tx); + CHashSigner::SignHash(::SerializeHash(proTx), mnKey, proTx.vchSig); + SetTxPayload(tx, proTx); + SignTransaction(tx, coinbaseKey); + + return tx; +} + +static CMutableTransaction CreateProUpRevTx(SimpleUTXOMap& utxos, const uint256& proTxHash, const CKey& mnKey, const CKey& coinbaseKey) +{ + CAmount change; + auto inputs = SelectUTXOs(utxos, 1 * COIN, change); + + CProUpRevTx proTx; + proTx.proTxHash = proTxHash; + + CMutableTransaction tx; + tx.nVersion = 3; + tx.nType = TRANSACTION_PROVIDER_UPDATE_REVOKE; + FundTransaction(tx, utxos, GetScriptForDestination(coinbaseKey.GetPubKey().GetID()), 1 * COIN, coinbaseKey); + proTx.inputsHash = CalcTxInputsHash(tx); + CHashSigner::SignHash(::SerializeHash(proTx), mnKey, proTx.vchSig); + SetTxPayload(tx, proTx); + SignTransaction(tx, coinbaseKey); + + return tx; +} + +static CScript GenerateRandomAddress() +{ + CKey key; + key.MakeNewKey(false); + return GetScriptForDestination(key.GetPubKey().GetID()); +} + +static CDeterministicMNCPtr FindPayoutDmn(const CBlock& block) +{ + auto dmnList = deterministicMNManager->GetListAtChainTip(); + + for (auto txout : block.vtx[0]->vout) { + for (auto& dmn : dmnList.valid_range()) { + if (txout.scriptPubKey == dmn->pdmnState->scriptPayout) { + return dmn; + } + } + } + return nullptr; +} + +BOOST_AUTO_TEST_SUITE(evo_dip3_activation_tests) + +BOOST_FIXTURE_TEST_CASE(dip3_activation, TestChainDIP3BeforeActivationSetup) +{ + auto utxos = BuildSimpleUtxoMap(coinbaseTxns); + CKey mnKey; + auto tx = CreateProRegTx(utxos, 1, GetScriptForDestination(payoutAddress.Get()), coinbaseKey, mnKey); + std::vector txns = {tx}; + + int nHeight = chainActive.Height(); + + // We start one block before DIP3 activation, so mining a block with a DIP3 transaction should fail + auto block = std::make_shared(CreateBlock(txns, coinbaseKey)); + ProcessNewBlock(Params(), block, true, nullptr); + BOOST_ASSERT(chainActive.Height() == nHeight); + BOOST_ASSERT(block->GetHash() != chainActive.Tip()->GetBlockHash()); + BOOST_ASSERT(!deterministicMNManager->GetListAtChainTip().HasMN(tx.GetHash())); + + // This block should activate DIP3 + CreateAndProcessBlock({}, coinbaseKey); + BOOST_ASSERT(chainActive.Height() == nHeight + 1); + + // Mining a block with a DIP3 transaction should succeed now + block = std::make_shared(CreateBlock(txns, coinbaseKey)); + ProcessNewBlock(Params(), block, true, nullptr); + deterministicMNManager->UpdatedBlockTip(chainActive.Tip()); + BOOST_ASSERT(chainActive.Height() == nHeight + 2); + BOOST_ASSERT(block->GetHash() == chainActive.Tip()->GetBlockHash()); + BOOST_ASSERT(deterministicMNManager->GetListAtChainTip().HasMN(tx.GetHash())); +} + +BOOST_FIXTURE_TEST_CASE(dip3_protx, TestChainDIP3Setup) +{ + CKey sporkKey; + sporkKey.MakeNewKey(false); + CBitcoinSecret sporkSecret(sporkKey); + CBitcoinAddress sporkAddress; + sporkAddress.Set(sporkKey.GetPubKey().GetID()); + sporkManager.SetSporkAddress(sporkAddress.ToString()); + sporkManager.SetPrivKey(sporkSecret.ToString()); + + auto utxos = BuildSimpleUtxoMap(coinbaseTxns); + + int nHeight = chainActive.Height(); + int port = 1; + + std::vector dmnHashes; + std::map mnKeys; + + // register one MN per block + for (size_t i = 0; i < 6; i++) { + CKey mnKey; + auto tx = CreateProRegTx(utxos, port++, GenerateRandomAddress(), coinbaseKey, mnKey); + dmnHashes.emplace_back(tx.GetHash()); + mnKeys.emplace(tx.GetHash(), mnKey); + CreateAndProcessBlock({tx}, coinbaseKey); + deterministicMNManager->UpdatedBlockTip(chainActive.Tip()); + + BOOST_ASSERT(chainActive.Height() == nHeight + 1); + BOOST_ASSERT(deterministicMNManager->GetListAtChainTip().HasMN(tx.GetHash())); + + nHeight++; + } + + // activate spork15 + sporkManager.UpdateSpork(SPORK_15_DETERMINISTIC_MNS_ENABLED, chainActive.Height() + 1, *g_connman); + CreateAndProcessBlock({}, coinbaseKey); + deterministicMNManager->UpdatedBlockTip(chainActive.Tip()); + nHeight++; + + // check MN reward payments + for (size_t i = 0; i < 20; i++) { + auto dmnExpectedPayee = deterministicMNManager->GetListAtChainTip().GetMNPayee(); + + CBlock block = CreateAndProcessBlock({}, coinbaseKey); + deterministicMNManager->UpdatedBlockTip(chainActive.Tip()); + BOOST_ASSERT(!block.vtx.empty()); + + auto dmnPayout = FindPayoutDmn(block); + BOOST_ASSERT(dmnPayout != nullptr); + BOOST_CHECK_EQUAL(dmnPayout->proTxHash.ToString(), dmnExpectedPayee->proTxHash.ToString()); + + nHeight++; + } + + // register multiple MNs per block + for (size_t i = 0; i < 3; i++) { + std::vector txns; + for (size_t j = 0; j < 3; j++) { + CKey mnKey; + auto tx = CreateProRegTx(utxos, port++, GenerateRandomAddress(), coinbaseKey, mnKey); + dmnHashes.emplace_back(tx.GetHash()); + mnKeys.emplace(tx.GetHash(), mnKey); + txns.emplace_back(tx); + } + CreateAndProcessBlock(txns, coinbaseKey); + deterministicMNManager->UpdatedBlockTip(chainActive.Tip()); + BOOST_ASSERT(chainActive.Height() == nHeight + 1); + + for (size_t j = 0; j < 3; j++) { + BOOST_ASSERT(deterministicMNManager->GetListAtChainTip().HasMN(txns[j].GetHash())); + } + + nHeight++; + } + + // test ProUpServTx + auto tx = CreateProUpServTx(utxos, dmnHashes[0], mnKeys[dmnHashes[0]], 1000, CScript(), coinbaseKey); + CreateAndProcessBlock({tx}, coinbaseKey); + deterministicMNManager->UpdatedBlockTip(chainActive.Tip()); + BOOST_ASSERT(chainActive.Height() == nHeight + 1); + nHeight++; + + auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(dmnHashes[0]); + BOOST_ASSERT(dmn != nullptr && dmn->pdmnState->addr.GetPort() == 1000); + + // test ProUpRevTx + tx = CreateProUpRevTx(utxos, dmnHashes[0], mnKeys[dmnHashes[0]], coinbaseKey); + CreateAndProcessBlock({tx}, coinbaseKey); + deterministicMNManager->UpdatedBlockTip(chainActive.Tip()); + BOOST_ASSERT(chainActive.Height() == nHeight + 1); + nHeight++; + + dmn = deterministicMNManager->GetListAtChainTip().GetMN(dmnHashes[0]); + BOOST_ASSERT(dmn != nullptr && dmn->pdmnState->nPoSeBanHeight == nHeight); + + // test that the revoked MN does not get paid anymore + for (size_t i = 0; i < 20; i++) { + auto dmnExpectedPayee = deterministicMNManager->GetListAtChainTip().GetMNPayee(); + BOOST_ASSERT(dmnExpectedPayee->proTxHash != dmnHashes[0]); + + CBlock block = CreateAndProcessBlock({}, coinbaseKey); + deterministicMNManager->UpdatedBlockTip(chainActive.Tip()); + BOOST_ASSERT(!block.vtx.empty()); + + auto dmnPayout = FindPayoutDmn(block); + BOOST_ASSERT(dmnPayout != nullptr); + BOOST_CHECK_EQUAL(dmnPayout->proTxHash.ToString(), dmnExpectedPayee->proTxHash.ToString()); + + nHeight++; + } + + // test reviving the MN + CKey newOperatorKey; + newOperatorKey.MakeNewKey(false); + dmn = deterministicMNManager->GetListAtChainTip().GetMN(dmnHashes[0]); + tx = CreateProUpRegTx(utxos, dmnHashes[0], mnKeys[dmnHashes[0]], newOperatorKey.GetPubKey().GetID(), newOperatorKey.GetPubKey().GetID(), dmn->pdmnState->scriptPayout, coinbaseKey); + CreateAndProcessBlock({tx}, coinbaseKey); + deterministicMNManager->UpdatedBlockTip(chainActive.Tip()); + BOOST_ASSERT(chainActive.Height() == nHeight + 1); + nHeight++; + + tx = CreateProUpServTx(utxos, dmnHashes[0], newOperatorKey, 100, CScript(), coinbaseKey); + CreateAndProcessBlock({tx}, coinbaseKey); + deterministicMNManager->UpdatedBlockTip(chainActive.Tip()); + BOOST_ASSERT(chainActive.Height() == nHeight + 1); + nHeight++; + + dmn = deterministicMNManager->GetListAtChainTip().GetMN(dmnHashes[0]); + BOOST_ASSERT(dmn != nullptr && dmn->pdmnState->addr.GetPort() == 100); + BOOST_ASSERT(dmn != nullptr && dmn->pdmnState->nPoSeBanHeight == -1); + + // test that the revived MN gets payments again + bool foundRevived = false; + for (size_t i = 0; i < 20; i++) { + auto dmnExpectedPayee = deterministicMNManager->GetListAtChainTip().GetMNPayee(); + if (dmnExpectedPayee->proTxHash == dmnHashes[0]) { + foundRevived = true; + } + + CBlock block = CreateAndProcessBlock({}, coinbaseKey); + deterministicMNManager->UpdatedBlockTip(chainActive.Tip()); + BOOST_ASSERT(!block.vtx.empty()); + + auto dmnPayout = FindPayoutDmn(block); + BOOST_ASSERT(dmnPayout != nullptr); + BOOST_CHECK_EQUAL(dmnPayout->proTxHash.ToString(), dmnExpectedPayee->proTxHash.ToString()); + + nHeight++; + } + BOOST_ASSERT(foundRevived); +} +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/evo_simplifiedmns_tests.cpp b/src/test/evo_simplifiedmns_tests.cpp new file mode 100644 index 000000000..a3684cb3b --- /dev/null +++ b/src/test/evo_simplifiedmns_tests.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2018 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "test/test_dash.h" + +#include "evo/simplifiedmns.h" +#include "netbase.h" + +#include + +BOOST_FIXTURE_TEST_SUITE(evo_simplifiedmns_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(simplifiedmns_merkleroots) +{ + std::vector entries; + for (size_t i = 0; i < 15; i++) { + CSimplifiedMNListEntry smle; + smle.proRegTxHash.SetHex(strprintf("%064x", i)); + + std::string ip = strprintf("%d.%d.%d.%d", 0, 0, 0, i); + Lookup(ip.c_str(), smle.service, i, false); + + smle.keyIDOperator.SetHex(strprintf("%040x", i)); + smle.keyIDVoting.SetHex(strprintf("%040x", i)); + smle.isValid = true; + + entries.emplace_back(smle); + } + + std::vector expectedHashes = { + "aa8bfb825f433bcd6f1039f27c77ed269386e05577b0fe9afc4e16b1af0076b2", + "686a19dba9b515f77f11027cd1e92e6a8c650448bf4616101fd5ddbe6e2629e7", + "c2efc1b08daa791c71e1d5887be3eaa136381f783fcc5b7efdc5909db38701bb", + "ce394197d6e1684467fbf2e1619f71ae9d1a6cf6548b2235e4289f95d4bccbbd", + "aeeaf7b498aa7d5fa92ee0028499b4f165c31662f5e9b0a80e6e13b38fd61f8d", + "0c1c8dc9dc82eb5432a557580e5d3d930943ce0d0db5daebc51267afb46b6d48", + "1c4add10ea844a46734473e48c2f781059b35382219d0cf67d6432b540e0bbbe", + "1ae1ad5ff4dd4c09469d21d569a025d467dca1e407581a2815175528e139b7da", + "d59b231cdc80ce7eda3a3f37608abda818659c189d31a7ef42024d496e290cbc", + "2d5e6c87e3d4e5b3fdd600f561e8dec1ea720560569398006050480232f1257c", + "3d6af35f08efeea22f3c8fcb78038e56dac221f3173ca4e2230ea8ae3cbd3c60", + "ecf547077c37b79da954c4ef46a3c4fb136746366bfb81192ed01de96fd66348", + "626af5fb8192ead7bbd79ad7bfe2c3ea82714fdfd9ac49b88d7a411aa6956853", + "6c84a4485fb2ba35b4dcd4d89cbdd3d813446514bb7a2046b6b1b9813beaac0f", + "453ca2a83140da73a37794fe6fddd701ea5066f21c2f1df8a33b6ff6134043c3", + }; + std::vector calculatedHashes; + + for (auto& smle : entries) { + calculatedHashes.emplace_back(smle.CalcHash().ToString()); + //printf("\"%s\",\n", calculatedHashes.back().c_str()); + } + + BOOST_CHECK(expectedHashes == calculatedHashes); + + CSimplifiedMNList sml(entries); + + std::string expectedMerkleRoot = "926efc8dc7b5b060254b102670b918133fea67c5e1bc2703d596e49672878c22"; + std::string calculatedMerkleRoot = sml.CalcMerkleRoot(nullptr).ToString(); + + BOOST_CHECK(expectedMerkleRoot == calculatedMerkleRoot); +} +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/test_dash.cpp b/src/test/test_dash.cpp index 5eb785557..a62d1b319 100644 --- a/src/test/test_dash.cpp +++ b/src/test/test_dash.cpp @@ -24,7 +24,9 @@ #include "test/testutil.h" +#include "evo/specialtx.h" #include "evo/deterministicmns.h" +#include "evo/cbtx.h" #include @@ -100,12 +102,12 @@ TestingSetup::~TestingSetup() boost::filesystem::remove_all(pathTemp); } -TestChain100Setup::TestChain100Setup() : TestingSetup(CBaseChainParams::REGTEST) +TestChainSetup::TestChainSetup(int blockCount) : TestingSetup(CBaseChainParams::REGTEST) { // Generate a 100-block chain: coinbaseKey.MakeNewKey(true); - CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; - for (int i = 0; i < COINBASE_MATURITY; i++) + CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; + for (int i = 0; i < blockCount; i++) { std::vector noTxns; CBlock b = CreateAndProcessBlock(noTxns, scriptPubKey); @@ -118,7 +120,25 @@ TestChain100Setup::TestChain100Setup() : TestingSetup(CBaseChainParams::REGTEST) // scriptPubKey, and try to add it to the current chain. // CBlock -TestChain100Setup::CreateAndProcessBlock(const std::vector& txns, const CScript& scriptPubKey) +TestChainSetup::CreateAndProcessBlock(const std::vector& txns, const CScript& scriptPubKey) +{ + const CChainParams& chainparams = Params(); + auto block = CreateBlock(txns, scriptPubKey); + + std::shared_ptr shared_pblock = std::make_shared(block); + ProcessNewBlock(chainparams, shared_pblock, true, NULL); + + CBlock result = block; + return result; +} + +CBlock TestChainSetup::CreateAndProcessBlock(const std::vector& txns, const CKey& scriptKey) +{ + CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; + return CreateAndProcessBlock(txns, scriptPubKey); +} + +CBlock TestChainSetup::CreateBlock(const std::vector& txns, const CScript& scriptPubKey) { const CChainParams& chainparams = Params(); std::unique_ptr pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey); @@ -128,20 +148,39 @@ TestChain100Setup::CreateAndProcessBlock(const std::vector& block.vtx.resize(1); BOOST_FOREACH(const CMutableTransaction& tx, txns) block.vtx.push_back(MakeTransactionRef(tx)); + + // Manually update CbTx as we modified the block here + if (block.vtx[0]->nType == TRANSACTION_COINBASE) { + CCbTx cbTx; + if (!GetTxPayload(*block.vtx[0], cbTx)) { + BOOST_ASSERT(false); + } + CValidationState state; + if (!CalcCbTxMerkleRootMNList(block, chainActive.Tip(), cbTx.merkleRootMNList, state)) { + BOOST_ASSERT(false); + } + CMutableTransaction tmpTx = *block.vtx[0]; + SetTxPayload(tmpTx, cbTx); + block.vtx[0] = MakeTransactionRef(tmpTx); + } + // IncrementExtraNonce creates a valid coinbase and merkleRoot unsigned int extraNonce = 0; IncrementExtraNonce(&block, chainActive.Tip(), extraNonce); while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce; - std::shared_ptr shared_pblock = std::make_shared(block); - ProcessNewBlock(chainparams, shared_pblock, true, NULL); - CBlock result = block; return result; } -TestChain100Setup::~TestChain100Setup() +CBlock TestChainSetup::CreateBlock(const std::vector& txns, const CKey& scriptKey) +{ + CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; + return CreateBlock(txns, scriptPubKey); +} + +TestChainSetup::~TestChainSetup() { } diff --git a/src/test/test_dash.h b/src/test/test_dash.h index 0bb1717a8..c95167522 100644 --- a/src/test/test_dash.h +++ b/src/test/test_dash.h @@ -43,24 +43,44 @@ class CBlock; struct CMutableTransaction; class CScript; -// -// Testing fixture that pre-creates a -// 100-block REGTEST-mode block chain -// -struct TestChain100Setup : public TestingSetup { - TestChain100Setup(); +struct TestChainSetup : public TestingSetup +{ + TestChainSetup(int blockCount); + ~TestChainSetup(); // Create a new block with just given transactions, coinbase paying to // scriptPubKey, and try to add it to the current chain. CBlock CreateAndProcessBlock(const std::vector& txns, const CScript& scriptPubKey); - - ~TestChain100Setup(); + CBlock CreateAndProcessBlock(const std::vector& txns, + const CKey& scriptKey); + CBlock CreateBlock(const std::vector& txns, + const CScript& scriptPubKey); + CBlock CreateBlock(const std::vector& txns, + const CKey& scriptKey); std::vector coinbaseTxns; // For convenience, coinbase transactions CKey coinbaseKey; // private/public key needed to spend coinbase transactions }; +// +// Testing fixture that pre-creates a +// 100-block REGTEST-mode block chain +// +struct TestChain100Setup : public TestChainSetup { + TestChain100Setup() : TestChainSetup(100) {} +}; + +struct TestChainDIP3Setup : public TestChainSetup +{ + TestChainDIP3Setup() : TestChainSetup(431) {} +}; + +struct TestChainDIP3BeforeActivationSetup : public TestChainSetup +{ + TestChainDIP3BeforeActivationSetup() : TestChainSetup(430) {} +}; + class CTxMemPoolEntry; class CTxMemPool;