PIVX Core  5.6.99
P2P Digital Currency
wallet_sapling_transactions_validations_tests.cpp
Go to the documentation of this file.
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 
6 
7 #include "primitives/block.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  */
19 {
20  std::unique_ptr<CWallet> pwalletMain;
21 
23  {
24  initZKSNARKS(); // init zk-snarks lib
25 
26  bool fFirstRun;
27  pwalletMain = std::make_unique<CWallet>("testWallet", WalletDatabase::CreateMock());
28  pwalletMain->LoadWallet(fFirstRun);
30 
31  int SAPLING_ACTIVATION_HEIGHT = 101;
32  UpdateNetworkUpgradeParameters(Consensus::UPGRADE_V5_0, SAPLING_ACTIVATION_HEIGHT);
33 
34  // setup wallet
35  {
36  LOCK(pwalletMain->cs_wallet);
37  pwalletMain->SetMinVersion(FEATURE_SAPLING);
38  gArgs.ForceSetArg("-keypool", "5");
39  pwalletMain->SetupSPKM(true);
40 
41  // import coinbase key used to generate the 100-blocks chain
43  }
44  WalletRescanReserver reserver(pwalletMain.get());
45  BOOST_CHECK(reserver.reserve());
46  pwalletMain->RescanFromTime(0, reserver, true /* update */);
47  }
48 
50  {
52  }
53 };
54 
55 BOOST_FIXTURE_TEST_SUITE(wallet_sapling_transactions_validations_tests, TestSaplingChainSetup)
56 
57 static SaplingOperation createOperationAndBuildTx(std::unique_ptr<CWallet>& pwallet,
58  std::vector<SendManyRecipient> recipients,
59  bool selectTransparentCoins)
60 {
61  // Create the operation
62  SaplingOperation operation(Params().GetConsensus(), pwallet.get());
63  auto operationResult = operation.setRecipients(recipients)
64  ->setSelectTransparentCoins(selectTransparentCoins)
65  ->setSelectShieldedCoins(!selectTransparentCoins)
66  ->build();
67  BOOST_ASSERT_MSG(operationResult, operationResult.getError().c_str());
68 
69  CValidationState state;
70  BOOST_ASSERT_MSG(
71  CheckTransaction(operation.getFinalTx(), state, true),
72  "Invalid Sapling transaction");
73  return operation;
74 }
75 
76 // Test double spend notes in the mempool and in blocks.
77 BOOST_AUTO_TEST_CASE(test_in_block_and_mempool_notes_double_spend)
78 {
79  auto ret = pwalletMain->getNewAddress("coinbase");
80  BOOST_CHECK(ret);
81  CTxDestination coinbaseDest = *ret.getObjResult();
82  BOOST_ASSERT_MSG(ret, "cannot create address");
83  BOOST_ASSERT_MSG(IsValidDestination(coinbaseDest), "invalid destination");
84  BOOST_ASSERT_MSG(IsMine(*pwalletMain, coinbaseDest), "destination not from wallet");
85 
86  // create the chain
87  int tipHeight = WITH_LOCK(cs_main, return chainActive.Tip()->nHeight);
88  BOOST_CHECK_EQUAL(tipHeight, 100);
89  const CScript& scriptPubKey = GetScriptForDestination(coinbaseDest);
90  int nGenerateBlocks = 110;
91  for (int i = tipHeight; i < nGenerateBlocks; ++i) {
92  CreateAndProcessBlock({}, scriptPubKey);
94  }
95 
96  // Verify that we are at block 110
97  tipHeight = WITH_LOCK(cs_main, return chainActive.Tip()->nHeight);
98  BOOST_CHECK_EQUAL(tipHeight, nGenerateBlocks);
99  // Verify that the wallet has all of the coins
100  BOOST_CHECK_EQUAL(pwalletMain->GetAvailableBalance(), CAmount(250 * COIN * 10)); // 10 blocks available
101  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  std::vector<SendManyRecipient> recipients;
106  libzcash::SaplingPaymentAddress pa = pwalletMain->GenerateNewSaplingZKey("sapling1");
107  recipients.emplace_back(pa, CAmount(100 * COIN), "", false);
108 
109  // Create the operation and build the transaction
110  SaplingOperation operation = createOperationAndBuildTx(pwalletMain, recipients, true);
111  // broadcast the tx to the network
112  std::string retHash;
113  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  for (int i = 1; i <= 5; ++i) {
117  CreateAndProcessBlock({}, scriptPubKey, false /*fNoMempoolTx*/);
118  }
120  BOOST_CHECK_EQUAL(pwalletMain->GetAvailableShieldedBalance(), CAmount(100 * COIN)); // 100 shield PIVs
121  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  auto res = pwalletMain->getNewAddress("receiveValid");
130  BOOST_CHECK(res);
131  CTxDestination tDest2 = *res.getObjResult();
132  std::vector<SendManyRecipient> recipients2;
133  recipients2.emplace_back(tDest2, CAmount(90 * COIN), false);
134  SaplingOperation operation2 = createOperationAndBuildTx(pwalletMain, recipients2, false);
135 
136  // Create a second transaction that spends the same note with a different output now
137  res = pwalletMain->getNewAddress("receiveInvalid");
138  BOOST_CHECK(res);
139  CTxDestination tDest3 = *res.getObjResult();;
140  std::vector<SendManyRecipient> recipients3;
141  recipients3.emplace_back(tDest3, CAmount(5 * COIN), false);
142  SaplingOperation operation3 = createOperationAndBuildTx(pwalletMain, recipients3, false);
143 
144  // Now that both transactions were created, broadcast the first one
145  std::string retTxHash2;
146  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  std::string retTxHash3;
150  auto opResult3 = operation3.send(retTxHash3);
151  BOOST_CHECK(!opResult3);
152  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  const CBlock& block = CreateAndProcessBlock({operation3.getFinalTx()}, scriptPubKey, false /*fNoMempoolTx*/);
158 
159  {
160  LOCK(cs_main);
161  // Same tip as before, no block connection
163  BOOST_ASSERT_MSG(chainActive.Tip()->nHeight, 115);
164  }
165 }
166 
int64_t CAmount
Amount in PIV (Can be negative)
Definition: amount.h:13
void UpdateNetworkUpgradeParameters(Consensus::UpgradeIndex idx, int nActivationHeight)
Allows modifying the network upgrade regtest parameters.
const CChainParams & Params()
Return the currently selected parameters.
void ForceSetArg(const std::string &strArg, const std::string &strValue)
Definition: system.cpp:489
static std::unique_ptr< BerkeleyDatabase > CreateMock()
Return object for accessing temporary in-memory database.
Definition: db.h:138
uint256 GetHash() const
Definition: block.cpp:15
Definition: block.h:80
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
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition: key.cpp:186
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:381
Capture information about block/transaction validation.
Definition: validation.h:24
CTransaction getFinalTx()
OperationResult send(std::string &retTxHash)
RAII object to check and reserve a wallet rescan.
Definition: wallet.h:1324
Sapling functions.
Definition: address.h:30
BOOST_AUTO_TEST_SUITE_END()
isminetype IsMine(const CKeyStore &keystore, const CTxDestination &dest)
Definition: ismine.cpp:29
@ LOCK
Definition: lockunlock.h:16
@ UPGRADE_V5_0
Definition: params.h:36
bool IsValidDestination(const CWDestination &address)
RecursiveMutex cs_main
Global state.
Definition: validation.cpp:80
#define BOOST_FIXTURE_TEST_SUITE(a, b)
Definition: object.cpp:14
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
#define BOOST_CHECK(expr)
Definition: object.cpp:17
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a PIVX scriptPubKey for the given CTxDestination.
Definition: standard.cpp:278
boost::variant< CNoDestination, CKeyID, CScriptID, CExchangeKeyID > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:72
CKey coinbaseKey
Definition: test_pivx.h:118
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:247
void initZKSNARKS()
Definition: system.cpp:624
ArgsManager gArgs
Definition: system.cpp:89
bool CheckTransaction(const CTransaction &tx, CValidationState &state, bool fColdStakingActive)
Transaction validation functions.
Definition: tx_verify.cpp:54
CChain chainActive
The currently-connected chain of blocks (protected by cs_main).
Definition: validation.cpp:84
void UnregisterValidationInterface(CValidationInterface *pwalletIn)
Unregister a wallet from core.
void RegisterValidationInterface(CValidationInterface *pwalletIn)
Register a wallet to receive updates from core.
void SyncWithValidationInterfaceQueue()
This is a synonym for the following, which asserts certain locks are not held: std::promise<void> pro...
@ FEATURE_SAPLING
Definition: wallet.h:118
BOOST_AUTO_TEST_CASE(test_in_block_and_mempool_notes_double_spend)