LCOV - code coverage report
Current view: top level - src/wallet/test - wallet_sapling_transactions_validations_tests.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 85 85 100.0 %
Date: 2025-02-23 09:33:43 Functions: 7 7 100.0 %

          Line data    Source code
       1             : // Copyright (c) 2020-2021 The PIVX Core developers
       2             : // Distributed under the MIT software license, see the accompanying
       3             : // file COPYING or https://www.opensource.org/licenses/mit-license.php.
       4             : 
       5             : #include "wallet/test/wallet_test_fixture.h"
       6             : 
       7             : #include "primitives/block.h"
       8             : #include "sapling/transaction_builder.h"
       9             : #include "sapling/sapling_operation.h"
      10             : #include "wallet/wallet.h"
      11             : 
      12             : #include <boost/test/unit_test.hpp>
      13             : 
      14             : /*
      15             :  * A text fixture with a preloaded 100-blocks regtest chain, with sapling activating at block 101,
      16             :  * and a wallet containing the key used for the coinbase outputs.
      17             :  */
      18             : struct TestSaplingChainSetup: public TestChain100Setup
      19             : {
      20             :     std::unique_ptr<CWallet> pwalletMain;
      21             : 
      22           1 :     TestSaplingChainSetup() : TestChain100Setup()
      23             :     {
      24           1 :         initZKSNARKS(); // init zk-snarks lib
      25             : 
      26           1 :         bool fFirstRun;
      27           1 :         pwalletMain = std::make_unique<CWallet>("testWallet", WalletDatabase::CreateMock());
      28           1 :         pwalletMain->LoadWallet(fFirstRun);
      29           1 :         RegisterValidationInterface(pwalletMain.get());
      30             : 
      31           1 :         int SAPLING_ACTIVATION_HEIGHT = 101;
      32           1 :         UpdateNetworkUpgradeParameters(Consensus::UPGRADE_V5_0, SAPLING_ACTIVATION_HEIGHT);
      33             : 
      34             :         // setup wallet
      35           1 :         {
      36           1 :             LOCK(pwalletMain->cs_wallet);
      37           1 :             pwalletMain->SetMinVersion(FEATURE_SAPLING);
      38           2 :             gArgs.ForceSetArg("-keypool", "5");
      39           1 :             pwalletMain->SetupSPKM(true);
      40             : 
      41             :             // import coinbase key used to generate the 100-blocks chain
      42           2 :             BOOST_CHECK(pwalletMain->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()));
      43             :         }
      44           2 :         WalletRescanReserver reserver(pwalletMain.get());
      45           2 :         BOOST_CHECK(reserver.reserve());
      46           1 :         pwalletMain->RescanFromTime(0, reserver, true /* update */);
      47           1 :     }
      48             : 
      49           1 :     ~TestSaplingChainSetup()
      50           2 :     {
      51           1 :         UnregisterValidationInterface(pwalletMain.get());
      52           1 :     }
      53             : };
      54             : 
      55             : BOOST_FIXTURE_TEST_SUITE(wallet_sapling_transactions_validations_tests, TestSaplingChainSetup)
      56             : 
      57           3 : static SaplingOperation createOperationAndBuildTx(std::unique_ptr<CWallet>& pwallet,
      58             :                                                   std::vector<SendManyRecipient> recipients,
      59             :                                                   bool selectTransparentCoins)
      60             : {
      61             :     // Create the operation
      62           3 :     SaplingOperation operation(Params().GetConsensus(), pwallet.get());
      63           3 :     auto operationResult = operation.setRecipients(recipients)
      64             :             ->setSelectTransparentCoins(selectTransparentCoins)
      65           3 :             ->setSelectShieldedCoins(!selectTransparentCoins)
      66           3 :             ->build();
      67           3 :     BOOST_ASSERT_MSG(operationResult, operationResult.getError().c_str());
      68             : 
      69           6 :     CValidationState state;
      70           3 :     BOOST_ASSERT_MSG(
      71             :             CheckTransaction(operation.getFinalTx(), state, true),
      72             :             "Invalid Sapling transaction");
      73           6 :     return operation;
      74             : }
      75             : 
      76             : // Test double spend notes in the mempool and in blocks.
      77           2 : BOOST_AUTO_TEST_CASE(test_in_block_and_mempool_notes_double_spend)
      78             : {
      79           1 :     auto ret = pwalletMain->getNewAddress("coinbase");
      80           2 :     BOOST_CHECK(ret);
      81           2 :     CTxDestination coinbaseDest = *ret.getObjResult();
      82           1 :     BOOST_ASSERT_MSG(ret, "cannot create address");
      83           1 :     BOOST_ASSERT_MSG(IsValidDestination(coinbaseDest), "invalid destination");
      84           1 :     BOOST_ASSERT_MSG(IsMine(*pwalletMain, coinbaseDest), "destination not from wallet");
      85             : 
      86             :     // create the chain
      87           3 :     int tipHeight = WITH_LOCK(cs_main, return chainActive.Tip()->nHeight);
      88           1 :     BOOST_CHECK_EQUAL(tipHeight, 100);
      89           2 :     const CScript& scriptPubKey = GetScriptForDestination(coinbaseDest);
      90           1 :     int nGenerateBlocks = 110;
      91          11 :     for (int i = tipHeight; i < nGenerateBlocks; ++i) {
      92          10 :         CreateAndProcessBlock({}, scriptPubKey);
      93          10 :         SyncWithValidationInterfaceQueue();
      94             :     }
      95             : 
      96             :     // Verify that we are at block 110
      97           3 :     tipHeight = WITH_LOCK(cs_main, return chainActive.Tip()->nHeight);
      98           1 :     BOOST_CHECK_EQUAL(tipHeight, nGenerateBlocks);
      99             :     // Verify that the wallet has all of the coins
     100           1 :     BOOST_CHECK_EQUAL(pwalletMain->GetAvailableBalance(), CAmount(250 * COIN * 10)); // 10 blocks available
     101           1 :     BOOST_CHECK_EQUAL(pwalletMain->GetImmatureBalance(), CAmount(250 * COIN * 100)); // 100 blocks immature
     102             : 
     103             :     // Now that we have the chain, let's shield 100 PIVs
     104             :     // single recipient
     105           2 :     std::vector<SendManyRecipient> recipients;
     106           1 :     libzcash::SaplingPaymentAddress pa = pwalletMain->GenerateNewSaplingZKey("sapling1");
     107           1 :     recipients.emplace_back(pa, CAmount(100 * COIN), "", false);
     108             : 
     109             :     // Create the operation and build the transaction
     110           1 :     SaplingOperation operation = createOperationAndBuildTx(pwalletMain, recipients, true);
     111             :     // broadcast the tx to the network
     112           2 :     std::string retHash;
     113           1 :     BOOST_ASSERT_MSG(operation.send(retHash), "error committing and broadcasting the transaction");
     114             : 
     115             :     // Generate a five blocks to fully confirm the tx and test balance
     116           6 :     for (int i = 1; i <= 5; ++i) {
     117           5 :         CreateAndProcessBlock({}, scriptPubKey, false /*fNoMempoolTx*/);
     118             :     }
     119           1 :     SyncWithValidationInterfaceQueue();
     120           1 :     BOOST_CHECK_EQUAL(pwalletMain->GetAvailableShieldedBalance(), CAmount(100 * COIN)); // 100 shield PIVs
     121           1 :     BOOST_CHECK_EQUAL(pwalletMain->GetUnconfirmedShieldedBalance(), CAmount(0)); // 0 shield PIVs
     122             : 
     123             :     // ##############################################
     124             :     // Context set!
     125             :     // Now let's try to double spend the same note twice in the same block
     126             : 
     127             :     // first generate a valid tx spending only one note
     128             :     // Create the operation and build the transaction
     129           2 :     auto res = pwalletMain->getNewAddress("receiveValid");
     130           2 :     BOOST_CHECK(res);
     131           2 :     CTxDestination tDest2 = *res.getObjResult();
     132           2 :     std::vector<SendManyRecipient> recipients2;
     133           1 :     recipients2.emplace_back(tDest2, CAmount(90 * COIN), false);
     134           1 :     SaplingOperation operation2 = createOperationAndBuildTx(pwalletMain, recipients2, false);
     135             : 
     136             :     // Create a second transaction that spends the same note with a different output now
     137           2 :     res = pwalletMain->getNewAddress("receiveInvalid");
     138           2 :     BOOST_CHECK(res);
     139           2 :     CTxDestination tDest3 = *res.getObjResult();;
     140           2 :     std::vector<SendManyRecipient> recipients3;
     141           1 :     recipients3.emplace_back(tDest3, CAmount(5 * COIN), false);
     142           1 :     SaplingOperation operation3 = createOperationAndBuildTx(pwalletMain, recipients3, false);
     143             : 
     144             :     // Now that both transactions were created, broadcast the first one
     145           2 :     std::string retTxHash2;
     146           2 :     BOOST_CHECK(operation2.send(retTxHash2));
     147             : 
     148             :     // Now broadcast the second one, this one should fail when tried to enter in the mempool as there is already another note spending the same nullifier
     149           2 :     std::string retTxHash3;
     150           2 :     auto opResult3 = operation3.send(retTxHash3);
     151           2 :     BOOST_CHECK(!opResult3);
     152           4 :     BOOST_CHECK(opResult3.getError().find("bad-txns-nullifier-double-spent"));
     153             : 
     154             :     // let's now test it inside a block
     155             :     // create the block with the two transactions and test validity
     156           3 :     const CBlock& block = CreateAndProcessBlock({operation3.getFinalTx()}, scriptPubKey, false /*fNoMempoolTx*/);
     157           1 :     SyncWithValidationInterfaceQueue();
     158             : 
     159           1 :     {
     160           2 :         LOCK(cs_main);
     161             :         // Same tip as before, no block connection
     162           3 :         BOOST_CHECK(chainActive.Tip()->GetBlockHash() != block.GetHash());
     163           2 :         BOOST_ASSERT_MSG(chainActive.Tip()->nHeight, 115);
     164             :     }
     165           1 : }
     166             : 
     167             : BOOST_AUTO_TEST_SUITE_END()

Generated by: LCOV version 1.14