Skip to content

Commit 92b64d8

Browse files
committed
Added basic deniability unit tests to wallet_tests
1 parent 246589f commit 92b64d8

File tree

1 file changed

+123
-0
lines changed

1 file changed

+123
-0
lines changed

src/wallet/test/wallet_tests.cpp

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@
1515
#include <policy/policy.h>
1616
#include <rpc/server.h>
1717
#include <test/util/logging.h>
18+
#include <test/util/mining.h>
1819
#include <test/util/setup_common.h>
1920
#include <util/translation.h>
2021
#include <validation.h>
2122
#include <wallet/coincontrol.h>
2223
#include <wallet/context.h>
24+
#include <wallet/feebumper.h>
2325
#include <wallet/receive.h>
2426
#include <wallet/spend.h>
2527
#include <wallet/test/util.h>
@@ -1002,5 +1004,126 @@ BOOST_FIXTURE_TEST_CASE(wallet_sync_tx_invalid_state_test, TestingSetup)
10021004
HasReason("DB error adding transaction to wallet, write failed"));
10031005
}
10041006

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+
10051128
BOOST_AUTO_TEST_SUITE_END()
10061129
} // namespace wallet

0 commit comments

Comments
 (0)