|
15 | 15 | #include <policy/policy.h>
|
16 | 16 | #include <rpc/server.h>
|
17 | 17 | #include <test/util/logging.h>
|
| 18 | +#include <test/util/mining.h> |
18 | 19 | #include <test/util/setup_common.h>
|
19 | 20 | #include <util/translation.h>
|
20 | 21 | #include <validation.h>
|
21 | 22 | #include <wallet/coincontrol.h>
|
22 | 23 | #include <wallet/context.h>
|
| 24 | +#include <wallet/feebumper.h> |
23 | 25 | #include <wallet/receive.h>
|
24 | 26 | #include <wallet/spend.h>
|
25 | 27 | #include <wallet/test/util.h>
|
@@ -1002,5 +1004,126 @@ BOOST_FIXTURE_TEST_CASE(wallet_sync_tx_invalid_state_test, TestingSetup)
|
1002 | 1004 | HasReason("DB error adding transaction to wallet, write failed"));
|
1003 | 1005 | }
|
1004 | 1006 |
|
| 1007 | +BOOST_FIXTURE_TEST_CASE(deniability_tests, TestChain100Setup) |
| 1008 | +{ |
| 1009 | + CKey walletKey; |
| 1010 | + // Make a new wallet key |
| 1011 | + walletKey.MakeNewKey(true); |
| 1012 | + // Mine a block with the wallet key |
| 1013 | + MineBlock(m_node, GetScriptForRawPubKey(walletKey.GetPubKey())); |
| 1014 | + |
| 1015 | + // Mine 100 blocks to mature the wallet coinbase tx |
| 1016 | + mineBlocks(COINBASE_MATURITY); |
| 1017 | + |
| 1018 | + // Make a wallet with the wallet key |
| 1019 | + auto wallet = CreateSyncedWallet(*m_node.chain, WITH_LOCK(Assert(m_node.chainman)->GetMutex(), return m_node.chainman->ActiveChain()), walletKey); |
| 1020 | + |
| 1021 | + std::map<CTxDestination, std::vector<COutput>> list; |
| 1022 | + { |
| 1023 | + LOCK(wallet->cs_wallet); |
| 1024 | + list = ListCoins(*wallet); |
| 1025 | + } |
| 1026 | + // We expect a single coinbase (mining reward) transaction |
| 1027 | + BOOST_CHECK_EQUAL(list.size(), 1u); |
| 1028 | + const auto& outputs = list.begin()->second; |
| 1029 | + BOOST_CHECK_EQUAL(outputs.size(), 1u); |
| 1030 | + const auto& output = outputs.front(); |
| 1031 | + CAmount initial_amount = WITH_LOCK(wallet->cs_wallet, return AvailableCoins(*wallet).GetTotalAmount()); |
| 1032 | + |
| 1033 | + // Verify the deniabilization cycle calculation detects the coinbase transactions correctly |
| 1034 | + auto cycles_res = CalculateDeniabilizationCycles(*wallet, output.outpoint); |
| 1035 | + unsigned int deniabilization_cycles = cycles_res.first; |
| 1036 | + bool from_coinbase_tx = cycles_res.second; |
| 1037 | + BOOST_CHECK_EQUAL(deniabilization_cycles, 0u); |
| 1038 | + BOOST_CHECK_EQUAL(from_coinbase_tx, true); |
| 1039 | + |
| 1040 | + // Prepare the deniabilize transaction |
| 1041 | + std::set<COutPoint> inputs; |
| 1042 | + inputs.emplace(output.outpoint); |
| 1043 | + const unsigned int confirm_target = 6; |
| 1044 | + bool sign = !wallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS); |
| 1045 | + CTransactionRef tx; |
| 1046 | + CAmount tx_fee = 0; |
| 1047 | + { |
| 1048 | + bool insufficient_amount = false; |
| 1049 | + auto res = CreateDeniabilizationTransaction(*wallet, inputs, confirm_target, deniabilization_cycles, sign, insufficient_amount); |
| 1050 | + BOOST_CHECK(res); |
| 1051 | + tx = res->tx; |
| 1052 | + tx_fee = res->fee; |
| 1053 | + } |
| 1054 | + // Commit the deniabilization transaction |
| 1055 | + { |
| 1056 | + LOCK(wallet->cs_wallet); |
| 1057 | + wallet->SetBroadcastTransactions(true); |
| 1058 | + wallet->CommitTransaction(tx, {}, {}); |
| 1059 | + } |
| 1060 | + |
| 1061 | + // Get the resulting coins |
| 1062 | + { |
| 1063 | + LOCK(wallet->cs_wallet); |
| 1064 | + list = ListCoins(*wallet); |
| 1065 | + } |
| 1066 | + // We expect to have two unique addresses, with one output each |
| 1067 | + BOOST_CHECK_EQUAL(list.size(), 2U); |
| 1068 | + for (const auto& pair : list) { |
| 1069 | + BOOST_CHECK_EQUAL(pair.second.size(), 1u); |
| 1070 | + const auto& output = pair.second.front(); |
| 1071 | + // Check the deniabilization count is 1 |
| 1072 | + auto cycles_res = CalculateDeniabilizationCycles(*wallet, output.outpoint); |
| 1073 | + unsigned int deniabilization_cycles = cycles_res.first; |
| 1074 | + bool from_coinbase_tx = cycles_res.second; |
| 1075 | + BOOST_CHECK_EQUAL(deniabilization_cycles, 1u); |
| 1076 | + BOOST_CHECK_EQUAL(from_coinbase_tx, true); |
| 1077 | + } |
| 1078 | + |
| 1079 | + // Check the resulting amount matches the initial amount (minus the tx fee) |
| 1080 | + CAmount post_tx_amount = WITH_LOCK(wallet->cs_wallet, return AvailableCoins(*wallet).GetTotalAmount()); |
| 1081 | + BOOST_CHECK_EQUAL(initial_amount, post_tx_amount + tx_fee); |
| 1082 | + |
| 1083 | + // Bump the min fee to test fee bumping |
| 1084 | + wallet->m_min_fee = CFeeRate(10000); |
| 1085 | + |
| 1086 | + // Prepare a fee bump transaction |
| 1087 | + CTransactionRef new_tx; |
| 1088 | + CAmount old_fee = 0; |
| 1089 | + CAmount new_fee = 0; |
| 1090 | + { |
| 1091 | + bilingual_str error; |
| 1092 | + auto res = feebumper::CreateRateBumpDeniabilizationTransaction(*wallet, tx->GetHash(), confirm_target, sign, error, old_fee, new_fee, new_tx); |
| 1093 | + BOOST_CHECK_EQUAL(res, feebumper::Result::OK); |
| 1094 | + } |
| 1095 | + // Verify the calculated old fee matches what we paid |
| 1096 | + BOOST_CHECK_EQUAL(tx_fee, old_fee); |
| 1097 | + // Verify the new fee is larger than the old fee |
| 1098 | + BOOST_CHECK_GT(new_fee, old_fee); |
| 1099 | + |
| 1100 | + // Commit the fee bump transaction |
| 1101 | + std::vector<bilingual_str> errors; |
| 1102 | + uint256 new_hash; |
| 1103 | + auto commit_res = feebumper::CommitTransaction(*wallet, tx->GetHash(), CMutableTransaction(*new_tx), errors, new_hash); |
| 1104 | + BOOST_CHECK_EQUAL(commit_res, feebumper::Result::OK); |
| 1105 | + |
| 1106 | + // mine a block to confirm the transactions, so we can get the available balance |
| 1107 | + MineBlock(m_node, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); |
| 1108 | + { |
| 1109 | + LOCK(wallet->cs_wallet); |
| 1110 | + LOCK(Assert(m_node.chainman)->GetMutex()); |
| 1111 | + wallet->SetLastBlockProcessed(wallet->GetLastBlockHeight() + 1, m_node.chainman->ActiveChain().Tip()->GetBlockHash()); |
| 1112 | + } |
| 1113 | + // Sync the wallet |
| 1114 | + { |
| 1115 | + WalletRescanReserver reserver(*wallet); |
| 1116 | + reserver.reserve(); |
| 1117 | + CWallet::ScanResult result = wallet->ScanForWalletTransactions(Params().GetConsensus().hashGenesisBlock, /*start_height=*/0, /*max_height=*/{}, reserver, /*fUpdate=*/true, /*save_progress=*/false); |
| 1118 | + BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS); |
| 1119 | + BOOST_CHECK_EQUAL(result.last_scanned_block, WITH_LOCK(m_node.chainman->GetMutex(), return m_node.chainman->ActiveChain().Tip()->GetBlockHash())); |
| 1120 | + BOOST_CHECK(result.last_failed_block.IsNull()); |
| 1121 | + } |
| 1122 | + |
| 1123 | + // Check the resulting amount matches the initial amount (minus the new tx fee) |
| 1124 | + CAmount post_bump_amount = WITH_LOCK(wallet->cs_wallet, return AvailableCoins(*wallet).GetTotalAmount()); |
| 1125 | + BOOST_CHECK_EQUAL(initial_amount, post_bump_amount + new_fee); |
| 1126 | +} |
| 1127 | + |
1005 | 1128 | BOOST_AUTO_TEST_SUITE_END()
|
1006 | 1129 | } // namespace wallet
|
0 commit comments