Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Makefile.test.include
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ BITCOIN_TESTS =\
test/policyestimator_tests.cpp \
test/pow_tests.cpp \
test/prevector_tests.cpp \
test/privatesend_tests.cpp \
test/raii_event_tests.cpp \
test/random_tests.cpp \
test/ratecheck_tests.cpp \
Expand Down Expand Up @@ -106,6 +105,7 @@ BITCOIN_TESTS =\

if ENABLE_WALLET
BITCOIN_TESTS += \
test/privatesend_tests.cpp \
wallet/test/wallet_test_fixture.cpp \
wallet/test/wallet_test_fixture.h \
wallet/test/accounting_tests.cpp \
Expand Down
168 changes: 168 additions & 0 deletions src/test/privatesend_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
#include <test/test_dash.h>

#include <amount.h>
#include <consensus/validation.h>
#include <privatesend/privatesend-util.h>
#include <privatesend/privatesend.h>
#include <validation.h>
#include <wallet/wallet.h>

#include <boost/test/unit_test.hpp>

Expand All @@ -26,4 +30,168 @@ BOOST_AUTO_TEST_CASE(ps_collatoral_tests)
BOOST_CHECK(!CPrivateSend::IsCollateralAmount(0.00100001 * COIN));
}

class CTransactionBuilderTestSetup : public TestChain100Setup
{
public:
CTransactionBuilderTestSetup()
{
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
wallet = MakeUnique<CWallet>("mock", WalletDatabase::CreateMock());
bool firstRun;
wallet->LoadWallet(firstRun);
AddWallet(wallet.get());
{
LOCK(wallet->cs_wallet);
wallet->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
}
WalletRescanReserver reserver(wallet.get());
reserver.reserve();
wallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver);
}

~CTransactionBuilderTestSetup()
{
RemoveWallet(wallet.get());
}

std::unique_ptr<CWallet> wallet;

CWalletTx& AddTxToChain(uint256 nTxHash)
{
assert(wallet->mapWallet.find(nTxHash) != wallet->mapWallet.end());
CMutableTransaction blocktx;
{
LOCK(wallet->cs_wallet);
blocktx = CMutableTransaction(*wallet->mapWallet.at(nTxHash).tx);
}
CreateAndProcessBlock({blocktx}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
LOCK(cs_main);
LOCK(wallet->cs_wallet);
auto it = wallet->mapWallet.find(blocktx.GetHash());
BOOST_CHECK(it != wallet->mapWallet.end());
it->second.SetMerkleBranch(chainActive.Tip(), 1);
return it->second;
}
CompactTallyItem GetTallyItem(const std::vector<CAmount>& vecAmounts)
{
CompactTallyItem tallyItem;
CWalletTx tx;
CReserveKey destKey(wallet.get());
CReserveKey reserveKey(wallet.get());
CAmount nFeeRet;
int nChangePosRet = -1;
std::string strError;
CCoinControl coinControl;
CPubKey pubKey;
BOOST_CHECK(destKey.GetReservedKey(pubKey, false));
tallyItem.txdest = pubKey.GetID();

for (CAmount nAmount : vecAmounts) {
BOOST_CHECK(wallet->CreateTransaction({{GetScriptForDestination(tallyItem.txdest), nAmount, false}}, tx, reserveKey, nFeeRet, nChangePosRet, strError, coinControl));
CValidationState state;
BOOST_CHECK(wallet->CommitTransaction(tx, reserveKey, nullptr, state));
CWalletTx& wtx = AddTxToChain(tx.GetHash());
for (size_t n = 0; n < wtx.tx->vout.size(); ++n) {
if (nChangePosRet != -1 && n == nChangePosRet) {
// Skip the change output to only return the requested coins
continue;
}
tallyItem.vecOutPoints.push_back(COutPoint(wtx.GetHash(), n));
tallyItem.nAmount += wtx.tx->vout[n].nValue;
}
}
assert(tallyItem.vecOutPoints.size() == vecAmounts.size());
destKey.KeepKey();
return tallyItem;
}
};

BOOST_FIXTURE_TEST_CASE(CTransactionBuilderTest, CTransactionBuilderTestSetup)
{
// NOTE: Mock wallet version is FEATURE_BASE which means that it uses uncompressed pubkeys
// (65 bytes instead of 33 bytes), so CTxIn size is 180 bytes, not 148 bytes as one might expect.
// Each output is 34 bytes, vin and vout compact sizes are 1 byte each.
// Therefore base size (i.e. for a tx with 1 input, 0 outputs) is expected to be
// 4(n32bitVersion) + 1(vin size) + 180(vin[0]) + 1(vout size) + 4(nLockTime) = 190 bytes.

minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
// Tests with single outpoint tallyItem
{
CompactTallyItem tallyItem = GetTallyItem({5000});
CTransactionBuilder txBuilder(wallet.get(), tallyItem);

BOOST_CHECK_EQUAL(txBuilder.CountOutputs(), 0);
BOOST_CHECK_EQUAL(txBuilder.GetAmountInitial(), tallyItem.nAmount);
BOOST_CHECK_EQUAL(txBuilder.GetAmountLeft(), 4810); // 5000 - 190

BOOST_CHECK(txBuilder.CouldAddOutput(4776)); // 4810 - 34
BOOST_CHECK(!txBuilder.CouldAddOutput(4777));

BOOST_CHECK(txBuilder.CouldAddOutput(0));
BOOST_CHECK(!txBuilder.CouldAddOutput(-1));

BOOST_CHECK(txBuilder.CouldAddOutputs({1000, 1000, 2708})); // (4810 - 34 * 3) split in 3 outputs
BOOST_CHECK(!txBuilder.CouldAddOutputs({1000, 1000, 2709}));

BOOST_CHECK_EQUAL(txBuilder.AddOutput(5000), nullptr);
BOOST_CHECK_EQUAL(txBuilder.AddOutput(-1), nullptr);

CTransactionBuilderOutput* output = txBuilder.AddOutput();
BOOST_CHECK(output->UpdateAmount(txBuilder.GetAmountLeft()));
BOOST_CHECK(output->UpdateAmount(1));
BOOST_CHECK(output->UpdateAmount(output->GetAmount() + txBuilder.GetAmountLeft()));
BOOST_CHECK(!output->UpdateAmount(output->GetAmount() + 1));
BOOST_CHECK(!output->UpdateAmount(0));
BOOST_CHECK(!output->UpdateAmount(-1));
BOOST_CHECK_EQUAL(txBuilder.CountOutputs(), 1);

std::string strResult;
BOOST_CHECK(txBuilder.Commit(strResult));
CWalletTx& wtx = AddTxToChain(uint256S(strResult));
BOOST_CHECK_EQUAL(wtx.tx->vout.size(), txBuilder.CountOutputs()); // should have no change output
BOOST_CHECK_EQUAL(wtx.tx->vout[0].nValue, output->GetAmount());
BOOST_CHECK(wtx.tx->vout[0].scriptPubKey == output->GetScript());
}
// Tests with multiple outpoint tallyItem
{
CompactTallyItem tallyItem = GetTallyItem({10000, 20000, 30000, 40000, 50000});
CTransactionBuilder txBuilder(wallet.get(), tallyItem);
std::vector<CTransactionBuilderOutput*> vecOutputs;
std::string strResult;

auto output = txBuilder.AddOutput(100);
BOOST_CHECK(output != nullptr);
BOOST_CHECK(!txBuilder.Commit(strResult));

if (output != nullptr) {
output->UpdateAmount(1000);
vecOutputs.push_back(output);
}
while (vecOutputs.size() < 100) {
output = txBuilder.AddOutput(1000 + vecOutputs.size());
if (output == nullptr) {
break;
}
vecOutputs.push_back(output);
}
BOOST_CHECK_EQUAL(vecOutputs.size(), 100);
BOOST_CHECK_EQUAL(txBuilder.CountOutputs(), vecOutputs.size());
BOOST_CHECK(txBuilder.Commit(strResult));
CWalletTx& wtx = AddTxToChain(uint256S(strResult));
BOOST_CHECK_EQUAL(wtx.tx->vout.size(), txBuilder.CountOutputs() + 1); // should have change output
for (const auto& out : wtx.tx->vout) {
auto it = std::find_if(vecOutputs.begin(), vecOutputs.end(), [&](CTransactionBuilderOutput* output) -> bool {
return output->GetAmount() == out.nValue && output->GetScript() == out.scriptPubKey;
});
if (it != vecOutputs.end()) {
vecOutputs.erase(it);
} else {
// change output
BOOST_CHECK_EQUAL(txBuilder.GetAmountLeft() - 34, out.nValue);
}
}
BOOST_CHECK(vecOutputs.size() == 0);
}
}

BOOST_AUTO_TEST_SUITE_END()